DataNucleus is an extensible persistence tool. The DataNucleus Core persistence engine allows the
user to plug in many user extensions which will contribute to augment the numerous persistence
aspects faced by the Object/Datastore mapping. Plugins are loaded by a plugin manager which uses a
registry mechanism, inspecting jars in the CLASSPATH.
These plugins are defined in
OSGi format.
This format relies on there being a set of
extension-point
s where DataNucleus can be extended,
and then adding
extension
s for each of these points. The format means that with very little
effort any user can provide their own DataNucleus plugins and have them available very quickly.
In short, the three steps necessary for creating a DataNucleus plugin are
-
Review the DataNucleus interface that you will need to implement to generate the plugin, and
implement it
-
Create a file
plugin.xml
at the top level of your JAR defining the plugin details
(see below).
-
Update the META-INF/MANIFEST.MF file contained in the jar so that it includes necessary information
for OSGi.
A minimum META-INF/MANIFEST.MF for a plugin jar should look like this
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: myplugin
Bundle-SymbolicName: org.datanucleus.myplugin
Bundle-Version: 1.0.0
Bundle-Vendor: My Company
If you write a DataNucleus plugin and you either want it to be included in the DataNucleus
distribution, or want it to be listed here then please contact us via the
DataNucleus Forum
Non managed environment is a runtime environment where DataNucleus runs and plug-ins are not
managed by a container.
In this environment the plug-in discovery and lifecycle is managed by DataNucleus.
There is a 1 to N instance relationship from DataNucleus to a plug-in per PMF. More exactly, if only
one PMF exists, there is only one Plug-in instance for a Connection Pool Plug-in, and if "N"
PMF exist, there are "N" Plug-in instances for a Connection Pool Plug-in.
Standard Java runtimes and J2EE containers are considered non managed environments.
In non managed environments there is no lifecycle itself of plug-ins. Extensions implemented by
plug-ins are instantiated on demand and terminated on PMF closing, PM closing or in another
form depending in what the extension is used for.
Managed environment is a runtime environment where DataNucleus plug-ins are managed by a container.
The discovery, registry and lifecycle of plug-ins are controlled by the container.
There is no plug-in instance relationship from DataNucleus to a plug-in regarding PMF instances. In
managed environments, there is only one plug-in instance for one or "N" PMFs. Again, this is
managed by the container.
DataNucleus supports OSGi containers as managed environment.
In OSGi managed environments plug-in lifecycle is determined by OSGi specification. Once
activated, a plug-in is only stopped when the OSGi container finishes its execution, or the
plug-in is stopped by an OSGi command.
DataNucleus has mechanism that allows extending its persistence engine while not coupling the
DataNucleus engine to the extensions. This mechanism is known as "Extension Points", which are
well defined interfaces that allows the extension of certain aspects of DataNucleus in a dynamic
and consistent model. The "Extensions" are the implementation of Extension Points that are
registered in a PluginRegistry and used by DataNucleus Core or other DataNucleus Plug-ins.
A plugin owns an Extension. The behaviour is defined below :-
-
Lifecycle
: Each extension is created by a segment of code during the runtime
execution, and destroyed/released whenever they are no longer needed. This have no influence
with the plug-in lifecycle.
-
Manageability
: In non managed environments, the plug-ins are managed by DataNucleus
and maintained with a composition relation to the PMF instance. This allows a plug-in
"instance" per PMF. If multiple PMFs are created multiple extensions for an extension point
are instantiated. In managed environments, more precisely in OSGi containers, the plug-ins
are managed by the OSGi framework. Each plug-in will mostly be a singleton inside the OSGi
container.
-
Registration
: In non managed environments all plugins are registered using an
instance of JDOClassLoaderResolver (so using the current ClassLoader of the
PMF and the current thread). This means that the /plugin.xml and /META-INF/MANIFEST.MF files
must be accessible to the classloader. In managed environment this is handled by the
container.
-
ClassLoading
: The classloading in non managed environments is usually made of one
single ClassLoader, while in managed environments each plug-in has it's own ClassLoader.
-
Configuration
: Some Extensions needs to retrieve a configuration that was set in
the PMF. This means that Plug-ins should not hold singleton / static configurations if they
want to serve to multiple PMFs at the same time.
-
Constructors/Methods
: In order of having consistent and avoid changes to
extension-point interfaces, the Extension Constructors or Methods (either one) should have
receive a PMFContext instance as argument. If by the time the Extension Point is designed
clearly there is usage for a PMFContext, then the Extension-Point does not need to take the
PMFContext as argument, but keep in mind that a 3rd Extension may need one due to different
reasons.
-
Instantiation
: Inside the DataNucleus Core, regardless if the runtime is OSGi
managed or non managed, extension instances are created per PMF. DataNucleus Extensions
should always be created through a PluginManager, regardless if the managed environment
would allow you to instantiate using their own interfaces. This allows DataNucleus and
its Plug-ins to run in non managed environments.
Plugins can make use of existing persistence properties defined by "core" etc. They can also
add on their own persistence properties. They define these in the
plugin.xml
. Let's take
an example
<!-- PERSISTENCE PROPERTIES -->
<extension point="org.datanucleus.persistence_properties">
<persistence-property name="datanucleus.rdbms.query.fetchDirection" value="forward"
validator="org.datanucleus.store.rdbms.RDBMSPropertyValidator"/>
</extension>
So here we have a property defined in the "store.rdbms" plugin which defines a persistence
property
org.datanucleus.rdbms.query.fetchDirection
with a default value of
forward
and with values validated by the class
org.datanucleus.store.rdbms.RDBMSPropertyValidator
.
This means that when we instantiate a PMF/EMF we automatically get that property defined
with value "forward", and if we provide it ourselves we override this default. We do the
same with any properties we want in our own plugins.
In order to avoid conflicts between different property names that can be set in the PMF and to
have a consistent naming schema for properties, the following recommendations should be
applied.
Naming
the persistence property name should be prefixed by the plug-in id. Example:
Plugin: org.datanucleus.myplugin1
Property: myprop1
The persistence property should look like
datanucleus.myplugin1.myprop1
.
If an extension point defines a new persistence configuration, the property name should be prefixed by
the extension-point id. Example:
Extension-Point id: org.datanucleus.myplugin2.myextensionpoint2
Property: myprop2
The persistence property should look like
datanucleus.myplugin2.myextensionpoint2.myprop2
.
Another form of persistence configuration could happen if multiple Extension-Points uses the same
information. In this case an abstraction of the plug-in id and extension point id could be
used, as the following example. However, make sure to use this naming schema only when
absolutelly necessary.
Extension-Point id: org.datanucleus.myplugin3.myextensionpoint3
Extension-Point id: org.datanucleus.yourplugin3.yourextensionpoint4
Extension-Point id: org.datanucleus.theirplugin4.theirextensionpoint5
Property: myprop345
The persistence property should look like
datanucleus.somepluginXXXX.someextensionpointYYYY.myprop345
. Alternativelly,
the PMF property could also look like
datanucleus.myplugin3.myextensionpoint3.myprop345