views:

528

answers:

5

I'm in a difficult position: We have a 3rd party enterprise system that exposes a Java-based API. However, we are a 100% .Net oriented development team. Essentially, I need to wrap the Java API with something that C# code can call.

Web services would be great, but the only Java application server supported on our infrastructure is WebSphere 6.1. This means the ancient (and deprecated) JAX-RPC web service framework is the only way for us to expose web services. Just getting a simple proof-of-concept working here has been a nightmare (because of Java inexperience, WebSphere being awful, JAX-RPC being clunky, and lots of JAR hell).

The new JAX-WS 2.0 web service framework in JAVA EE 5 looks great-- is there any way to run this without an entire Java application server? For example, in .Net's WCF (Windows Communication Framework), you can host services pretty much anywhere you want (in-process, Windows Service, IIS 6/7 etc).

What is the most lightweight way to wrap this library with some web services?

A: 

Since you can't update your version of the JDK, as I expect you are tied to what WebSphere needs, you may want to look at trying the axis famework, from apache.

This would require you to write a webservice that would just pass calls to the java code, but it should provide you the tools to get online, and it works well with older versions of java.

I expect that Jax-WS would be a problem unless by some miracle you are on JDK5 at least, then JAX-WS would be helpful, as the annotations makes the development more like the webservice model under .NET 2.0.

James Black
+1  A: 

I don't quite understand your statement "the only Java application server supported on our infrastructure is WebSphere 6.1" with respect to the question about how to run other stuff. But no, you don't need a full App Server just to expose a web service.

I think this is a good starting point for you: http://docs.codehaus.org/display/JETTY/J2se6HttpServerSPI

ykaganovich
In other words, Glassfish, JBoss, or WebSphere v7 are not options for us. Our configuration of WebSphere 6.1 only supports the old JAX-RPC web services which are a pain. Thanks for the link to Jetty! +1
On further reflection, why would that J2se6HttpServerSPI Jetty project be required if I'm just exposing web services?
"This extension has been written not only to workaround the internal Sun HttpServer problem (http://forums.java.net/jive/message.jspa?messageID=130831) but also to allow tight integration of JAX-WS web services and servlets." Probably not really needed. But it's a single link that points to an embeddable servlet container and JAX-WS implementation you can use. I'm lazy :)
ykaganovich
+2  A: 

Yes.

If you can create a Java method which is 1) annotated with @WebMetod and 2) takes the needed parameters and call into your 3'rd party code, and wrap it up as a web application you can use the Metro stack - https://metro.dev.java.net/ - with any Servlet 2.5 web container (put it in the web containers global lib folder) to expose the above method as a web service. We are using an embedded Jetty, but I've verified this works with Tomcat.

I wrote up my findings in http://archive.midrange.com/java400-l/200904/msg00071.html


I downloaded Metro 1.4 from https://metro.dev.java.net/1.4/ (version 1.5 is very new and I haven't looked at it), which eventually unpacks to several jar files.

Copy webservices-api.jar, webservices-rt.jar, webservices-extra-api.jar and webservices-extra.jar (four files) to the folder containing "blessed" jarfiles common to all of tomcat - I believe it is ${TOMCAT}/lib for Tomcat 6.[1]

In your Eclipse project eventually ending up to be a WAR file:

  • If your workspace JRE is Java 5, you must add webservices-api.jar to the classpath (it should not be deployed in the end). If it is Java 6 you should be able to skip this step.

  • Create a class foo.Ping looking like:


package foo;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 Ping is a simple web service class providing a "yes, we have contact" class.
 Currently the doPing() method provides a response with the host name and
 address (if available) and the current server time.

