Executing JAR file within another java application - java

I'm trying to run a External Jar file, without actually inserting it into my jar itself. Because the jar file needs to be located in the same folder as the main jar file.
So, Within the main jar file I want to execute the other executable jar file, And I need to be able to know when the jar file is ended AND when you close the main jar file, the jar file that is started within the jar file needs to be closed to,
I currently do that by using this code:
public void LaunchLatestBuild()
{
try {
String path = new File(".").getCanonicalPath() +
"\\externaljar.jar";
List commands = new ArrayList();
commands.add(getJreExecutable().toString());
commands.add("-Xmx" + this.btd_serverram + "M");
commands.add("-Xms" + this.btd_serverram + "M");
commands.add("-jar");
commands.add(path);
int returnint = launch(commands); //It just waits and stops the tread here. And my Runtime.getRuntime().addShutdownHook doesn't get triggerd.
if (returnint != 201) //201 is a custom exit code I 'use' to know when the app needs a restart or not.
{
System.out.println("No restart needed! Closing...");
System.exit(1);
}
else
{
CloseCraftBukkit();
Launcher.main(new String[] { "" });
}
}
catch (Exception e)
{
System.out.println(e.toString());
e.printStackTrace();
}
}
public int launch(List<String> cmdarray) throws IOException, InterruptedException
{
byte[] buffer = new byte[1024];
ProcessBuilder processBuilder = new ProcessBuilder(cmdarray);
processBuilder.redirectErrorStream(true);
this.CBProcess = processBuilder.start();
InputStream in = this.CBProcess.getInputStream();
while (true) {
int r = in.read(buffer);
if (r <= 0) {
break;
}
System.out.write(buffer, 0, r);
}
return this.CBProcess.exitValue();
}
Limitations of this code:
Doesn't close my externaljar.jar java
process on exit of the main
application.
Cannot redirect input if main console, to external jar.
That are the most Important things I need.
I hope someone can tell me how I should do this.
Current source code is available at:
http://code.google.com/p/bukkit-to-date/

Why can't you just set your classpath so that it includes the second jar, and then you can simply use it as a library ? You can even invoke the MainClass.main() method manually, if you really want that to be executed, but from within the same VM and without spawning a separate process.
EDIT: If you don't know the name of the jar file when your application is launched, but you'll only figure that out at runtime, in order to invoke it, create a URLClassLoader provided with the path to your jar file and then:
URLClassLoader urlClassLoader = new URLClassLoader(
new File("/path/to/your/jar/file.jar").toURI().toURL() );
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// switch to your custom CL
Thread.currentThread().setContextClassLoader(urlClassLoader);
// do your stuff with the other jar
// ....................
// now switch back to the original CL
Thread.currentThread().setContextClassLoader(cl);
Or simply grab a reference to a class in that other jar and make use of reflection:
Class<?> c = urlClassLoader.loadClass("org.ogher.packag.ClassFromExternalJar");

Related

Run outer jar and this should execute inner jar

