In Java I can have multiple instances of the some resource in the class path, and can access them thus:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = loader.getResources("META-INF/services/myService.properties");
while(resources.hasMoreElements()){
URL resource = resources.nextElement();
// process each resource here
}
This allows use of service provider interfaces, where multiple implementations can be made available at runtime by dropping additional jars into the classpath.
Is there any equivalent to this in Java ME?
[UPDATE 2011-April-11]
If I could somehow get a list of jar files in the classpath, I could probably just pass the URLs to the Connector like this:
String [] jarFiles = ... // somehow I got this
for(int i = jarFiles.length-1; i >= 0; i--){
String url = "jar:file:"+jarFiles[i]+"!/META-INF/services/myService.properties";
InputStream in = Connector.openInputStream(url);
//process each resource here
}
Any idea how I might get all jar files in the classpath in J2ME?
After some research, it looks like the application must be contained within a single Jar file in a J2ME environment, so concepts like the classpath simply do not apply, meaning that this is not possible. All dependencies must be available at compile time.
Related
I am trying to load all resources with a specific file-extension which are loaded dynamically at runtime using a URLClassloader.
Unfortunately the PathMatchingResourcePatternResolver return no Resources when I use the pattern classpath*:/*.myextension. When I specify a file with its complete name like classpath*:/test.myextension the resource gets loaded, so I think the Classloader is configured right.
URLClassloader classloader = new URLClassloader(jarURLs); // jarURLs look like "file:C:/Temp/test.jar"
Thread.getCurrentThread().setContextClassloader(classloader)
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classloader);
Resource[] resources = resolver.getResources("classpath*:/*.myextension") // yields empty array
....
How can I do this? I have to load the jars dynamically and I dont know the resource-names in advance.
Loading the files dynamically in Spring is simple, I'd change the approach to finding the files with extensions.
Try the following:
ClassLoader cl = this.getClass().getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources("classpath*:/*.xml") ;
for (Resource resource: resources){
logger.info(resource.getFilename());
}
As Tech Trip mentioned in the comment to his answer, I had an error in my pattern. The Spring-documentation is also quiet clear about that (see Warning): "classpath*:" when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts...originates from a limitation in the JDK's ClassLoader.getResources()
So I changed my pattern to
classpath*/model/*.myextension
Since the JARs are created from an xText-DSL I have to enforce a convention that the model-folder has to be used.
in Java 7 & 8 I use to be able to do
URLClassLoader urlLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
for (URL u : urlLoader.getURLs()) {
System.out.println("*************** url=" + url);
}
But in Java 9 the above gives an error
java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClass
Loader
So how do I list all resources without knowing the package prefix or the filesystem path of the jar files?
The Java9 Release Note states this:
The application class loader is no longer an instance of
java.net.URLClassLoader (an implementation detail that was never
specified in previous releases). Code that assumes that
ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
need to be updated. Note that Java SE and the JDK do not provide an
API for applications or libraries to dynamically augment the class
path at run-time.
To add to this from the mailing list there are few resource-lookup methods which include ways such as :
A class in a named module can read its own resources via the Class::getResourceAsStream method, which returns an InputStream. It can get a URL to one of its own resources via the Class::getResource method. These methods will not locate resources in other named modules.
The ClassLoader::getResource* methods do not locate resources in any
named modules.
All existing resource-lookup methods in Class and ClassLoader work as they do today for resources on the classpath.
The new Module::getResourceAsStream method can be
used to read the resources of any named module, without restriction.
These choices provides a balance between both resource encapsulation and readable artifact requirements on Jigsaw.
Best answer I found is to provide an argument to getResources, of course means you know prefix of the path the resources are located
ArrayList<URL> resources = new ArrayList<URL>();
ClassLoader urlLoader = ClassLoader.getSystemClassLoader();
Enumeration<URL> eU = urlLoader.getResources("com");
while (eU.hasMoreElements()) {
URL u = eU.nextElement();
JarFile jarFile = new JarFile(u.getFile().replace("file:/", "").replaceAll("!.*$", ""));
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
JarEntry jarEntry = e.nextElement();
resources.add(urlLoader.getResource(jarEntry.getName()));
}
}
Because of the move to modular architecture, the old approach for looking up class paths is no longer supported:
The application class loader is no longer an instance of
URLClassLoader but, rather, of an internal class. It is the default
loader for classes in modules that are neither Java SE nor JDK
modules.
https://docs.oracle.com/javase/9/migrate/toc.htm
You can try to parse the classpath property directly or try to do something with modules using ModuleFinder and friends.
I am using maven and the standard directory layout. So I have added a testdata.xml file in the src/test/resources folder, and I also added it as:
.addAsWebInfResource("testdata.xml", "testdata.xml")
in the deployment method, and I have confirmed that it is there. This will make the file appear in /WEB-INF/testdata.xml. Now I need to have a reference to this file in my code and I tried several different getClass().getResourceAsStream(...) and failing again and again so I need some advise now.
I need it for my DBUnit integration test. Is this not possible?
Option A) Use ServletContext.getResourceXXX()
You should have a Aquillarian MockHttpSession and a MockServletContext. E.g.:
#Test
public void myTest()
{
HttpSession session = new MockHttpSession(new MockServletContext());
ServletLifecycle.beginSession(session);
..testCode..
// You can obtain a ServletContext (will actually be a MockServletContext
// implementation):
ServletContext sc = session.getServletContext();
URL url = sc.getResource("/WEB-INF/testdata.xml")
Path resPath = new Path(url);
File resFile = new File(url);
FileReader resRdr = new FileReader(resFile);
etc...
..testCode..
ServletLifecycle.endSession(session);
}
You can create resource files & subdirectories in:
the web module document root - resources are accessible from the browser and from classes
WEB-INF/classes/ - resources are accessible to classes
WEB-INF/lib/*.jar source jar - accessible to classes
WEB-INF/lib/*.jar dedicated resource-only jar - accessible to classes
WEB-INF/ directly within directory - accessible to classes. This is what you are asking for.
In all cases the resource can be accessed via:
URL url = sc.getResource("/<path from web doc root>/<resourceFileName>");
OR
InputStream resIS = sc.getResourceAsStream("/<path from web doc root>/<resourceFileName>");
>
These will be packaged into the WAR file and may be exploded into directories on the deployed app server OR they may stay within the WAR file on the app server. Either way - same behaviour for accessing resources: use ServletContext.getResourceXXX().
Note that as a general principle, (5) the top-level WEB-INF directory itself is intended for use by the server. It is 'polite' not to put your web resources directly in here or create your own directory directly in here. Instead, better to use (2) above.
JEE5 tutorial web modules
JEE6 tutorial web modules
Option B): Use Class.getResourceXXX()
First move the resource out of WEB-INF folder into WEB-INF/classes (or inside a jar WEB-INF/lib/*.jar).
If your test class is:
com.abc.pkg.test.MyTest in file WEB-INF/classes/com/abc/pkg/test/MyTest.class
And your resource file is
WEB-INF/classes/com/abc/pkg/test/resources/testdata.xml (or equivalent in a jar file)
Access File using Relative File Location, via the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTedy.class.getResourceAsStream("resources/testdata.xml");
Access File Using full Package-like Qualification, Using the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("/com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getResourceAsStream("/com/abc/pkg/test/resources/testdata.xml");
OR
java.net.URL resFileURL = MyTest.class.getClassLoader().getResource("com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getClassLoader().getResourceAsStream("com/abc/pkg/test/resources/testdata.xml");
Hope that nails it! #B)
The way to access files under WEB-INF is via three methods of ServletContext:
getResource("/WEB-INF/testdata.xml") gives you a URL
getResourceAsStream gives you an input stream
getRealPath gives you the path on disk of the relevant file.
The first two should always work, the third may fail if there is no direct correspondence between resource paths and files on disk, for example if your web application is being run directly from a WAR file rather than an unpacked directory structure.
Today I was struggling with the same requirement and haven't found any full source sample, so here I go with smallest self contained test I could put together:
#RunWith(Arquillian.class)
public class AttachResourceTest {
#Deployment
public static WebArchive createDeployment() {
WebArchive archive = ShrinkWrap.create(WebArchive.class).addPackages(true, "org.apache.commons.io")
.addAsWebInfResource("hello-kitty.png", "classes/hello-kitty.png");
System.out.println(archive.toString(true));
return archive;
}
#Test
public void attachCatTest() {
InputStream stream = getClass().getResourceAsStream("/hello-kitty.png");
byte[] bytes = null;
try {
bytes = IOUtils.toByteArray(stream);
} catch (IOException e) {
e.printStackTrace();
}
Assert.assertNotNull(bytes);
}
}
In your project hello-kitty.png file goes to src/test/resources. In the test archive it is packed into the /WEB-INF/classes folder which is on classpath and therefore you can load it with the same class loader the container used for your test scenario.
IOUtils is from apache commons-io.
Additional Note:
One thing that got me to scratch my head was related to spaces in path to my server and the way getResourceAsStream() escapes such special characters: sysLoader.getResource() problem in java
Add this class to your project:
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
public class Init {
private static final String WEB_INF_DIR_NAME="WEB-INF";
private static String web_inf_path;
public static String getWebInfPath() throws UnsupportedEncodingException {
if (web_inf_path == null) {
web_inf_path = URLDecoder.decode(Init.class.getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF8");
web_inf_path=web_inf_path.substring(0,web_inf_path.lastIndexOf(WEB_INF_DIR_NAME)+WEB_INF_DIR_NAME.length());
}
return web_inf_path;
}
}
Now wherever you want to get the full path of the file "testdata.xml" use this or similar code:
String testdata_file_location = Init.getWebInfPath() + "/testdata.xml";
I want to list all files with a specific name in the class path. I am expecting multiple occurrences, hence Class.getResource(String) will not work.
Basically I have to identify all files with a specific name (ex: xyz.properties) anywhere in the class path and then read the metadata in them cumulatively.
I want something of the effect Collection<URL> Class.getResources(String) but could not find anything similar.
PS: I don't have the luxury of using any third party libraries, hence in need of a home grown solution.
You can use Enumeration getResources(String name) on the class loader to achieve the same.
For example:
Enumeration<URL> enumer = Thread.currentThread().getContextClassLoader().getResources("/Path/To/xyz.properties");
while (enumer.hasMoreElements()) {
System.out.print(enumer.nextElement());
}
What I do is I read java source files from classpath and process them using ClassLoader. I am using follwing code :
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert (classLoader != null);
// pkgName = "com.comp.pkg"
String path = pkgName.replace('.', '/');
// resources will contain all java files and sub-packages
Enumeration<URL> resources = classLoader.getResources(path);
if(resources.hasMoreElements()) {
URL resource = resources.nextElement();
File directory = new File(resource.getFile());
// .. process file, check this directory for properties files
}
Hope this helps you.
The existing answers that propose using ClassLoader.getResources(String) only work if the "name" in question is the full path of the resource. As the methods's javadoc states, "The name of a resource is a /-separated path name that identifies the resource." It's unfortunate and misleading that the parameter is called "name" instead of "path."
For those who, like me, are really looking for all resources with a given (simple) name - without knowing the full path of the resource - the current answers won't work. #NandkumarTekale's code example states "resources will contain all java files and sub-packages" in the comments, but unfortunately that is not true. Nor does it make any difference to use ClassLoader.findResources(String) (which is indeed protected in the ClassLoader class itself, but pulled into the public API by concrete subclasses like URLClassLoader, which in turn serves as the base class for most commonly used class loaders).
The most straightforward solution that I could find uses the ClassGraph library:
try (ScanResult result = new ClassGraph().acceptClasspathElementsContainingResourcePath("*/html5.dtd").scan())
{
System.err.println(result.getResourcesWithLeafName("html5.dtd").getURLs());
}
Sadly, this clashes with the OP's request for a solution without the use of third-party libraries. However, ClassGraph's source code is available on GitHub and can be used as an inspiration for a "home grown" solution. In broad strokes, one would have to find the class loader's base URLs (e.g., using URLClassLoader.getURLs()) and then write URL-specific code for searching the contents of each URL (e.g., if it's a jar:file:... URL, load the JAR and iterate over its contents, or if it's a file:... URL use java.io.File to explore the folder contents). There are a lot of special cases to consider, though (e.g., OSGi class loaders), and a truly general solution would probably replicate a fair amount of what ClassGraph does under the hood. If it is valid to assume that the resources will, for example, always be packaged in a JAR (or always as plain files in the file system), a more tailored solution could be created for just those cases.
I have a webapp which contains a manifest file, in which I write the current version of my application during an ant build task. The manifest file is created correctly, but when I try to read it in during runtime, I get some strange side-effects. My code for reading in the manifest is something like this:
InputStream manifestStream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("META-INFFFF/MANIFEST.MF");
try {
Manifest manifest = new Manifest(manifestStream);
Attributes attributes = manifest.getMainAttributes();
String impVersion = attributes.getValue("Implementation-Version");
mVersionString = impVersion;
}
catch(IOException ex) {
logger.warn("Error while reading version: " + ex.getMessage());
}
When I attach eclipse to tomcat, I see that the above code works, but it seems to get a different manifest file than the one I expected, which I can tell because the ant version and build timestamp are both different. Then, I put "META-INFFFF" in there, and the above code still works! This means that I'm reading some other manifest, not mine. I also tried
this.getClass().getClassLoader().getResourceAsStream(...)
But the result was the same. What's the proper way to read the manifest file from inside of a webapp running in tomcat?
Edit: Thanks for the suggestions so far. Also, I should note that I am running tomcat standalone; I launch it from the command line, and then attach to the running instance in Eclipse's debugger. That shouldn't make a difference, should it?
Maybe your side-effects come from the fact that almost all jars include a MANIFEST.MF and you're not getting the right one. To read the MANIFEST.MF from the webapp, I would say:
ServletContext application = getServletConfig().getServletContext();
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);
Please note that running Tomcat from Eclipse is not the same as running Tomcat alone as Eclipse plays with the classloader.
a bit late, but this works for me (web appl in Glassfish)
Properties prop = new Properties();
prop.load(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"));
System.out.println("All attributes:" + prop.stringPropertyNames());
System.out.println(prop.getProperty("{whatever attribute you want}"));
Try to use jcabi-manifests, that does all this loading work for you. For example:
String version = Manifests.read("My-Version");
loads My-Version attribute from one of available MANIFEST.MF files.
Important to mention that (more details are here) in most web containers current thread class loader is not the same as servlet context class loader. That's why you should append your servlet context to the register in runtime (more info):
Manifests.append(servletContext);
Also, check this out: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
The default way class loaders work is to defer to the parent before attempting to lookup their own resources. So if a parent class loader has any manifest available, that's what you'll get. In fact, app servers don't necessarily do this, to allow applications to override versions of libraries. Further, class loaders can have multiple jars and hence multiple manifests.
It may be able to get a resource URL of one of your uniquely named resource. Open a connection. Cast to JarURLConnection. Get the JarFile. Load the manifest from that. That may not work, particularly if Tomcat explodes the war.
[Update] Of course, the war file itself isn't on the classpath. The classpath will have something like WEB-INF/lib/(.jar|.zip) and WEB-INF/classes/. Getting a resource from the ServletContext should work.
Best solution: Do something different. :)
The right manifest exists in application root at server.
Find out the appication root, for instance by finding out classpath of your class:
String rootPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
Then replace the path above with the founded path: Glassfish example:
/applications/<webProject>/META-INF/MANIFEST.MF
It work for me.
Don't know about a "official" way to read it, but if the MANIFEST.MF can't be properly loaded as a resource, how about trying to derive its path from a "ServletContext.getRealPath()" on some web path defined in your app?
Writing the app version also to some else place (a property file in WEB-INF/classes) by ant during build is another solution that comes to my mind.
This is what I do to print various versions to a logfile. I have hardcoded an expanded path but apps may use servletContext.getRealPath("/") to read a full path to webapp folder. May print just given libraries or everything from lib folder.
// print library versions (jersey-common.jar, jackson-core-2.6.1.jar)
try {
List<String> jars = Arrays.asList( "jersey-common", "jackson-core", "openjpa", "mylib" );
StringBuilder verbuf = new StringBuilder();
for(File file : new File("/opt/tomcat/webapps/myapp/WEB-INF/lib/").listFiles() ) {
String name = file.getName();
if (file.isDirectory() || !file.isFile() || !name.endsWith(".jar") ) continue;
name = name.substring(0, name.length()-4);
boolean found = jars.contains(name);
if (!found) {
int idx = name.lastIndexOf('-');
if (idx>0)
found = jars.contains( name.substring(0, idx) );
}
if (!found) continue;
JarFile jarFile = new JarFile(file, false);
try {
String ver;
Manifest mf = jarFile.getManifest();
if (mf!=null) {
ver = mf.getMainAttributes().getValue("Bundle-Version");
if (ver==null || ver.isEmpty())
ver = mf.getMainAttributes().getValue("Implementation-Version");
} else ver=null;
if (verbuf.length()>0) verbuf.append(", ");
verbuf.append(name + "=" + (ver!=null?ver:"") );
} finally {
jarFile.close();
}
}
System.out.println( verbuf.toString() );
} catch(Exception ex) {
ex.printStackTrace();
}