How to get control over JAXBContext in JAX-WS? - java

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.

Related

How to get all binding information from WSDL using JAX-WS API

How to get all bindings declared in a WSDL using JAX-WS (Metro implementation) API?
Use case : I am going to enable the users of my application to dynamically invoke a web service. For that I am discovering the bindings first, then ports associated with the binding etc. at runtime.
I was going through the Metro documentation and found WSDLport class might useful to get port information and then binding information. But any idea how to get the reference of this class in the first place?
Wesnt through JAX-WS reference implementation documentation, there are few steps by which we can parse WSDL at runtime to get the required information. But all of these methods return XML representations i.e. QName class.
WSDLModel model = WSDLModel.WSDLParser.parse(parser, null, true, new WSDLParserExtension[0]);
//To get all service tags
Map serviceMap = model.getServices();
//To get all the bindings
Map bindingInfomation = model.getBindings();
//To get ports
Map portMap = model.getPortTypes();

Getting empty sequence for the complex type in generated XSD when developing JAX-WS web services

I seem to be having an issue with Java and NetBeans when it comes to writing web services.
I have searched for a couple of days with no luck, finding people with the same issue as me with zero replies.
I have created a web service which returns a complex type (LoginReply), and that complex type contains an array of another complex type (AppInfo)
However when I generate the WSDL from this, the complex type definition in the XSD is blank, and manually adding the information still makes the web service return null even when data is successfully passed to the web service.
<xs:complexType name="appInfo">
<xs:sequence/>
</xs:complexType>
LoginReply: http://pastebin.com/Umx6ayvi
AppInfo: http://pastebin.com/566WnZ4H
If anyone could point out what I'm doing wrong, or if this is a bug with NetBeans, I'm new to Java so I can't rule out that I'm simply not understanding something, but I'm close to pulling my hair out here.
EDIT:
Just noticed when i deploy to tomcat via NetBeans I get the following error:
WARNING: duplicate class definition bug occured? Please report this : uk/co/example/ComplexTypes/LoginReply$JaxbAccessorM_getApplications_setApplications_[Luk_co_example_ComplexTypes_AppInfo;
java.lang.ClassFormatError: Illegal class name "uk/co/example/ComplexTypes/LoginReply$JaxbAccessorM_getApplications_setApplications_[Luk_co_example_ComplexTypes_AppInfo;" in class file uk/co/example/ComplexTypes/LoginReply$JaxbAccessorM_getApplications_setApplications_[Luk_co_example_ComplexTypes_AppInfo;
Notice the random L before co_uk_example. My research suggests this is an old bug that should be fixed, and that no one else has reported this issue in over a year, no sure where to go from here.
Another edit:
Just added a new web method on the service that simply gets a list of appInfo and returns it to the client. This still fails the same way with NetBeans refusing to generate a sequence inside AppInfo.
I'm sure I'm missing something to declare the class, but I have checked it countless times to ensure I'm not missing anything.
warning gives you good hint: "WARNING: duplicate class definition bug occured"
your ws implementation class directly uses LoginReply class which directly uses AppInfo (+you are maybe also directly using this class in your ws implementation) => jaxb finds it
#XMLSeeAlso(...) tells jaxb to "link" referenced class
=> two definitions of the same class (not sure if it is by design or a bug that jaxb is not able to handle this case more gracefully)
to fix this just remove #XmlSeeAlso from your LoginReply class and you should be fine
This Issue came down to a very simple mistake. The AppInfo class was using non-standard getters and setters.
public void SetAppID(int AppID)
{
this.AppID = AppID;
}
This is INCORRECT (notice the capital on the Set), it should be:
public void setAppID(int AppID)
{
this.AppID = AppID;
}
Using a capital is not standard for JavaBeans and as such JAX-WS didn't know how to generate WSDL for this class. Thanks too shyam from the following link for answering the question
How to return a custom complex type in JAX-WS web services?
I don't think you can send "complex types" over the net (programmed port types) in http protocol, however an array may be implicitly converted to a delimited string set , check the docs for data transfer.

How to set custom JAXBContext

i would like to ask a question about #UsesJAXBContext annotation in jax-ws. I try to make it work on client side but I'm probably missing something. Here is my case:
I've got webservice with operation:
#WebMethod(operationName = "putToQueue")
public boolean put(#WebParam(name = "queueName") String queueName, #WebParam(name = "element") Object element) {
return queues.get(queueName).offer(element);
}
On the client side i generated QueueService and Queue (port)... and other stuff... [respones requests. In this case irrelevant.]
I would like to let user define object that he/she could put to queue. However to invoke operation put(...) I need bind object (that I try to send) into JAXBContext. I could do that by
#XmlSeeAlso in the top of the generated Queue stub [i tried this one and it works]. Nonetheless I need more generic solution that help me bind object at runtime.
I thought that I could create #QueueMessage annotation and ClientJAXBContextFactory and add marked class to the context when creating it.
public class ClientJAXBContextFactory implements JAXBContextFactory {
#Override
public JAXBRIContext createJAXBContext(SEIModel seim, List<Class> classes, List<TypeReference> references) throws JAXBException {
Reflections reflections = new Reflections("");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(QueueMessage.class);
classes.addAll(annotated);
return JAXBContextFactory.DEFAULT.createJAXBContext(seim, classes, references);
}
}
Next i tried use #UsesJAXBContext on top of generated Queue.
#WebService(name = "Queue")
#UsesJAXBContext(ClientJAXBContextFactory.class)
public interface Queue {
...
}
But createJAXBContext(...) is not invoked and jax-ws just simply create his JAXBContextImpl.
I have read:
http://weblogs.java.net/blog/jitu/archive/2008/08/control_of_jaxb.html
http://www.techques.com/question/1-5627173/Specify-JAXB-Packages-in-SLSB-and-JAX-WS
and some question on stackOverFlow. I would be grateful for advises.
Is it possible to implement idea presented in my question?
Also i might add that on the server side ... #UsesJAXBContext works. But its important for me to make it work on the client side.
Ok I could manage problem i was facing. Still i couldn't use #UsesJAXBContext with client consuming the webservice. But i found that this annotations are tied to beans with post-fix Feature. So there is a class UsesJAXBContextFeature
https://jax-ws.java.net/nonav/2.2.7/javadocs/com/sun/xml/ws/developer/UsesJAXBContextFeature.html
and it could be passed as argument of port or service(service since jax-ws 2.2). I have got a little trouble with versions so i decided to generate class and use jax-ws 2.1. Now i just simply create port like this:
new QueueService().getQueuePort(new UsesJAXBContextFeature(new ClientJAXBContextFactory()));
And it works!

How instantiate several OSGi services?

In the context of an Eclipse RCP application I decided to use OSGi services to provide "Interfaces" out of a plugin (i.e a bundle).
In one of my plugin I have the following Parser interface:
public interface Parser {
public void start(File file);
public boolean hasNext();
public Object next();
}
Consumer plugins will use this interface to parse files. Because several parsing can be done in the same time and because an implementation of this interface will need several "state" private field each consumer of this service must use a dedicated service instance.
In this case, the default solution provided by manu OSGi tutorials consisting in registering ONE service instance in the start method of the parser bundle doesn't work. What is the best solution to handle such a solution ?
I can create a ParserFactory service with one unique method:
public Parser create(File file);
??
Any comment is welcome,
As you're suggesting, I would change your service interface to be a provider of Parsers.
And your Parser is just an Iterator, so maybe something like
public interface ParserFactory<T> {
/** Iterating on the returned object
* provides Ts parsed from the InputStream.
*
* #param input must be closed by the returned object
* when done iterating.
*/
Iterable<T> createParser(InputStream input);
}
Using an InputStream or Reader also makes it more flexible that requiring a File.
Have a look at the OSGi ServiceFactory; this allows you to instantiate services for different requesting bundles. You can read more about it in section 5.6 of the core specification.

How to deserialize xml into a java object generated from axis2

I used Eclipse to generate a java client code stub given a third party wsdl. The client works great, I'm able to access the webservice an do what I need to do.
Now, I'd like to write some unit tests that can run without needing to be connected to the webservice. Is it possible to use some mechanism within axis2 stack to deserialize a xml file into one of the java objects in the java client stub code?
For example, one of the classes in the client stub code is "Contact". Say I have an xml file that mimics the xml that would usually is found in soap request. How can I deserialize that into a java Contact object?
I've used XMLBeans before, but hoping there's an easier way since it seems that the java client is already doing this deserialization under the hood somewhere? Maybe axis2 has a method to do take a chunk of xml and return a java object?
UPDATE:
I tried this:
String packageName = Contact.class.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance( packageName );
I get this:
java.lang.AssertionError: javax.xml.bind.JAXBException: "com.sforce.soap._2006._04.metadata" doesnt contain ObjectFactory.class or jaxb.index
Then, I tried this:
Contact c = new Contact();
JAXBContext jc = JAXBContext.newInstance( c.getClass() );
But then I get exception that one of the classes that the Contact Class uses does not have a no-arq default constructor
I was hoping this would be a quick and easy thing to do, but until I have time to fully grok axis2 and how it uses jaxb, I'm just gonna parse the xml manually.
This is called "unmarshalling" in Axis. Have a look at org.apache.axis2.jaxws.message.databinding.JAXBUtils.getJAXBUnmarshaller(JAXBContext context). Once you have an unmarshaller, you can deserialize the XML back into objects.

Categories

Resources