I have a main class in 'outer.jar' file. Inside this jar, there is another jar called 'inner.jar' in classes/lib folder. How can I make the inner jar run when I run the outer jar using the command java -jar outer.jar?
My main class is running another jar called 'inner.jar' and consuming the output. Now my application is also packaged as a jar called 'outer.jar' and when I run this 'outer.jar', then the main class is unable to run the inner.jar
outer jar main class code:
public class OuterJarMain{
public static void main(String[] args) {
String path = OuterJarMain.class.getProtectionDomain().getCodeSource().getLocation().getPath();
ProcessBuilder pb = new ProcessBuilder("java", "-jar", path.substring(1)+"lib/inner.jar", "1");
try {
Process p = pb.start();
} catch (IOException e) {
e.printStackTrace();
}
The code works fine when the main class is executed from IDE because the inner.jar is available in target/classes/lib folder. But when I run the outer.jar from command line then the path is displayed as /C:/....../outer.jar!/.../classes!/ and the inner.jar is not executed
Since files inside jar is archived, you can't access them the same way as normal files.
What you can do is copy the entry inside the jar out to a folder, as demonstrated in this post, and then execute the jar with java -jar , afterwards if you don't need the jar you can call File.deleteOnExit()
EDIT: I modify the code in that post and the example is shown below.
The method returns the absolute location of the exported inner jar.
public static String exportInnerJar(){
String jarDir = "";
try {
//Get the parent directory of the outer jar.
jarDir = URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(),"UTF-8");
} catch (UnsupportedEncodingException e) {
System.err.println("Fail to find the outer jar location");
e.printStackTrace();
}
//The location of the inner jar
//The example means the innar jar is at outer.jar/example/inner.jar where "example" is a folder under the root of the outer jar.
InputStream is = ClassName.class.getResourceAsStream("/example/inner.jar");
//The location to be exported to, you can change this to wherever you want
File exportInnerJar = new File(jarDir).toPath().resolve("inner.jar").toFile();
//Create the file
exportInnerJar.createNewFile();
//Use FileOutputStream to write to file
FileOutputStream fos = new FileOutputStream(exportInnerJar);
//setup a buffer
int readBytes;
byte[] buffer = new byte[4096];
//export
while ((readBytes = is.read(buffer)) > 0) {
fos.write(buffer, 0, readBytes);
}
// close streams
is.close();
fos.close();
return exportInnerJar.getAbsolutePath();
}

File.createNewFile() failing in java (Ubuntu 12.04)

I am trying to createNewFile() in java.I have written down the following example.I have compiled it but am getting a run time error.
import java.io.File;
import java.io.IOException;
public class CreateFileExample
{
public static void main(String [] args)
{
try
{
File file = new File("home/karthik/newfile.txt");
if(file.createNewFile())
{
System.out.println("created new fle");
}else
{
System.out.println("could not create a new file");
}
}catch(IOException e )
{
e.printStackTrace();
}
}
}
It is compiling OK.The run time error that I am getting is
java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:947)
at CreateFileExample.main(CreateFileExample.java:16)
some points here
1- as Victor said you are missing the leading slash
2- if your file is created, then every time you invoke this method "File.createNewFile()" will return false
3- your class is very platform dependent (one of the main reasons why Java is powerful programming language is that it is a NON-PLATFORM dependent), instead you can detect a relative location throw using the System.getProperties() :
// get System properties :
java.util.Properties properties = System.getProperties();
// to print all the keys in the properties map <for testing>
properties.list(System.out);
// get Operating System home directory
String home = properties.get("user.home").toString();
// get Operating System separator
String separator = properties.get("file.separator").toString();
// your directory name
String directoryName = "karthik";
// your file name
String fileName = "newfile.txt";
// create your directory Object (wont harm if it is already there ...
// just an additional object on the heap that will cost you some bytes
File dir = new File(home+separator+directoryName);
// create a new directory, will do nothing if directory exists
dir.mkdir();
// create your file Object
File file = new File(dir,fileName);
// the rest of your code
try {
if (file.createNewFile()) {
System.out.println("created new fle");
} else {
System.out.println("could not create a new file");
}
} catch (IOException e) {
e.printStackTrace();
}
this way you will create your file in any home directory on any platform, this worked for my windows operating system, and is expected to work for your Linux or Ubuntu as well
You're missing the leading slash in the file path.
Try this:
File file = new File("/home/karthik/newfile.txt");
That should work!
Actually this error comes when there is no directory "karthik" as in above example and createNewFile() is only to create file not for directory use mkdir() for directory and then createNewFile() for file.

Java, cannot delete file on Windows

