I need to deploy the same web service for each customer. This @javax.jws.WebService
uses Object
as method arguments and return types (resulting in <xs:anyType/>
in wsdl). Each instance of web service is deployed along with customer's jar on the classpath. This jar has known structure and contains JAXB-annotated classes which client wants to handle via my service.
The problem is that when customer passes an instance of his class as method agrument, server-side JAXB context unmarshals it into some strange xerces dom node because (as I understand it) during the deployment time only @WebMethod
and @WebService
annotations were scanned which, as was already said, are all dealing with Object
only.
Simply speaking, I need to hint JAXB at WEB-INF/lib/customer_classes_14586.jar
which means taking some control over JAXBContext
creation during JAX-WS deployment.
Is it possible at all?
Server-specific solutions are fine (glassfish 3.1 with metro ws stack)
UPDATE
I've missed one thing that might be important: I deploy these web services as OSGI bundles at runtime via web admin console. When I press deploy button new jar is programmatically built up from customer library, webservice class, wsdl and manifests. So I could interfere in build process and provide hinting information at this point of time if this helps.
First option is @UsesJAXBContext
annotation. More info here: Specify JAXB Packages in SLSB and JAX-WS
I haven't tested it cause when I found this annotation I've been already halfway towards other solution which might be helpful for others.
The key is using @WebServiceProvider
instead of @WebService
, a bit low-level but simple:
@WebServiceProvider(
wsdlLocation = "WEB-INF/wsdl/injector.wsdl"
)
@ServiceMode(value = Service.Mode.PAYLOAD)
public class InjectorService implements Provider<Source> {
private Unmarshaller unmarshaller;
@Override
public Source invoke(Source request) {
try {
DOMResult requestDom = new DOMResult();
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.transform(request, requestDom);
Node requestNode = requestDom.getNode();
// Get the operation name node.
Node operationNode = requestNode.getFirstChild();
// Get the parameter node.
Node parameterNode = operationNode.getFirstChild();
// Unmarshal
JAXBElement<Object> element = unmarshaller.unmarshal(parameterNode, Object.class);
Object unmarshalled = element.getValue();
// Handling customer object and response ......
} catch (Exception e) {
throw new RuntimeException("Endpoint error", e);
}
}
protected Class[] getCustomerClasses() {
// return customer classes somehow
}
@PostConstruct
public void init() throws Exception {
JAXBContext jbc = JAXBContext.newInstance(getCustomerClasses());
unmarshaller = jbc.createUnmarshaller();
}
}
That's it. Customer classes can be obtained from classpath, bundle context or whatever.
From what I know, there is no "declarative" way of hinting an alternative way to unmarshall, on top of the one you already have in place as per JAX-WS, or JAXB - what you're looking for. By the way, the "strange" Xerces node is actually expected, since xsd:any/anyType and Object go hand in hand in your scenario.
My suggestion is to use a relatively simple and portable solution: build your own thin "binding" layer inside your generic web method. All it does for the inbound, is to do the unmarshalling of the XML node to the Java class as per your other JAXB bindings. It must then lookup a Java package name (for your JAXBContext) from the QName of the DOM Element unmarshalled by your WS stack. The lookup can use properties file, reflection or any other mechanism specific to your deployment. For the outbound (return) you then apply a reverse logic to marshall the response. This approach is actually quite common, particularly when other type of unsupported-XML bindings technologies are "tunnelled" through a standard WS stack.