All our jars contain a certain file version.properties which contains specific information from the build.
When we start a jar from command line (with several jars on the class path), we would like access the version.properties from the same jar. To be more precise: We would like to write Java code that gives us the content of the properties file in the jar where the calling class file resides.
The problem is that all jars on the class path contain version.properties and we do not want to read the first on the class path but the one from the correct jar. How can we achieve that?
Funny problem. You have to find the location of a representative class of the particular jar and use the result to build the URL for the properties file. I hacked together an example using String.class as example and access MANIFEST.MF in META-INF, since the rt.jar has no properties in it (at least a quick jar tf rt.jar | grep properties resulted in zero results)
Class clazz = String.class;
String name = clazz.getName().replace('.', '/') + ".class";
System.out.println(name);
String loc = clazz.getClassLoader().getResource(name).toString();
System.out.println(loc);
if (loc.startsWith("jar:file")) {
String propertyResource = loc.substring(0, loc.indexOf('!')) + "!" + "/META-INF/MANIFEST.MF";
InputStream is = new URL(propertyResource).openStream();
System.out.println(propertyResource);
Properties props = new Properties();
props.load(is);
System.out.println(props.get("Implementation-Title"));
}
Related
I want to access file on my classpath called reports/invoiceSweetChoice.jasper in jar on production server. Whatever I do I get null.
I have tried this.getClass().getResourceAsStream("reports/invoiceSweetChoice.jasper"), tried via InputStream etc. I have printed out content of System.getProperty("java.class.path") and it is empty. Not sure how is that possible. Do you have any suggestion how to resolve this ?
In manifest.mf, the classpath is defined using the class-path key and a space-delimited list of files, as follows:
MANIFEST.MF at root of jarfile.
Class-Path: hd1.jar path/to/label007.jar path/to/foo.jar
If there are spaces in the jar filename, you should enclose them in quotes.
If it's a webapp, the reports path should be in your BOOT-INF subdirectory of your classpath -- this is automatically performed by maven if you put it in src/main/resources in the standard layout.
EDIT:
Now that you've clarified what you're trying to do, you have 2 approaches. Like I said above, you can grab the file from the BOOT-INF subdirectory of your webapp or you can enumerate the entries in the jar until you find the one you want:
JarInputStream is = new JarInputStream(new FileInputStream("your/jar/file.jar"));
JarEntry obj = null
while ((obj = is.getNextJarEntry()) != null) {
JarEntry entry = (JarEntry)obj;
if (entry.getName().equals("file.abc")) {
ByteArrayOutputStream baos = new ByetArrayOutputStream();
IOUtils.copy(jarFile.getInputStream(entry), baos);
String contents = new String(baos.toByteArray(), "utf-8");
// your entry is now read into contents
}
}
#Autowired private ResourceLoader resLoad;
void someMethod() {
Resource r = resLoad.getResource("classpath:reports/file.abc");
r.getInputStream()...
...
I currently develop an open-source project where people may add their own .jar to extend the included features. However, I'm stuck with how to load the classes in the jar.
I have an abstract class which has an abstract method onEnable() and some getter that provides some objects to work with the application. The plugin needs the subclass my plugin-class BasePlugin. The jar should be added to /plugins and thus I want all *.jar files in the /plugins folder to be loaded when the application starts.
The problem I'm running to now is that, of all the approaches I found, I need to declare a classpath of the classes in the jar file, which I do not know. Neither do I know the name of the jar file. Thus, I need to scan the /plugins folder for any *.jar file and load the corresponding class inside the jar which implements BasePlugin and invoke the onEnable() method.
The basic idea is too...
Read all the files in a specific directory
Convert the File reference to a URL for each result
Use a URLClassLoader, seeded with the URL results to load each result
Use URLClassLoader#findResources to find all the match resources with a specific name
Iterate over the matching resources and load each one, which should give, at least, the "entry point" class name
Load the class specified by the "entry point" property
For example...
public List<PluginClass> loadPlugins() throws MalformedURLException, IOException, ClassNotFoundException {
File plugins[] = new File("./Plugins").listFiles(new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".jar");
}
});
List<URL> plugInURLs = new ArrayList<>(plugins.length);
for (File plugin : plugins) {
plugInURLs.add(plugin.toURI().toURL());
}
URLClassLoader loader = new URLClassLoader(plugInURLs.toArray(new URL[0]));
Enumeration<URL> resources = loader.findResources("/META-INFO/Plugin.properties");
List<PluginClass> classes = new ArrayList<>(plugInURLs.size());
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
Properties properties = new Properties();
try (InputStream is = resource.openStream()) {
properties.load(is);
String className = properties.getProperty("enrty-point");
PluginClass pluginClass = loader.loadClass(className);
classes.add(pluginClass);
}
}
return classes
}
nb: I've not run this, but this is the "basic
SpigotMC uses JAR files as plugins as well, inside the jar is a plugin.yaml file that stores extra information about the plugin including the classpath. You don't need to use a YAML file, instead you could use something like JSON or even a plain text file.
The YAML file is inside the jar and can be accessed by using some of the methods explained here. You can then get the classpath property and then load the jar using the methods explained here. Extra information can be stored about the plugin such as the name, version, and dependencies.
Java already has a class for this: ServiceLoader.
The ServiceLoader class was introduced with Java 6, but the “SPI jar” concept is actually as old as Java 1.3. The idea is that each jar contains a short text file that describes its implementations of a particular service provider interface.
For instance, if a .jar file contains two subclasses of BasePlugin named FooPlugin and BarPlugin, the .jar file would also contain the following entry:
META-INF/services/com.example.BasePlugin
And that jar entry would be a text file, containing the following lines:
com.myplugins.FooPlugin
com.myplugins.BarPlugin
Your project would scan for the plugins by creating a ClassLoader that reads from the plugins directory:
Collection<URL> urlList = new ArrayList<>();
Path pluginsDir = Paths.get(
System.getProperty("user.home"), "plugins");
try (DirectoryStream<Path> jars =
Files.newDirectoryStream(pluginsDir, "*.jar")) {
for (Path jar : jars) {
urlList.add(jar.toUri().toURL());
}
}
URL[] urls = urlList.toArray(new URL[0]);
ClassLoader pluginClassLoader = new URLClassLoader(urls,
BasePlugin.class.getClassLoader());
ServiceLoader<BasePlugin> plugins =
ServiceLoader.load(BasePlugin.class, pluginClassLoader);
for (BasePlugin plugin : plugins) {
plugin.onEnable();
// etc.
}
An additional advantage of using ServiceLoader is that your code will be capable of working with modules, a more complete form of code encapsulation introduced with Java 9 which offers increased security.
There is an example here it may be helpful. Also, you should take a look at OSGi.
I want to use a small program as a dependency in my Maven project. This program is configured by a properties file which can be edited before its execution. So far I have added the dependency as JAR in a local repository.
Now I want to make that dependency's properties file accessible in my own superior classpath, i.e. in myprogram/src/main/resources/config/myprogram.properties and not in myprogram/local-repository/com/example/mydependency/mydependency.jar/mydependency.properties.
I tried to modify the part of the code where the path for the properties file is defined:
public example() {
Properties prop = new Properties();
InputStream input = example.class.getResourceAsStream("/config/myprogram.properties");
prop.load(input);
...
}
I also deleted the original properties file of the dependency before adding it to my local repository.
The whole program is working, but not as expected. Strangely, neither my new properties file in /src/main/resources/config nor the old one in the mydependency.jar is used. It seems that some kind of default properties file is put into my final fat JAR. But I cannot find its source anywhere - even when I try to debug it. That default properties file just seems to appear out of nowhere.
Now, how can I properly move the dependency's properties file to my own classpath?
And where could this default properties file appear from?
Is this an issue with Maven or with the source code itself?
Thanks in advance!
You can do something like this:
public static String getValueFromDependencyProps(ClassLoader cl, String propertiesFile, String key) throws IOException {
Properties prop = new Properties();
prop.load(cl.getResourceAsStream(propertiesFile));
String value = prop.getProperty(key);
if (value == null)
throw new NullPointerException();
else
return value;
}
And then:
String value = getValueFromDependencyProps(YourDependencyClass.class.getClassLoader(), "your.properties", key);
Tested and working. With that I'm able to access any properties file from an external Maven dependency (using one of its classes, YourDependencyClass, to reach it).
I am having a web application having following structure:
WEB-INF
|-classes
|-templates
|-abc.properties
|- xyz.properties
|-lib
|-internal.jar (all classes of our application)
|- other jars
These are bundled as a war
I want to get name of the files present in WEB-INF/classes/templates
Also I want to get the file object of a file in WEB-INF/classes/templates based on name of the file.
NOTE: Above operation needs to be done from one of the class present in internal.jar. Basically they are on classpath
I want to get the file object of a file in WEB-INF/classes/templates based on name of the file
With java.io.File, you can't do that.
With the java.nio.file API, you can:
final Path warpath = Paths.get("path to your war file here");
final URI uri = URI.create("jar:" + warpath.toUri());
try (
final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
) {
final Path templates = zipfs.getPath("/WEB-INF/classes/templates");
// walk "templates" with Files.walkFileTree()
}
Right now I'm manually writing song file names into a String array, then sending the strings to InputStream is = this.getClass().getResourceAsStream(filename); to play the song.
I would rather loop through all the song names in my resources folder and load them into the array so that each time I add a song, I don't need to manually type it into the array.
All of my songs are located in resources folder:
Is there a java method or something to grab these names?
The files need to work when I export as .jar.
I thought I could use something like this?
InputStream is = this.getClass().getClassLoader().getResourceAsStream("resources/");
Is there a listFiles() or something for that?
Thanks
No, because the classpath is dynamic in Java. Any additional Jar could contribute to a package, a classloader can be as dynamic as imaginable and could even dynamically create classes or resources on demand.
If you have a specific JAR file and you know the path within that JAR file then you could simply treat that file as archive and access it via java.util.jar.JarFile which lets you list all entries in the file.
JarFile jar = new JarFile("res.jar");
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
System.out.println(entry.getName());
}
If you want to do this without knowing the JAR filename and path (e.g. by a class within the JAR itself) you need to get the filename dynamically as Evgeniy suggested; just use your own class name (including package; your screenshot looks like you are in default package, which is a bad idea).
You can try something like this
Enumeration<URL> e = ClassLoader.getSystemResources("org/apache/log4j");
while (e.hasMoreElements()) {
URL u = e.nextElement();
String file = u.getFile();
...
file:/D:/.repository/log4j/log4j/1.2.14/log4j-1.2.14.jar!/org/apache/log4j
extract jar path from here and use JarFile class to know org/apache/log4j contents.
If it is unpacked, you will get straight path and use File.listFIles