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?
Related
I made a function that executes a command from BASH, and i want to make it run in background and never stop the execution of the main program.
I could use screen -AmdS screen_thread123 php script.php but the main ideea is that i learn and understand how threads work.
I have a basic knowledge about this, but right now i want to create a quick dynamic thread like the example of bellow :
public static void exec_command_background(String command) throws IOException, InterruptedException
{
List<String> listCommands = new ArrayList<String>();
String[] arrayExplodedCommands = command.split(" ");
// it should work also with listCommands.addAll(Arrays.asList(arrayExplodedCommands));
for(String element : arrayExplodedCommands)
{
listCommands.add(element);
}
new Thread(new Runnable(){
public void run()
{
try
{
ProcessBuilder ps = new ProcessBuilder(listCommands);
ps.redirectErrorStream(true);
Process p = ps.start();
p.waitFor();
}
catch (IOException e)
{
}
finally
{
}
}
}).start();
}
and it gives me this error
NologinScanner.java:206: error: local variable listCommands is accessed from within inner class; needs to be declared final
ProcessBuilder ps = new ProcessBuilder(listCommands);
1 error
Why is that and how can i solve it? I mean how can i access the variable listCommands from this block?
new Thread(new Runnable(){
public void run()
{
try
{
// code here
}
catch (IOException e)
{
}
finally
{
}
}
}).start();
}
Thanks.
You don't need that inner class (and you don't want to waitFor)... just use
for(String element : arrayExplodedCommands)
{
listCommands.add(element);
}
ProcessBuilder ps = new ProcessBuilder(listCommands);
ps.redirectErrorStream(true);
Process p = ps.start();
// That's it.
As for your question of accessing the variable listCommands in your original block; make the reference final - like so
final List<String> listCommands = new ArrayList<String>();
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.
The goal is to have a simple Java class start a jar main-class. When the main-class finishes, it can be queried if it would like to be reloaded. By this method it can hot-update itself and re-run itself.
The Launcher is to load the jar via a URLClassloader and then unload/re-load the changed jar. The jar can be changed by a modification to Launcher, or by a provided dos/unix script that is called to swap the new jar into place of the old jar.
The entire program is below. Tested and it seems to work without a hitch.
java Launcher -jar [path_to_jar] -run [yourbatchfile] [-runonce]? optional]
1) Launcher looks for the "Main-Class" attribute in your jarfile so that it does not need to be giventhe actual class to run.
2) It will call 'public static void main(String[] args)' and pass it the remaining arguments that you provided on the command line
3) When your program finishes, the Launcher will call your programs method 'public static boolean reload()' and if the result is 'true' this will trigger a reload.
4) If you specified -runonce, then the program will never reload.
5) If you specified -run [batchfile] then the batchfile will run before reloading.
I hope this is helpful to some people.
Happy coding!
import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
public class Launcher {
public static void main(String[] args) {
new Launcher().run(new ArrayList<>(Arrays.asList(args)));
}
private void run(List<String> list) {
final String jar = removeArgPairOrNull("-jar", list);
final boolean runonce = removeArgSingle("-runonce", list);
final String batchfile = removeArgPairOrNull("-run", list);
if (jar == null) {
System.out.println("Please add -jar [jarfile]");
System.out.println("All other arguments will be passed to the jar main class.");
System.out.println("To prevent reloading, add the argument to -runonce");
System.out.println("To provide another program that runs before a reload, add -run [file]");
}
boolean reload;
do {
reload = launch(list.toArray(new String[0]), new String(jar), new String(batchfile), new Boolean(runonce));
System.out.println("Launcher: reload is: " + reload);
gc();
if (reload && batchfile != null) {
try {
System.err.println("Launcher: will attempt to reload jar: " + jar);
runBatchFile(batchfile);
} catch (IOException | InterruptedException ex) {
ex.printStackTrace(System.err);
System.err.println("Launcher: reload batchfile had exception:" + ex);
reload = false;
}
}
} while (reload);
}
private boolean launch(String[] args, String jar, String batchfile, boolean runonce) {
Class<?> clazz = null;
URLClassLoader urlClassLoader = null;
boolean reload = false;
try {
urlClassLoader = new URLClassLoader(new URL[]{new File(jar).toURI().toURL()});
String mainClass = findMainClass(urlClassLoader);
clazz = Class.forName(mainClass, true, urlClassLoader);
Method main = clazz.getMethod("main", String[].class);
System.err.println("Launcher: have method: " + main);
Method reloadMethod;
if (runonce) {
// invoke main method using reflection.
main.invoke(null, (Object) args);
} else {
// find main and reload methods and invoke using reflection.
reloadMethod = clazz.getMethod("reload");
main.invoke(null, (Object) args);
System.err.println("Launcher: invoked: " + main);
reload = (Boolean) reloadMethod.invoke(null, new Object[0]);
}
} catch (final Exception ex) {
ex.printStackTrace(System.err);
System.err.println("Launcher: can not launch and reload this class:" + ex);
System.err.println("> " + clazz);
reload = false;
} finally {
if (urlClassLoader != null) {
try {
urlClassLoader.close();
} catch (IOException ex) {
ex.printStackTrace(System.err);
System.err.println("Launcher: error closing classloader: " + ex);
}
}
}
return reload ? true : false;
}
private static String findMainClass(URLClassLoader urlClassLoader) throws IOException {
URL url = urlClassLoader.findResource("META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(url.openStream());
Attributes attr = manifest.getMainAttributes();
return attr.getValue("Main-Class");
}
private static void runBatchFile(String batchfile) throws IOException, InterruptedException {
System.out.println("Launcher: executng batchfile: " + batchfile);
ProcessBuilder pb = new ProcessBuilder("cmd", "/C", batchfile);
pb.redirectErrorStream(true);
pb.redirectInput(Redirect.INHERIT);
pb.redirectOutput(Redirect.INHERIT);
Process p = pb.start();
p.waitFor();
}
private static String removeArgPairOrNull(String arg, List<String> list) {
if (list.contains(arg)) {
int index = list.indexOf(arg);
list.remove(index);
return list.remove(index);
}
return null;
}
private static boolean removeArgSingle(String arg, List<String> list) {
if (list.contains(arg)) {
list.remove(list.indexOf(arg));
return true;
}
return false;
}
private void gc() {
for (int i = 0; i < 10; i++) {
byte[] bytes = new byte[1024];
Arrays.fill(bytes, (byte) 1);
bytes = null;
System.gc();
System.runFinalization();
}
}
}
After debugging, I determined the original difficulty was that the Launcher was not isolating the UrlClassloader.
By putting the part of the program that starts the classloader in a seperate method, I was able to allow all the system to determine that no more references existed.
After reworking the code, it all works now :)
I have made a preference page whose program code is --
public class SAML
extends FieldEditorPreferencePage
implements IWorkbenchPreferencePage {
public SAML() {
super(GRID);
setPreferenceStore(RmpPlugin.getDefault().getPreferenceStore());
setDescription("Browse Appropriate files");
}
public FileFieldEditor f;
public FileFieldEditor f1;
public void createFieldEditors() {
f = new FileFieldEditor(PreferenceConstants.P_PATH,
"&Prism.bat File:", getFieldEditorParent());
addField(f);
f1 = new FileFieldEditor(PreferenceConstants.P_PATH1,
"&NuSMV Application File:", getFieldEditorParent());
addField(f1);
}
public void init(IWorkbench workbench) {
}
}
In this preference page, there are two FileFieldEditor which is use to select "prism.bat" and "NuSMV.exe" file.
I have accessed path in my another button programming whose code is ---
try {
IPreferenceStore store = plugin.getPreferenceStore();
ProcessBuilder pb=new ProcessBuilder(store.getString(PreferenceConstants.P_PATH));
pb.directory(new File(store.getString(PreferenceConstants.P_PATH)));
Process p=pb.start();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String in;
while((in = input.readLine()) != null) {
out.println(in);
}
int exitVal=p.waitFor();
if(exitVal==0)
{
out.println("Printing on console");
}
else
out.println("Process failed");
}
catch (Exception e)
{
out.println(e.toString());
e.printStackTrace();
}
Whenever I am clicking the button after selecting Prism.bat file from preference page, it says that file not found.
What am I missing?
Debug your code and see what this computes to
store.getString(PreferenceConstants.P_PATH));
pb.directory(new File(store.getString(PreferenceConstants.P_PATH))
excute it from the command line and see what is missing. My guess is that there is a space character in your path which is breaking it. Quotify the path may fix it. But see the string and decide accordingly.
I need a Java way to find a running Win process from which I know to name of the executable. I want to look whether it is running right now and I need a way to kill the process if I found it.
private static final String TASKLIST = "tasklist";
private static final String KILL = "taskkill /F /IM ";
public static boolean isProcessRunning(String serviceName) throws Exception {
Process p = Runtime.getRuntime().exec(TASKLIST);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
if (line.contains(serviceName)) {
return true;
}
}
return false;
}
public static void killProcess(String serviceName) throws Exception {
Runtime.getRuntime().exec(KILL + serviceName);
}
EXAMPLE:
public static void main(String args[]) throws Exception {
String processName = "WINWORD.EXE";
//System.out.print(isProcessRunning(processName));
if (isProcessRunning(processName)) {
killProcess(processName);
}
}
You can use command line windows tools tasklist and taskkill and call them from Java using Runtime.exec().
Here's a groovy way of doing it:
final Process jpsProcess = "cmd /c jps".execute()
final BufferedReader reader = new BufferedReader(new InputStreamReader(jpsProcess.getInputStream()));
def jarFileName = "FileName.jar"
def processId = null
reader.eachLine {
if (it.contains(jarFileName)) {
def args = it.split(" ")
if (processId != null) {
throw new IllegalStateException("Multiple processes found executing ${jarFileName} ids: ${processId} and ${args[0]}")
} else {
processId = args[0]
}
}
}
if (processId != null) {
def killCommand = "cmd /c TASKKILL /F /PID ${processId}"
def killProcess = killCommand.execute()
def stdout = new StringBuilder()
def stderr = new StringBuilder()
killProcess.consumeProcessOutput(stdout, stderr)
println(killCommand)
def errorOutput = stderr.toString()
if (!errorOutput.empty) {
println(errorOutput)
}
def stdOutput = stdout.toString()
if (!stdOutput.empty) {
println(stdOutput)
}
killProcess.waitFor()
} else {
System.err.println("Could not find process for jar ${jarFileName}")
}
There is a little API providing the desired functionality:
https://github.com/kohsuke/winp
Windows Process Library
You could use a command line tool for killing processes like SysInternals PsKill and SysInternals PsList.
You could also use the build-in tasklist.exe and taskkill.exe, but those are only available on Windows XP Professional and later (not in the Home Edition).
Use java.lang.Runtime.exec to execute the program.
Use the following class to kill a Windows process (if it is running). I'm using the force command line argument /F to make sure that the process specified by the /IM argument will be terminated.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class WindowsProcess
{
private String processName;
public WindowsProcess(String processName)
{
this.processName = processName;
}
public void kill() throws Exception
{
if (isRunning())
{
getRuntime().exec("taskkill /F /IM " + processName);
}
}
private boolean isRunning() throws Exception
{
Process listTasksProcess = getRuntime().exec("tasklist");
BufferedReader tasksListReader = new BufferedReader(
new InputStreamReader(listTasksProcess.getInputStream()));
String tasksLine;
while ((tasksLine = tasksListReader.readLine()) != null)
{
if (tasksLine.contains(processName))
{
return true;
}
}
return false;
}
private Runtime getRuntime()
{
return Runtime.getRuntime();
}
}
You will have to call some native code, since IMHO there is no library that does it. Since JNI is cumbersome and hard you might try to use JNA (Java Native Access). https://jna.dev.java.net/
small change in answer written by Super kakes
private static final String KILL = "taskkill /IMF ";
Changed to ..
private static final String KILL = "taskkill /IM ";
/IMF option doesnot work .it does not kill notepad..while /IM option actually works