I am trying to deploy a dynamic web project in a way that allows me to dynamically add Quartz jobs to the classpath. Here is my thinking.
'If I read configuration from an XML file containing a fully qualified classpath, then use Class.forName() in the class to create an instance of said class from the config XML, then I should be able to access dynamically added classes placed in Tomcats TOMCAT_HOME/lib directory'.
Before this update, the application worked fine, but I had these newly externalized classes contained in the war. However, I can no longer do this because I:
Don't want to redeploy the war every time a new job is required.
Cannot take the server down to add new jobs as there are jobs that need to run continuously.
However, I am getting a NoClassDefFoundError when I run the class.forName() method. I have already verified in catalina.properties that the lib directory in Tomcat is in the common.loader property.
My question is, how can I get my WAR classes to recognize the classes in a jar in the Tomcat common library. Any ideas? Thanks.
addition:
#BalusC: I have actually already developed a web based admin screen which allows the user to edit the XML config file to add new jobs. However, to add not just another instance of a job, but an entirely new job, there needs to be code definition of this new job. I want that to be placed into a jar file to be dropped into the tomcat lib directory to be picked up by class.forName().
Hopefully, Tomcat does not reload automatically your context or loads the jars in your classloader automatically. It could result in uncontrollable behaviors.
You will never be able to access those new classes in the WebAppClassLoader (the one managed per tomcat context) without loading the explicitly the jar through an URLClassLoader. I'll advice to use an absolute path to the jar. For some dark reasons I had issues with relative path.
If you want to know the tomcat installation path and for instance the lib directory, you could use the catalina.home and catalina.base environment variables.
HIH
Related
I have an application (WAR) deployed on Tomcat, containing multiple jar files. For reasons, I need to extend a class inside one of these jar files and override one of his methods. The new method will be different for every customer based on their necessities.
What I did is creating a small jar file that has a dependency on the main jar, added the class that extends the original one, and compiled it.
Now I want to deploy it on Tomcat so that the new class is loaded by Spring. This works fine if I put the new jar inside WEB-INF/lib of the deployed WAR, but it doesn't work if I put it inside shared/lib (or just lib) of Tomcat. The error is ClassNotFoundException for the one that I'm extending (basically every class that comes from the main jar can't be found). I already checked catalina.properties and it correctly loads the .jar in both locations.
My question is: why this JAR works only if I put it inside the WAR libraries but not if I put it inside the shared lib of Tomcat?
Tomcat version is 7.0.86
A little scheme to give a better idea of what works and what doesn't work:
- tomcat folder
- lib
- jar-that-extends-main-jar.jar (ClassNotFoundException for the one I'm extending)
- shared
- lib
- jar-that-extends-main-jar.jar (ClassNotFoundException for the one I'm extending)
- webapps
- my-application
- WEB/INF
- lib
- jar-that-extends-main-jar.jar (this works correctly and I can use my custom class)
Tomcat uses different ClassLoaders in kind of a tree hierarchy (you can see a picture as well as some explanations here: https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html).
Basically, you have a Classloader per webapp, one for shared libraries, one for system and one for bootstrap, where the webapp classloaders are at the bottom of the tree, and then it goes up. A Classloader however can only access its parent classloaders, but not the other way round, so when looking for a class, a classloader will look at its own classes, and then ask its parent classloaders for that specific class.
That's why you can access classes in the shared classloader from your webapp, but not the otherway round.
Hi In my project I have many many jars.
Every time I change code and need to upload it to the server takes very long time, because I m adding the jars to the war to be deploy on tomcat.
I m trying to put all the jars in the server, in some folder and to upload the rest of the project only, to speed the cycle.
What will be the best way of doing that ?
I m using tomcat 8.5 also for production deployment without any build tool.
I would like to set an ABSOLUTE path in the classpath but when doing that in my local machine it won't work after deployment to the unix server.
I never saw where or if I can set an absolute path for the jars (NOT OF THE LOCAL MACHINE)
Thanks in advance
You can read about this in Tomcat docs: Class Loader HOW-TO.
The most simple case and way - put these commonly used jars into $CATALINA_BASE/lib dir - they will be loaded by Tomcat class loader.
But, it doesn't seems to be very nice practice, as mentioned tutorials claims:
Normally, application classes should NOT be placed here.
Personally me, in practice purposes, I would ignore this hint and still place jars inside this folder. But if you want to be accurate, you could create separate path on server (or even inside CATALINA_BASE folder) and place jars there. After that you have to specify this path in $CATALINA_BASE/conf/catalina.propertiesfile in common.loader property:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
Instead of using a certain jar in my WEB-INF/lib folder, I want to use its source code (same directory structure and everything) in my WEB-INF/classes folder, so that I may be able to modify its classes more story.
Yet (re)starting my tomcat after deleting the original jar and uploading the corresponding directory into WEB-INF/classes gives me the following error:
SEVERE: Error configuring application listener of class no.something.something1.http.LifecycleListener
java.lang.ClassNotFoundException: no.something.something1.http.LifecycleListener
I am certain that the directory path is the same as the one inside the jar. Also, I have previously tried using classes in my WEB-INF folder for this web application, and tomcat has also been unable to load them, for some reason.
Does anyone know how I go about troubleshooting this error?
Tomcat can only load .class files, it doesn't know what to do with raw source code files. Tomcat doesn't do hot loading of .class files like that anyway. You would have to restart the application or server after you recompiled them either way, packaging them as a .war isn't that much of a burden either way once you automate it.
If you take the time to automate the build and deployment of a proper .war you can just rebuild the .war and it will automagically undeploy and redeploy the application itself, which is many times faster than restarting the entire server.
You can't do what you are trying to do the way you are trying to do it. Tools like JRebel address these issues, but I don't find them as useful as their marketing makes them sound.
You could use embedded tomcat (or embedded jetty) to map directory structure of the application as you like. It probably will require some tinkering if you need some JNDI resources within your application but still worth the trouble.
Here's an example
I am using weblogic 10.3.4, I am trying to write log with log4j. but at runtime my application is not getting any log4j.properties. even this is not generating any warning as "initialization of log4j has error".
I have tried my properties file to put in src folder, classes folder and then I created one jar and put it in domain lib. still its not picking. even when I am writing log with same jar in standalone application, its working fine.
please help me with valuable suggestions.
I tried the solution proposed at Oracle forums.
Excerpt from that link at Oracle forums:
I've only modified the scritp startWebLogic.cmd:
set LOG4J_CONFIG_FILE=log4j.xml
set SAVE_JAVA_OPTIONS=%JAVA_OPTIONS% -Dlog4j.configuration=%LOG4J_CONFIG_FILE%
#REM set SAVE_CLASSPATH=%CLASSPATH%
set SAVE_CLASSPATH=%CLASSPATH%;C:\Oracle\Middleware\user_projects\domains\domain\config
In this way I've put all the config folder inside the classpath, and I can use it in future to hold other libraries configuration files (for example oracle coherence config).
I tried this approach on a different properties file as well and that worked well!
You need to either specify where the application should find its log4j.properties, or put it onto the classpath of the application. Where the classpath is varies, but in general WEB-INF/classes should work. Other options depend upon how you're deploying the application.
A better long term strategy is to configure your system so that you can change the log4j.properties depending upon the environment. When you're in production, you won't want all of the debug information to appear. Look at the answer to this question or this question for more ideas. One strategy is to define a variable on the command line which gets picked up and defines a directory which contains your configuration files. This works for Tomcat, but there may be other, better, strategies for Weblogic.
It is not a good idea to change the configuration of your server, in particular, don't replace the log4j.jar or log4j.properties in your server directories. The server will depend upon the version that it was designed to use, which may or may not be the same as your version. You can do everything you need to do by changing the war that you're deploying.
I have used this code:
ClassLoader cl = this.getClass().getClassLoader();
URL log4jCfg = cl.getResource(configFile);
if (log4jCfg != null) {
DOMConfigurator.configure(log4jCfg);
}
log.info("log4j is now working on Web App.");
In my case, we used XML configuration:
log4jCfg = "mylog4j.xml";
In WebLogic, we were able to place such file (mylog4j.xml), equivalent to your log4j.properties file, at WebLogic's domain path (specific to the domain were we deploy). This means that domain folder belongs to your application's path. I just tested it with Web applications, I'm not sure if with SOA or EJB projects it works the same way.
When you deploy any application on any server that application should use servers log4j jar.
So if you have added any log4j jar in your application jar/tar/ear, remove it and copy log4j.properties file in the conf folder of the server from where server is picking its configuration files. Or just copy your log4j property content in servers log4j property file.
In our application(applet) I want to enable export functionality if one of the required jars is found. I do not want to add this jar applet references to avoid download size.
I am using Class.forName with one of the classed to check whether particular is available. In local machine Class.forName call retruns an instance although the jar is not in any of the class paths.
Can anybody explain tomcat class discovery mechanism.
Applets run at the client side(inside the browser of the user) not on the Tomcat web server, so this is unrelated to Tomcat.
You'd want to investigate how Applet classloaders work.
Usually they will try to downloading the classes from the web server under the same Url as the applet was fetched from. So if the applet is at http://www.example.com/Hello/HelloApplet
and needs the class foo.bar.MyClass it will try to download http://www.example.com/Hello/foo/bar/MyClass.class if it isn't found locally.
Based on your description of having an Applet I don't see any option but to include the jar file in the applet tag as the applet runs on the client side.
You could set HTTP cache headers for the jar files to allow the client browser to cache them, therefore, you only pay the download cost only once.
For frequently changing jar files include the a version number in the jar file name to avoid client side caching issues with same named but contentually different jars.
Edit: Although the question is about the way tomcat discovers the jars I think the root cause of the problem is elsewhere.
In Tomcat 6 on the server side Tomcat searches the $TOMCAT_HOME/lib and WEB-INF/lib directories for your jar files. If you add or remove files there you usually need to restart the entire Tomcat instance.
Edit2:
Your experience about locating the jar file might be because you run the HTML page from the same directory where your webapp resides or you have the JAR file in a common place or common classpath location (for example in the JRE/lib/ext directory).
I am having a hard time following your question. Are you trying to download classes into an applet if a particular runtime condition is met? From 6u10 I believe you could dynamically download extensions with DownloadService. Going back to 1.2, you can use URLCLassLoader.newInstance, although that wont be so good on the cache side of things.
This question isn't very clear. Tomcat and Applets are completely different in terms of class loading. Applets have a security manager that prevents certain things, such as loading arbitrary classes. They have to download the classes from the web server. The web server doesn't have to be tomcat or even Java; the applet files are just files served over plain HTTP.
As for Tomcat, this article explains version 6's classloading. In particular, Tomcat uses a heirarchy of classloaders to find classes. There are several well-known locations where jars are automatically loaded, such as $CATALINA_HOME/lib or $CATALINA_HOME/shared/lib. It also loads the web-app's own jars and classes. The classloaders work as follows:
The bootstrap class loader looks in the core Java classes folders.
The system class loader looks in the $CATALINA_HOME/bin/bootstrap.jar and
$CATALINA_HOME/bin/tomcat-juli.jar
The WebAppX class loader looks in WEB-INF/classes and then WEB-INF/lib
The common class loader looks in $CATALINA_HOME/lib folder.
The shared class loader looks in $CATALINA_HOME/shared/classes and $CATALINA_HOME/shared/lib if the shared.loader property is set in conf/catalina.properties file.
Reason: One of the other libraries(jars) referenced earlier had the class I was looking for.