tomee-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Anthony Fryer <apfr...@hotmail.com>
Subject Re: Use single JAXBContext for multiple web services
Date Sun, 15 Jun 2014 00:55:17 GMT
I've managed (after lots of pain and suffering) to get my web services using
the same JAXBContext.  The great thing is this significantly speeds up my
tomee boot time.  Before, tomee was taking 37594 ms to boot and now it is
taking 10150 ms, so the performance gain is massive.  The steps i went
through to get this to work are detailed below.

I tried to follow the steps described in
https://www.mail-archive.com/users@cxf.apache.org/msg35563.html, changing
the spring config into tomee config.  So the idea was to try to create a
global JAXBContext instance and then pass that as a constructor parameter
into the org.apache.cxf.jaxb.JAXBDataBinding.  I thought I could do this
using <Service> elements in my resources.xml file.

I created a class to create the JAXBContext...

public class GlobalJAXBContext {
	
	public static JAXBContext getJAXBContext() {
		try {
			return JAXBContext.newInstance(new Class[] {
							com.virginaustralia.model.schema.utility.ObjectFactory.class,
						
com.virginaustralia.service.contract.departure_management.ObjectFactory.class
});
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		
		return null;
	}
}

Then I added the following to my resources.xml...

<resources>
   ...
   <Service id="myJAXBContext"
class-name="com.virginaustralia.service.GlobalJAXBContext "
factory-name="getJAXBContext"/>
   
  <Service id="myJAXBDataBinding"
class-name="org.apache.cxf.jaxb.JAXBDataBinding" constructor="jaxbContext">
     jaxbContext = $myJAXBContext
  </Service>

</resources> 

Took me a long time and looking through source code to discover you can pass
other services as a constructor parameter using the $<serviceId> notation.

My openejb-jar.xml then looked like this...

<openejb-jar xmlns="http://www.openejb.org/openejb-jar/1.1">

   <ejb-deployment ejb-name="AcceptPaymentForAncillariesImpl">
      <properties>
         cxf.jaxws.databinding = myJAXBDataBinding
      </properties>
   </ejb-deployment>
   
   <ejb-deployment ejb-name="ActivatePassengerBagTagImpl">
      <properties>
         cxf.jaxws.databinding = myJAXBDataBinding
      </properties>
   </ejb-deployment>

   ... plus 30 other services similar to above 

</openejb-jar>

Booting tomee after this change is where the errors started happening. 
First error was this one...

org.apache.cxf.service.factory.ServiceConstructionException: Service class
com.virginaustralia.service.contract.departure_management.DeletePassengerBagTagPortType
method deletePassengerBagTag part
{urn:www.virginaustralia.com:service:contract:departure-management}request
cannot be mapped to schema. Check for use of a JAX-WS-specific type without
the JAX-WS service factory bean.

To fix this, i had to specify the wsdlLocation in the @WebService
annotation.  Fortunately you can do this using a classpath url (this
requires you to package the WSDL with the service and port classes generated
using wsimport).

@Stateless
@Local(AcceptPaymentForAncillariesPortType.class)
@WebService(
		portName="AcceptPaymentForAncillariesPortType",
		serviceName="AcceptPaymentForAncillariesService",
		targetNamespace =
"urn:www.virginaustralia.com:service:contract:departure-management",
	
endpointInterface="com.virginaustralia.service.contract.departure_management.AcceptPaymentForAncillariesPortType",
	
wsdlLocation="classpath:/com/virginaustralia/service/contract/departure_management/AcceptPaymentForAncillaries.wsdl"
		)
public class AcceptPaymentForAncillariesImpl implements
AcceptPaymentForAncillariesPortType {
 ...
}

After adding wsdlLocation to every service, I booted tomee, but the boot
time was exactly the same.  Turns out that a new instance of the
"myJAXBDataBinding" service defined in resources.xml was being instantiated
for every web service.  And since i used the "myJAXBContext" as a
constructor argument, a new JAXBContext was being created for every service
and consequently i was no better off than before.

I only wanted a single JAXBContext created, so I tried to configure it as a
Resource instead of a Service as below...

<resources>
   ...
   <Resource id="myJAXBContext"
class-name="com.virginaustralia.service.GlobalJAXBContext "
factory-name="getJAXBContext"/>
   
  <Service id="myJAXBDataBinding"
class-name="org.apache.cxf.jaxb.JAXBDataBinding" constructor="jaxbContext">
     jaxbContext = $myJAXBContext
  </Service>

</resources> 


Unfortunately when i tried that, the $myJAXBContext being passed as a
constructor parameter was null.  Is there a way you can pass a resource to a
service as a constructor parameter?

To get around this, I had to change the Resource back to being a Service and
modify my class that creates the JAXBContext to only return a singleton
instance...

public class GlobalJAXBContext {
	
	private static JAXBContext jaxbContext = null;

	public static JAXBContext getJAXBContext() {
		if (jaxbContext == null) {
			try {
				jaxbContext = JAXBContext.newInstance(new Class[] {
								com.virginaustralia.model.schema.utility.ObjectFactory.class,
							
com.virginaustralia.service.contract.departure_management.ObjectFactory.class
});
			} catch (JAXBException e) {
				e.printStackTrace();
			}
		}
		
		return jaxbContext;
	}
	
}


After that change, all my services were booting up successfully and reusing
a single JAXBContext.

Regards,

Anthony



--
View this message in context: http://openejb.979440.n4.nabble.com/Use-single-JAXBContext-for-multiple-web-services-tp4670019p4670021.html
Sent from the OpenEJB User mailing list archive at Nabble.com.

Mime
View raw message