OSGi Remoting EJB
OSGi Remoting over EJB remoting
The purpose of this project is allowing to call remote EJBs from within any OSGi container. The EJB service interfaces will be presented as (remote) OSGi services. This project does not implement any remoting protocol, instead it delegates to the corresponding, vendor dependent EJB client library.
At its core the what this project does is setting the thread context classloader (TCCL) to an appropriate classloader during
new InitialContext()
InitialContext#lookup()
- method calls on service proxies retrieved trough the above JNDI look up (EJB service calls)
This makes it possible to run any EJB client library that builds on top of JNDI.
The TCCL is set to a custom classloader that contains:
- the classloader of the ejb-client JAR
- the classloader of the EJB client library bundles
To support an EJB client library only two things have to be provided
- the environment used to create the
InitialContext
- the bundle symbolic names of the bundles that make up the EJB client library
In addition the ejb-client JARs have to be OSGi bundles with correct dependencies (minus the EJB client library) and OSGi Remoting service.xml files that map the service interfaces to JNDI names.
Contents
This project includes the following components:
- core, sever independent library that switches the TCCL and registers the OSGi services
- Java 6, 7 and 8 annotation processors that generate the required service.xml files for the ejb-client JARs
- sample integration for JBoss
- sample JBoss EJB client library bundle
- sample EJB
- sample EAR
- sample client
The provided JBoss integration is just a sample (eg. only supports localhost) and can be replaced by one that fits custom needs.
Pros
The advantages of this project are:
- supports dynamic starting and stopping of EJB client bundles at run time
- supports any OSGi runtime
- with little effort support every EJB client library that uses JNDI can be supported
- remote EJBs can be injected using OSGi Declarative Services
- remote EJBs can be injected using Eclipse 4 Dependency Injection
- client code has no dependencies on EJB libraries
- the look up of service proxies (can involve network access) is in its own thread to reduce impact on framework start up
- unobtrusive on build process, just an additonal xml that can be placed flexibly
In addition the following «OSGi smells» are avoided:
- buddy classloading
- DynamicImport-Package
- dependencies from the ejb-client JARs to the EJB client library
- one huge bundle containing all ejb-client JARs
- using fragments to make classes available to a bundle
Cons
Only one EJB client library is supported at runtime — starting, stopping and restarting this library is not supported.
Annotation Processor
A Java 6, 7 and 8 annotation processor are provided that generate OSGi Remoting service.xml files that follow Java EE 6 portable JNDI syntax
app-name/module-name/bean-name!bean-interface
To use the processor
- a dependency on
com.github.marschall:osgi-remoting-ejb-processor6
,com.github.marschall:osgi-remoting-ejb-processor7
orcom.github.marschall:osgi-remoting-ejb-processor8
with scopeprovided
has to be specified in the EJB project - the processor argument
javax.ejb.module.name
has to be set to the module name - the processor argument
javax.ejb.application.name
has to be set to the application name
In addition when the argument org.jboss.distinct.name
is set (can be empty) then JBoss proprietary EJB names are generated
ejb:app-name/module-name/distinct-name/bean-name!fully-qualified-classname-of-the-remote-interface(?stateful)
A concrete example can be found in osgi-remoting-ejb-sample-ejb.
Authentication
As OSGi Remoting does not cover authentication no API for authentication is provided. Authentication has to be done through the proprietary EJB client library API.
Lazy Login
There is support for starting with unauthenticated calls first, logging in and from then on performing only authenticated calls. The following steps have to be taken
- make unauthenticated calls
- authenticate through client library (proprietary EJB client library API)
- look up
com.github.marschall.osgi.remoting.ejb.api.ProxyFlusher
- call
com.github.marschall.osgi.remoting.ejb.api.ProxyFlusher#flushProxies()
- wait for method call to return
- no need to re-lookup OSGi services, they stay valid and use authenticated calls now
Design Decisions/Trade Offs
To enable dynamic starting and stopping of ejb-client JARs an InitialContext
per bundle is created (and closed). This adds a small overhead.
The methods #equals(Object)
, #hashCode()
and #toString()
are not intercepted by the OSGi service but instead forwarded to the EJB client proxy.
3.4.7 Session Object Identity
A client can test two EJB 3.x Remote/Local view references for identity by means of the
Object.equals
andObject.hashCode
methods.
Deploying
When deploying care has to be taken that that
- the bundle
osgi-remoting-ejb-client
is started automatically - an implementation of
com.github.marschall.osgi.remoting.ejb.api.InitialContextService
(eg.osgi-remoting-ejb-jboss
) is registered (eg. through Dynamic Services / Service Component Runtime)
Sample Client on Equinox
When deploying the sample client on Equinox the bundles org.eclipse.equinox.ds
and org.eclipse.equinox.util
have to be deployed. In addition it's recommended to deploy the bundles org.eclipse.equinox.console
and org.apache.gogo.shell
. In addition osgi-remoting-ejb-client
has to be auto stared.
JBoss
To make the JBoss client libraries provided with this project work the following steps have to be taken:
- the following VM argument has to be set
-Dorg.osgi.framework.system.packages.extra=sun.nio.ch,sun.refelect
- the following bundles have to be deployed:
org.jboss.spec.javax.transaction.jboss-transaction-api_1.1_spec
org.jboss.spec.javax.ejb.jboss-ejb-api_3.1_spec
javax.xml.jaxrpc-api-osgi
org.jboss.logging.jboss-logging
- the client library (
osgi-remoting-ejb-jboss-client
) has to be deployed unpacked
OSGi Service Lookup
When looking up the EJB service proxies though OSGi "manually" ServiceTracker#open(true)
or BundleContext#getAllServiceReferences
have to be used.
The filter (service.imported=*)
can be used to ensure only imported remote services are found.
ejb-client JARs
ejb-client JARs deployed in OSGi have to meet the following requirements
- valid OSGi bundles with (direct) dependencies specified through
Require-Bundle
orImport-Package
- no dependencies on EJB client libraries
- when running in Equinox the bundle header
Bundle-ActivationPolicy: lazy
has to be set - an OSGi remote service.xml in
OSGI-INF/remote-service
or a different location specified through theRemote-Service
bundle header. The easiest way to do this is through the included annotation processor.
Requirements
This project requires at least Java 6.
Caveats
You must not make any service calls from an OSGi callback thread (BundleListener
, ServiceListener
, ServiceTrackerCustomizer
, …).
You should not make any service calls from the UI thread.
Service proxies can only be registered once the sole InitialContextService
is registered because it's required for defining proper class loader.
The sample does work only with JBoss remote EJB, not with remote JNDI. The sample EJB does only work with JBoss to make it work with other servers <Aorg.jboss.distinct.name />
has to be commented out.
Customization
To add custom features like call logging or exception handling implementing a ServiceListener
and wrapping the services is recommended.
Open Issues
GlassFish client library is not yet implemented.
Geronimo client library depends on TOMEE-903 being fixed.
Use http://wiki.osgi.org/wiki/Use_Configuration_Admin_for_Configuration
An ESA (Enterprise Subsystem Archive) would be nice http://coderthoughts.blogspot.ch/2013/04/osgi-subsystems.html http://svn.apache.org/repos/asf/aries/trunk/esa-maven-plugin/pom.xml