Run jar from within a Java application - java

I am trying to build a simple auto updater for my application. I am currently checking the local application version against my remote version. If there is a newer version I want to start my updater.jar - which basically downloads and replaces the old application.
My problem is that I cannot seem to get the updater.jar to start if there is a new version.
The code I am currently using is:
Runtime runtime = Runtime.getRuntime();
try {
Process proc = runtime.exec("java -jar updater.jar");
} catch (IOException ex) {
Logger.getLogger(Splash.class.getName()).log(Level.SEVERE, null, ex);
}
System.exit(0);
The application exits but updater.jar is never launched..
Any ideas?

Your child process is likely exiting when your parent process exits.
When you launch a process you should usually:
consume stdout/stderr from the child process. If you don't do this your child process can block waiting for its output to be consumed. You should consume in separate threads. See this answer for more details
use Process.waitFor() to capture the exit code from the child process
It looks to me like you want to spawn the updater, let it perform a download and then exit your parent process. Anything more complex would likely be platform dependent (e.g. spawning a background process and disowning it)

Maybe the path to updater.jar should be specified in the java -jar command.

You better use the URLClassLoader since jre1.2
see How to load a jar file at runtime

Related

Gradle console() is null with daemon disabled

I'm using Gradle 2.13 with Java 1.8.0_121.
One of our Gradle tasks relies on user input.
def user = System.console().readLine('Please enter new user username: ')
However I get the following error: > Cannot invoke method readLine() on null object
So console() must be null... okay. I found this related issue suggesting to disable the daemon.
I did that and ran it with ./gradlew configureTask --no-daemon but got the same result... same error. I'm pretty sure it's not using the daemon as I get the following message: To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/2.13/userguide/gradle_daemon.html.
So if the Gradle daemon is not causing this issue, what else could be? Does anyone more experienced with Gradle know?
Gradle says it needs to run the build in a subprocess because of something in your build settings:
To honour the JVM settings for this build a new JVM will be forked.
And I guess Gradle creates that subprocess in a way that allows it to grab the output (which is the default with the APIs Java offers to spawn subprocesses). As a result, the subprocess does not have access to I/O of your terminal, and System.console() is null within that process: it is not attached to the system console.
It got me curious so I came up with a script that demonstrates the issue (using Groovy for its conciseness, it's the same thing as Java here):
import java.io.Console
println "Console for main JVM: " + System.console()
Process p1 = new ProcessBuilder("groovy", "-e", "print System.console()")
.redirectErrorStream(true)
.start()
p1.waitFor()
println "Console for child JVM: " + p1.text
Process p2 = new ProcessBuilder("groovy", "-e", "println 'Console for child JVM with inherited IO: ' + System.console()")
.redirectErrorStream(true)
.inheritIO() // <- this changes everything, as now p2 is attached to system console
.start()
p2.waitFor()
// No need to (actually cannot) get output of p2, as I/O is inherited by p2 it gets printed to terminal directly
Result:
Console for main JVM: java.io.Console#64cd705f
Console for child JVM: null
Console for child JVM with inherited IO: java.io.Console#3c130745
So Gradle is probably building the subprocess like p1 in my example. And I guess it needs to, because it needs to inspect the output (and not let it go directly to system output).
I think your only solutions are:
find a way to get Gradle do the build in the main JVM, without forking. Not a Gradle expert so I don't know how but the message seems to imply it's possible.
find another way to get user input. Maybe a Swing dialog? (not very elegant but hey, a build that takes user input is not very elegant in the first place, so the way it is collected does not matter much at this point)

Starting a Java application at startup

I have a Java application.
The application has a setting that decides whether or not the application starts at startup.
Currently, I have it this by placing/removing a shortcut in the StartUp items folder.
However, I am wondering if there is a better way to handle this behaviour.
EDIT
Yes, it's Windows. Sorry for not clearing that before.
The application has an UI where the user may trigger actions, also the application runs a few tasks in the background periodically while running.
#Peter, how could I change the registry with code from within the application? Is that approach compatible with all versions of Windows?
Below is a small example snippet of how it can be done from inside your application
static final String REG_ADD_CMD = "cmd /c reg add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\" /v \"{0}\" /d \"{1}\" /t REG_EXPAND_SZ";
private void exec(String[] args) throws Exception
{
if (args.length != 2)
throw new IllegalArgumentException("\n\nUsage: java SetEnv {key} {value}\n\n");
String key = args[0];
String value = args[1];
String cmdLine = MessageFormat.format(REG_ADD_CMD, new Object[] { key, value });
Runtime.getRuntime().exec(cmdLine);
}
I'm pretty sure this will work with all versions of Windows since they all use the same Startup\Run registry entry.
Hope that helps! :)
Credit
On Windows I have used open source Java Service Wrapper to make our application as window service which you can setup automatic at startup.
What you need to do is to download latest wrapper.exe and create wrapper.config file put all the configuration like Main class any VM arument other parameters in defined standards and create a window service by this exe
Use the Registry to start your program at the startup and then it will be shown in the list provided by msconfig commnd through Run.
Use this registry path
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

