Overcome Class Loader confusion

Source: Internet
Author: User

The classloader solution only requires one cost. It provides a solution for class version conflicts.

Recently, I have heard colleagues and acquaintances complain about software version conflicts on the J2EE application server. This basic problem has been around for a long time. HoweverProgramThe number of Java libraries shared with the application server is increasing, and this problem seems to be getting worse and worse. If the application server uses version A of a Java package while the application on this server uses Version B of the package, A version conflict occurs. When an application tries to use this package, the system loads classes in version A instead of the classes in Version B. If the two types of behavior are different, problems may occur.
This situation is quite common, in part because so many application servers depend on open source software to some extent. The release cycle of commercial software is usually not as short as that of open-source software. Therefore, new application servers often do not contain the latest versions of some databases. In addition, the enterprise software upgrade cycle lags behind the supplier's release cycle, and in order to maintain stability, sometimes it will skip the release cycle. As a result, developers take it for granted to use the latest and best Java libraries, and tie them to the J2EE servers that combine earlier versions of some libraries.
This problem also occurs in other directions. When upgrading an application server, enterprise application software that has been used for many years may encounter compatibility problems. If the program depends on the old version library packaged with the application server, when the program attempts to access a nonexistent API element, the new library will cause runtime exceptions, such as nosuchmethodexception.
Many times, programmers do not notice that they are using libraries of different versions (here, I think classes are a collection of Java packages), because they have not used the changed APIs, or it does not cause any defects that have been corrected in later versions. Once a problem occurs, developers have to solve it. Replacing the application server library will interrupt the application server or other applications on the server. If developers do not have control over the application server, this solution is not feasible at all.

Sharing and class sharing
The crux of this problem is that most J2EE vendors have designed a class loading hierarchy for their products. This hierarchy will eventually load the class to the class loading program of the application server. Even if a separate class loader is assigned to each web application to prevent interference between the web application, the database used by the server can still be shared by all web applications. I want to be sure that some products do not have this problem, but I have heard reports that this problem has almost appeared in most major open scenarios. Source code Product and closed source Code Product.
A common method for programmers to solve version conflicts is to change the package name in the source code of the Library to the unique name used only by their own code and recompile the library, update all import statements and references to the package name in the source code. This solution only works when accessing the source code of the library, but not always. Although source code correction and recompilation are a short-term and reliable solution, in the long run, it actually takes longer. Write a shell script or use the IDE plug-in to automatically rename the package.
Find and replace the names of all packages in the source code. This will capture the usage of the package name outside the package declaration and Import Statement. For example, when you use a fully qualified class name, or when the name is inserted in the string used for reflection. The configuration file and other supported files can contain names, and some names used in reflection can be automatically generated. You do not always have to carefully check the code to rename all instances with a specific name. More importantly, when upgrading to a new version, you must repeat the entire process. The patch files based on previous modifications do not rename the names of all packages that appear in the new version, and the shell script may require updates to handle code changes.
Finally, source code correction brings a challenge to maintenance. You need to spend manpower and time to maintain something that is not originally needed. It is actually an independent branch of the source code tree.
The two main defects of source code correction are: the source code must be accessed, and a lot of work is required to maintain the source code. Maintaining the branch of a single database does not seem very difficult, but those who want to solve this problem must solve the conflict with multiple databases.
A solution for renaming source code packages is to overwrite binary files. There is one benefit of rewriting class files: you do not need to maintain a separate source code branch or the source code. The only requirement is the JAR file. There are few tools dedicated to renaming packages in jar files, but most code obfuscation tools have the ability to rename packages and classes contained in jar files. This method can make the Database Upgrade easier. All you need to do is to use the rewrite tool to process the JAR file, and then you will be done.

If you want to do this, do it!
Although class file rewriting seems very effective, it is not a perfect solution. This method does not work when the library uses reflection and the name of the embedded package in the string or configuration file. It does not free you from the unremitting efforts to change the use of the name in the application source code.
A more comprehensive solution is to do the first thing application server providers should do, then, you can use a custom classloader to split the database copy from the database copy on the server. To do this, you must write some additional code, but you do not need to change the package name of the existing source file. Library upgrade is easy because you only need to replace the old file with the new JAR file. How can we achieve this?
The root cause of version conflict is the class loading design of the application server. Before attempting to locate a class, the Web application Class Loader delegates the class loading to the parent class loader of the class. Therefore, if the class loader of the application server finds the class at the system location, it loads the version instead of the version packaged together with the Web application. If you use your own classloader without a parent class to guide the application, you can bypass the library used by the application server.
As an example of this technique, I have defined an interface called printer and an implementation class called versionprinter, which represents an application. Versionprinter depends on the version class, but requires a specific version 5.0.0. However, the application server uses version 1.0.2. Therefore, when versionprinter. Print is called, the "version: 1.0.2" character string is output ".