*/
@javax.jws.WebService
public class Ping {

@javax.jws.WebMethod(action = "doPing")
public String doPing() {
System.out.println("Ping.doPing() called.");

String hostName;
try {
  hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
  hostName = "unknown (" + e.getMessage() + ")";
}

String hostAddress;
try {
  hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
  hostAddress = "unknown (" + e.getMessage() + ")";
}

return "Reached '" + hostName + "' (" + hostAddress + ") at "
    + new java.util.Date() + " java.version="
    + System.getProperty("java.version", "(not set)");
  }
}


  • In your WEB-INF/web.xml add this snippet:
    <listener>
    <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>
    <servlet>
    <description>JAX-WS endpoint - this servlet must handle all endpoints</description>
    <display-name>webservice</display-name>
    <servlet-name>webservice</servlet-name>
    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- each endpoint must have a mapping to the JAX-WS endpoint servlet -->

    <servlet-mapping>
    <servlet-name>webservice</servlet-name>
    <url-pattern>/ws</url-pattern>
    </servlet-mapping>
  • Create a NEW file WEB-INF/sun-jaxws.xml:
    <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
        <endpoint name='ping' implementation='foo.Ping'url-pattern='/ws'>
        </endpoint
    </endpoints>
  • Ensure that both web.xml and sun-jaxws.xml are included in the deployment!

  • Done!

Now deploy your war file to the Tomcat prepared above, and open "/ws" under your deployed web application. This might be "http://localhost:8080/foo/ws";. This will give you a page with information including a link to WSDL for all web services, including the Ping. This link can be used directly in any WSDL processing tool, including the web service tool in Eclipse JEE and WSDCi.

Hope this helps you :)

[1] Not making them global WILL give you classloader problems!

Thorbjørn Ravn Andersen
A: 

I ended up finding a solution that was far easier than any of the above. We created some simple classes (like the doPing() method in @Thorbjørn Ravn Andersen's answer) with the @javax.jws.WebService and @javax.jws.WebMethod annotations, then deployed them using:

string url = "http://localhost:8282/MyService"
MyService serviceInstance = new MyService();
Endpoint svc = Endpoint.publish(url, serviceInstance);

I was then able to point Visual Studio at http://localhost:8282/MyService?wsdl and generate a client. Easy as pie.

We have run a lot of requests through this service over a large span of time and have not noticed any problems. We wrapped this with the Java Service Wrapper so that it comes back up across reboots/JVM crashes, etc. A poor man's application server.

I hope this might help any other .NET developer looking to interoperate with Java without having to remap your brain to do it.

Just for the record. That requires Java 6, which I implictly did assume was not available as you said your only environment was WebSphere 6.1.Glad you found a solution!
Thorbjørn Ravn Andersen
A: 

Ah, this is a wonderful solution. I'm stuck with a Java Web Service situation and I think this will save the day.

But I actually have a couple of questions @user177367. I couldn't figure out how to post this as a comment to the last answer rather than a whole new answer.

So, it looks like once you set this up, it will deploy (sort of miraculously!) and the Web Service seems to outlive the Java application. My application finishes executing and stops, but if I forget to Endpoint.stop() then the Web Service will continue to exist. So I think I should code the application in a way that its process continues to run while required and then stop the Endpoint before closing down.

So the first question would be, what is the best approach to wait on the service. Should I do some sort of thread.sleep() kind of a thing, or otherwise .wait() on any particular object? What makes sense?

And the second question is, say there's an unexpected crash or error or shutdown of the application, so the application quits while the Web Service continues to exist (and respond I assume). In this case, what sort of logic can I implement to reconnect (gain access to the instance of) the preexisting Web Service and continue controlling it; or somehow kill it and restart it?

Because I noticed if I forget (or otherwise fail) to .stop() the Endpoint and then restart my application, I get an error that the binding exists and so the new Endpoint instance cannot use the same binding (which I'm assuming is the URI on which the previous Endpoint was published). So, how do I clean this up? (Obviously, programmatically, not by restarting the JVM).

Cheers, Arash

Arash
@Arash-- We use the Tanuki/Java service wrapper to host our web services. That allows the services to listen until they are shut down. For error handling, we just make sure that on any exception, we call .stop() on our Endpoints. In .NET this would be like IDisposable. In the Java realm, we just use try->finally. That Java Service Wrapper has some facilities to restart a JVM as well.