Is this possible without knowledge of the name of the main class? In particular I am after the Implementation Version property that I want sent in an info email on application startup. The only ways I know to access this require knowing the package or the name of the jar file whose information I want to access; I do not know how to do it simply by looking for the main class.
The comments from #Lee Meador point at good hints for finding your Manifest. You don't need to know the jar name, you just need to be able to identify your manifest. Suggestion: use any combination of the other IMPLEMENTATION_* attributes to identify the correct manifest. If you're already setting IMPLEMENTATION_VERSION, why not also set IMPLEMENTATION_NAME so you can find it. Once you've got it, you can look through the entries to find the one you want. The Manifest APImakes it even easier:
Attributes mainAtts = mf.getMainAttributes();
if(mainAtts.containsKey(Attributes.Name.MAIN_CLASS)){
String mainClass = mainAtts.getValue(Attributes.Name.MAIN_CLASS);
System.out.println(mainClass);
String mainVer = mainAtts.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
System.out.println(mainVer);
}
So you want to know about the main class but don't want to know what it's name is?
In order to find what you're looking for, you need to:
Identify the main class (sorry, this will be a requirement)
Find out where that class was loaded
If it's a JAR file, load the manifest from that JAR file
Step 1 might be difficult: the thread that launched the main class might have finished -- for instance, many GUI programs will execute a Class.main(String[]) method and then terminate while the AWT thread keeps the process alive. You might not be able to identify the main class at all. If that's the case, you are probably out of luck. You could try the sun.java.command system property, but that is probably only valid on Sun/Oracle JVMs.
Step 2 is fairly easy: get the Class's ClassLoader, then ask for the URL of the .class file itself.
Step 3 is also fairly easy: open the JAR file and look for the META-INF/MANIFEST.MF file.
Related
I've mostly only created application for personal use and the rare occasions where I have distributed my code have been in the form of uploading my source code on GitHub. I'm currently finishing up a project and plan on using launch4j to package it up as an exe. However, my application has a handful of png files that I coded in with the unique filepath of my computer. Obviously if my code were to run on any other computer in the world, those files would not be found.
I'm vaguely aware that java does not require the full filepath for a file (ie C:\Users...\file_name.ext) but I've never gotten a program to run correctly unless I write out the filepath like that, so that's been my default up until this point.
The resource system. Think about it: What's the difference between the many class files that comprise your application, and those png files, from an application distribution perspective?
The answer is, essentially, nothing. They are file-like concepts, they might prefer to be shipped in a packaged-up file (a jar file) instead of separately. They must be found at some point halfway through your app's existence (java does not pre-load all classes. It just loads your main class, and then loads whatever is needed the first time you mention any class).
You don't have to hardcode the absolute path to those class files in your app, so they clearly don't suffer from this 'coding filepaths' issue.
Thus, the answer is somewhat obvious: Simply stick those PNG files in the exact same place as your class files, and ask the VM to provide you with the data in them using whatever mechanism it is using itself, as it is doing that exact same job (find resource, obtain data in the resource) all the time, on your class files.
But, how?
You have 2 different methods, and these 2 methods take the same kind of argument, which comes in 2 forms: A grand total of 4 'modes' to choose from.
Pick a method
If the API you have that needs an image file so happens to have an overload that accepts a URL, this is very simple (ImageIcon is one such resource, that's probably what you're passing these PNG files to, so that's great):
URL loadIcon = ContextClass.class.getResource("/icons/load.png");
new ImageIcon(loadIcon);
Quite simple. Sometimes you want to read it yourself directly, and a URL object is rather unwieldy. Sometimes, you want to pass it to an API which does not have a URL overload, but it does have an InputStream overload. Then, you can fetch an InputStream. Given that this is a resource, like all resources, you must safely close it, thus, let's use try-with:
byte[] pngData;
try (var in = ContextClass.class.getResourceAsStream("/icons/load.png")) {
pngData = in.readAllBytes();
}
ContextClass.class is a somewhat exotic java syntax feature: It is an expression that resolves to the java.lang.Class instance of the so-named class. For example, Class<?> c = String.class; is legal java and gives you the class object that represents the class concept of all java.lang.String objects. The class object itself has these getResource methods. Thus, substitute some relevant class that you wrote as context here. Presumably, if you want to load an image in source file MyStatusWindow.java, you'd just use that class: MyStatusWindow.class.getResource.
These methods will look in the same location that the class itself was loaded from. If ContextClass is loaded from a jar, then the system will fetch PNGs from within that jar. If it's loaded from a build dir during development/debug, the png is loaded from there. If you've got some fancypants module system that is loading classes straight from the network, then the PNG will also be loaded from there.
resourceKey
A resourcekey is simply a path. It's not really a path, just - a string with slashes. You can't use .., for example, it's not really a path. You also, weirdly, can't use filenames that include more than a single dot in the name, for historic (read: silly) reasons.
You have 2 variants - classpackage relative and absolute.
.getResource("/icons/load.png") is absolute. .getResource("icons/load.png") is relative. The leading slash is the difference.
If you have:
package com.foo;
public class MyStatusWindow {
...
MyStatusWindow.class.getResource("icons/load.png");
}
And this is all in a jar file (i.e. /com/foo/MyStatusWindow.class is one of the entries listed if you execute jar tvf myapp.jar on the command line), then the above would look in that jar for /com/foo/icons/load.png - the relative form takes the context-class's package and sticks it in front. The absolute form would just look in /icons/load.png, still in the jar (so it's never C:\ - never the root of your disk - it's the root of the classpath entry).
Build systems
Maven, Gradle, and just about every other build system has a proscribed directory structure. The above example should go in src/main/java/com/foo/MyStatusWindow.java, relative to some 'root project dir'. Only java source files are supposed to go there. There's also a resources: src/main/resources/com/foo/icons/load.png, that's where your icon file would go. Then MyStatusWindow.getResource("icons/load.png") will just work, in your build system, and in your IDE, and when you ship it all as a jar file. If it doesn't, you've misconfigured your IDE or have a broken build configuration - and you should fix that. Out of the box, this just works.
Scenario
I am working on a big application consist of hundred of class files. As part of giving a patch to fix the issue in production set up, we generally give a jar file. This jar file contains the class file(s) which includes a fix and put inside one patch path(This is the path which is used to put the patches/fixes as jar file). In the production set up, we recommend to restart the application. As part of restart , application first looks the class files from patch path and then from the other path where all the class files are present. So this way first classes from patch path is loaded.
Expectation
Now instead of restarting the application, i want to reload the class file so that restart of application can be avoided. I checked in net and found the following:
We can use customize/dynamic class loader as well. I think this is also not full proof way of doing it.
URLClassLoader is also one of the option.
If we use customize class loader, then we have to load all the application class files using this because class file can refer to another class file if it is loaded from the same class loader. This is what i read from net. If yes, then i think this option is not valid. right ?
After checking all the above options, i still feel the above options are not good enough to load the existing class file (which is getting used in running application as well) at run time.
So question arises that is there any full proof possibily to replace the class file at run time for java application.
if Yes, Could you please guide in this regard.
Also, if this kind of requirement or question is already answered properly(I didn't find any good link), please redirect
NOTE: I think OSGi is also one option. But i feel not possible in such a big application since it will requires lot of changes which i want to avoid.
Please suggest.
Is it possible that we keep our jar/war file running and one class file in the same directory which can be referred from jar/war? The requirement is I want to keep some common code in single file which can be referred by multiple running jars/wars and I don't have to re-deploy application if I can successfully just change the class file.
I would go for this question here: How should I load Jars dynamically at runtime?
And them try the answers from jodonnell, chris and allain-lalonde depending on how do you want to access these class files, if by URL or by file path, and if you want to use JAR files or CLASS files.
In any case you should try and see it for yourself if it fits your needs and is acceptable by your employer standards. At certain point, we need to think out of the box. Don't worry if one or other user dislikes this or that approach. This community if meant for ask and receive answers on how to do it, and not to not do it.
I'm making a program that needs to be able to let Clients change a setting, and using what I'm calling a "Builder", create a .jar that replaces some constants in a class with their settings.
In other words, I have a GUI that has a few textfields so that when they press the JButton labeled Build, it creates a new Runnable Jar that in a Constants class whose settings are changed with what was in the textfields.
Is this possible? I've heard about ANT Scripts, but I'm not really sure if that's what I'm looking for here.
Thanks
have you considered using a .properties files or something similar instead? You can use ant scripts for what you are describing (check out http://ant.apache.org/manual/Tasks/replaceregexp.html, you could use this task in your build.xml to dynamically change the .java files but it seems a little kludgy) but it might not be the best solution.
Check this page: http://www.mkyong.com/java/java-properties-file-examples/ which has some detail about saving to/loading from a properties file. You could set up your constants class to load it's state variables from this file, and set up the Build JButton to create that properties file.
I'm trying to think of a use case where you would want to modify the class source itself rather than use a properties file, but to be honest I can't. So I suppose you may have some special circumstance where this is not a tenable solution for you, but 99% of the time this is how I would suggest you go about it.
Is it possible to get the path to my .class file containing my main function from within main?
URL main = Main.class.getResource("Main.class");
if (!"file".equalsIgnoreCase(main.getProtocol()))
throw new IllegalStateException("Main class is not stored in a file.");
File path = new File(main.getPath());
Note that most class files are assembled into JAR files so this won't work in every case (hence the IllegalStateException). However, you can locate the JAR that contains the class with this technique, and you can get the content of the class file by substituting a call to getResourceAsStream() in place of getResource(), and that will work whether the class is on the file system or in a JAR.
According to http://www.cs.caltech.edu/courses/cs11/material/java/donnie/java-main.html, no. However, I suggest reading $0 (Program Name) in Java? Discover main class? , which at least gives you the main class .
What do you need it for? If you need it to get hold of any files that are in the same directory, that's what Class.getResourceAsStream() is for.
That looks more like an end-user issue to me. Also consider the possible need to run multiple instances of any given application, and preventing users from doing so is going to become a major annoyance.
If the problem is with temporary file conflicts, then just make sure all your temporary files have unique names. This, as I understand it, is the most common reason people feel a need to prevent multiple instances of their applications from running.
P.S.: The java.io.File.createTempFile() methods are ideally suited for preventing temporary file conflicts because they automatically generate unique filenames.