You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Avner Levy <av...@checkpoint.com> on 2012/10/02 14:16:06 UTC

Reuse JAXB context in jaxws

Hi,
I have multiple services which can return results out of thousands of classes. 
Since each CXF service contains a private otherwise-identical JAXB context, it causes a huge memory waste. 
Is there a way to create the JAXB context myself and share it between the services?
Thanks in advance,
 Avner

Re: Reuse JAXB context in jaxws

Posted by Voß, Marko <Ma...@fiz-Karlsruhe.de>.
Hello Avner,

I did this once because of the same idea. In the end I decided to remove this because this caused too much trouble due 
to the nature of the implementation of the JAXBContext. Anyways, this is the way I did that:

First of all, you need a good way to initialize the JAXBContext. I was using Freemaker for this.

/src/main/freemarker/jaxb-packages.fmt:

	<@file package="package.of.jaxb.generated.classes" name="jaxb.packages">
	<@forAllPackages var="package">
	<#assign printpackage=false>
	<#list package.classes as class>
	<@ifHasAnnotation declaration=class annotation="javax.xml.bind.annotation.XmlRegistry">
	<#assign printpackage=true>
	</...@ifHasAnnotation>
	</#list>
	<#if printpackage>
	${package.qualifiedName}
	</#if>
	</...@forAllPackages>
	</...@file>

Run this code using the apt-maven-plugin after the maven-jaxb2-plugin:

       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>apt-maven-plugin</artifactId>
         <configuration>
           <factory>net.sf.jelly.apt.freemarker.FreemarkerProcessorFactory</factory>
           <fork>false</fork>
           <options>
             <value>template=${basedir}/src/main/freemarker/jaxb-packages.fmt</value>
           </options>
           <outputDirectory>${project.build.outputDirectory}</outputDirectory>
           <sourceOutputDirectory>${project.build.outputDirectory}</sourceOutputDirectory>
           <additionalSourceRoots>
             <additionalSourceRoot>target/generated-sources/java</additionalSourceRoot>
           </additionalSourceRoots>
         </configuration>
         <executions>
           <execution>
             <goals>
               <goal>process</goal>
             </goals>
           </execution>
         </executions>
         <dependencies>
           <dependency>
             <groupId>net.sf.apt-jelly</groupId>
             <artifactId>apt-jelly-core</artifactId>
             <version>2.14</version>
           </dependency>
           <dependency>
             <groupId>net.sf.apt-jelly</groupId>
             <artifactId>apt-jelly-freemarker</artifactId>
             <version>2.14</version>
           </dependency>
         </dependencies>
       </plugin>

This will generate a file which contains all the packages of the generated JAXB classes. One package per line. You will 
need these packages to initialize the JAXBContext for all your classes at once.

Now, I have added a JAXBContextProvider. (Note: there are OVal annotations in this code)

	/**
	 * @author Marko Voss (marko.voss@fiz-karlsruhe.de)
	 */
	@Guarded
	@Provider
	public class JAXBContextProvider {
	
	    private static final Logger LOG = LoggerFactory.getLogger(JAXBContextProvider.class);
	
	    private JAXBContext jaxbContext;
	
	    private String packageFile;
	
	    protected JAXBContextProvider() {}
	
	    @NotNull
	    public final JAXBContext getJAXBContext() throws JAXBException {
	        if (this.jaxbContext == null) {
	            this.jaxbContext = initJAXBContext();
	            if (this.jaxbContext == null) {
	                this.jaxbContext = JAXBContext.newInstance();
	                LOG.warn("Returning default JAXBContext instance.");
	            }
         	}
	        return this.jaxbContext;
	    }

	    public final void setPackageFile(@NotNull @NotBlank final String packageFile) {
	        this.packageFile = packageFile;
	    }

	    private JAXBContext initJAXBContext() throws JAXBException {
         	if (this.packageFile == null) return null;
	
         	final InputStream stream = getClass().getClassLoader().getResourceAsStream(this.packageFile);
	        try {
         	    if (stream != null) {
                 	final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
	                final StringBuilder packages = new StringBuilder();
	
         	        String line;
                 	while ((line = reader.readLine()) != null) {
	                    packages.append(line);
         	            packages.append(':');
                 	}
	                packages.deleteCharAt(packages.length() - 1);
         	        return JAXBContext.newInstance(packages.toString());
	            } else {
         	        LOG.warn("Unable to find JAXB package list: " + this.packageFile);
	            }
         	} catch (IOException e) {
	            LOG.error("Error reading JAXB package list: " + this.packageFile, e);
         	}
	        return null;
	    }
	}

