How can I introduce automatic updates and restart feature in Java Swing applications.
Also I needed to roll back to previous versions.
I have made a application jar file and launcher jar file, which launches the application jar file.
This attempt was successful. But I can not integrate these two jar files, when creating a installer for MacOSX.
The Launcher class as follows:
public class Launcher {
private final Logger logger = Logger.getLogger(Launcher.class.getName());
private String getVersionNumber() throws IOException {
try {
JarFile runningJarFile = new JarFile(new File("Application.jar"));
String versionNumber = runningJarFile.getManifest()
.getMainAttributes().getValue("Bundle-Version");
runningJarFile.close();
logger.log(
Level.SEVERE,
new StringBuilder()
.append("The version number of existing Application.jar file is ")
.append(versionNumber).toString());
return versionNumber;
} catch (IOException e) {
logger.log(
Level.SEVERE,
new StringBuilder()
.append("Could not read the version number from existing Application.jar")
.append(Arrays.toString(e.getStackTrace()))
.toString());
throw new IOException(e);
}
}
private void updateApplication() {
try {
File updateDirectory = new File("Update");
if (updateDirectory.isDirectory()) {
if (updateDirectory.list(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.startsWith("\\.");
}
}).length > 0) {
String versionNumber = getVersionNumber();
logger.log(
Level.SEVERE,
new StringBuilder()
.append("A new update is available. Rename the existing Application.jar to ")
.append(versionNumber)
.append(".jar")
.append(" and rename the new_Application.jar to Application.jar")
.toString());
File Application = new File("Application.jar");
Application.renameTo(new File(new StringBuilder()
.append(versionNumber).append(".jar").toString()));
File newApplication = new File("Update/new_Application.jar");
newApplication.renameTo(new File("Application.jar"));
newApplication.delete();
}
}
} catch (Exception e) {
logger.log(Level.SEVERE,
new StringBuilder().append("Could not update Application")
.append(Arrays.toString(e.getStackTrace()))
.toString());
}
}
private void launchApplication() {
try {
logger.log(Level.SEVERE, "Lauching Application.jar");
ProcessBuilder pb = new ProcessBuilder("Java", "-jar", "Application.jar");
pb.start();
} catch (IOException e) {
logger.log(Level.SEVERE,
new StringBuilder().append("Could not launch Application.jar ")
.append(Arrays.toString(e.getStackTrace()))
.toString());
}
}
private void quitLauncher() {
logger.log(Level.SEVERE, "Launcher is exiting");
System.exit(0);
}
private void startApplication() {
updateApplication();
launchApplication();
quitLauncher();
}
public static void main(String[] args) {
Launcher launcher = new Launcher();
launcher.startApplication();
}
}
Thanks
First of all, are you creating a standard installer package (mpkg or pkg)?
You should make the launcher download the update, and then execute it (with /usr/sbin/installer ). After that, the installer should have a postflight/postinstall/postprocessing script that kills any app with the name of yours (this can be as tricky as looking for a process with a given name..), and then launches the recently installed app.
This makes that your app will be launched even, after the first install (you may avoid this making it check if it is an update or not -saving a marker file from the Launcher "updating.txt" may suffice- )
Here is a full guide of how to create installer packages (almost form scratch): Making OS X Installer Packages like a Pro - Xcode Developer ID ready pkg
Or well, you may use this cool tool: http://s.sudre.free.fr/Software/Packages/about.html
I expect this all is not an overkill for what are you looking for.
Related
I'm working on a text-based RPG with some friends using Netbeans. It works all fine and dandy in Netbeans but when I export it to a .jar file I get this error.
Jan 28, 2019 2:27:15 PM Operator.DragonsHead startActionPerformed
SEVERE: null
java.io.FileNotFoundException: File "src\Operator\files\Opening.mid" does not exist!
This happens when the game starts, as we have a "theme" that plays at boot up.
The song plays on Netbeans but not when exported.
I'm relatively new to Java programming, I took a course on it last year.
I've tried looking around the web for people having the same issue, but I can't quite get it to duplicate with my code.
Here's the midi class:
import javax.sound.midi.*;
import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
public class MIDI {
private File file = null;
private Sequencer sequencer = null;
public MIDI (String midiFile) throws FileNotFoundException {
this.file = new File(midiFile);
if (!file.isFile()) {
throw new FileNotFoundException("File \"" + midiFile + "\" does not exist!");
}
try{
sequencer = MidiSystem.getSequencer();
if (sequencer == null){
System.err.println("Error: Sequencer not supported");
return;
}
sequencer.open();
Sequence sequence = MidiSystem.getSequence(file);
sequencer.setSequence(sequence);
}
catch (MidiUnavailableException | InvalidMidiDataException | IOException ex){
}
}
public void play(){
sequencer.start();
}
public void stop() {
sequencer.stop();
}
public void waitAndStop(int millis) {
Runnable song = () -> {
try {
Thread.sleep(millis);
}
catch (InterruptedException e) {
System.err.println("MIDI playback interrupted");
}
stop();
};
Thread t = new Thread(song);
t.start();
}
public long songLengthMicroseconds() {
return sequencer.getMicrosecondLength();
}
public Sequence getSequence(String resource) {
try {
return MidiSystem.getSequence(new File(resource));
}
catch (InvalidMidiDataException | IOException ex) {
return null;
}
}
}
Here's the lines that initialize it and call the song to play:
MIDI midiTest;
midiTest = new MIDI("src\\Operator\\files\\Opening.mid");
midiTest.play();
I'm not sure what the API is of 'MIDI', but unless you want to go through the rigamarole of writing an installer, you cannot use direct file access for resources like icons, pictures, music, and datafiles.
Instead, use the getResource/getResourceAsStream mechanism, which returns URLs/InputStreams. Well written libraries take these just as well as files.
Basic format:
try (InputStream resource = MyClassName.class.getResourceAsStream("Opening.mid")) {
// do something with resource here.
}
where Opening.mid is in the exact same place that MyClassName.class is (so, if you are shipping as a jar, it's in the jar, in the same folder structure as myClassName.class. If you prefer to have a root dir 'music' in your jar, you can pass for example: /music/Opening.mid, with the leading slash to indicate you're going off of the jar root.
secondary observation, if you don't know what to do with an exception, best solution is to add the exception(s) you cannot handle to your method's 'throws' line. If that is somehow not possible, the proper body for a catch block is:
throw new RuntimeException("unhandled checked exception", e);
because right now if an error occurs, your code will silently just keep going. If that was your intent (because, hey, music is optional I guess), I'd still log it SOMEWHERE, right now if an error occurs, you just won't know about it.
I want to delete some files from a specific folder when I start tomcat from eclipse. Is there any way to do such a thing and NOT manually go there to delete the files? In visual studio you have the ability to do that.
I have 3 options:
You can make a script that delete these files and then start your Eclipse. Use that function to delete the file:
rm -rf [path_to_the_file]/your_file
After, you can use use whatever method you want to run Eclipse from the script.
Note: You can replace your_file by * to target all the files in the folder.
You can use the same command in the script which is launched when you launch your server
If there is a Java class related to your action, you can add a delete method in it which be executed before launching your server.
Add this code before launching your server:
final File file_to_delete = new File("[path_to_the_file]/your_file");
if (file_to_delete.exists()) {
try {
delete(file_to_delete);
} catch (IOException e) {
throw new IOException(e);
}
}
Add the following method in your class:
public static void delete(File file) throws IOException {
if (file.isDirectory()) {
if (file.list().length == 0) {
file.delete();
} else {
final String[] files = file.list();
for (String temp : files) {
final File fileDelete = new File(file, temp);
delete(fileDelete);
}
if (file.list().length == 0) {
file.delete();
}
}
} else {
file.delete();
}
}
Alright, i'm trying to Xbootclasspath a jar from within my project. Currently I have to load my application through command-line with the follow command:
java -Xbootclasspath/p:canvas.jar -jar application.jar
This works perfectly fine but I want to do this without having to enter command line, is there I way I can Xbootclasspath from within the jar?
Thanks.
The most clear solution is to have two main classes.
Your first class, named Boot or similar, will be the outside entry point into the application, as set in the jar's manifest. This class will form the necessary runtime command to start your actual main class (named Application or similar), with the Xboot parameter.
public class Boot {
public static void main(String[] args) {
String location = Boot.class.getProtectionDomain().getCodeSource().getLocation().getPath();
location = URLDecoder.decode(location, "UTF-8").replaceAll("\\\\", "/");
String app = Application.class.getCanonicalName();
String flags = "-Xbootclasspath/p:canvas.jar";
boolean windows = System.getProperty("os.name").contains("Win");
StringBuilder command = new StringBuilder(64);
if (windows) {
command.append("javaw");
} else {
command.append("java");
}
command.append(' ').append(flags).append(' ');
command.append('"').append(location).append('"');
// append any necessary external libraries here
for (String arg : args) {
command.append(' ').append('"').append(arg).append('"');
}
Process application = null;
Runtime runtime = Runtime.getRuntime();
if (windows) {
application = runtime.exec(command.toString());
} else {
application = runtime.exec(new String[]{ "/bin/sh", "-c", command.toString() });
}
// wire command line output to Boot to output it correctly
BufferedReader strerr = new BufferedReader(new InputStreamReader(application.getErrorStream()));
BufferedReader strin = new BufferedReader(new InputStreamReader(application.getInputStream()));
while (isRunning(application)) {
String err = null;
while ((err = strerr.readLine()) != null) {
System.err.println(err);
}
String in = null;
while ((in = strin.readLine()) != null) {
System.out.println(in);
}
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {
}
}
}
private static boolean isRunning(Process process) {
try {
process.exitValue();
} catch (IllegalThreadStateException e) {
return true;
}
return false;
}
}
And your Application class runs your actual program:
public class Application {
public static void main(String[] args) {
// display user-interface, etc
}
}
Feels yucky, but could you do a Runtime.exec that calls to java with the provided options and a new parameter (along with some conditional code that looks for that) to prevent a recursive loop of spawning new processes?
I am currently working on a project about calculations.I have done the main part of my project,Also integrated SVN Commit function to my code (using .ini file to read the specific address etc. )
I can easily Commit the files, what I am trying is I want to implement the real-time log to my console. Is there any way to implement the log to the console ? Not the general log but the commit log which should be real time.
I am using eclipse for mac, I've heard about SVNKit but I am really poor about SVN.
Thanks in advance for any information
--- EDIT ---
This is the code for reading the svn commands from .ini file
public static String iniSVNOkut(String deger, String getObje, String fetchObje){
Ini uzantilariAlIni = null;
try
{
String uzantiAyarlari = "Uzantilar.ini";
try
{
uzantilariAlIni = new Ini(new FileReader(uzantiAyarlari));
}
catch (InvalidFileFormatException e)
{
System.err.print("Hata InvalidFileFormat : " + e.getMessage() + "\n" );
e.printStackTrace();
}
catch(FileNotFoundException e)
{
System.err.print("Hata FileNotFoundException : " + e.getMessage() + "\n" );
e.printStackTrace();
}
catch (IOException e)
{
System.err.print("Hata IOException : " + e.getMessage() + "\n" );
e.printStackTrace();
}
return deger = uzantilariAlIni.get(getObje).fetch(fetchObje);
}
finally
{
}
}
This is what .ini includes
[svnAdresi]
svnAdresiniAl = svn co http://svn.svnkit.com/repos/svnkit/trunk/ /Users/sample/Documents/workspace/SatirHesaplaGUI/svnTestMAC
This is how I call it
String svnAdresi;
svnAdresi = IniFonksiyon.iniSVNOkut(svnAdresi, "svnAdresi", "svnAdresiniAl");
Runtime cmdCalistir = Runtime.getRuntime();
try
{
Process islem = cmdCalistir.exec(svnAdresi);
}
catch (Exception e)
{
e.printStackTrace();
}
If I understand your question correctly, you want to read the Subversion commit log into your console application.
The easiest way is to use SVNKit.
Here's how I did it.
private static List<SVNLogEntry> logEntryList;
/*
* Gets the Subversion log records for the directory
*/
LogHandler handler = new LogHandler();
String[] paths = { directory };
try {
repository.log(paths, latestRevision, 1L, false, true, handler);
} catch (SVNException svne) {
if (svne.getMessage().contains("not found")) {
logEntryList = new ArrayList<SVNLogEntry>();
} else {
CobolSupportLog.logError(
"Error while fetching the repository history: "
+ svne.getMessage(), svne);
return false;
}
}
logEntryList = handler.getLogEntries();
directory - string pointing to a particular directory or module
latestRevision - largest revision number from Subversion. Placing the latestRevision second in the log method invocation returns the log records in most recent order.
If you want the log records in sequential order, from 1 to latestRevision, then the 1L would be placed second, and the latestRevision would be placed third.
repository - Subversion repository that you've already authenticated.
Here's LogHandler.
public class LogHandler implements ISVNLogEntryHandler {
protected static final int REVISION_LIMIT = 5;
protected List<SVNLogEntry> logEntryList;
public LogHandler() {
logEntryList = new ArrayList<SVNLogEntry>();
}
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
logEntryList.add(logEntry);
}
public List<SVNLogEntry> getLogEntries() {
if (logEntryList.size() <= REVISION_LIMIT) {
return logEntryList;
} else {
return logEntryList.subList(0, REVISION_LIMIT);
}
}
}
I'm trying to find a way to detect when a flash drive has been plugged into my computer. So far, the solution I found was to poll FileSystem#getFileStores for changes. This does indeed tell me when the flash drive has been inserted, but as far as I can tell there is no way to retrieve the location for it. FileStore#type and FileStore#name both seem highly unreliable as their return value is implementation specific, but they appear to be the only methods that might return any relevant information that might help find the directory for the FileStore.
With that in mind, the following code:
public class Test {
public static void main(String[] args) throws IOException {
for (FileStore store : FileSystems.getDefault().getFileStores()) {
System.out.println(store);
System.out.println("\t" + store.name());
System.out.println("\t" + store.type());
System.out.println();
}
}
}
Gave me this output:
/ (/dev/sda5)
/dev/sda5
ext4
/* snip */
/media/TI103426W0D (/dev/sda2)
/dev/sda2
fuseblk
/media/flashdrive (/dev/sdb1)
/dev/sdb1
vfat
As it turns out, FileStore#type returns the format of the drive and FileStore#name returns the location of the device file for the drive. As far as I can tell, the only method which has the location of the drive is the toString method, but extracting the path name out of it seems dangerous because I'm not sure how well that particular solution would hold up on other operating systems and future versions of Java.
Is there something I'm missing here or is this simply not possible purely with Java?
System Information:
$ java -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.1pre) (7~u3-2.1.1~pre1-1ubuntu2)
OpenJDK Client VM (build 22.0-b10, mixed mode, sharing)
$ uname -a
Linux jeffrey-pc 3.2.0-24-generic-pae #37-Ubuntu SMP Wed Apr 25 10:47:59 UTC 2012 i686 athlon i386 GNU/Linux
Here's a temporary work around until a better solution is found:
public Path getRootPath(FileStore fs) throws IOException {
Path media = Paths.get("/media");
if (media.isAbsolute() && Files.exists(media)) { // Linux
try (DirectoryStream<Path> stream = Files.newDirectoryStream(media)) {
for (Path p : stream) {
if (Files.getFileStore(p).equals(fs)) {
return p;
}
}
}
} else { // Windows
IOException ex = null;
for (Path p : FileSystems.getDefault().getRootDirectories()) {
try {
if (Files.getFileStore(p).equals(fs)) {
return p;
}
} catch (IOException e) {
ex = e;
}
}
if (ex != null) {
throw ex;
}
}
return null;
}
As far as I know, this solution will only work for Windows and Linux systems.
You have to catch the IOException in the Windows loop because if there is no CD in the CD drive an exception is thrown when you try to retrieve the FileStore for it. This might happen before you iterate over every root.
This is what I have ended up doing. This is limited to Windows + UNIX but avoids using external tools or additional library calls. It steals the information Java already has in the FileStore objects
LinuxFileStore definitely extends UnixFileStore, so it will work. Same deal for Solaris. Since Mac OS X is UNIX, it probably works there but I'm not sure because I couldn't see its subclass in any place I was looking.
public class FileStoreHacks {
/**
* Stores the known hacks.
*/
private static final Map<Class<? extends FileStore>, Hacks> hacksMap;
static {
ImmutableMap.Builder<Class<? extends FileStore>, Hacks> builder =
ImmutableMap.builder();
try {
Class<? extends FileStore> fileStoreClass =
Class.forName("sun.nio.fs.WindowsFileStore")
.asSubclass(FileStore.class);
builder.put(fileStoreClass, new WindowsFileStoreHacks(fileStoreClass));
} catch (ClassNotFoundException e) {
// Probably not running on Windows.
}
try {
Class<? extends FileStore> fileStoreClass =
Class.forName("sun.nio.fs.UnixFileStore")
.asSubclass(FileStore.class);
builder.put(fileStoreClass, new UnixFileStoreHacks(fileStoreClass));
} catch (ClassNotFoundException e) {
// Probably not running on UNIX.
}
hacksMap = builder.build();
}
private FileStoreHacks() {
}
/**
* Gets the path from a file store. For some reason, NIO2 only has a method
* to go in the other direction.
*
* #param store the file store.
* #return the path.
*/
public static Path getPath(FileStore store) {
Hacks hacks = hacksMap.get(store.getClass());
if (hacks == null) {
return null;
} else {
return hacks.getPath(store);
}
}
private static interface Hacks {
Path getPath(FileStore store);
}
private static class WindowsFileStoreHacks implements Hacks {
private final Field field;
public WindowsFileStoreHacks(Class<?> fileStoreClass) {
try {
field = fileStoreClass.getDeclaredField("root");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new IllegalStateException("file field not found", e);
}
}
#Override
public Path getPath(FileStore store) {
try {
String root = (String) field.get(store);
return FileSystems.getDefault().getPath(root);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Denied access", e);
}
}
}
private static class UnixFileStoreHacks implements Hacks {
private final Field field;
private UnixFileStoreHacks(Class<?> fileStoreClass) {
try {
field = fileStoreClass.getDeclaredField("file");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new IllegalStateException("file field not found", e);
}
}
#Override
public Path getPath(FileStore store) {
try {
return (Path) field.get(store);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Denied access", e);
}
}
}
}
I've not really explored this area of java, but I found this, which seems to be related. It uses File.listRoots()
There also seems to be a number of related questions linked there too.
This works for Windows:
public Path getFileStoreRootPath(FileStore fs) throws Exception {
for (Path root : FileSystems.getDefault().getRootDirectories()) {
if (Files.isDirectory(root) && Files.getFileStore(root).equals(fs)) {
return root;
}
}
throw new RuntimeException("Root directory for filestore " + fs + " not found");
}
Basically, by filtering by condition Files.isDirectory(root) we are excluding all CD/DVD drives which will throw IOException when compact-disc is not inserted.