After Updating log4j - to mitigate the log4shell vulnerability (CVE-2021-44228) - in class path, (or any other library generally), Do I need to restart JVM to make updates get into count?
Here, is mentioned that the "newly added classes" inside class path are getting loaded to the JVM automatically without restart, but what about the classes that are already loaded with the same name via class loader? Do they get overwritten?
The same question applies for tomcat (Although I guess it would be the same as JVM?)
Even if the new classpath would immediately be used by the JVM, there may be a number of objects instantiated from the old classes in memory. The new classes would then only apply to new instances. AFAIK log4j would not throw away it's objects during runtime.
To be on the safe side you definitely want to restart the JVM.
Is it required to restart JVM after updating log4j in classpath?
Probably yes. It depends on which classloader loads log4j.
If the log4j libraries are exclusively part of your webapps, you might be able to get away with hot-loading all of the webapps.
But you said "in classpath" and I guess that mean's in Tomcat's classpath; i.e. the shared libraries.
My advice would be not to take the risk. Restart Tomcat.
(Your systems should be designed so that Tomcat restarts are not a significant problem. There are various ways to do that. Indeed, one could argue that if downtime of your (single) Tomcat instance is an operational concern, then you should be running multiple copies.)
... but what about the classes that are already loaded with the same name via class loader? Do they get overwritten?
A classloader won't notice things have changed in its classpath. They are not designed to work that way.
And even if it did, a classloader cannot reload a class. The JVM architecture / runtime type safety don't allow it.
The hot-loading feature that (some) people use to avoid Tomcat restarts actually involves creating a brand new classloader to load the new version. The old version of the class will still exist in its original classloader, and other code will remain bound to it.
Related
Ok, here is the issue. We are using an old version of Tomcat (6.0.35) because our version of linux (ubuntu 12.04) doesn't have a repository with a newer version of it. Unfortunately, there is a bug in 6.0.35 that is affecting us ( https://issues.apache.org/bugzilla/show_bug.cgi?id=53725 ). IT doesn't want to/can't change tomcat versions. They want to use the package management system, they don't want to rebuild tomcat. They can't maintain something like this on a whole bunch of servers and we don't particularly want to disable Gzip. We can deal with the corruption until we can finally upgrade.
So with that background, the real question. The changes which fix this bug reside in one single class. We came up with a crazy solution (which will probably never see production, but was more of a "this would be so terrible, lets try it!" sort of idea), How about we compile the fixed version of the class and hotswap it in. After all, tomcat and our project use the same JVM.
Is this doable/possible/more than a little crazy?
Some hurdles. We are using Java 6. The new class adds methods and fields. I'm not sure that tomcat keeps it around.
In most cases this is unreasonable to actually do. The only way to cleanly unload a class and then load a replacement is to let the ClassLoader used to load that particular class be garbage collected. This will require you to remove all references to any of the classes that ClassLoader loads (which is probably at least all of TomCat) and reload them using a ClassLoader that loads all classes like normal, except for the class you 'fixed'. You would have to make it so that it loads that class instead of the old one, while still allowing the other classes to load normally.
In short, it is possible, however it will require digging into java ClassLoaders. I'd recommend that you do not actually attempt this, as it will be very annoying to get right and it will certainly not work without shutting down the TomCat server first (since you have to reload all of TomCat's classes).
Who is doing the unzipping, Tomcat or the application? If its the application, just make a copy of the class in error to the root of your project classpath in src/main/java and put there the patched version of the class.
Upon packaging the patched class will be in WEB-INF/classes, and override the version in the server. But its overridden only from the point of view of the application, the server cannot see the patched version and still sees the buggy version.
If its tomcat itself that needs to unzip something, then its not possible to fix that without tinkering with the server.
But if its the application doing the unzipping its for sure possible, there is no obligation to use the libraries of the server we can use our own more updated copies, usually not in patches but in jars in WEB-INF/lib - the server is designed for that.
I'm using JDK 1.6 to run a small application. However I set a very massive classpath which includes a lot of classes.
When I run the application will all classes in the classloader been loaded even if they're not actually used in my application? If not, how to force the classloader do so, and if yes, how to avoid it? Thanks!
E.g.,I'm using ant 1.7 to run my application.
Best Regards,
Robert Ji
No, The ClassLoader loads the class when the class is needed in memory. It doesn't load all classes at once as it can run out of memory.
They are loaded when needed. But what "when needed" means, might depend on the classloader. Typically, when a class is loaded it also checks the existence of all classes it references - and it might also load them.
You can check it by adding the option -verbose to your Java JVM, it outputs then all the classes it loads, and from where.
To my knowledge it's impossible to load all classes if they are not accessed explicitly. Class in only loaded when it's constructor or any other static member is first accessed, this rule applies to nested classes as well.
I have multiple web-apps running on an app server and each web-app WAR file contains a copy of the same jar file.
Does this mean that a class in that jar file will be loaded multiple times in the JVM, once for each WAR file it exists in? Following on from that, if I have a static synchronized method in such a class, is it only synchronized among threads within the web-app it exists in but not synchronized against the same method in the same class in a different jar file in a different WAR file? (Hope the question makes sense, will clarify if necessary).
If this is the case I presume the best solution is to remove the jar file from each WAR file and deploy it to a shared classpath folder on the server?
A Java classloader typically works by looking for classes in one or more places in a fixed sequence. For instance, the classloader that loads your application when you run it from the command line looks first in the rt.jar file (and others on the bootclasspath), and then in the directories and JAR files specified by your classpath.
A webapp classloading is similar in principle, but a bit more complicated in practice. For a particular webapp, a webapp's classloader looks for classes in the following order. For example Tomcat 6 looks for classes in this order:
Bootstrap classes of your JVM
System class loader classes (described here)
/WEB-INF/classes of the webapp
/WEB-INF/lib/*.jar of the webapp
$CATALINA_HOME/lib
$CATALINA_HOME/lib/*.jar
Of course, once the classloader has found the class it is looking for, it looks no further. So classes with the same name later in the order won't get loaded.
The complication is that the web container has one classloader for each webapp, and these classloaders delegate to other classloaders that manage the common classes. In practice, this means that some classes will only ever be loaded once for the entire container (e.g. 1. and 2.) and others may get loaded multiple times by different classloaders.
(When a class is loaded more than once, it results in distinct Class objects and distinct class statics. The versions of the class are different types as far as the JVM is concerned and you cannot typecast from one version to the other.)
Finally, Tomcat can be configure to allow individual webapps to be "hot loaded". This entails stopping a webapp, creating a new classloader for it, and restarting it.
FOLLOWUP
So ... synchronizing a static method will not protect access to a shared resource where the class has been loaded multiple times?
It depends on the details, but it probably won't. (Or to look at if another way, if a class has actually been loaded multiple times, then a static method of each "load" of the class will access a different set of static fields.)
If you really want a singleton application class instance to be shared by multiple webapps in the same container, it is simplest if you put the class into $CATALINA_HOME/lib or the equivalent. But you also should ask yourself if this is good system design. Consider combining the webapps, or to using request forwarding etc instead of a shared data structure. The singleton pattern tends to be troublesome in webapps, and this flavor is even more so.
Java EE application servers typically use multiple classloaders to isolate applications from each other, and allow new versions of one application to be deployed without affecting other apps.
You get patterns such as several WAR files and one EJB file in an EAR with a hierarchy of classloaders, each WAR having it's own.
This does lead to duplication as you describe, but this is not necesserily a bad thing. It means that you can even have different versions of the same JARs deployed a the same time, and that may actually be benficial, allowing incremental migration to new versions.
Some application servers (WebSphere for exmaple) have explicit support for a shared library concept, and I do use that.
Be wary of just popping JARs into arbitrary classpaths, you run the risk of destabilising the app server itself.
Most application server use most specific along a path takes precedence policy.
If you have multiple library that do the same thing, You should consider to put them inside application server lib (f.e: TOMCAT_HOME/lib)
Is there a way to check if all boot (core) java classes (belonging to the Java Runtime Environment) have been loaded/initialized for use in Java?
I need to check this in a rare situation where I have have access to the JRE but not to the actual application, so I cannot simply wait for the main application to run and execute from that point on.
The JVM will load classes on an "as-needed" basis, so there's no one point at which "all" of the classes on the bootstrap classpath will have been loaded.
That said, from 1.5 and onward, the Sun JVMs use "class data sharing" to pre-load a specific set of classes. I don't know which classes get loaded, but would suspect it's limited to those in the java.lang package.
If you simply want to keep track of when classes get loaded, use the -verbose:class command-line option when starting the JVM.
having read your comments (and accidentally deleted my cookie), all I can say is that JVMTI is pretty much guaranteed to be a much better choice for whatever it is that you're trying to do.
But if you're hell-bent on modifying a JRE class, why not simply add a static boolean variable that will get set during FileWriter initialization?
Why do we need to restart a tomcat server whenever a class file is changed, is there no other way?
You can configure Tomcat and make your webapp "reloadable". To do so, add reloadable=true to the <Context> element of your webapp. About the reloadable attribute, the documentation says:
Set to true if you want Catalina to monitor classes in /WEB-INF/classes/ and /WEB-INF/lib for changes, and automatically reload the web application if a change is detected. This feature is very useful during application development, but it requires significant runtime overhead and is not recommended for use on deployed production applications. That's why the default setting for this attribute is false. You can use the Manager web application, however, to trigger reloads of deployed applications on demand.
There certainly is! Start Tomcat in development mode, then each webapp will restart itself upon being redeployed.
From the Tomcat docs:
The servlet which implements Jasper is configured using init parameters in your global $CATALINA_BASE/conf/web.xml.
...
development - Is Jasper used in development mode (will check for JSP modification on every access)? true or false, default true.
There are settings you can change to adjust what exactly Tomcat will look for to check for updates. I usually deploy individual class files to their appropriate directory under WEB-INF/classes and then
touch WEB-INF/web.xml
to kick-start a restart of the application; I think web.xml is one of the files Tomcat checks by default.
On a more general note, the reason you have to do this is because in Java, when a classloader loads a class, it cannot unload it. What Tomcat has to do is use a new classloader and reload all the classes it needs.
Check out JRebel.
If you develop, your IDE should be able to do this transparently on a suitable server. E.g. the Dynamic Web Project in Eclipse knows how to talk to Tomcat.
If you deploy, then create WAR-files and deploy those. Tomcat knows how to redeploy a WAR-file.
If you're using WAR files to deploy, you can set autoDeploy=true in the Tomcat config, which causes Tomcat to watch the web application root ("webapps" by default) for new or changed WAR files. If such a file is found, it is automatically deployed.
As Pascal Thivent said, though, you can use the Tomcat Manager application (/manager/html) to start, stop, deploy, and undeploy specific applications. If the files you're changing are in a specific application, this is a good way to get Tomcat to recognize the changes.
Besides setting autoDeploy=true in server.conf , you also should be careful not to put any classes in the shared classloader. Classes which are loaded by the shared class loader cannot be re-loaded.
Your question doesn't actually say whether you are concerned about a production downtime (i.e. reduce it by reloading the classes) or you want non-stop development. So I will try to clarify using the following points:
1) using <Context reloadable=true... in your catalina.home/conf directory you can make sure that your webapp reloads when a any class changes. You can add the resource change watchlist in <WatchedResources> element.
2) But this will reload the context, re-initialise the classloader, empty it's cache, and everything will be as if the webapplication has just started.
This approach will still leave your server unusable, because you have reloaded the Servlet's context. The true "Reload" is
1) You swap the byte code of the class, with some restrictions
2) JVM will return that class when "loadClass()" is called for that classloader.
This is java instrumentation. You can write your own agent which can hook into your JVM either at the beginning or in flight. However, you cannot define new method, and change static variables. These are JVM native restrictions (for Oracle HotSpot JVM, that I know of). You can use a different JVM e.g. DCEVM which doesn't have such restriction. So it's up to you how you want to handle your problem. If you know what you are doing (!), you can get away with replacing classes one-by-one. And you can even define a "Brand New Class", reference that class object/method in an existing/loaded class and instrument it to to pick up changes.
I hope this helps. All the answers here are what you need to make your decision.