Now, register the JAXBContextProvider to the JAXBElementProvider:

	public class MyJAXBElementProvider extends JAXBElementProvider<Object> {

	  private JAXBContextProvider jaxbContextProvider;

	  // register
	  public void setJaxbContextProvider(final JAXBContextProvider jaxbContextProvider) {
	    this.jaxbContextProvider = jaxbContextProvider;
	  }

	  public JAXBContext getJAXBContext(Class<?> type, Type genericType) throws JAXBException {
             if (this.jaxbContextProvider != null) {
               return this.jaxbContextProvider.getJAXBContext();
             } else {
               return super.getJAXBContext(type, genericType);
             }
	  }
	}

I configured this using Spring.

The JAXBContext will now be available for all the generated JAXB classes. BUT this works only for unmarshalling! (iirc) 
On marshalling I got the problem, that the JAXBContext was not able to recognize some namespaces or something. I cannot 
really remember the problem. In the end I have decided to remove this, because it is just not possible to have one 
context for everything. I think the problem was like this:

The XML allows any content and you put some XML inside this any content. You have JAXB objects for both XML structures, 
the outside one and the "any-content one". Iirc, the JAXBContext was not able to map the inner XML to an object because 
of it could not find the JAXB object mapping for it, even through it was registered. Due to lack of time I did not 
investigate this any further.


best

Am 02.10.2012 14:16, schrieb Avner Levy:
> Hi,
> I have multiple services which can return results out of thousands of classes.
> Since each CXF service contains a private otherwise-identical JAXB context, it causes a huge memory waste.
> Is there a way to create the JAXB context myself and share it between the services?
> Thanks in advance,
>   Avner


-------------------------------------------------------

Fachinformationszentrum Karlsruhe, Gesellschaft für wissenschaftlich-technische Information mbH. 
Sitz der Gesellschaft: Eggenstein-Leopoldshafen, Amtsgericht Mannheim HRB 101892. 
Geschäftsführerin: Sabine Brünger-Weilandt. 
Vorsitzender des Aufsichtsrats: MinDirig Dr. Thomas Greiner.



Re: Reuse JAXB context in jaxws

Posted by "Avner.Levy" <av...@gmail.com>.
I've tried to do the same through code:
    jaxwsEndpoint.getProperties().put("org.apache.cxf.jaxb.JAXBDataBinding",
jaxbDataBinding);
before calling:
    jaxwsEndpoint.publish(URL);
And the service starts very fast as if they are reusing the context in
jaxbDataBinding.
But when connecting later I've discovered that the service context contains
only 50 classes while the original contained 700.
Any idea how come I finish with a different context?
Thanks in advance,
   Avner



--
View this message in context: http://cxf.547215.n5.nabble.com/Reuse-JAXB-context-in-jaxws-tp5715356p5722297.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Reuse JAXB context in jaxws

Posted by rhalferty <ry...@cgifederal.com>.
Thanks Daniel for your help.

I've implemented this, but to no avail did it reduce our memory footprint.
Unfortunately, I'm starting to think it's just the size and number of the
objects our project defines/requires. Unless maybe there is a way to delay
the creation of the context until it is required rather then at deploy time.



--
View this message in context: http://cxf.547215.n5.nabble.com/Reuse-JAXB-context-in-jaxws-tp5715356p5715584.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Reuse JAXB context in jaxws

Posted by Daniel Kulp <dk...@apache.org>.
On Oct 3, 2012, at 10:17 AM, rhalferty <ry...@cgifederal.com> wrote:

> We are using jaxws:endpoint in a spring context file. We have 15 endpoints
> which are similar to:

OK.   Should just then be something like:

<jaxws:endpoint xmlns:tns="urn:ihe:iti:xcpd:2009" …….>
    <jaxws:dataBinding>
        <bean class="org.apache.cxf.jaxb.JAXBDataBinding" > 
            <constructor-arg index="0" ref="globalJAXBContextBean"/> 
        </bean> 
    </jaxws:dataBinding>
    ………
</jaxws:endpoint>


or similar where you have another bean definition that would create the JAXBContext.