I have a simple updater for my application. In code i am downloading a new version, deleting old version and renaming new version to old.
It works fine on Linux. But doesn't work on Windows. There are no excepions or something else.
p.s. RemotePlayer.jar it is currently runned application.
UPDATED:
Doesn't work - it means that after file.delete() and file.renameTo(...) file still alive.
I use sun java 7. (because I use JavaFX).
p.s. Sorry for my English.
public void checkUpdate(){
new Thread(new Runnable() {
#Override
public void run() {
System.err.println("Start of checking for update.");
StringBuilder url = new StringBuilder();
url.append(NetworkManager.SERVER_URL).append("/torock/getlastversionsize");
File curJarFile = null;
File newJarFile = null;
try {
curJarFile = new File(new File(".").getCanonicalPath() + "/Player/RemotePlayer.jar");
newJarFile = new File(new File(".").getCanonicalPath() + "/Player/RemotePlayerTemp.jar");
if (newJarFile.exists()){
newJarFile.delete();
}
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
System.err.println("Cannot find curr Jar file");
return;
}
if (curJarFile.exists()){
setAccesToFile(curJarFile);
try {
String resp = NetworkManager.makeGetRequest(url.toString());
JSONObject jsresp = new JSONObject(resp);
if (jsresp.getString("st").equals("ok")){
if (jsresp.getInt("size") != curJarFile.length()){
System.out.println("New version available, downloading started.");
StringBuilder downloadURL = new StringBuilder();
downloadURL.append(NetworkManager.SERVER_URL).append("/torock/getlatestversion");
if (NetworkManager.downLoadFile(downloadURL.toString(), newJarFile)){
if (jsresp.getString("md5").equals(Tools.md5File(newJarFile))){
setAccesToFile(newJarFile);
System.err.println("Deleting old version. File = " + curJarFile.getCanonicalPath());
boolean b = false;
if (curJarFile.canWrite() && curJarFile.canRead()){
curJarFile.delete();
}else System.err.println("Cannot delete cur file, doesn't have permission");
System.err.println("Installing new version. new File = " + newJarFile.getCanonicalPath());
if (curJarFile.canWrite() && curJarFile.canRead()){
newJarFile.renameTo(curJarFile);
b = true;
}else System.err.println("Cannot rename new file, doesn't have permission");
System.err.println("last version has been installed. new File = " + newJarFile.getCanonicalPath());
if (b){
Platform.runLater(new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null, String.format("Внимание, %s", "Установлена новая версия, перезапустите приложение" + "", "Внимание", JOptionPane.ERROR_MESSAGE));
}
});
}
}else System.err.println("Downloading file failed, md5 doesn't match.");
}
} else System.err.println("You use latest version of application");
}
}catch (Exception e){
e.printStackTrace();
System.err.println("Cannot check new version.");
}
}else {
System.err.println("Current jar file not found");
}
}
}).start();
}
private void setAccesToFile(File f){
f.setReadable(true, false);
f.setExecutable(true, false);
f.setWritable(true, false);
}
I found the solution to this problem. The problem of deletion occurred in my case because-:
File f1=new File("temp.txt");
RandomAccessFile raf=new RandomAccessFile(f1,"rw");
f1.delete();//The file will not get deleted because raf is open on the file to be deleted
But if I close RandomAccessFile before calling delete then I am able to delete the file.
File f1=new File("temp.txt");
RandomAccessFile raf=new RandomAccessFile(f1,"rw");
raf.close();
f1.delete();//Now the file will get deleted
So we must check before calling delete weather any object such as FileInputStream, RandomAccessFile is open on that file or not. If yes then we must close that object before calling delete on that file.
windows locks files that are currently in use. you cannot delete them. on windows, you cannot delete a jar file which your application is currently using.
Since you are using Java 7, try java.nio.file.Files.delete(file.toPath()), it'll throw exception if deletion fails.
There are several reasons:
Whether you have permissions to edit the file in windows.
The file is in use or not.
The path is right or not.
I don't know wich version of Java you are using.
I know when Java was sun property they publish that the Object File can't delete files correctly on windows plateform (sorry I don't find the reference no more).
The tricks you can do is to test the plateform directly. When you are on linux just use the classic File object.
On windows launch a command system to ask windows to delete the file you want.
Runtime.getRuntime().exec(String command);
I just want to make one comment. I learned that you can delete files in Java from eclipse if you run eclipse program as Administrator. I.e. when you right click on the IDE Icon (Eclipse or any other IDE) and select Run as Administrator, Windows lets you delete the file.
I hope this helps. It helped me.
Cordially,
Fernando

How can I access a folder inside of a resource folder from inside my jar File?

