We recommend that plug-in JAR files are installed outside the web server's
classpath. If you are using Tomcat this means that you should not
install the plug-in in the <base-dir>/www/WEB-INF/lib
directory or any other directory where the web server keeps it's classes.
The rest of the information in this section only applies to plug-ins that
have been installed following this restriction.
If the above recommendation has been followed BASE will use it's own classloader to load the plug-in classes. This have several benefits:
New plug-ins can be installed and existing plug-ins can be updated
without restarting the web server. If the plugins.autounload
setting in base.config
has been enabled all you have to
do to update a plug-in is to replace the JAR file with a new version.
BASE will automatically load the new classes the next time the plug-in
is used. If the option isn't enabled, the server admin has to manually
unload the old code from the web interface first.
Plug-ins may use it's own 3-rd party libraries without interfering with other plug-ins. This may be important because a plug-in may depend on a certain version of a library while another plug-in may depend on a different version. Since BASE is using different class-loaders for different plug-ins this is not a problem.
The classloading scheme used by BASE also means plug-in developers must pay attention to a few things:
A plug-in can only access/use classes from it's own JAR file, BASE core
classes, Java system classes and from JAR files listed in the plug-in's
MANIFEST.MF
file. See Section 26.1, “How to organize your plug-in project”.
A plug-in can also access other plug-ins, but only via the methods and
interfaces defined in BASE. In the following example we assume that
there are two plug-ins, ex.MyPlugin
and ex.MyOtherPlugin
,
located in two different JAR files. The code below is executing
in the ex.MyPlugin
:
// Prepare to load MyOtherPlugin SessionControl sc = ... DbControl dc = ... PluginDefinition def = PluginDefinition.getByClassName(dc, "ex.MyOtherPlugin"); // Ok Plugin other = def.newInstance(Plugin.class, null, sc, null, null); // Not ok; fails with ClassCastException MyOtherPlugin other = def.newInstance(MyOtherPlugin.class, null, sc, null, null);
The reason that the second call fails is that BASE uses a different
classloader to load the ex.MyOtherPlugin
class. This
class is not (in Java terms) the same as the ex.MyOtherPlugin
class loaded by the classloader that loaded the ex.MyPlugin
class. If, on the other hand, both plug-ins are located in the same
JAR file BASE uses the same classloader and the second call will succeed.
The first call always succeeds because it uses the Plugin interface which is defined by BASE. This class is loaded by the web servers class loader and is the same for all plug-ins.
A third option is that the ex.MyPlugin
lists the JAR file where ex.MyOtherPlugin
is
located in it's MANIFEST.MF
file. Then, the following
code can be used: MyOtherPlugin other = new MyOtherPlugin();
Tomcat includes a good document describing how classloading is implemented in Tomcat: http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html. BASE's classloading scheme isn't as complex as Tomcat's, but it very similar to how Tomcat loads different web applications. The figure on the linked document could be extended with another level with separate classloaders for each plug-in as child classloaders to the web application classloaders.
As of BASE 2.13 the default search order for classes has been changed. The
default is now to first look in the plug-ins class path (eg. in the same
JAR file and in files listed in the MANIFEST.MF
file).
Only if the class is not found the search is delegated to the parent class
loader. This behaviour can be changed by setting X-Delegate-First: true
in the MANIFEST.MF
file. If this property is set the
parent class loader is search first. This is the same as in BASE 2.12 and
earlier.
Note | |
---|---|
The benefit with the new search order is that plug-ins may use a specific version of any external package even if the same package is part of the BASE distribution. This was not possible before since the package in the BASE distribution was loaded first. |