views:

399

answers:

3

I've created a Java web service using the @WebService annotation and deployed the service to a Glassfish server that lives behind a proxy server.

The problem is when someone accesses our WSDL and looks at the service endpoint address location they see

http://app server url/service...

instead of

http://proxy server url/service...

I'd like to have the proxy server URL returned in the service endpoint address location of the WSDL instead of the app server url.

What I'm wondering is, can I write a Filter or a Listener to listen for requests to the WSDL, then modify the WSDL service endpoint address with the proxy server URL? What might be the recommended way to do this - I was thinking a Filter is the best choice, but wasn't 100% sure?

BTW - I thought there might be a simple setting in Glassfish to do this, but I haven't been able to locate one that works.

+1  A: 

From Javaboutique article on servlet-filters

public final class TimerFilter implements Filter 
{

  public void doFilter(ServletRequest request, 
                      ServletResponse response,
                      FilterChain chain)
     throws IOException, ServletException 
 {

     long startTime = System.currentTimeMillis();
     chain.doFilter(request, response);
     long stopTime = System.currentTimeMillis();
     System.out.println("Time to execute request: " + (stopTime - startTime) + 
         " milliseconds");

 }
...

the call to chain.doFilter will call the normal servlet in your case (I'm assuming the @WebService is provided through a servlet API), and you can then read the result from the original web service, modify it, and pass it back to your own client.

Chris Kaminski
+1  A: 

Did you consider using a web service registry for that?

The glassfish doc about web service registry says:

If you are using a load balancer, enter the Load Balancer host name, port number, and the SSL port number. If you are publishing the web service to an external registry, where the WSDL can be found over the internet, these options will replace the hostname and port name specified in the WSDL to the one of the load balancer.

So that should work for your proxy as well.

Here are two resources that might be interesting: the glassfish ws management page, an article aobut how to set up the registry.

I never used the registry myself, and agree that it's a bit heavy-weighted for what you want to achieve, but it seems to be a possible way to publish web service endpoint information for consumers.

ewernli
A: 

I took the filter approach and here's what I came up with. It seems to work.

I've included the code for my Filter's doFilter method. I also wrote a custom HttpServletResponseWrapper, but that implementation was pretty straightforward.

the Apache reverse-proxy adds the x-forwarded-host value to the HTTP header (and is the name I use to replace the app servers name). Another alternative I thought of was to place the proxy server's address as a property on the app server and grab that. Ultimately, I thought this was a cleaner way to go.

/*
 * Replace the server URL with the proxy server URL when called from a proxy server 
 */
@Override
public void doFilter(
    ServletRequest request, 
    ServletResponse response,
    FilterChain filterChain) throws IOException, ServletException 
{
    WsdlResponseWrapper myResponse = new WsdlResponseWrapper((HttpServletResponse) response);
    filterChain.doFilter(request, myResponse);
    boolean isResponseOutputStream = myResponse.getOutputStreamContent().length > 0;

    /*
     * The servlet response sent into this method only allows access to
     * getOutputStream or getWriter, not both. It will throw an
     * exception if an attempt is made to to access both.
     * 
     * If this reason, I'm checking the output stream first and writing
     * that content if it exists, then printing to the writer only if
     * the output stream is empty.
     */
    StringBuffer responseBuffer;
    if (isResponseOutputStream) {
         responseBuffer = new StringBuffer(new String(myResponse.getOutputStreamContent()));
    } else {
        responseBuffer = new StringBuffer(myResponse.getWriterContent());
    }

    // Change the URL when called from a proxy server
    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        String requestedHostname = httpServletRequest.getHeader("x-forwarded-host");
        if ((null != requestedHostname) && !requestedHostname.isEmpty()) {
            String myHostname = httpServletRequest.getHeader("host");
            int myPort = httpServletRequest.getLocalPort();

            // change the hostname
            int index = responseBuffer.indexOf(myHostname);
            int length = myHostname.length();
            while (index > -1) {
                responseBuffer.replace(index, index+length, requestedHostname);
                index = responseBuffer.indexOf(myHostname);
            }

            // remove the port
            String portString = ":" + myPort;
            length = portString.length();
            index = responseBuffer.indexOf(portString);
            while (index > -1) {
                responseBuffer.replace(index, index+length, "");
                index = responseBuffer.indexOf(portString);
            }
        }
    }

    // forward the response
    if (isResponseOutputStream) {
        response.getOutputStream().write(responseBuffer.toString().getBytes());
    } else {
        response.getWriter().print(responseBuffer);
    }
}
Vinnie