Custom Tomcat Webapp ClassLoader

I am trying to implement a custom class loader for tomcat. My first attempt gave an exception to the cast class (apparently, tomcat is trying to throw my bootloader at org.apache.catalina.loader.WebappLoader). Ok, I expanded WebappLoader and added the catalina.jar file to my build path.

Now I am ready for deployment (I think). I get this error:

SEVERE: Catalina.start: LifecycleException: start :: java.lang.NoClassDefFoundError: org / Apache / Catalina / loader / WebappLoader

Tomcat comes with a .jar katalin to run, so I'm 99.9% sure it is already loaded into tomcat. I checked this by checking /server/lib/catalina.jar which contains apache WebappLoader. In addition, manually linking another catalina.jar file creates all the problems as expected.

I'm confused. Any advice would be hot.

Thanks!

Update. Interestingly, the same thing on tomcat6 (the WebappLoader extension running on tomcat5.5) still throws a ClassCastException. It sounds to me like the throwing class was loaded using another loader from the one that loaded my class. I don’t understand how I would still control this, unless there was a tomcat configuration file somewhere else? Any ideas for tomcat6 too?

+4
source share
2 answers

I may be tight, but I think it should be WebappClassLoader, not WebappLoader. Import looks fine.

+5
source

Without knowing the specifics of your code and settings, it is impossible to be sure, but this reminds me of a problem that I encountered when trying to use my own custom class loaders.

The scenario is this:

  • The bootloader (JVM / Tomcat / whichever itself) loads your code
  • Your classloader loads your add-ons that are not available on the class path above.
  • Your code refers to these add-ons.
  • These add-ons are not available in the same namespace as the code loaded by the loader.
  • Your code executes in the loader namespace, tries to reference the code in the user namespace, and this is not visible from the loader namespace. So, at this point, the JVM is freed from NoClassDefFound errors.

The reason for this is that the class loader hierarchy works in only one direction: for example, code from namespaces (child class loaders) is not available (see) in a wider parent namespace (parent class loader); and there is no good way to crack this system.

Fortunately, you can define the interfaces available in the parent namespace and then implement them in code only visible in the sub-namespace. Then, code that lives inside the parent namespace can use that interface name for casting and method calls, and thus, regardless of your namespace component.

For this to work, you must make sure that your custom classloader not only loads components that the parent loader does not have access to (i.e., is outside its classpath), but also those bits of your code that interact directly with these components and explicitly refer to these characters (typenames / methods, etc.). Otherwise, references to these components end in the parent namespace (remember, the bootloader loader loads all its own code by default), and you are returned to the original problem.

You can do this by subordinating the delegation model to a given model. You should usually put off the parent loader before trying to load the classes yourself. However, now you must check in advance that your code does not touch any of these components that are not available to the parent loader. The easiest way is probably to configure your code path so that the class loader supports the set of classes that it should load, rather than allowing the parent loader to boot.

You should find a way to tell your custom classloader somehow, for this you could use type annotation in class declarations. The idea here is that your classloader examines the classes loaded by the parent, and if it finds a specific user type type annotation, it will call annotation methods to get the class name of the class, which should not allow its parent load loader.

Example:

@MyCustomAnnotation(implementation="my.custom.package.MyImpl") public class MyFeatureProvider { public MyFeature getFeature() { // return an instance of MyImpl here } } 

Note that since the MyFeatureProvider class will be loaded before MyImpl , your classloader will know in advance about the annotation in MyFeatureProvider so that it can override the default delegation model for MyImpl. Since the rest of your code only interacts with MyImpl as an instance of MyFeature , the parent loader should never stop when it sees undefined characters - and the ClassNoDefFound error has been fixed.

+1
source

Source: https://habr.com/ru/post/1333416/


All Articles