/* * Copyright 2013 Guidewire Software, Inc. */ package gw.xml.ws; import gw.internal.ext.org.mortbay.jetty.Connector; import gw.internal.ext.org.mortbay.jetty.servlet.Context; import gw.internal.ext.org.mortbay.jetty.servlet.ServletHolder; import gw.internal.xml.ws.GosuWebservicesServlet; import gw.internal.xml.ws.server.ServerAnnotationVerifier; import gw.lang.PublishInGosu; import gw.lang.parser.IUsageSiteValidatorReference; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.util.GosuExceptionUtil; import java.util.HashMap; import java.util.Map; /** * Allows easy launch of an embedded web server to serve Webservices. */ @PublishInGosu public final class Server { private static final Map<Integer,Server> _servers = new HashMap<Integer, Server>(); private boolean _started; private gw.internal.ext.org.mortbay.jetty.Server _server; private GosuWebservicesServlet _servlet; private Integer _registeredPort; private Server( Integer registeredPort, int port ) { _registeredPort = registeredPort; try { _server = new gw.internal.ext.org.mortbay.jetty.Server( port ); Context context = new Context( _server, "/" ); ServletHolder servletHolder = new ServletHolder(); _servlet = new GosuWebservicesServlet(); servletHolder.setServlet( _servlet ); context.addServlet( servletHolder, "/*" ); } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } } /** * Publishes a type annotated with @WsiWebService on a port. * @param type the type to publish */ @IUsageSiteValidatorReference(ServerAnnotationVerifier.class) public void publish( IType type ) { TypeSystem.lock(); try { String errorMessage = GosuWebservicesServlet.checkWebServiceForErrors( type ); if ( errorMessage != null ) { throw new IllegalArgumentException( type.getName() + " is not a valid webservice: " + errorMessage ); } _servlet.addWebService( type.getName() ); } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } finally { TypeSystem.unlock(); } } /** * Starts a server on the specified port. If the server has already been started, calling this method has no * effect. */ public void start() { TypeSystem.lock(); try { if ( _started ) { return; } _server.start(); _started = true; } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } finally { TypeSystem.unlock(); } } /** * Stops a server on the specified port. Once stopped, the server is no longer valid, and cannot be started again. * Any future call to get() for this server's port will cause a new server to be created. */ public void stop() { try { _server.stop(); } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } TypeSystem.lock(); try { if ( ! _started ) { return; } //noinspection ObjectEquality if ( _registeredPort != null && _servers.get( _registeredPort ) == this ) { _servers.remove( _registeredPort ); } } finally { TypeSystem.unlock(); } } /** * Returns the local port of the server. This method can only be used once the server has been started. * @return the local port of the server */ public int getPort() { if ( ! _started ) { throw new IllegalStateException( "Server has not been started" ); } try { Connector[] connectors = _server.getConnectors(); return connectors[0].getLocalPort(); } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } } /** * Returns the server on the specified port, creating it if needed. * @param port the port, or zero for a random port. The random port number can be queried after starting the server by using the getPort() method. * @return the server */ public static Server get( int port ) { if ( port < 0 ) { throw new IllegalArgumentException( "Port cannot be less than zero" ); } TypeSystem.lock(); try { Server server = _servers.get( port ); if ( server == null ) { server = new Server( port, port ); _servers.put( port, server ); } return server; } finally { TypeSystem.unlock(); } } /** * Creates a new server on a random port and returns it. This server will not be accessible through the get() method. * @param port the port, or zero for a random port. The random port number can be queried after starting the server by using the getPort() method. * @return the created server */ public static Server newPrivateServer( int port ) { if ( port < 0 ) { throw new IllegalArgumentException( "Port cannot be less than zero" ); } return new Server( null, port ); } }