Some Confusion about the source code of Tomcat
Some day before, I began to read the source code of the tomcat
First, the server starts from the main method in the org.apache.catalina.startup.BootStrap, but when I go into the code bootstrap.init(),
and I was confused by the following code.Like:
Just for easy to debug the tomcat, I change the code like this:
The code can still work. And in the Tomcat source code, there are many code block like this, For example,in org.apache.catalina.startup.BootStrap.start()
we can find the following code:
Still for easy to debug, I turn the code into:
>
The code still works fine.So I was confused, Here are my questions.
what's difference between the two kinds of code?
Why the coder of Tomcat do not write the code like what I write? what's the benefit?
If the Tomcat with my kind code works in a production environment, what will happen or nothing will happen?
1) what's difference between the two kinds of code?
The Tomcat code eliminates a static dependency on the Catalina class. In general, that has a couple of potential benefits:
It may allow you to substitute an alternative version of that class ... without changing the code. (But not here, because the FQN for the class is hard-wired.)
For some JVMs, it may result in deferred class loading which can give faster JVM startup. (I'm not sure if that is relevant here.)
In addition, the Tomcat code is explicitly creating a new classloader and loading the Catalina using that ... rather than the default one for the Bootstrap class. I'm not sure of the significance of that.
2) Why the coder of Tomcat do not write the code like what I write? what's the benefit?
Ask the coder. The (possible) benefits are as I described above, but it may have been done for some other reason ...
3) If the Tomcat with my kind code works in a production environment, what will happen or nothing will happen?
Probably nothing, though it might affect the classloader organization for Tomcat in some way that impacts on your webapps.
But if you are worried, don't change the code! There is no real need to change this ... based on why you said you are doing it. There's a saying:
"If it ain't broken, don't fix it."
Related
I'm working on a sandbox feature for my java antivirus, and I've come into a question: Does the specified package on a class matter for compilation?
Example:
I'm running a program that wants to use Runtime.getRuntime().exec(), when the classloader attempts to load that to run a method, does it check the package qualified in the file, if they exist? I would prefer not to try and change files in the JVM, but to simply load ones from a different package. I can accomplish the loading and such, but my only dilemma, will it crash and burn? Inside the java, it would be registered as say, java.lang.Runtime, but the compiled code will say for example pkg.pkg.Runtime and will it need to extend the old runtime? My guess is that extending the old runtime would just break it. Does anyone know anything about this? I'm working on making a testable example, but I'm still a bit away and wanted to get some answers, as well as this might benefit some people.
Does the specified package on a class matter for compilation?
Yes it does matter. A class called pkg.pkg.Runtime() cannot be loaded as if it was java.lang.Runtime.
Furthermore, if my memory is correct, the JVM has some additional security measures in it to prevent normal applications from injecting classes into core packages such as java.lang.
If you need to change the behaviour of the java.lang.Runtime class (for experimental purposes!) then I think you will need to put your modified version on the boot classpath, ahead of the "rt.jar" file.
However:
This level of tinkering can easily result in JVM instability; i.e. hard JVM crashes that are difficult to diagnose.
If your aim is to produce a "production quality" tool, then you will find that things that involve tinkering with the JVM are not considered acceptable. People are going to be very suspicious of installation instructions that say things like "add this to your installed JVM's bootclasspath".
Distributing a "tinkered with" JVM may fall foul of Oracle's Java licensing agreement.
My advice would be to look for a less intrusive way of doing what you are trying to do. For instance, if you are trying to do virus checking, either do it outside of the JVM, or in a custom application classloader.
You commented:
I have a custom classloader, my question is: If I compile a class that is labelled as say, pkg.pkg.Runtime, can I register in my classloader as java.lang.Runtime?
As I said above, no you can't. A bytecode file has the classname embedded in it. If you attempt to "pull a swifty" by loading a class with a different name, the JVM will throw an Error.
And:
If not, then how can I replace the class? If the compiled package name has to equal the request referenced naming, then can I modify the .class file to to match, or perhaps compile it as if it were in the java.lang package?
That's what you would have to do. You need to name the class java.lang.Runtime in the source code and compile it as such.
But what I meant by my advice above is that you should use do the virus checking in the class loader. Forget about trying to replace / modify the behaviour of Runtime. It is a bad idea for the reasons I listed above.
The situation at hand is not as simple as the title seems to indicate.
Java 1.6_17 running via JWS.
I have a class, lets say MyClass and one of its instance member variables is a Type from an errant 3rd party library where during class initialization it dynamically tries loading some of its own classes with Class.forName(String). In one of these cases it happens to dynamically call: Class.forName("foo/Bar").This class name doesn't follow the JLS for binary names and ultimately leads to a java.lang.NoClassDefFoundError: foo/Bar.
We have a custom ClassLoader which I've added a sanitize method to ClassLoader.findClass(String) and ClassLoader.loadClass(String) which fixes this problem.
I can call stuff like:
myCustomClassLoader.findClass("foo/Bar")
Which then loads the class without any problems. But even if I load the class ahead of time, I still get the exception later. This is because during initialization of MyClass which refers to Bar - their code ends up calling Class.forName("foo/Bar") in a static block somewhere. This actually would be OK if the ClassLoader it was trying to use was my custom class loader. But it isn't. It is the com.sun.jnlp.JNLPClassLoader which doesn't do such sanitation, thus my problem.
I've made sure that Thread.currentThread().getContextClassLoader() is set to my custom class loader. But this (as you know) has no effect. I even set it as the first thing i do in main() due to some stuff I read and still, MyClass.class.getClassLoader() - is the JNLPClassLoader. If I could force it to NOT be the JNLPClassLoader and to use mine instead, problem solved.
How can I control which ClassLoader is used to load the class via their static Class.forName("foo/Bar") call made during class initialization? I believe if I can force MyClass.class.getClassLoader() to return my custom class loader, my problem will be resolved.
I'm open to other options if anyone has ideas.
TL;DR: Help me force all Class.forName(String) calls in a third party library which are referenced by MyClass - to use the classloader of my choosing.
This reminds me of an article I read 10 years ago about the classloading arrangements in Java. It's still there on JavaWorld.
The article won't answer your question directly, but it may help understand your problem. You need to cause MyClass to be loaded through your custom class loader and trump the default class loading behavior, which is to first delegate class loading to the parent classloader and only attempt to load a class if that fails.
Allowing MyClass to get loaded by a classloader other than yours will store a relationship from the instantiated class to that classloader (via getClassLoader) and cause Java to use that other classloader to try to discover any referenced classes found at compile time, effectively bypassing your custom class loader by virtue of the class loader hierarchy and the delegation model. If MyClass is instead defined by your class loader, you get a second chance.
It sounds like a job for something like URLClassLoader, overriding loadClass and trumping the delegation model for classes residing in your JARs. You'll probably want to use a bootstrap approach (as suggested by Thomas in a comment above) to force a single entrypoint class to be loaded through your custom class loader, dragging all the others with it.
Also informative is this other JavaWorld article by the same guy, which warns you about the caveats of Class.forName. That too may trip your classloading arrangements.
I hope this helps and proves informative. In any case, it sounds like a difficult solution that is easy to break as your code evolves.
I think everyone gave good solid attempts at answering the problem. However, it turns out that I misdiagnosed the problem.
I had a coworker take over the problem and asked him to get a JDK with debug flags on so we could debug the JNLPClassLoader to see what was going on as I had tried all of the suggestions here + some.
We ended up getting OpenJDK because recompiling the JDK from scratch is a total nightmare (we tried). After getting OpenJDK working with our product and debugging through the JNLPClassLoader - it turns out that it was still using a REALLY old .jnlp from months earlier that had the resource path wrong and thus why it couldn't find the class.
We were confused why it was still using the ancient .jnlp even though we had redeployed the server correctly many times with the correct .jnlp and lots of code changes between which were reflected in our client application when run.
Well, it turns out that on client machines, Java caches the .jnlp file. Even if your application changes and it redownloads your application, it still won't re-download the new .jnlp for whatever reason. So it will use all of the new code, but look up resources/class paths using the cached .jnlp.
If you run:
javaws -uninstall
On the client machine then that will clear the .jnlp cache and next time it will use the correct .jnlp file.
Really sad that this was the problem. Hopefully, this saves someone else endless hours of frustration like it caused us.
If you run out of ideas with patching the ClassLoaders themselves, you might consider rewriting the library bytecode itself -- just replace the "foo/bar" constant with the correct value, and then you don't need to customize further class loading at all!
You could do this either at runtime or beforehand.
Im' running this code on a Linux Red Hat with the sun/oracle JVM 1.6_23, inside a VMWare server.
After some times the JVM seem to be unable to access my anonymous inner classes.
My classpath is fine, since it works for a period.
All i got is errors like this one :
java.lang.NoClassDefFoundError: com/mycompany/impl/MyClassImpl$1
at com.mycompany.impl.MyClassImpl.markAsDeletable(MyClassImpl.java:45).
line 45 is the first line below, it can't find my new Predicate
DomaineVO domaineVO = Iterables.find(domainesVO, new Predicate<DomaineVO>() {
#Override
public boolean apply(DomaineVO input) {
return input.getId().equals(domaine.getIdentifier().toString());
}
});
Any ideas ?
Finally, i think we might have put the finger on the problem.
We are running this code on jetty, and we use automatic deploy of the .war files.
By default jetty use the java.io.tmpdir to deploy the .war files.
Our problem was only on linux, and mostly early in the morning (as soon as the first office worker use the app).
The cause was the cleaning of the /tmp at night (made by a LOGROTATE command on our servers).
Rules of thumb : Never use /tmp for too long time, and make jetty deploy war in a directory of your own.
Thanks everyone
It sounds like the JVM can't find the class file for the anonymous class. This would be named 'MyClassImpl$1.class' - if it's not present in the classpath, something must have deleted it. If it is present then there's something wrong with the JVM.
This sounds very odd. Firstly if the code works for a time, as you say, the file must be there. Secondly, it is rare for a JVM to unload a class from memory once it has been used. Some JVMs will do it in tight memory situations or as part of a GC, but as an optimisation they normally stick around.
My only guess is you are using the JVM in a situation where ClassLoaders are changing. If you are using Netbeans (especially) but I think also eclipse, then if you partially recompile the code then classloaders might not match. Is this running within an IDE?
An alternative is a ClassLoader changing. If you re-release to a running webserver or application server, then an old class will not have a matching classloader for the new instance. The ClassLoader may not be able to find the old version, even though the file is there. Are you re-releasing to an Application/Webserver?
Finally, I guess this might be possible with Serialization. If the class is Serializable, and the serialVersionUIDs don't match I guess this might be able to happen. Are you doing any object serialisation here?
I am using Eclipse Helios to program JSP and POJOs running on Tomcat 5.5. I am trying to use the org.apache.catalina.connector.Response class as follows:
import org.apache.catalina.connector.*;
...
Response resp = (Response) r;
where r is an instance of HttpServletResponse. Eclipse gives no warnings, and the project compiles fine. However, when I browse to a page that contains this code, I get the following error:
java.lang.ClassNotFoundException: org.apache.catalina.connector.Response
any idea of what is going wrong?
I have also tried it this way, with the same result.
org.apache.catalina.connector.Response resp = (org.apache.catalina.connector.Response) r;
Thanks,
Ean
A ClassNotFoundException means that a class was available at compile time, but not at run time. So it makes no difference how you import it in your source code.
I haven't checked, but it's quite possible that Tomcat doesn't let web apps access its internal classes. And the reason I haven't checked is that I know it'd be a bad idea even if I could do it. You should code to the API (in this case, the stuff under javax.servlet.*) not to the implementation.
Clearly, the Tomcat designers don't think that it is a good idea for webapps get at its internal classes. Here's a couple of possible reasons:
It makes your code dependent on Tomcat.
It makes your code fragile in the face of changes to internal details; i.e. it depends on the Tomcat version.
The Tomcat stack or other things inside your webapp context could do things that mean that your Response object is a wrapper rather than the class you are expecting.
To change make your code "work" you would probably need to tinker with the Tomcat JAR files ... which will make it even less portable. For more information that might help you to slit your own throat, refer here. Note that this is Tomcat 5.5 specific ... and the Tomcat 6.0 version of the page is different.
The reason I want to turn it to a org.apache.catalina.connector.Response is to read what's in the HTTP response headers before they are sent to the client.
I see. Well perhaps you should be implementing a custom Valve. (This is a Tomcat-specific thing.) Or better still, see if one of the existing Valve implementations will do the job for you.
I don't' think you can use that class. I am not sure what you are trying to do, but use HttpServletResponse and you should be good to go.
I have a jar package that I wrote using netbeans. This package is called from other java file. The jar calls a webservice and is supposed to do something with it. Now everything works fine locally. I compiled the files and locally and uploaded them to the server and when I run it, I get the "Service could not be initialized".I am not sure how to debug this. I am pretty new to java. What is the best approach here to solve the issue?
I would start by implementing logging (I like log4J) in your project so you can get some better details of what is actually going wrong. This will be very useful not only now but in the future as things go wrong (they inevitably will) you will be able to solve them based on how good of a job you did logging what is happening in your application. Right now it sounds like an error is bubbling up and you're not getting much detail about it. Logging should help you determine not only what went wrong but where it happened and what the application was doing at the time.
Try this short introduction to log4j to get started.