Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
267 views
in Technique[技术] by (71.8m points)

java - Configuring CXF with Spring to use MOXY for XML marshalling/unmarshalling

I have a Java server application that uses CXF to provide SOAP and REST web services. Currently it uses the reference implementation of JAX-B for XML marshalling/unmarshalling, but I have configured it to replace Jettison with Jackson for JSON marshalling/unmarshalling. I use Spring for DI and application context configuration.

The REST web service configuration snippets looks as follows:

web.xml

<servlet>
    <display-name>Myapp REST Services</display-name>
    <servlet-name>MyappWebServices</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>MyappWebServices</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />

<jaxrs:server id="myappCoreSvcRest" address="/rest">
    <jaxrs:serviceBeans>
        <ref bean="fooService" />
        <ref bean="barService" />
    </jaxrs:serviceBeans>

    <jaxrs:providers>
        <ref bean="jsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

This configuration works and will return either XML or JSON depending on the HTTP Accept header. What I like about this configuration is that it is based in Spring and it is super easy to create and use an alternate JSON encoder. Details on configuring CXF can be found here.

My problem is that now I have a new (additional) REST web service to provide and I would like to use a different JAX-B XML binding for this new web service. I understand that MOXy can do this, but I am unable to figure out how to configure a CXF end point so that it will use MOXy for marshalling/unmarshalling (and furthermore how to tell Moxy about my custom XML mapping file). I also would like this new web service to return either XML or JSON depending on the Accept header. I also have read that MOXy 2.4+ can handle that too!

Ideally I could use MOXy for this new endpoint without affecting the other existing servlets.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.


Off hand I do not know the exact config for CXF, but below I have provided some links to using MOXy with Spring. Please feel free to contact me, and I can help you implement this:

My problem is that now I have a new (additional) REST web service to provide and I would like to use a different JAX-B XML binding for this new web service. I understand that MOXy can do this, but I am unable to figure out how to configure a CXF end point so that it will use MOXy for marshalling/unmarshalling (and furthermore how to tell Moxy about my custom XML mapping file).

When using MOXy with a JAX-RS implementation you can use a ContextResolver to bootstrap from MOXy's external mapping file:

package blog.bindingfile.jaxrs;

import java.io.*;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;     
import org.eclipse.persistence.jaxb.JAXBContextFactory;

import blog.bindingfile.Customer;

@Provider
@Produces({"application/xml", "application/json"})
public class CustomerContextResolver implements ContextResolver<JAXBContext> {

    private JAXBContext jc;

    public CustomerContextResolver() {
        ClassLoader cl = Customer.class.getClassLoader();
        InputStream bindings =
            cl.getResourceAsStream("blog/bindingfile/binding.xml");
        try {
            Map<String, Object> props = new HashMap<String, Object>(1);
            props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindings);
            jc = JAXBContext.newInstance(new Class[] {Customer.class} , props);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                bindings.close();
            } catch(IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public JAXBContext getContext(Class<?> clazz) {
        if(Customer.class == clazz) {
            return jc;
        }
        return null;
    }

} 

For a Complext Example

For More Information on Using MOXy with Spring


I also would like this new web service to return either XML or JSON depending on the Accept header. I also have read that MOXy 2.4+ can handle that too!

Yes, JSON binding is being added to EclipseLink 2.4. To leverage this in your application it should be a simple matter of creating a MessageBodyReader and a MessageBodyWriter:

package org.example;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import javax.xml.transform.stream.StreamSource;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MOXyJSONProvider implements 
    MessageBodyReader<Object>, MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public Object readFrom(Class<Object> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {
            try {
                Class<?> domainClass = getDomainClass(genericType);
                Unmarshaller u = getJAXBContext(domainClass, mediaType).createUnmarshaller();
                u.setProperty("eclipselink.media-type", mediaType.toString());
                u.setProperty("eclipselink.json.include-root", false);
                return u.unmarshal(new StreamSource(entityStream), domainClass).getValue();
            } catch(JAXBException jaxbException) {
                throw new WebApplicationException(jaxbException);
            }
    }

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public void writeTo(Object object, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException,
        WebApplicationException {
        try {
            Class<?> domainClass = getDomainClass(genericType);
            Marshaller m = getJAXBContext(domainClass, mediaType).createMarshaller();
            m.setProperty("eclipselink.media-type", mediaType.toString());
            m.setProperty("eclipselink.json.include-root", false);
            m.marshal(object, entityStream);
        } catch(JAXBException jaxbException) {
            throw new WebApplicationException(jaxbException);
        }
    }

    public long getSize(Object t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    private JAXBContext getJAXBContext(Class<?> type, MediaType mediaType) 
        throws JAXBException {
        ContextResolver<JAXBContext> resolver 
            = providers.getContextResolver(JAXBContext.class, mediaType);
        JAXBContext jaxbContext;
        if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
            return JAXBContext.newInstance(type);
        } else {
            return jaxbContext;
        }
    }

    private Class<?> getDomainClass(Type genericType) {
        if(genericType instanceof Class) {
            return (Class<?>) genericType;
        } else if(genericType instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
        } else {
            return null;
        }
    }

}

You may also be able to create an extension of JSONProvider:

For More Information


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...