How does jstack -F affect a running Java process?

I am trying to diagnose a problem where a Java web application I'm using (Jenkins) becomes unresponsive. If I run jstack without the -F flag it doesn't give me anything, but if I put the flag in to force a thread dump not only do I get a result, but the application starts responding and goes on as if nothing had happened until it eventually stops responding again.
What does jstack -F flag do that would affect a running JVM and cause an unresponsive application to start responding again?
You can see the source to jstack here. The -F argument changes how jstack connects to the jvm. With -F (or -m) JStack connects to the jvm using the java debugger interface. If a pid is specified, JStack connects with the SA PID Attaching Connector which says,
The process to be debugged need not have been started in debug
mode(ie, with -agentlib:jdwp or -Xrunjdwp). It is permissable for the
process to be hung.
I don't know why it would cause an unresponsive application to start responding again, but the link above also says,
The process is suspended when this connector attaches and resumed when
this connector detaches.
This may have an effect.
jstack -F -l pid is similarly to (assume working dir is JAVA_HOME)
bin/java -Dsun.jvm.hotspot.debugger.useWindbgDebugger -Dsun.jvm.hotspot.debugger.useProcDebugger -cp lib/sa-jdi.jar;lib/tools.jar sun.tools.jstack.JStack -F -l pid
and in the sun.tools.jstack.JStack code
if (arg.equals("-F")) {
useSA = true;
}
.....
// now execute using the SA JStack tool or the built-in thread dumper
if (useSA) {
// parameters (<pid> or <exe> <core>
...
runJStackTool(mixed, locks, params);
} else {
// pass -l to thread dump operation to get extra lock info
String pid = args[optionCount];
...
runThreadDump(pid, params);
}
and since -F is passed in, runJStackTool is called to load sun.jvm.hotspot.tools.JStack, it have same effect of invoking directly
bin\java -Dsun.jvm.hotspot.debugger.useWindbgDebugger -Dsun.jvm.hotspot.debugger.useProcDebugger -cp lib/sa-jdi.jar;lib/tools.jar sun.jvm.hotspot.tools.JStack pid
and sun.jvm.hotspot.tools.JStack will call sun.jvm.hotspot.bugspot.BugSpotAgent attach -> go ->setupVM method
Maybe below code is the magic
jvmdi = new ServiceabilityAgentJVMDIModule(debugger, saLibNames);
if (jvmdi.canAttach()) {
jvmdi.attach();
jvmdi.setCommandTimeout(6000);
debugPrintln("Attached to Serviceability Agent's JVMDI module.");
// Jog VM to suspended point with JVMDI module
resume();
suspendJava();
suspend();
debugPrintln("Suspended all Java threads.");
}
it will suspend all Java threads in the target process. if your application is hang for thread starvation, the suspend method call may relax them.

Java - get PID of external process by command line in Windows 7

