Platinum Partner
java

Java SE Endpoints and Providers

Recently I had a repeat of a problem I was unable to solve the first time I encountered it. When this first occurred, I was happily serving up content from a Java SE-based web service (if you're not familiar with this, it involves specially annotating a class and exposing it by publishing it to a javax.xml.ws.Endpoint). I ran into a problem when I tried accessing the web service (I believe from a Flex app), because the Flex runtime really "wanted" to find a crossdomain.xml file at the HTTP server root. Since it was unable to do so, it obligingly refused to expose my web service to possible attack and would not access the web service.

Leaving aside for the moment the philosophy of the client's enforcing security upon itself (this doesn't protect my web service from other unscrupulous clients), I was left with a problem. This approach is a little naive because the client is assuming the web service has something like a root document directory, where you can just drop the crossdomain.xml file and get on with your life. As you know, when you expose a Java bean as an Endpoint, there is no file system; just an internal HTTP server servicing the web-service requests. So where do you put the file?

The answer is: wherever you want, as long as you figure out how to serve it up! I ran into this same problem recently as I was building a Silverlight-based application. Silverlight enforces the same constraints on itself, and indeed will actually look for a crossdomain.xml file (after searching first for a clientaccesspolicy.xml file). This time I worked a little harder to find an answer, and got a lucky break from an online posting.

Using tcpmon, I noticed an interesting point. While my web service was hosted at a URL like http://www.example.com:8080/WebManager, the Silverlight runtime actually looks for the policy file at http://www.example.com:8080/crossdomain.xml. In other words, not at the context at which my web service was deployed, but at the root of the HTTP server itself. This distinction proves to greatly facilitate the solution.

The solution is to publish two endpoints -- one for the web service, and one for a document server. The javax.xml.ws.Endpoint class allows you to publish multiple beans at the same host and port (a crucial point, which makes this solution work). You just need to ensure the context, or full URL, is different for each. So, while I may publish my web service with the following statement:

Endpoint.publish("http://www.example.com:8080/WebManager", new WebManager());

I am able to publish a different endpoint to satisfy Flex/Silverlight with:

Endpoint.publish("http://www.example.com:8080/", new DocumentProvider());

When my Silverlight control attempts to access the web service, it will first look for a crossdomain.xml file at the web server root; if it determines it is allowed to access the service, it will then do so.

What is a DocumentProvider? It is a class which I've published to an Endpoint much like my actual business logic, but it intercepts the HTTP requests and only processes requests for documents. In other words, instead of letting the Java SE implementation handle all the requests, delegating them to @WebMethod-annotated methods, it handles all the work itself. The API for this can be found at javax.xml.ws.Provider, which defines a single method:

public T invoke(T request);

where T must be either a Source, a SOAPSource, or a DataSource. This looks a little generic, and it is. But this is where all the work occurs. In my case, I'm going to return a DataSource whose input stream is the requested document.

Following is a complete class which implements only the document server (exposing methods as web service operations is fairly well documented):

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import javax.activation.DataSource;
import javax.annotation.Resource;
import javax.xml.ws.BindingType;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;

@WebServiceProvider
@BindingType(HTTPBinding.HTTP_BINDING)
@ServiceMode(value=Service.Mode.MESSAGE)
public class DocumentServer implements Provider
{

protected static final String CROSSDOMAIN_XML = "<?xml version=\"1.0\" ?><cross-domain-policy><allow-access-from domain=\"*\" /></cross-domain-policy>";

@Resource
protected WebServiceContext wsContext;

public static void main(String [] args)
{
DocumentServer docServer = new DocumentServer();
}

public DocumentServer()
{
Endpoint.publish("http://localhost:10486/", this);
}

public DataSource invoke(DataSource ds) throws WebServiceException
{
MessageContext msgCtx = wsContext.getMessageContext();

String method = (String)msgCtx .get(MessageContext.HTTP_REQUEST_METHOD);
String pathInfo = (String)msgCtx.get(MessageContext.PATH_INFO);
if (method.equals("GET") && pathInfo.equals("crossdomain.xml"))
{
return new DataSource()
{
public InputStream getInputStream()
{
return new ByteArrayInputStream(CROSSDOMAIN_XML.getBytes());
}
public OutputStream getOutputStream()
{
return null;
}
public String getContentType()
{
return "text/xml";
}
public String getName()
{
return "";
}
};
}

throw new WebServiceException();
}
}

Some things to note about the above code:

  • There is a lot of flexibility in the Provider interface.
  • How your implementation of Provider is interpreted by the Java compiler depends in great part on the annotations you have added to your implementation. The WebServiceProvider, BindingType, and ServiceMode annotations are tightly coupled with the specifics of your implementation. Try compiling the example without them, to see what I mean.
  • The WebContext is vital but can be obtained by simple injection.
  • I only process "GET" requests for "crosssdomain.xml", and return a very liberal policy; of course, this is just an example. You could just as easily create a web-serviceable management interface on your crossdomain policy and create it dynamically, as well as serve it -- and other documents -- from a file system, rather than the way I've done it here.
Finally, I'm no expert in this area but am happy to pass the information along, as it is somewhat difficult to find examples of Provider implementations. In that spirit, I acknowledge the folks who posted this example: http://acm2010.cct.lsu.edu/localdoc/java/6-jdk/sample/webservices/EbayClient/. It was greatly appreciated!

From http://wayne-adams.blogspot.com/2010/12/java-se-endpoints-and-providers.html

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}