Which version?
Listing 1. versionprinter uses a new version 5.0.0, but the application server loads the old version 1.0.2.

 
Public interface printer {public void print ();} public class versionprinter implements printer {public void print () {Version V = new version (); system. out. println ("version:" + v. getversion () ;}} public class version {Public String getversion () {return "1.0.2" ;}} public class version {Public String getversion () {return "5.0.0 ";}}

Custom class loading
Listing 2. Using a custom classloader and a dynamic proxy, you can bypass the class path of the application server.

Package example; import Java. io. *; import java.net. URL; import java.net. urlclassloader; import Java. lang. reflect. *; public final class main {static class printerinvoker implements invocationhandler {object adaptee; Public printerinvoker (Object adaptee) {This. adaptee = adaptee;} public object invoke (Object proxy, method, object [] ARGs) throws throwable {method adapteemethod = adaptee. getclas S (). getmethod (method. getname (), method. getparametertypes (); If (! Adapteemethod. isaccessible () adapteemethod. setaccessible (true); Return adapteemethod. invoke (adaptee, argS) ;}} public static final void main (string [] ARGs) throws exception {versionprinter Vp = new versionprinter (); VP. print (); // hardcoded demo paths from build. XML file path1 = new file (system. getproperty ("user. dir ")," build. src2 "); file path2 = new file (system. getproperty ("user. dir ")," build. SRC "); Url [] classpath = new URL [] {path1.tourl (), path2.tourl ()}; urlclassloader Cl = new urlclassloader (classpath, null); object OBJ = Cl. loadclass ("example. versionprinter "). newinstance (); printer P = (printer) proxy. newproxyinstance (printer. class. getclassloader (), new class [] {printer. class}, new printerinvoker (OBJ); p. print ();}}

by defining a class loader, you can bypass the library of the application server. This Class Loader first views its own library directory before viewing the server library directory. Put the two version classes into two different build directories, and first place the 5.0.0 directory containing the version class in the class loading path (see Listing 2 ), I simulated this class loader. Then, I created a urlclassloade instance and initialized it with a custom path and an empty parent class. An empty parent class ensures that the class loading is not delegated to the parent class. Then, I loaded the class and used a dynamic proxy to map it to a known interface. When running this sample program, call versionprinter directly. print will output "version: 1.0.2", while dynamic proxy calls will output "version: 5.0.0". These output results show the desired class version, rather than the default version.
tips in the example, you do not have to change the application code at all. Sometimes programmers load special classes similar to version, but you may not want to do that. If you plan to do so, you will have to change the versionprinter. In this way, each conflicting class must be accessed through reflection. This will make the code messy. What you want to do is to create an application entry point (for example, printer) defined by the interface and customize the application to load. Then, the custom Class Loader loads all the deeper classes used by the application.

One-time purchase
It is possible to implement a package servlet that can use a custom class path and delegate servlet configuration. The wrapper servlet will use this custom class path to load the delegate Servlet and delegate all calls to that delegate servlet. Unfortunately, some servlet methods on the Application Server need to access the resources loaded by the application server class loader. Therefore, the Servlet technology of the package cannot be guaranteed to be effective in all circumstances. You can still use a technique to selectively attach a class to the Class Loader assigned to the parent class. When you load a class from a specific package, you can configure the class loader to not attach the class to the parent class.
The extra effort required by the classloader solution is a disadvantage, but the cost of the solution is one-time. The use of dynamic proxy should not reduce performance, as long as you use it wherever possible from the main application entry point, This will minimize the number of reflective method calls. The loaded class consumes additional memory, but that is the price to use different versions of the same class in the same JVM. Some new versions of J2EE servers may provide their own version conflict solutions. At least I remember a server rename the packages it uses, so you don't have to rename these table packages. However, even if you encounter a version conflict, you already have a way to get rid of it.

About the author
Daniel F. savarese is an independent software developer and technical consultant. He was the founder of Oro, a senior scientist at caltech's advanced computing processing center, and Vice President of WebOS software development. Daniel is the original author of the Jakarta Oro Text Processing Package and the Jakarta commons net network protocol library. He is also a co-author of how to build a Beowulf (MIT Press, 1999.

Source
Http://www.ftponline.com/channels/java/javapro/2005_03/magazine/columns/proshop/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.