Dan





> 
> <jaxws:endpoint xmlns:tns="urn:ihe:iti:xcpd:2009"
> id="RespondingGateway_Service" address="/NhinService/NhinPatientDiscovery"
>        serviceName="tns:RespondingGateway_Service"
> endpointName="tns:RespondingGateway_Port_Soap"
> 
> implementor="gov.hhs.fha.nhinc.patientdiscovery._10.gateway.ws.NhinPatientDiscovery"
> wsdlLocation="/WEB-INF/wsdl/NhinPatientDiscovery.wsdl">
>        <jaxws:properties>
>            <entry key="ws-security.callback-handler"
> value="gov.hhs.fha.nhinc.callback.cxf.CXFSAMLCallbackHandler" />
>            <entry key="ws-security.signature.properties"
> value="keystore.properties" />
>            <entry key="ws-security.encryption.properties"
> value="truststore.properties" />
>            <entry key="ws-security.saml2.validator">
>                <bean
> class="gov.hhs.fha.nhinc.callback.cxf.CONNECTSamlAssertionValidator" />
>            </entry>
>        </jaxws:properties>
>        <jaxws:inInterceptors>
>            <ref bean="securityInInterceptor" />
>        </jaxws:inInterceptors>
>        <jaxws:handlers>
>            <ref bean="SOAPHeaderHandler" />
>        </jaxws:handlers>
>    </jaxws:endpoint>
> 
> 
> 
> --
> View this message in context: http://cxf.547215.n5.nabble.com/Reuse-JAXB-context-in-jaxws-tp5715356p5715526.html
> Sent from the cxf-user mailing list archive at Nabble.com.

-- 
Daniel Kulp
dkulp@apache.org - http://dankulp.com/blog
Talend Community Coder - http://coders.talend.com


Re: Reuse JAXB context in jaxws

Posted by rhalferty <ry...@cgifederal.com>.
We are using jaxws:endpoint in a spring context file. We have 15 endpoints
which are similar to:

<jaxws:endpoint xmlns:tns="urn:ihe:iti:xcpd:2009"
id="RespondingGateway_Service" address="/NhinService/NhinPatientDiscovery"
        serviceName="tns:RespondingGateway_Service"
endpointName="tns:RespondingGateway_Port_Soap"
       
implementor="gov.hhs.fha.nhinc.patientdiscovery._10.gateway.ws.NhinPatientDiscovery"
wsdlLocation="/WEB-INF/wsdl/NhinPatientDiscovery.wsdl">
        <jaxws:properties>
            <entry key="ws-security.callback-handler"
value="gov.hhs.fha.nhinc.callback.cxf.CXFSAMLCallbackHandler" />
            <entry key="ws-security.signature.properties"
value="keystore.properties" />
            <entry key="ws-security.encryption.properties"
value="truststore.properties" />
            <entry key="ws-security.saml2.validator">
                <bean
class="gov.hhs.fha.nhinc.callback.cxf.CONNECTSamlAssertionValidator" />
            </entry>
        </jaxws:properties>
        <jaxws:inInterceptors>
            <ref bean="securityInInterceptor" />
        </jaxws:inInterceptors>
        <jaxws:handlers>
            <ref bean="SOAPHeaderHandler" />
        </jaxws:handlers>
    </jaxws:endpoint>



--
View this message in context: http://cxf.547215.n5.nabble.com/Reuse-JAXB-context-in-jaxws-tp5715356p5715526.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Reuse JAXB context in jaxws

Posted by Daniel Kulp <dk...@apache.org>.
On Oct 3, 2012, at 9:57 AM, rhalferty <ry...@cgifederal.com> wrote:

> I think this is exactly what I've been looking for, but could you explain
> more on where to put it?
> Does it just sit in the cxf-servlet.xml? Does it have to be under a
> <jaxws:dataBinding/> element?
> 


It completely depends on how you are starting your services.  Are you using the Spring based jaxws:endpoint elements in a spring context file?  Calling Endpoint.publish manually? Using the JaxWsServerFactory things?  

-- 
Daniel Kulp
dkulp@apache.org - http://dankulp.com/blog
Talend Community Coder - http://coders.talend.com


RE: Reuse JAXB context in jaxws

