How to load DTDs from a jar file? - java

The intended machine doesn't have a connection to the internet and I do not want to load it using a fixed location.
Aim: To load DTDs from a jar, the jar will be a dependency.

To load any file from the classpath (it is, the space where youre classes reside, usually a bunch of jars) you can do:
InputStream is = this.getClass().getResourceAsStream("my/package/ResourceFile.dtd");
And then you can use the input stream where you want.
Note: getResourceAsStream() loads the resource using the class loader that loaded the class. If you are making an application any class from your application (and hence loaded by the same classloader with your jars) will be fine.

There is no standard way to provide a local cache (CATALOG if I recall correctly) of DTD's.
Hence, you will need to investigate the parser that will use the local copies, and use its non-standard configuration API to let it know about these local copies so that the trip to the net is avoided.
Use the getResourceAsStream() method to pick out entries from the classpath.

Related

Maintain Java Resource properties file Externally

Earlier I put my properties file within my classpath src/. Now I would like to put it within a folder called config/. This way the end users can actually modify the file by themselves.
However now my code below does not work anymore
ResourceBundle.getBundle("Messages", Locale.getDefault());
What I meant by the code doesn't work anymore is, after I deploy the application and I modify the Messages_en_US.properties the changes do not take place.
How can I achieve what I want ? Should I use getBundle at all ?
EDIT
I have added config folder into the classpath as well, but I am not sure if this is relevant. This is a plain Java application where I am not using Maven or any building tools.
By default, a ResourceBundle is only loaded when it is first requested, and reused for subsequent requests. You can throw away the cached ResourceBundles with ResourceBundle.clearCache();
Additionally, by default, ResourceBundles are loaded from the classpath. You must therefore ensure that the classloader in question does not cache the resource either. Or you can provide your own ResourceBundle.Control to load the properties file by whatever means you prefer.

Reloading jar files contents dynamically

I have one jar file in my application's class path. At run time, I add new classes to the jar file and sometimes also modify the fields/methods of the existing classes. Currently I am using URLClassLoader to load the classes dynamically. The new classes added dynamically are loaded correctly and I am able to use them at runtime. But it fails to reload the existing classes that are modified at runtime. I read many articles which states we need to explicitly handle reloading because class once loaded will not be reloaded until all the references to the class are destroyed. Also I tried out sample code that I found but none of them worked.
Can anyone suggest me a proper approach for reloading ? Any sample code for the same will be highly appreciated.
Normally to reload a class you need to unload the entire class loader. i.e. remove all references to all classes loaded for that class loader.
Another option is to use instrumentation to change the byte code of an existing class. This usually comes with limitations and changing fields is something you cannot do. i.e. the objects of that type would have to be translated somehow.
What I normally do is have services which are very quick to start/restart. This way to you easily restart a process which needs updated code ideally by pressing the Run in my IDE. This minimises deployment time as well.
In principle, a class that has already been loaded cannot be reloaded with the same classloader.
For a new load, it is necessary to create a new classloader and thus load the class.
Using URLClassLoader has one problem and that is that the jar file remains open.
If you have multiple classes loaded from one jar file by different instances of URLClassLoader and you change the jar file at runtime, you will usually get this error: java.util.zip.ZipException: ZipFile invalid LOC header (bad signature). The error may be different.
In order for the above errors not to occur, it is necessary to use the close method on all URLClassLoaders using the given jar file. But this is a solution that actually leads to a restart of the entire application.
A better solution is to modify the URLClassLoader so that the contents of the jar file are loaded into the RAM cache. This no longer affects other URLClassloaders that read data from the same jar file. The jar file can then be freely changed while the application is running. For example, you can use this modification of URLClassLoader for this purpose: in-memory URLClassLoader

if I have a classloader instance, can I find where it looks for the class bytes?

if I have dira,jarb and dirc in the classpath in that order, and I have a java app with 3 classloaders with a parent/child/grandchild relationship, will they all read the same directory ?
I guess I am trying to figure out where each classloader looks... is there a way to find this path given an instance of the classloader ?
In general no, a classloader is permitted to construct bytes however it likes. E.g. the JSP classloader might invoke the JSP compiler dynamically if the JSP file has a recent timestamp.
Running the JVM with the -verbose:class flag will enable a lot of logging which should help you if you're just using the standard bootstrap classloaders.
If there's some custom classloader, you could supply your own URLConnectionFactory and see what URLs are being fetched.
You have actually several questions here.
The classes in the classpath directories and jars will usually be loaded by one classloader (the application classloader), not by several ones for each entry.
If you have classloaders in a parent-child-relationship, the child one should first ask its parent to load the class and only lookup the bytecode itself when the parent did not find anything. (There are special-purpuse classloaders in some frameworks which do this the other way around. If each class exists only once, then this should not make a difference.)
If you have an URLClassLoader, then you can ask its getURLs() method to find out from where it loads. For other classloaders, there may or may not be a way to find this.
Take a look at the ClassLoader API and you will realise there is a method that passes a name and eventually the class loader passes a byte[] to define the class. Because it is a proper class it can grab those bytes from anywhere it wants to. ClassLoader is just another public class anyone can implement their own implementation and do their own thing. ClassLoaders are everywhere, we have the version that reads the classpath system property, in tomcat we have another that reads from a war file, in osgi it reads from a jar file. Each does a few extra things besides simply reading some file and tahts the beauty and flexibility of classloading.
There is no method on ClassLoader that returns a String, because what would it return given the above mentioned CLassLoaders ? A file path, a jar file path ? etc
In general no, but in practice you often want to find out where some class, resource is being loaded from and you can do,
System.out.println(someClassLoader.getResource("someResource.txt"));
Even more useful, if you are looking to find which .class file a Class is from, do
Class c = SomeClass.class;
System.out.println(c.getResource(c.getSimpleName() + ".class"));
The above is not guaranteed to work if the .class file is generated dynamically, but works in most situations.

