How to get control over JAXBContext in JAX-WS?

10,131

Solution 1

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.

Solution 2

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.

Share:
10,131

Related videos on Youtube

andbi
Author by

andbi

Updated on June 05, 2022

Comments

  • andbi
    andbi almost 2 years

    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.

  • andbi
    andbi over 12 years
    Thanks for reply, it looks interesting. You said it's a quite common approach, could you please elaborate more on that - any links, resources or code samples?
  • Petru Gardea
    Petru Gardea over 12 years
    Please take a look at this article: ibm.com/developerworks/websphere/library/techarticles/…; I've used it as a reference back in the days where JAXB was emerging and a number of service stacks were based exclusively on JAX-RPC. You'll notice a similitude with your approach (SOAPElement being your Object in the signature). I believe these articles, quite common then, are still relevant in your case.
  • Petru Gardea
    Petru Gardea over 12 years
    I noticed your update; you could rewrite generated Java code or config files as part of the automatic build process. I would look at what is created vs. what is needed and come up with a transform (text replace, XSLT, whatever works with you). Many people may sneeze at this; things such as the experience one gets while developing under a specific IDE may suffer since artifacts are somewhat "dynamic" or require additional steps. It really depends a lot more on the details of your solution.