I have a resources folder/package in the root of my project, I "don't" want to load a certain File. If I wanted to load a certain File, I would use class.getResourceAsStream and I would be fine!! What I actually want to do is to load a "Folder" within the resources folder, loop on the Files inside that Folder and get a Stream to each file and read in the content... Assume that the File names are not determined before runtime... What should I do? Is there a way to get a list of the files inside a Folder in your jar File?
Notice that the Jar file with the resources is the same jar file from which the code is being run...
Finally, I found the solution:
final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(path + "/")) { //filter according to the path
System.out.println(name);
}
}
jar.close();
} else { // Run with IDE
final URL url = Launcher.class.getResource("/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
}
} catch (URISyntaxException ex) {
// never happens
}
}
}
The second block just work when you run the application on IDE (not with jar file), You can remove it if you don't like that.
Try the following.
Make the resource path "<PathRelativeToThisClassFile>/<ResourceDirectory>" E.g. if your class path is com.abc.package.MyClass and your resoure files are within src/com/abc/package/resources/:
URL url = MyClass.class.getResource("resources/");
if (url == null) {
// error - missing folder
} else {
File dir = new File(url.toURI());
for (File nextFile : dir.listFiles()) {
// Do something with nextFile
}
}
You can also use
URL url = MyClass.class.getResource("/com/abc/package/resources/");
The following code returns the wanted "folder" as Path regardless of if it is inside a jar or not.
private Path getFolderPath() throws URISyntaxException, IOException {
URI uri = getClass().getClassLoader().getResource("folder").toURI();
if ("jar".equals(uri.getScheme())) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
return fileSystem.getPath("path/to/folder/inside/jar");
} else {
return Paths.get(uri);
}
}
Requires java 7+.
I know this is many years ago . But just for other people come across this topic.
What you could do is to use getResourceAsStream() method with the directory path, and the input Stream will have all the files name from that dir. After that you can concat the dir path with each file name and call getResourceAsStream for each file in a loop.
I had the same problem at hands while i was attempting to load some hadoop configurations from resources packed in the jar... on both the IDE and on jar (release version).
I found java.nio.file.DirectoryStream to work the best to iterate over directory contents over both local filesystem and jar.
String fooFolder = "/foo/folder";
....
ClassLoader classLoader = foofClass.class.getClassLoader();
try {
uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
throw new FooException(e.getMessage());
} catch (NullPointerException e){
throw new FooException(e.getMessage());
}
if(uri == null){
throw new FooException("something is wrong directory or files missing");
}
/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
/** jar case */
try{
URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
//jar.toString() begins with file:
//i want to trim it out...
Path jarFile = Paths.get(jar.toString().substring("file:".length()));
FileSystem fs = FileSystems.newFileSystem(jarFile, null);
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
for(Path p: directoryStream){
InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
performFooOverInputStream(is);
/** your logic here **/
}
}catch(IOException e) {
throw new FooException(e.getMessage());
}
}
else{
/** IDE case */
Path path = Paths.get(uri);
try {
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
for(Path p : directoryStream){
InputStream is = new FileInputStream(p.toFile());
performFooOverInputStream(is);
}
} catch (IOException _e) {
throw new FooException(_e.getMessage());
}
}
Another solution, you can do it using ResourceLoader like this:
import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;
#Autowire
private ResourceLoader resourceLoader;
...
Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
load(fi.next())
}
If you are using Spring you can use org.springframework.core.io.support.PathMatchingResourcePatternResolver and deal with Resource objects rather than files. This works when running inside and outside of a Jar file.
PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver();
Resource[] resources = r.getResources("/myfolder/*");
Then you can access the data using getInputStream and the filename from getFilename.
Note that it will still fail if you try to use the getFile while running from a Jar.
As the other answers point out, once the resources are inside a jar file, things get really ugly. In our case, this solution:
https://stackoverflow.com/a/13227570/516188
works very well in the tests (since when the tests are run the code is not packed in a jar file), but doesn't work when the app actually runs normally. So what I've done is... I hardcode the list of the files in the app, but I have a test which reads the actual list from disk (can do it since that works in tests) and fails if the actual list doesn't match with the list the app returns.
That way I have simple code in my app (no tricks), and I'm sure I didn't forget to add a new entry in the list thanks to the test.
Below code gets .yaml files from a custom resource directory.
ClassLoader classLoader = this.getClass().getClassLoader();
URI uri = classLoader.getResource(directoryPath).toURI();
if("jar".equalsIgnoreCase(uri.getScheme())){
Pattern pattern = Pattern.compile("^.+" +"/classes/" + directoryPath + "/.+.yaml$");
log.debug("pattern {} ", pattern.pattern());
ApplicationHome home = new ApplicationHome(SomeApplication.class);
JarFile file = new JarFile(home.getSource());
Enumeration<JarEntry> jarEntries = file.entries() ;
while(jarEntries.hasMoreElements()){
JarEntry entry = jarEntries.nextElement();
Matcher matcher = pattern.matcher(entry.getName());
if(matcher.find()){
InputStream in =
file.getInputStream(entry);
//work on the stream
}
}
}else{
//When Spring boot application executed through Non-Jar strategy like through IDE or as a War.
String path = uri.getPath();
File[] files = new File(path).listFiles();
for(File file: files){
if(file != null){
try {
InputStream is = new FileInputStream(file);
//work on stream
} catch (Exception e) {
log.error("Exception while parsing file yaml file {} : {} " , file.getAbsolutePath(), e.getMessage());
}
}else{
log.warn("File Object is null while parsing yaml file");
}
}
}
Took me 2-3 days to get this working, in order to have the same url that work for both Jar or in local, the url (or path) needs to be a relative path from the repository root.
..meaning, the location of your file or folder from your src folder.
could be "/main/resources/your-folder/" or "/client/notes/somefile.md"
Whatever it is, in order for your JAR file to find it, the url must be a relative path from the repository root.
it must be "src/main/resources/your-folder/" or "src/client/notes/somefile.md"
Now you get the drill, and luckily for Intellij Idea users, you can get the correct path with a right-click on the folder or file -> copy Path/Reference.. -> Path From Repository Root (this is it)
Last, paste it and do your thing.
Simple ... use OSGi. In OSGi you can iterate over your Bundle's entries with findEntries and findPaths.
Inside my jar file I had a folder called Upload, this folder had three other text files inside it and I needed to have an exactly the same folder and files outside of the jar file, I used the code below:
URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);
URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);
URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

