All plug-ins should be installed in the location specified by the
plugins.dir
setting in base.config
.
While it is possible to also install them in a location that is on the
classpath, for example <base-dir>/www/WEB-INF/lib
,
it is nothing that we recommend. The rest of the information in this section
only applies to plug-ins that have been installed in the plugins.dir
location.
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
update the plug-in 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(); // Not ok; fails with ClassCastException MyOtherPlugin other = (MyOtherPlugin)def.newInstance(); // Ok; since now we are using the correct class loader MyOtherPlugin other = def.newInstance(MyOtherPlugin.class);
The first call 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.
The second call fails because 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 third call succeeds because now that we specify the class as an argument, BASE uses that classloader instead.
Another 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:
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. |