7

Does anyone know how to configure it so that a SOAP service and REST service can share the same port using Jersey (Grizzly) outside of an application server?

  • My soap service is at www.someurl.com:port/soap/crm
  • My rest service is at www.someurl.com:port/crm

These services share the same port but not the same base url and therefor should be able to run side by side on that port. However, there is a port bind error ..

All of this is in a custom service application and there is no web.xml or such.

The REST service is using Jersey and the Soap service is a class 'ReceptionService' published on an endpoint.

URI soapUri = URI.create("192.168.0.0:1234\soap\Crm")
URI restUri = URI.create("192.168.0.0:1234\crm")

// START SOAP SERVICE
Object reception = getObjectResource(ReceptionService.class);
Endpoint e = Endpoint.publish(soapUri, reception);

// START REST SERVICE    
ResourceConfig rc = new ResourceConfig().packages("company.rest");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(restUri, rc);

When I try this, Grizzly spits out 'java.net.BindException: Address already in use: bind'

My soap service reception is setup like this:

    @WebService(targetNamespace = "company.crm")
    @SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL,      parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
    public class Reception implements ReceptionService {
     ... methods
    }

My rest service classes are annotated as usual like ...

@Path("hello/{username}")
public class Hello { ... }

I am a bit of a newbie at this and any pointers would be appreciated. And please, don't bother suggesting I run an application server. That is not the problem here -- the problem is how do I get around the port-bind issue without moving to some other webservice framework?

NOTE RE BOUNTY: I CANNOT ITERATE THIS ENOUGH

"The bounty winner will demonstrate how to use Jersey for REST and a Java SOAP service (annotated JAX-WS) together on the same endpoint. The answer will not require changes to the Rest and Soap annotated classes. However, and HTTP server code changes or configuration changes to make it work are acceptable. Switching to Jetty or some other application server are not acceptable The solution must be 100% embedded and run using Java SE and Java web service libraries."

informatik01
  • 16,038
  • 10
  • 74
  • 104
The Coordinator
  • 13,007
  • 11
  • 44
  • 73
  • A little bit of more information would be helpful, e.g. which type of web server/application server do you use? How does the `web.xml` look like? – Uwe Plonus Jul 15 '13 at 06:15
  • have you found the solution? I am facing the same problem – mr. Holiday Aug 05 '15 at 13:20
  • Nope. I am still keeping them separate ports. I would have to do a lot of reworking my code and I don't have the time or patience. Hoping it is configurable in the future. – The Coordinator Aug 08 '15 at 07:51

5 Answers5

4

Jetty's overlay feature allows you to deploy two different webapps with different paths on the same Jetty instance/port.

You would have one web.xml with your Jersey servlet (for REST) and the other web.xml with the SOAP servlet.

http://www.eclipse.org/jetty/documentation/current/overlay-deployer.html

informatik01
  • 16,038
  • 10
  • 74
  • 104
marathon
  • 7,881
  • 17
  • 74
  • 137
  • OK. That might work if I move it all to Jetty. And since I am now using Jetty for the Restful side of things, because I like the way it easily configs with Guice, then maybe the Soap can go that way too. – The Coordinator Jul 24 '13 at 20:36
  • But then, it must be a solution that does not involve web.xml files. If you can show how to do this with Jetty and without xml files, then I will consider that a proper answer. – The Coordinator Jul 25 '13 at 11:16
3

What you are saying is nothing but two different interfaces for exposing services and yes you can host them on a single port just deploy it in the same container and you will be having both the interfaces up and running.

Just make sure you don't have a context path conflict, which does not seem to happen in the urls mentioned in the question above.

e.g. Let the rest interface be deployed as:

www.someurl.com:port2/crm , so soap should not be deployed in the same url, www.someurl.com:port1/soap/crm , which is alright.

You should also explain a bit how you are deploying the interfaces, as separate war files or in a single war file.

Himanshu Bhardwaj
  • 4,038
  • 3
  • 17
  • 36
  • Thanks. But I need them to share the same URL port. No War files, no application server, just coded as above. It works fine, on seperate ports, but causes a bind exception when sharing the same port (although the endpoint is actually different.) – The Coordinator Jul 15 '13 at 06:46
  • Well the trouble here is, Endpoint.publish will start and HTTP Server and the Grizzly part also starts an HTTP Server because of this you have a port clash. – Himanshu Bhardwaj Jul 15 '13 at 06:50
  • So then how do I get the Grizzly to also provide the soap service? IE. Publish the reception service properly side-by-side? There must be a way since Microsoft Server and Apache CMX also allow the same behaviour. – The Coordinator Jul 15 '13 at 07:38
  • The best bet you have I guess is to have a single deployment descriptor here web.xml for the application which will integrate the services you offer and deploy them as a unit together. – Himanshu Bhardwaj Jul 15 '13 at 07:59
  • Sorry, I am not using an application server in this context. – The Coordinator Jul 15 '13 at 08:00
  • Its not necessary to have a container to satisfy the purpose, refer to embedded jetty something you should find for Grizzly. Sorry I am not much exposed to Grizzly so don't know the exact APIs. – Himanshu Bhardwaj Jul 15 '13 at 08:02
