Java tutorial
/** * Copyright (c) 2012 centeractive ag. All Rights Reserved. * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.centeractive.ws.server.core; import com.centeractive.ws.server.ServiceRegistrationException; import com.centeractive.ws.server.SoapServerException; import com.centeractive.ws.server.endpoint.GenericContextDomEndpoint; import com.centeractive.ws.server.responder.RequestResponder; import com.centeractive.ws.server.connector.SslSelectChannelConnector; import org.mortbay.jetty.AbstractConnector; import org.mortbay.jetty.Handler; import org.mortbay.jetty.Server; import org.mortbay.jetty.nio.SelectChannelConnector; //import org.mortbay.jetty.security.SslSelectChannelConnector; //import org.mortbay.jetty.security.SslSelectChannelConnector; import org.mortbay.jetty.servlet.Context; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext; import javax.servlet.ServletContext; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Properties; import static com.centeractive.ws.server.core.SoapServerConstants.*; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * SOAP server enables the user to handle a SOAP communication on a purely XML level. * It supports plain HTTP as well as HTTPS communication. * When it comes to SOAP it supports SOAP 1.1 and 1.2 * This class may throw an unchecked @see com.centeractive.ws.server.SoapServerException * * @author Tom Bujok * @since 1.0.0 */ public final class SoapServer { private Integer httpPort = HTTP_PORT; private Integer httpsPort = HTTPS_PORT; private boolean reuseAddress = REUSE_ADDRESS; private Integer connectionMaxIdleTimeInSeconds = CONNECTION_MAX_IDLE_TIME_IN_SECONDS; private Integer acceptorThreads = ACCEPTOR_THREADS_COUNT; private Integer coreThreads = CORE_THREADS_COUNT; private Integer maxThreads = MAX_THREADS_COUNT; private Integer threadKeepAliveTimeInSeconds = THREAD_KEEP_ALIVE_TIME_IN_SECONDS; private boolean http; private boolean https; private URL keyStoreUrl; private String keyStoreType = KEYSTORE_TYPE; private String keyStorePassword; // runtime attributes private ClassPathXmlApplicationContext context; private Server server; private GenericContextDomEndpoint endpoint; // ---------------------------------------------------------------- // PUBLIC API // ---------------------------------------------------------------- /** * Starts the SOAP server */ public void start() { try { server.start(); } catch (Exception ex) { throw new SoapServerException(ex); } } /** * Stops the SOAP server (does not release the allocated resources) */ public void stop() { try { server.stop(); } catch (Exception ex) { throw new SoapServerException(ex); } } /** * Stops the SOAP server and deallocates resources */ public void destroy() { stop(); context.close(); } /** * Registers responder under specified context path. Only one responder may be registered under one context path. * * @param contextPath * @param responder * @throws ServiceRegistrationException thrown if a registration error occurs - for example duplicate responder registered */ public void registerRequestResponder(String contextPath, RequestResponder responder) throws ServiceRegistrationException { checkNotNull(contextPath, "contextPath cannot be null"); checkNotNull(responder, "responder cannot be null"); endpoint.registerRequestResponder(contextPath, responder); } /** * Unregisters responder from the specified context path * * @param contextPath * @throws ServiceRegistrationException thrown if an unregistration error occurs - for example no responder registerd */ public void unregisterRequestResponder(String contextPath) throws ServiceRegistrationException { checkNotNull(contextPath, "contextPath cannot be null"); endpoint.unregisterRequestResponder(contextPath); } /** * @return a list of registered context paths */ public List<String> getRegisteredContextPaths() { return Collections.list(endpoint.getRegisteredContextPaths()); } // ---------------------------------------------------------------- // INTERNAL API // ---------------------------------------------------------------- private void configure() { configureParentContext(); configureConnectors(); configureWebContext(); } private void configureParentContext() { PropertyPlaceholderConfigurer config = new PropertyPlaceholderConfigurer(); config.setProperties(buildProperties()); context = new ClassPathXmlApplicationContext(); context.addBeanFactoryPostProcessor(config); context.setConfigLocation(SPRING_CONTEXT_LOCATION); context.refresh(); context.registerShutdownHook(); server = context.getBean(SERVER_BEAN_NAME, Server.class); } private void configureConnectors() { if (http) { SelectChannelConnector httpConnector = context.getBean(CONNECTOR_BEAN_NAME, SelectChannelConnector.class); configureHttpConnector(httpConnector); server.addConnector(httpConnector); } if (https) { checkNotNull(keyStoreUrl, "keyStore has to be set in https mode"); SslSelectChannelConnector httpsConnector = context.getBean(SSL_CONNECTOR_BEAN_NAME, SslSelectChannelConnector.class); configureHttpsConnector(httpsConnector); server.addConnector(httpsConnector); } } private SelectChannelConnector configureHttpConnector(SelectChannelConnector connector) { configureGenericConnector(connector); connector.setReuseAddress(reuseAddress); connector.setPort(httpPort); return connector; } private SslSelectChannelConnector configureHttpsConnector(SslSelectChannelConnector connector) { configureGenericConnector(connector); connector.setReuseAddress(reuseAddress); connector.setPort(httpsPort); connector.setKeystore(keyStoreUrl.toString()); connector.setKeystoreType(keyStoreType); // if (StringUtils.isNotBlank(keyStorePassword)) { connector.setKeyPassword(keyStorePassword); // } return connector; } private AbstractConnector configureGenericConnector(AbstractConnector connector) { connector.setAcceptors(acceptorThreads); connector.setMaxIdleTime(connectionMaxIdleTimeInSeconds * SECONDS_TO_MILLIS_RATIO); return connector; } private void configureWebContext() { ServletContext servletContext = getServletContext(); GenericWebApplicationContext webContext = new GenericWebApplicationContext(); webContext.setServletContext(servletContext); webContext.setParent(context); webContext.refresh(); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext); if (webContext != null) { endpoint = webContext.getBean(ENDPOINT_BEAN_NAME, GenericContextDomEndpoint.class); } } private ServletContext getServletContext() { for (Handler handler : server.getHandlers()) { if (handler instanceof Context) { return ((Context) handler).getServletContext(); } } return null; } private Properties buildProperties() { Properties properties = new Properties(); properties.setProperty(CORE_POOL_SIZE_PROP_KEY, coreThreads.toString()); properties.setProperty(MAX_POOL_SIZE_PROP_KEY, maxThreads.toString()); properties.setProperty(KEEP_ALIVE_PROP_KEY, threadKeepAliveTimeInSeconds.toString()); return properties; } // ---------------------------------------------------------------- // BUILDER API // ---------------------------------------------------------------- private SoapServer() { } /** * Builder to construct a properly populated SoapServer */ public static class SoapServerBuilder { private final SoapServer server = new SoapServer(); /** * @param value Sets the http port on which the server listens. Has to be not negative. * @return */ public SoapServerBuilder httpPort(int value) { checkArgument(value >= 0); server.http = true; server.httpPort = value; return this; } /** * @param value Sets the https port on which the server listens. Has to be not negative. * @return */ public SoapServerBuilder httpsPort(int value) { checkArgument(value >= 0); server.https = true; server.httpsPort = value; return this; } /** * @param value Sets the connection max idle time in seconds. Has to be not negative. * @return */ public SoapServerBuilder connectionMaxIdleTimeInSeconds(int value) { checkArgument(value >= 0); server.connectionMaxIdleTimeInSeconds = value; return this; } /** * @param value Sets the number of http server connector acceptor threads. Has to be positive. * @return */ public SoapServerBuilder acceptorThreads(int value) { checkArgument(value > 0); server.acceptorThreads = value; return this; } /** * @param value Sets the number of http server core threads. Has to be positive. * @return */ public SoapServerBuilder coreThreads(int value) { checkArgument(value > 0); server.coreThreads = value; return this; } /** * @param value Sets the maximal number of threads that the http server may spawn. Has to be positive. * @return */ public SoapServerBuilder maxThreads(int value) { checkArgument(value > 0); server.maxThreads = value; return this; } /** * @param value Sets the value of thread keep alive in seconds. Has to be not negative. * @return */ public SoapServerBuilder threadKeepAliveTimeInSeconds(int value) { checkArgument(value >= 0); server.threadKeepAliveTimeInSeconds = value; return this; } /** * @param value Specifies the URL of the keystore to use in the SOAP communication. Null is not accepted. If there's more than * one certificate in the keystore it is undefined which of them will be used * @return */ public SoapServerBuilder keyStoreUrl(URL value) { checkNotNull(value); server.keyStoreUrl = value; return this; } /** * @param value Specifies the type of the keystore. Null is not accepted. * @return */ public SoapServerBuilder keyStoreType(String value) { checkNotNull(value); server.keyStoreType = value; return this; } /** * @param value keystore password. Null is accepted. * @return */ public SoapServerBuilder keyStorePassword(String value) { server.keyStorePassword = value; return this; } /** * @param value Sets the reuseAddress on the underlying @see java.net.Socket * @return */ public SoapServerBuilder reuseAddress(boolean value) { server.reuseAddress = value; return this; } /** * Builds populated SoapServer instance * * @return */ public SoapServer build() { server.configure(); return server; } } /** * @return a new instance of a SoapServer Builder */ public static SoapServerBuilder builder() { return new SoapServerBuilder(); } }