I have written OSGi-Integration Part1 and Part2 about how to integrate Apache Aries Application into Glassfish v4. However, with the following reasons, maybe using EBA is not best or good solution for an user in Glassfish.
1. The plan of integrate Apache Aries Application into Glassfish v4 is still my solution rather than Glassfish v4( I have mentioned the factor in OSGi-Integration Part1).
2. In a real world, an user maybe is reluctantant to make big modification for his/her EAR, for example, splitting EAR into different OSGi bundles.
So, whether having no any way to make EAR to live in OSGi world?
Answer is Yes, and we have no way to directly make EAR to live in OSGi world in Glassfish. However, we can have some way to indirectly make EAR to use the power of OSGi world by using Glassfish OSGi-JavaEE!
Let us to see how to do.
[Background]
I have an EAR which is made up of WAR, a stateless EJB with local interface view and a shared library called “X”. And a servlet from the WAR called a method called “sayHello” from the stateless EJB. The EJB called a method called “HelloX” from “X”. That is to say, the simple dependent relationship is as following:
WAR -> EJB -> Shared Library.
Now, with the change of the user’s requirement, the shared library made some patches or made some new improvements. Then, the user wishes that rather than re-deploying the whole EAR(Business does not stop, and this is very important for some mission critical application, especially for Trade/Stock Business… ), only upgrading the shared library is enough.
The user or we naturally thinks of OSGi because of its dynamic nature.
However, Because of EAR’s nature, to some extent, he has got into trouble.
[Solution]
Here, still having two solution to resolve the scene,
1 Re-designing or Refactoring your application and splitting into different OSGi bundles, such EJB Bundle, WAR Bundle, and other common OSGi Bundle. The critical point is to remove EAR!
I have said that removing the whole EAR is not actual for the user or most of scenes.
2 Using EJB Bundle as a mediator or adapter, keeping EAR stay there.
In the above diagraph, We do the following adjustment:
1) Keeping the EAR and WAR stay there
2) Abstracting the EJB of EAR and making the EJB to be a standalone EJB Bundle and changing local interface view into remote interface view. (This point is very important, I will tell you why in “Proof”)
3) making that shared library to be a OSGi bundle
This should be very simple. OK, I want to say the most important thing : why this is feasible?
[Proof]
Firstly, EJB 3.1 has an very important feature called “portable global JNDI name”, and if you are not familiar with the feature, please seeing the [1] and [2].
[1]: http://glassfish.java.net/javaee5/ejb/EJB_FAQ.html#What_is_the_syntax_for_portable_global_
[2]: https://blogs.oracle.com/MaheshKannan/entry/portable_global_jndi_names
In solution 2, our EJB Bundle has been separated from EAR, that is to say, the EJB Bundle has been not in the same application with WAR. Noting the following contents from [1]:
“However, only the names corresponding to remote session beans can be portably looked up from code running outside the application in which the target bean is defined.”
So, In oder to make our WAR still to access the EJB, we must change the EJB’s interface view into Remote!
Secondly, here, I must say a thing that in Glassfish OSGi-JavaEE, EJB Bundle’s default portable global JNDI name rule is a litter different from the saying from [1]. Although general rule is the same as following,
“java:global[/<app-name>]/<module-name>/<bean-name>”
However, if being EJB Bundle, the module-name is made up of “${Bundle-SymbolicName_ Bundle-Version}”, for example, if having a EJB bundle which defines a bean called “ABean”, and its Bundle-SymbolicName is A , its Bundle-Version is 1.0.0, then, the EJB Bundle’s portable global JNDI name is “java:global/A_1.0.0/ABean”.
Update1: a community user asked me a question about portable global JNDI name.
[Question]"The portable JNDI names get quite complicated because they contain the version in it."
I can understand the reason he asks me because maybe on one day in the future, the EJB Bundle itself will also be upgraded, in this way, we do no wish to modify our WAR. However, the EJB Bundle's default portable JNDI names contains a version, which has hindered such flexiability.
So, we'd better not use the EJB Bundle's default portable JNDI names, instead, we need to config our ejb bundle to map an our own portable global JNDI name. In reality, this is very simple, and in [1], this is also mentioned,
"
The prefix of the string assigned to name can be ay one of the following :
"java:global/" -- to expose a global name
"java:app/" -- to expose a name only visible to code running within the same application
"java:module/" -- to expose a name only visible to code running within the same module
For example, to define a Singleton with a user-specified global JNDI name of "java:global/MySingleton" the corresponding code would be :
@Singleton
@EJB(name="java:global/MySingleton", beanInterface=SharedRemote.class)
public class SharedBean implements SharedRemote { ... }
"
Noting: we must specify both name and beanInterface properties in @EJB , if only specifying name property, while deploying the ejb bundle, you will see the following exception in the server.log
"Caused by: Invalid TYPE-level @EJB with name() = [java:global/EJBOSGi/HelloBean] and beanInterface = [class java.lang.Object] in class org.glassfish.ejb.example.HelloBean. Each TYPE-level @EJB must specify both name() and beanInterface().. Related annotation information: annotation [@javax.ejb.EJB(beanName=, mappedName=, beanInterface=class java.lang.Object, description=, name=java:global/EJBOSGi/HelloBean, lookup=)] on annotated element [class org.glassfish.ejb.example.HelloBean] of type [TYPE]"
In my updated sample, I used java:global/EJBOSGi/HelloBean as my ejb bundle's portable global JNDI name, needing to note that even if we config our portable global JNDI name, the EJB Bundle's default portable JNDI names are still available.
The reason that I mentions the portable global JNDI name is because we will use the portable global JNDI name in WAR’s servlet in order to access the EJB Bundle. OK, you should have known that this portable global JNDI name is just a glue connecting JavaEE EAR with OSGi!
Thirdly, the way that EJB bundle accesses OSGi bundle is very direct and clean, here, I used Glassfish OSGi/CDI to inject an OSGi Service. I wish that you can always use service-oriented way to make your application more portable and maintainable. Of course, if you are still not familar with Glassfish OSGi-JavaEE, you should truely see the following:
http://glassfish.java.net/public/GF-OSGi-Features.pdf
Update2: Currently, Glassfish OSGi-JavaEE only supports exporting Stateless Session Bean with local business interface views as OSGi services. So, for our ejb bundle with remote interface view, defaultly, no any ejb bean class will be regeristed with OSGi Services. And our ejb bundle plays a role of mediator. As for the reason not allowing stateful session bean and bean with remote interface view to be exported as OSGi Service has been out of the scope of the article. About the point, I have not still discussed with Sahoo. However, I guess that the reason not allowing bean with remote interface view to be exported as OSGi Service is that normally, bean with @Remote is deployed into a instance which is different from the instance of the bean's client . That is to say, normally, bean and bean's client are in two different jvms. Because Glassfish does not integrate or have a distributed OSGi runtime from some distributed provider(eg. Apache DCXF,...), the feature of remote OSGi service is not available. In this way, exporting beans with @Remote as OSGi services have not any sense. In addition, the reason of not allowing stateful session bean is a litter simple because of OSGi Service's nature: an OSGi Service is stateless!
[My Question]However, here's scene seems to have a litter unmatch : in order to access a standalone EJB, I use global JNDI name and change local interface view into remote view. This causes that we can not export the EJB as OSGi services because it is @Remote.In reality, our EJB and WAR are in the same jvm, so I also start to doubt whether the change of interface view is reasonable. I have sent a mail into glassfish ejb team and looked forward to team's reply.
Update3: once making our ejb bundle with local interface view, although deployment process is normal, while accessing our servlet, the following exception happened and access failed,
[2013-04-20T21:14:58.402+0800] [glassfish 4.0] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=106 _ThreadName=http-listener-1(3)] [timeMillis: 1366463698402] [levelValue: 900] [[
StandardWrapperValve[org.glassfish.web.example.ExampleServlet1]: Allocate exception for servlet org.glassfish.web.example.ExampleServlet1
java.lang.IllegalArgumentException: Can not set org.glassfish.common.example.HelloRemote field org.glassfish.web.example.ExampleServlet1.hello1 to com.sun.proxy.$Proxy300
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:680)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:665)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.inject(InjectionManagerImpl.java:484)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.injectInstance(InjectionManagerImpl.java:141)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.injectInstance(InjectionManagerImpl.java:127)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.createManagedObject(InjectionManagerImpl.java:324)
at com.sun.enterprise.web.WebContainer.createServletInstance(WebContainer.java:983)
...
Update4: After searching google, I found an valuable article[3], and the following should give clear explaination:
"EJB specification doesn’t guarantee any behaviour when you use the local business interfaces for inter-modules communication even if those modules are part of the same Application Server / JVM. Those views are used only for inner-module communication (e.g. access EJB-JAR from WAR when they’re are all packaged in one EAR.)
In case of inter-modules communication, you should use the remote business interfaces. Yes, there is an additional penalty of serizalization/deserialization (and perhaps something more?) even if you’re still residing on the same JVM."
So, if you want to use local interface for inter-module communication, maybe some application servers can make this to work successfully, however, from the above exception, glassfish does not allow us to do it.
In addition, my leader(Sahoo) has also specified that on such a scene, we should use @Remote, local interface does not work. So, in current glassfish or ejb spec, please using remote interface view !
However, I am looking forward to implementing EJB_SPEC-22 as earlier as possible very much!
[3]: http://piotrnowicki.com/2012/11/communication-between-ejb-modules-in-the-same-application-server/
[Sample]
Please seeing my attachment and trying it!
*Note*:
While using maven to build an Glassfish OSGi –JavaEE application, my leader(Sahoo) has mentioned that you’d better add fighterfish sample’s parent pom as your app’s parent pom because the fighterfish sample’s parent pom has prepared all for us( truly very cool!).
<parent>
<artifactId>sample.parent-pom</artifactId>
<groupId>org.glassfish.fighterfish</groupId>
<version>1.0.1</version>
</parent>
[Deployment/Running and Redeployment]
1 putting xbundle.api.jar and xbundle.impl.jar and ejbosgi-ejb.jar into glassfish/domains/domain1/autodeploy/bundles
2 deploying the ear using asadmin deploy command
3 accessing http://localhost:8080/earosgi-war/ExampleServlet1
You will see the following,
“Hello, World!! Sat Apr 20 14:08:53 JST 2013
The WAR calls EJB using Portable JNDI Name java:global/EJBOSGi/HelloBeanOutput from injected HelloBean ref: Hello, User, XSimpleImp' HelloX version1 has been called successfully!
Output from injected HelloBean ref: Hello, User1, XSimpleImp' HelloX version1 has been called successfully!”
4 modifing XSimpleImp class’s HelloX() method from xbundle.impl.jar
return "XSimpleImp' HelloX version2 has been called successfully!";
5 overriding the xbundle.impl.jar in glassfish/domains/domain1/autodeploy/bundles with new modified xbundle.impl.jar
6 accessing http://localhost:8080/earosgi-war/ExampleServlet1 again
You will see the following:
“Hello, World!! Sat Apr 20 14:19:35 JST 2013
The WAR calls EJB using Portable JNDI Name java:global/EJBOSGi/HelloBeanOutput from injected HelloBean ref: Hello, User, XSimpleImp' HelloX version2 has been called successfully!
Output from injected HelloBean ref: Hello, User1, XSimpleImp' HelloX version2 has been called successfully!”
Good luck to you!
Comments