2

If you are using a common container such as tomcat for your webservices, then you can get requests for both of the services arriving on the same port. You can deploy both the REST and SOAP based services as part of your application. The container will accept the incoming request and will forward them to the application depending on the application context. In your application web.xml, you can configure the where to send the request depending on the request URL mapping.

Juned Ahsan
  • 67,789
  • 12
  • 98
  • 136
  • Thanks. I am using Jersey (outside of Glassfish) in a customer server application. I will post more info above and maybe someone has a solution. – The Coordinator Jul 15 '13 at 06:23
2

It´s not possible to start more than one service on the same port.

When you want that your App is accessible over the same port, you have to use an Application Server.

You must take care that both Apps on the Server are accessible with different URL´s (web.xml).

All Applications on the same Application Server are now accessible over the same port.

Holger
  • 496
  • 3
  • 8
  • If it can be done with an application Server then it can be done programatically without one. How then does Glassfish host on the same port using jersey (grizzly http) as the rest server and a soap service... that is the million dollar question :) – The Coordinator Jul 24 '13 at 11:45
0

This is most of the code I use to host both rest and soap services on a single port (2 different context paths), completely embedded in my app (using Grizzly obviously), and spring configured...

package com.mycompany.structure.web.grizzly;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.BindException;
import java.util.EnumSet;
import java.util.LinkedList;
import javax.servlet.DispatcherType;
import javax.servlet.Servlet;
import com.mycompany.structure.web.jersey.jackson.JsonResourceConfig;
import com.mycompany.structure.web.jersey.spring.ExposedApplicationContext;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.jaxws.JaxwsHandler;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.jersey.servlet.ServletContainer;
import org.smallmind.nutsnbolts.lang.web.PerApplicationContextFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

public class GrizzlyInitializingBean implements DisposableBean, ApplicationContextAware, ApplicationListener, BeanPostProcessor {

  private static final Class[] NO_ARG_SIGNATURE = new Class[0];
  private HttpServer httpServer;
  private LinkedList<WebService> serviceList = new LinkedList<>();
  private ServletInstaller[] servletInstallers;
  private String host;
  private String contextPath;
  private String restPath;
  private String soapPath;
  private int port;
  private boolean debug = false;

  public void setHost (String host) {

    this.host = host;
  }

  public void setPort (int port) {

    this.port = port;
  }

  public void setContextPath (String contextPath) {

    this.contextPath = contextPath;
  }

  public void setRestPath (String restPath) {

    this.restPath = restPath;
  }

  public void setSoapPath (String soapPath) {

    this.soapPath = soapPath;
  }

  public void setServletInstallers (ServletInstaller[] servletInstallers) {

    this.servletInstallers = servletInstallers;
  }

  public void setDebug (boolean debug) {

    this.debug = debug;
  }

  @Override
  public synchronized void onApplicationEvent (ApplicationEvent event) {

    if (event instanceof ContextRefreshedEvent) {

      if (debug) {
        System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
      }
      httpServer = new HttpServer();
      httpServer.addListener(new NetworkListener("grizzly2", host, port));

      WebappContext webappContext = new WebappContext("Grizzly Application Context");
      webappContext.addServlet("JAX-RS Application", new ServletContainer(new JsonResourceConfig(ExposedApplicationContext.getApplicationContext()))).addMapping(restPath + "/*");
      webappContext.addFilter("per-application-data", new PerApplicationContextFilter()).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), restPath + "/*");
      webappContext.addListener("org.springframework.web.context.request.RequestContextListener");

      for (ServletInstaller servletInstaller : servletInstallers) {
        try {

          Constructor<? extends Servlet> servletConstructor;
          Servlet servlet;
          String urlPattern;

          servletConstructor = servletInstaller.getServletClass().getConstructor(NO_ARG_SIGNATURE);
          servlet = servletConstructor.newInstance();

          webappContext.addServlet(servletInstaller.getDisplayName(), servlet).addMapping((urlPattern = servletInstaller.getUrlPattern()) == null ? "/" : urlPattern);
        }
        catch (Exception exception) {
          throw new GrizzlyInitializationException(exception);
        }
      }

      webappContext.deploy(httpServer);

      for (WebService webService : serviceList) {

        HttpHandler httpHandler = new JaxwsHandler(webService.getService(), false);

        httpServer.getServerConfiguration().addHttpHandler(httpHandler, soapPath + webService.getPath());
      }

      try {
        httpServer.start();
      }
      catch (IOException ioException) {
        if (!(ioException instanceof BindException)) {
          throw new GrizzlyInitializationException(ioException);
        }
      }
    }
  }

  @Override
  public void setApplicationContext (ApplicationContext applicationContext) {

    ExposedApplicationContext.register(applicationContext);
  }

  @Override
  public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {

    return bean;
  }

  @Override
  public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {

    ServicePath servicePath;

    if ((servicePath = bean.getClass().getAnnotation(ServicePath.class)) != null) {
      serviceList.add(new WebService(servicePath.value(), bean));
    }

    return bean;
  }

  @Override
  public synchronized void destroy () {

    if (httpServer != null) {
      httpServer.shutdown();
    }
  }
}