How to reference files in a Java Pacakage

I have MyClassin package X, Also in package X there are packages Y and Z like this:
X - MyClass
X - Y - Some Files
X - Z - Some Files
How do I get a list of all the files in packages Y and Z from MyClass?
Java packages mirror directory structure. You can use the File class. In particular, see the listFiles() method.
EDIT
You can dynamically find your executing location. Here is code from a project I've recently worked on; I wanted to be able to find the directory I'm running the JAR from (if I'm running the JAR), or else the directory of the JAR if I'm running from the class files. In my case, my JAR is in <project root>/bin and my classes are in <project root>/classes.
final URL location;
final String classLocation = JavaPlanner.class.getName().replace('.', '/') + ".class";
final ClassLoader loader = JavaPlanner.class.getClassLoader();
if(loader == null)
{
try { throw new ClassNotFoundException("class loaded with bootstrap loader"); }
catch (ClassNotFoundException cnfe) { throw new InitializationException(cnfe); }
}
else
{
location = loader.getResource(classLocation);
}
if(location.toString().startsWith("file:/")) // Line 14
{
// Running from .class file
String path;
try { path = URLDecoder.decode(location.toString().substring(6), "UTF-8"); }
catch(UnsupportedEncodingException uee) { throw new InitializationException(uee); }
// Move up package folders to root, add /bin/
File package_ = new File(path).getParentFile();
binPath = package_.getParentFile().getParentFile().getParentFile().getParentFile() + File.separator + "bin" + File.separator;
}
else // Line 25
{
// Running from .jar file
String jarURL = JavaPlanner.class.getResource("/" + JavaPlanner.class.getName().replaceAll("\\.", "/") + ".class").toString();
jarURL = jarURL.substring(4).replaceFirst("/[^/]+\\.jar!.*$", "/");
try
{
File dir = new File(new URL(jarURL).toURI());
jarURL = dir.getAbsolutePath();
}
catch(MalformedURLException mue) { throw new InitializationException(mue); }
catch(URISyntaxException use) { throw new InitializationException(use); }
binPath = jarURL;
}
At line 14, I've found that I'm running the application from a class file. String path initially is set to the file path of JavaPlanner (the class containing my main method). I know the package structure JavaPlanner is in, so I use getParentFile an appropriate number of times to find the project root, and then append bin/.
At line 25, I've found that I'm running the application from a JAR. The block simply gets the path to the folder containing that executable JAR.
Obviously, this code is not 100% adapted to your purpose (most specifically, I'm calling getParentFile a specific number of times for my package structure), but I think it should help.
The entire purpose of the above code was to be able to find the correct resource files for my application. In the production version, only the JAR would be available to the user, but I didn't want to have to rebuild the JAR every time I needed to test some code, and I didn't want to duplicate my resource files, and I didn't want to pollute my bin/ folder with the class files (because everything in bin/ was meant to be sent to the user).
It depends on the source of the class. If they are from a JAR file, then you can use the JarFile class to get a list of entries.
If you're only looking for classes in the package, consider
Package.getPackage(String name)
Package.getPackages()
http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/lang/Package.html#getPackage%28java.lang.String%29
http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/lang/Package.html#getPackages%28%29

Categories

Resources