Posted by rhalferty <ry...@cgifederal.com>.
I think this is exactly what I've been looking for, but could you explain
more on where to put it?
Does it just sit in the cxf-servlet.xml? Does it have to be under a
<jaxws:dataBinding/> element?

Thanks



--
View this message in context: http://cxf.547215.n5.nabble.com/Reuse-JAXB-context-in-jaxws-tp5715356p5715519.html
Sent from the cxf-user mailing list archive at Nabble.com.

RE: Reuse JAXB context in jaxws

Posted by Avner Levy <av...@checkpoint.com>.
Thanks for the quick answer.
I've added the following to my spring configuration:

<bean class="org.apache.cxf.jaxb.JAXBDataBinding" >
    <constructor-arg index="0" value="#{GlobalContextBean.context}"/>
</bean>

And the start-up time of the application / memory was significantly changed (for good).
Am I supposed to add something more or this should be enough?
How should I configure the CXF endpoints to use it? (although it looks like they already did)
Thanks in advance,
  Avner

-----Original Message-----
From: Daniel Kulp [mailto:dkulp@apache.org] 
Sent: Tuesday, October 02, 2012 11:02 PM
To: users@cxf.apache.org
Subject: Re: Reuse JAXB context in jaxws


On Oct 2, 2012, at 8:16 AM, Avner Levy <av...@checkpoint.com> wrote:
> Hi,
> I have multiple services which can return results out of thousands of classes. 
> Since each CXF service contains a private otherwise-identical JAXB context, it causes a huge memory waste. 
> Is there a way to create the JAXB context myself and share it between the services?
> Thanks in advance,
> Avner

The JAXBDataBinding does have a constructor that takes a JAXBContext.   Thus, you could create the JAXBDataBinding object yourself with the JAXBContext you need, then configure that into the CXF endpoints.   There is a <jaxws:dataBinding> child element for the <jaxws:endpoint> if you are using that to setup the services.

That said, I HIGHLY doubt this is well tested.  You'd be in more or less uncharted territory.  :-)

--
Daniel Kulp
dkulp@apache.org - http://dankulp.com/blog Talend Community Coder - http://coders.talend.com


Scanned by Check Point Total Security Gateway.

Re: Reuse JAXB context in jaxws

Posted by Johan Edstrom <se...@gmail.com>.
Have used that for large scale projects for the last two years. Works well.

On Oct 2, 2012, at 15:02, Daniel Kulp <dk...@apache.org> wrote:

> 
> On Oct 2, 2012, at 8:16 AM, Avner Levy <av...@checkpoint.com> wrote:
>> Hi,
>> I have multiple services which can return results out of thousands of classes. 
>> Since each CXF service contains a private otherwise-identical JAXB context, it causes a huge memory waste. 
>> Is there a way to create the JAXB context myself and share it between the services?
>> Thanks in advance,
>> Avner
> 
> The JAXBDataBinding does have a constructor that takes a JAXBContext.   Thus, you could create the JAXBDataBinding object yourself with the JAXBContext you need, then configure that into the CXF endpoints.   There is a <jaxws:dataBinding> child element for the <jaxws:endpoint> if you are using that to setup the services.
> 
> That said, I HIGHLY doubt this is well tested.  You'd be in more or less uncharted territory.  :-)
> 
> -- 
> Daniel Kulp
> dkulp@apache.org - http://dankulp.com/blog
> Talend Community Coder - http://coders.talend.com
> 

Re: Reuse JAXB context in jaxws

Posted by Daniel Kulp <dk...@apache.org>.
On Oct 2, 2012, at 8:16 AM, Avner Levy <av...@checkpoint.com> wrote:
> Hi,
> I have multiple services which can return results out of thousands of classes. 
> Since each CXF service contains a private otherwise-identical JAXB context, it causes a huge memory waste. 
> Is there a way to create the JAXB context myself and share it between the services?
> Thanks in advance,
> Avner

The JAXBDataBinding does have a constructor that takes a JAXBContext.   Thus, you could create the JAXBDataBinding object yourself with the JAXBContext you need, then configure that into the CXF endpoints.   There is a <jaxws:dataBinding> child element for the <jaxws:endpoint> if you are using that to setup the services.

That said, I HIGHLY doubt this is well tested.  You'd be in more or less uncharted territory.  :-)

-- 
Daniel Kulp
dkulp@apache.org - http://dankulp.com/blog
Talend Community Coder - http://coders.talend.com