I'm a software architect/consultant in Boulder, Colorado. I started blogging at: http://wayne-adams.blogspot.com/, but have since started the new blog, Data Sceintist in Training which will cover the areas I am learning as part of my own "big data" training, including the Hadoop family, frameworks like OpenStack, the R programming language, column-oriented databases and the art of examining data sets for useful patterns. The original blog will be kept alive with Java-specific posts. Wayne is a DZone MVB and is not an employee of DZone and has posted 35 posts at DZone. You can read more from them at their website. View Full User Profile

Java SE Endpoints and Providers

12.22.2010
| 5063 views |
  • submit to reddit

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

Published at DZone with permission of Wayne Adams, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Robert Csala replied on Wed, 2010/12/22 - 5:45am

What happens if you want to have several services provided by the same webserver? While their endpoints are different, the crossplatform.xml-s would be served from the same endpoint, and that is bound to cause some problems at deploy time.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.