Is it ok to put configuration files in JARs?

We are having a debate at my office around what can and cannot go in a JAR file. It has been suggested that it is poor form to have anything that is not a .class file go into a JAR. We currently have some XML configurations for Ibatis/etc, some property files.. the usual. However, there is a push to extract all such files from JARs and put them onto the local file system of each deployment machine. Does this sound reasonable?
it is poor form to have anything that
is not a .class file go into a JAR
That is nonsense. It is in fact very good form to put resources like icons and other data files that user used by the code into the JAR together with the code. This is what the getResource() and getResourceAsStream() methods of Class and ClassLoader are for, and it makes for much more robust applications than messing around with resource paths manually.
However, config files are possibly a different matter. If they're meant to be changed during or after deployment, then having them inside a JAR file is rather inconvenient, and having them in a separate directory is preferable.
If you make changes in a configuration file inside a JAR (even without altering any line of Java code), the whole JAR needs to be rebuilt and redeployed. Does this sound reasonable?
It's absolutely OK to put non-class files in a JAR file, especially resources that the application needs (images, localized strings, etc.) Knowing this, you must decide which scenario fits your situation:
If the configuration is fixed and will only change when a new JAR file is deployed, put it in the JAR.
If the configuration must be altered, either manually or by the application, store it on the filesystem.
If you choose the latter, note that it's good practice to include a default configuration in the JAR file to handle the case when the external configuration file is missing. The default can be loaded directly from the JAR or copied to the filesystem to become the new editable configuration.
It does not sound reasonable to me. I believe, that some application's configuration should be in jar file. Such things as ORM mappings, spring config, custom spring namespace XSD, other XSDs, etc.. should be in most cases in jar. It's important part of deployment artifact.
The fact, that it's not class file, does not mean, that it should be taken out of jar just because it's theoretically can be modified without building a new jar. Can you imagine a modification of *.hbm.xml in production? for me it sounds very scary.
I think some configuration, like spring xml, is meant in most cases to better organize your application and dependencies, but not to change them at runtime in production.
Do you want or expect them to be changed without a new release of the code? Then you need to extract them.
If the answer to the question in no than you shouldn't extract them, as it would allow support to tinker around with them without going through the release process. (Of course this is also possible if they are in the JAR but slightly less tempting.)
Update: But since you mentioned multiple deployment machines, there's a third option: extract them and place them in a commonly accessible area on a network drive. Manually editable config files which are replicated on several machines (but should be identical) are notorious for getting out of sync.
ORM tools (such as Hibernate or IBatis) are not really supposed to be modified once the application is deployed. So, no, I would say that it doesn't make sense for that kind of files.
Depending on your requirements, application-wide configuration files can be placed outside the Jar/War so that they can be modified without having to rebuild the jar.
Do keep in mind that modifying application parameters in production is, imho, a bad practice. Changes should be tested first in some pre-production environment.

Reloading resources loaded by getResourceAsStream

Following best practices, I'm using Thread.currentThread().getContextClassLoader().getResourceAsStream to load resources in a web application (like text files or xml files), instead of going through the file API.
However, this has the disadvantage that if the resource changes on disk, a following call to getResourceAsStream keeps returning the old version indefinitely.
I would like it to pick up the new version though. In my debugger I see there's a simple HashMap called resourceEntries in the classLoader. Using reflection I've been able to remove a specific entry and this seems to work.
This method is however fragile.
Is there a more standard way to do this?
Try this:
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
URL resURL = ctxLoader.getResource(resName);
URLConnection resConn = resURL.openConnection();
resConn.setUseCaches(false);
InputStream resIn = resConn.getInputStream();
...
In addition to kschneid's answer which might work for Tomcat indeed, I wanted to add that for JBoss AS 5+ it already seems to work without needing any special tricks.
Caching of resources is probably class loader specific. The JBoss AS one either doesn't cache or is smart enough to see that the resource on disk has changed.
i finally solved this problem by change the jar file name, every time i change the resource content, i change a new name with current timestamp

Categories

Resources