Eclipse RCP/RAP and Remoting with JAX-RS, Spring Data JPA and CXF DOSGi [step4]
In [step3] we have managed our UserService on Client and Server side with JAX-RS and Apache CXF DOSGi.
Embedding an HTTP server in Equinox
At this step the server side works with the HTTP Server Jetty which is an OSGi bundles. This mode is called Embedding an HTTP server in OSGi container : it means that HTTP server and WEB application are OSGi bundles. The OSGi container starts the HTTP server bundle. Embedding an HTTP server in Equinox explains how this mean is done with Equinox.
You can resume « embedding an HTTP server in OSGi container » like this :
OSGi container -> HTTP Server (bundle) -> WEB Application (bundle).
Equinox in a Servlet Container (ServletBridge)
It works great but sometimes you have not the luck to have an HTTP Server which supports OSGi like Apache Tomcat. For instance in our case, when we wished to deploy our XDocReport RAP Application on CloudBees, whish provides an Non OSGI HTTP Server Apache Tomcat.
Fortunatly, Server-Side Equinox provides Embedding OSGi container in a servlet container mode : it means that it’s a WEB Application which contains the OSGi container (and another bundles) which start the OSGi container. Equinox in a Servlet Container explains how this mean is done with Equinox.
You can resume « embedding OSGi container in a servlet container » like this :
HTTP Server -> WEB Application -> OSGi container.
To manage this mode you must create a classic WAR which hosts your OSGi plugins and ServletBridge. In this article we will explain how to create a WAR with our UserService on server side with the Eclipse Plug-In Libra WAR Product which was created for creating WAR from RAP Application launches. But it can be used too with OSGi launches (like we need in this article).
To create a WAR of CXF DOSGi application (server side), you must :
- register your JAX-RS service with HttpService :
<!-- HttpService --> <entry key="org.apache.cxf.rs.httpservice.context" value="/UserService" />
- create a WAR which hosts OSGi bundles and ServletBridge. WAR Product gives you the capability to create a *.warproduct file from a launch (RAP, RCP, OSGi lanches, etc). This *.warproduct file is used to export it into a WAR to generate WAR.
At the end of this article we will create the the cxf-dosgi-jpa.war WAR. I have deployed this WAR on the CloudBees to see it on action. You can test for instance go at http://cxf-dosgi-war.opensagres.cloudbees.net/UserService/user/findAll to see JSON of the all users.
Download
WAR
You can download the following WAR that we will create in this article:
Bundles
You can download eclipse_spring_dosgi_step4.zip which contains the bundles and JARs explained in this article.
To use this zip, unzip it :
- import those projects in a workspace.
- open the TargetPlatform/eclipsespring.target file and click on Set as Target Platform. If you have problem with Target Definition, please read Problem with Install with Nebula (Incubation) P2.
- select TargetPlatform/launch/Server Remoting – JAXRS DOSGi – JPA Dao.launch to test the Remoting UserService JAX-RS with Mock Dao and Run it.
- select TargetPlatform/launch/Server Remoting – JAXRS DOSGi – JPA Dao.launch to test the Remoting UserService JAX-RS with JPA Dao and Run it.
- select TargetPlatform/launch/Client Remoting – JAXRS DOSGi – Simple OSGi Client.launch to test the Client Remoting UserService JAX-RS and Run it.
Prerequisite
Install WAR Product Plug-in
To follow this article you must install Eclipse Plug-In Libra WAR Product. To do that, please read Install WAR Product article.
Install RAP Target Platform.
To create a WAR from a WAR Product created by an OSGi launches, we need ServletBridge. The more easy mean is to install RAP Target Platform. To do that, open the TargetPlatform/eclipsespring_rap.target file and click on Set as Target Platform. If you have problem with Target Definition, please read Problem with Install with Nebula (Incubation) P2 :
Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP]
The launch works with with Embedding an HTTP server in OSGi container mode, on other words the HTTP Server (Jetty) is an OSGi bundle. The OSGi container (Equinox) starts the HTTP Server which is a n OSGi bundle.
The Server Remoting – JAXRS DOSGi – JPA Dao launch was created when RCP Target Platform was activated. Now we have activated RAP Target Platform, so we need adjust a little this launch. To do that duplicate Server Remoting – JAXRS DOSGi – JPA Dao to Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP].
After that, you must modify the launch, by selecting:
- javax.servlet.
and selecting:
- org.eclipse.equinox.console.
- org.apache.felix.gogo.shell.
- org.apache.felix.gogo.runtime.
to avoid having this error :
!MESSAGE Could not find bundle: org.eclipse.equinox.console !STACK 0 org.osgi.framework.BundleException: Could not find bundle: org.eclipse.equinox.console at org.eclipse.osgi.framework.internal.core.ConsoleManager.checkForConsoleBundle(ConsoleManager.java:211) fr.opensagres.dao.jpa.eclipselink
Run the launch, open a WEB Browser and go at http://127.0.0.1:9000/fr/opensagres/services/UserService/user/findAll , you will see JSON content :
{"user":[{"firstName":"Angelo","lastName":"Zerr"},{"firstName":"Pascal","lastName":"Leclercq"},{"firstName":"Amine","lastName":"Bousta"},{"firstName":"Mickael","lastName":"Baron"},{"firstName":"Jawher","lastName":"Moussa"},{"firstName":"Arnaud","lastName":"Cogoluegnes"},{"firstName":"Lars","lastName":"Vogel"},{"firstName":"Olivier","lastName":"Gierke"},{"firstName":"Tom","lastName":"Schindl"},{"firstName":"Wim","lastName":"Jongman"}]}
Server Remoting – JAXRS DOSGi – Mock Dao [RAP TP]
You can do the same thing for the Mock Dao by creating the Server Remoting – JAXRS DOSGi – Mock Dao [RAP TP] by duplicating the Server Remoting – JAXRS DOSGi – Mock Dao launch.
Modify fr.opensagres.dao.jpa.eclipselink
Remove the .qualifier used in the OSGi Fragment fr.opensagres.dao.jpa.eclipselink for Hosts:
to avoid having this error
when WAR will be generated. For more information please read Create a WAR from RAP Application with Libra WAR Product [step3].
HttpService
ServletBridge works with « Equinox in a Servlet Container (ServletBridge) » mode, it means that it’s classic WEB Application with web.xml which declares ServletBridge servlet. This servlet starts the OSGi container Equinox. This ServletBridge provides too an implementation of HttpService which is registered in the OSGi services registry. The HttpService gives the capability to register/unregister a Servlet.
To use ServletBridge with CXF DOSGi, the JAX-RS service must be registered in the HttpService.
Manage ONLY Equinox in a Servlet Container (ServletBridge)
The section Service Provider properties For Configuring RESTful JAXRS-based endpoints and consumers shows you that it’s possible to do that with the org.apache.cxf.rs.httpservice.context property.
In our case we will do that:
<!-- HttpService --> <entry key="org.apache.cxf.rs.httpservice.context" value="/UserService" />
and our service will be available with /UserService path like http://cxf-dosgi-war.opensagres.cloudbees.net/UserService?_wadl whish shows you the WADL of the UserService on the CloudBees.
Modify the META-INF/spring/module-osgi-context.xml of the fr.opensagres.remoting.exporter.dosgi.jaxrs bundle like this:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <osgi:reference id="userService" interface="fr.opensagres.services.UserService" /> <osgi:service interface="fr.opensagres.services.UserService"> <osgi:service-properties> <entry key="service.exported.interfaces" value="*" /> <entry key="service.exported.configs" value="org.apache.cxf.rs" /> <entry key="service.exported.intents" value="HTTP" /> <entry key="org.apache.cxf.rs.databinding" value="jaxb" /> <!-- Logs --> <entry key="org.apache.cxf.rs.in.interceptors" value="org.apache.cxf.interceptor.LoggingInInterceptor" /> <entry key="org.apache.cxf.rs.out.interceptors" value="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <!-- JAXB Context --> <entry key="org.apache.cxf.rs.provider"> <array> <ref bean="jsonProvider" /> </array> </entry> <!-- HttpService --> <entry key="org.apache.cxf.rs.httpservice.context" value="/UserService" /> </osgi:service-properties> <ref bean="userService" /> </osgi:service> <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider"> <property name="singleJaxbContext" value="true" /> <property name="extraClass"> <list> <value>fr.opensagres.domain.User</value> </list> </property> </bean> </beans>
At this step we can create a WAR Product to export after the WAR, but we will do that after.
Manage « Embedding an HTTP server in OSGi container » and « Equinox in a Servlet Container (ServletBridge) » both
At this step, our JAX-RS UserService works with HttpService. If you start the launch Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP] you will see this error :
INFO: ListenerHookImpl: skipping import request for excluded classs [org.osgi.service.http.HttpService] 6094 [pool-1-thread-1] INFO org.apache.cxf.dosgi.dsw.service.RemoteServiceAdminCore - found handler for fr.opensagres.services.UserService -> org.apache.cxf.dosgi.dsw.handlers.JaxRSHttpServiceConfigurationTypeHandler@d3199e Exception in thread "pool-1-thread-1" org.osgi.framework.ServiceException: CXF DOSGi: No HTTP Service could be found to publish CXF endpoint in. at org.apache.cxf.dosgi.dsw.handlers.HttpServiceConfigurationTypeHandler.getHttpService(HttpServiceConfigurationTypeHandler.java:235) ...
This error comes from that there is none HttpService registered because we are in Embedding an HTTP server in OSGi container mode. It’s possible to manage the both mode by setting the property org.apache.cxf.rs.httpservice.context with dynamic mean:
<!-- HttpService --> <entry key="${httpservice-config.key}" value="/UserService" />
The (optional) ${httpservice-config.key} value :
- for Embedding an HTTP server in OSGi container mode: is not defined. It means that you will do that :
<!-- HttpService --> <entry key="${httpservice-config.key}" value="/UserService" />
and HttpService for CXF DOSGi will be disabled.
- for Equinox in a Servlet Container (ServletBridge) mode : it comes from a property file httpservice-config.properties which is hosted by the fr.opensagres.remoting.exporter.dosgi.jaxrs-httpservice fragment linked to the fr.opensagres.remoting.exporter.dosgi.jaxrs bundle with this content :
httpservice-config.key=org.apache.cxf.rs.httpservice.context
It means that you will do that :
<!-- HttpService --> <entry key="org.apache.cxf.rs.httpservice.context" value="/UserService" />
and HttpService will be used to register our UserService with CXF DOSGi with /UserService path.
Use ${httpservice-config.key}
To use optional ${httpservice-config.key} we need to define a property-placeholder like this:
<context:property-placeholder location="classpath:httpservice-config.properties" ignore-resource-not-found="true" ignore-unresolvable="true" />
The attribute :
- ignore-resource-not-found= »true » means that if httpservice-config.properties field is not found, Spring doesn’t throw an error (in the case of Embedding an HTTP server in OSGi container mode.
- ignore-unresolvable= »true » means that the use of ${httpservice-config.key} will not throw an error if it doesn’t found (in the case of Embedding an HTTP server in OSGi container mode.
Modify the META-INF/spring/module-osgi-context.xml of the fr.opensagres.remoting.exporter.dosgi.jaxrs bundle like this:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:httpservice-config.properties" ignore-resource-not-found="true" ignore-unresolvable="true" /> <osgi:reference id="userService" interface="fr.opensagres.services.UserService" /> <osgi:service interface="fr.opensagres.services.UserService"> <osgi:service-properties> <entry key="service.exported.interfaces" value="*" /> <entry key="service.exported.configs" value="org.apache.cxf.rs" /> <entry key="service.exported.intents" value="HTTP" /> <entry key="org.apache.cxf.rs.databinding" value="jaxb" /> <!-- Logs --> <entry key="org.apache.cxf.rs.in.interceptors" value="org.apache.cxf.interceptor.LoggingInInterceptor" /> <entry key="org.apache.cxf.rs.out.interceptors" value="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <!-- JAXB Context --> <entry key="org.apache.cxf.rs.provider"> <array> <ref bean="jsonProvider" /> </array> </entry> <!-- HttpService --> <entry key="${httpservice-config.key}" value="/UserService" /> </osgi:service-properties> <ref bean="userService" /> </osgi:service> <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider"> <property name="singleJaxbContext" value="true" /> <property name="extraClass"> <list> <value>fr.opensagres.domain.User</value> </list> </property> </bean> </beans>
If you start the launch Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP] you will see that it’s work.
fr.opensagres.remoting.exporter.dosgi.jaxrs-httpservice
Create the OSGI fragment fr.opensagres.remoting.exporter.dosgi.jaxrs-httpservice linked to the fr.opensagres.remoting.exporter.dosgi.jaxrs OSGi bundle.
In this fragment, create the src/httpservice-config.properties with this content:
httpservice-config.key=org.apache.cxf.rs.httpservice.context
Create WAR Product
In this section we will create WAR Product from the OSGi Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP] launch like explained in the Create WAR Product article. Once the WAR Product will be created, we will add the fragment fr.opensagres.remoting.exporter.dosgi.jaxrs-httpservice to the WAR Product Configuration.
Create the TargetPlatform/warproducts/cxf-dosgi-jpa folder which will hosts the cxf-dosgi-jpa.warproduct :
Go to the File/New/Other… menu item, select Plug-in Development/WAR Product Configuration and click on Next button:
The New WAR Product Configuration wizard is displayed:
- Select the TargetPlatform/warproducts/cxf-dosgi-jpa folder.
- fill File name field with cxf-dosgi-jpa.warproduct.
- select the Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP] launch because we will create a WAR Product from this launch.
Click on Finish button to generate the WAR Product:
With the WAR Product Editor, Add the fr.opensagres.remoting.exporter.dosgi.jaxrs-httpservice fragment (to register the UserService with HttpService) with the Add button :
Create WAR
At this step we can generate the WAR by following the Create a WAR from RAP Application with Libra WAR Product [step3] article.
Validate WAR product
Before creating WAR, click on Validate WAR product menu item to check you have not problem with Plug-Ins/Fragments of the WAR Product:
Export WAR product
If you have none errors, we can start exporting the WAR. To do that, click on Export WAR product menu item:
This action opens the Export dialog :
- choose the file name of the generated WAR (here we will export WAR to generate the C:\cxf-dosgi-jpa.war).
- select the « Allow for binary cycles in target platform » checkbox.
Click on Finish button which starts the export of the WAR.
Test WAR cxf-dosgi-jpa.war
At this step we can test our WAR. In my case, I have tested the WAR with Apache Tomcat and follow thoses steps:
- Install Apache Tomcat.
- Copy/paste the cxf-dosgi-jpa.war in the webapps folder of the root of the Tomcat.
- Start the Tomcat on the 8080 port by using the bin/startup.bat (or startup.sh).
Web Browser
Once the HTTP Server has deployed the war, tries to go to the URL http://127.0.0.1:8080/cxf-dosgi-jpa/UserService/user/findAll and you will see the JSON of the user list :
{"user":[{"firstName":"Angelo","lastName":"Zerr"},{"firstName":"Pascal","lastName":"Leclercq"},{"firstName":"Amine","lastName":"Bousta"},{"firstName":"Mickael","lastName":"Baron"},{"firstName":"Jawher","lastName":"Moussa"},{"firstName":"Arnaud","lastName":"Cogoluegnes"},{"firstName":"Lars","lastName":"Vogel"},{"firstName":"Olivier","lastName":"Gierke"},{"firstName":"Tom","lastName":"Schindl"},{"firstName":"Wim","lastName":"Jongman"}]}
Our CXF DOSGi application works now with a WAR!
JAX-RS Clients
Now we will test our WAR with our OSGI, RCP and RAP launches. To do that we need just modify the module-osgi-context.xml of the fr.opensagres.remoting.importer.dosgi.jaxrs bundle :
<jaxrs:client id="jaxrsUserService" address="${jaxrs-config.base-url}/fr/opensagres/services/UserService"
with this new content :
<jaxrs:client id="jaxrsUserService" address="${jaxrs-config.base-url}/UserService"
Equinox in a Servlet Container (ServletBridge) – Localhost
To test with your Local Apache Tomcat, modify the base-url like this :
jaxrs-config.base-url=http://127.0.0.1:8080/cxf-dosgi-jpa/
Start the Client Remoting – JAXRS DOSGi – Simple OSGi Client or (Client Remoting – JAXRS DOSGi – Simple OSGi Client [RAP TP] if you have RAP Target Platform activated) launch, to check it works. You can test too RCP (Client Remoting – JAXRS DOSGI – RCP Client) and RAP (Client Remoting – JAXRS DOSGI – RAP Client) launches.
Cloudbees
The cxf-dosgi-jpa.war was deployed on CloudBees. You can test URLs with Web Browser like:
- http://cxf-dosgi-war.opensagres.cloudbees.net/UserService?_wadl to displays the WADL of the UserService.
- http://cxf-dosgi-war.opensagres.cloudbees.net/UserService/user/findAll to display the user list as JSON format.
To test with WAR deployed on CloudBees, modify the base-url like this :
jaxrs-config.base-url=http://cxf-dosgi-war.opensagres.cloudbees.net
Start the Client Remoting – JAXRS DOSGi – Simple OSGi Client or (Client Remoting – JAXRS DOSGi – Simple OSGi Client [RAP TP] if you have RAP Target Platform activated) launch, to check it works. You can test too RCP (Client Remoting – JAXRS DOSGI – RCP Client) and RAP (Client Remoting – JAXRS DOSGI – RAP Client) launches.
Embedding an HTTP server in Equinox – Localhost with Jetty
To test with OSGI Jetty (launch Server Remoting – JAXRS DOSGi – JPA Dao [RAP TP]), modify the base-url like this :
jaxrs-config.base-url=http://127.0.0.1:9000/fr/opensagres/services
Start the Client Remoting – JAXRS DOSGi – Simple OSGi Client or (Client Remoting – JAXRS DOSGi – Simple OSGi Client [RAP TP] if you have RAP Target Platform activated) launch, to check it works. You can test too RCP (Client Remoting – JAXRS DOSGI – RCP Client) and RAP (Client Remoting – JAXRS DOSGI – RAP Client) launches.
Conclusion
In this article we have seen how to create a WAR for a CXF DOSGi Application by using WAR Product which is created from OSGi launches and give you the capability to export a WAR with ServletBridge.
Hi there,
First off thank you very much for this great article. It did help me a lot.
I tried to deploy the war into Tomcat and it worked, but tried too with Websphere and it didn’t.
Do you have any idea the reason behind the failure?
Thank you in advance
Hi lilas,
No sorry I cannot help you. Problem with Websphere is that it override a lot of standard JAR and it causes problem (with OSGi, RAP, CXF etc).
I suggest you to post your question on CXF forum or RAP forum according your problem.
Good luck.
Regards Angelo
Thanks Angelo for your quick response 🙂
Hi Angelo,
Thanks again for your post. I did manage to get the WAR works within both Websphere/Tomcat. It’s just a matter of configuration regarding the classloader strategy (put it as Classes loaded with local class loader first(parent last)).
Thanks again
Hi Angelo
I’m struggling with Spring Data and OSGI.
Trying to use the actual Spring data doesn’t know the jpa:repository tag as used in your example. Do you have an example how to make OSGI know about spring data repositories? What du you suggest – usijng Spring DM or gemini blueprint?
Thanks
Rene
Hi rene,
I’m sorry, I cannot help you more. It was so long time that I have not played with Spring Data.
Please post your question on Spring Data forum.
Regards Angelo
thanks Angelo, I’ll try