I have Windows 7 32 bit with Java:
How do I get the PID of a process by command line in Windows 7?
I want to kill an application which I only can truly identify by the command line which ran it. We have several Java applications running on that machine. I need to stop specific ones.
To be exact: I need to find tomcat which is run by catalina.bat. What do you think is the best way to do this?
I know of tasklist, but it does not seem to be able to query the command line which started the process. Finding java.exe does not help me. I tried searching for something useful like pgrep/pkill for Windows, with no success.
You could use jps -lv command to determine java process by it's command line options. jps is utility that included in many up-to-date JDKs.
Try in a command prompt:
sc queryex type= service state= all | find "APP"
Where APP is the name of the program. This command will return all services that match that.
Then you can run SC QUERYEX APP and it will return the state and PID number.
Once you have the PID:
TASKKILL /f /pid ###
Where ### is the actual PID
Java, get the PID of the current running process in Windows
This should work on Linux, OSX, Windows, and HotSpot JVM's.
import sun.management.VMManagement;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public static int getCurrentPID() {
try{
java.lang.management.RuntimeMXBean runtime =
java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt =
(sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method =
mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
return (Integer) pid_method.invoke(mgmt);
}
catch(Exception e){
e.printStackTrace();
System.out.println("Failed at getting the process ID");
System.exit(0);
}
}
Invoke it like this:
System.out.println("PID: " + getCurrentPID());
For me it prints the processID: PID: 5728
Sources:
How can a Java program get its own process ID?
http://boxysystems.com/index.php/java-tip-find-process-id-of-running-java-process/
If you just need to kill a specific tomcat from a java application why not coding a simple servlet running inside each tomcat that will responde to a get request with a string that will identify it. Then use another servlet to execute something like:
System.exit(-1);
Finally found something. The solution working for me is called wmic (Windows Management Instrumentation Commandline). This nice tool comes built-in to Windows 7 Pro (mine) and possibly other Windows versions. It provides a quite large variety of actions like listing all the running tasks with every details you can imagine (like their corresponding command line), various hardware info, etc. Exactly what I need.

java executing bash script, error=26 Text file busy

I've got a java code that is writing a Linux bash script out, then doing a chmod to add execute permission, then trying to execute it. I'm getting an IOException during the start of the process saying error=26, Text file busy. I've verified that the file is finished being written and the stream was closed. The chmod works fine, but I keep getting this error.
I've noticed that if I run a debugger and step through the code, it doesn't get the error, so clearly there is a timing issue involved. How can I make sure the chmod is done before I try to execute the bash script? I'd like to avoid non-reliable solutions like adding Thread.sleep(10000), and "hacky" things like putting the execution in a try/catch block inside a loop that tries until it succeeds.
I have a fair amount of code wrapping the startup of the process with listening threads, etc., but here is a simplified version of what it is doing (tried this code also and it has same result):
String[] cmd1 = {"/bin/chmod", "750", postFile };
new ProcessBuilder(cmd1).redirectErrorStream(true).start().waitFor();
String[] cmd2 = { postFile };
new ProcessBuilder(cmd2).redirectErrorStream(true).start().waitFor();
Every time after execution, the "postFile" has the correct 750 permissions, but it has not executed (due to the IOException).
For future reference, it may have been caused by an unclosed stream in this particular case, but setting permissions on a file immediately followed by running the file can cause this error too:
java.io.IOException: Cannot run program "...": error=26, Text file busy
It is a probable bug in JDK. In my case, it was caused by this snippet of code
Files.setPosixFilePermissions(Paths.get(scriptPath), set(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ));
ProcessBuilder processBuilder = new ProcessBuilder(scriptPath).directory(workingDir);
processBuilder.start();
even if nothing was editing the script file.
Are you sure it is the chmod that is responsible for the subsequent error? Could you check that you definitely close the output file before you try to run it?
If you do close it then I'm at a loss why chmod should cause that error, but you could avoid the need to run chmod by using your shell to run the script:
String[] cmd = {"bash", postfile };
I don't know if it's related but usually you need to get or redirect the ErrorStream and the InputStream (I usually get them in a ResponseStreamReader that I create, don't know about the redirecting choice).
In my service file in /etc/systemd/system/ I have directed outputs to log files:
StandardOutput=file:/home/pi/ApplicationLogs/application_l_debug.log
StandardError=file:/home/pi/ApplicationLogs/application_l_error.log
The error message disappeared when I changed permissions on the ApplicationLogs directory to write permissions to all
(chmod a+w ApplicationLogs)

Categories

Resources