summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-09-07 23:52:54 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-09-07 23:52:54 +0000
commitd123827b572d59f86e23d4e7fac42864d646ee60 (patch)
treee85a7e9a64391cf9b11830b5b6654dd51185be0d /sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src
parentba5e7230636b321793db90d642b066b423d59c34 (diff)
Sandbox to experiment with deployment and management commands.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@993560 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src')
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java58
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java114
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java701
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/resources/META-INF/services/org.apache.tuscany.sca.host.http.ServletHost18
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/java/org/apache/tuscany/sca/http/tomcat/TomcatServerTestCase.java372
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/content/test.html21
-rw-r--r--sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/tuscany.keyStorebin0 -> 1265 bytes
7 files changed, 1284 insertions, 0 deletions
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java
new file mode 100644
index 0000000000..a0b384d554
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.tuscany.sca.http.tomcat;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.core.StandardWrapper;
+
+/**
+ * A Servlet wrapper.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletWrapper extends StandardWrapper {
+ private static final long serialVersionUID = 1L;
+
+ private final Servlet servlet;
+
+ public ServletWrapper(Servlet servlet) {
+ this.servlet = servlet;
+ }
+
+ @Override
+ public synchronized Servlet loadServlet() {
+ return servlet;
+ }
+
+ @Override
+ public Servlet getServlet() {
+ return servlet;
+ }
+
+ public void initServlet() throws ServletException {
+ servlet.init(facade);
+ }
+
+ public void destroyServlet() {
+ servlet.destroy();
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java
new file mode 100644
index 0000000000..5d9b65503a
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java
@@ -0,0 +1,114 @@
+/*
+ * 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 org.apache.tuscany.sca.http.tomcat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Hashtable;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.naming.resources.FileDirContext;
+import org.apache.naming.resources.ProxyDirContext;
+import org.apache.naming.resources.Resource;
+
+/**
+ * DefaultServlet implementation for Tomcat.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TomcatDefaultServlet extends DefaultServlet {
+ private static final long serialVersionUID = -7503581551326796573L;
+
+ private String documentRoot;
+ private ProxyDirContext proxyDirContext;
+
+ public TomcatDefaultServlet(String servletPath, String documentRoot) {
+ this.documentRoot = documentRoot;
+
+ DirContext dirContext;
+ URI uri = URI.create(documentRoot);
+ if (!"file".equals(uri.getScheme())) {
+
+ dirContext = new FileDirContext() {
+ @Override
+ public Attributes getAttributes(String name) throws NamingException {
+ return new BasicAttributes();
+ }
+
+ @Override
+ public Object lookup(String name) throws NamingException {
+
+ try {
+ final URL url = new URL(TomcatDefaultServlet.this.documentRoot + name);
+ return new Resource() {
+ @Override
+ public InputStream streamContent() throws IOException {
+ URLConnection connection = url.openConnection();
+ connection.setUseCaches(false);
+ return connection.getInputStream();
+ }
+ };
+ } catch (MalformedURLException e) {
+ throw new NamingException(e.toString());
+ }
+ }
+ };
+
+ } else {
+ dirContext = new FileDirContext();
+ ((FileDirContext)dirContext).setDocBase(uri.getPath());
+ }
+ proxyDirContext = new ProxyDirContext(new Hashtable(), dirContext);
+ resources = proxyDirContext;
+ }
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ resources = proxyDirContext;
+ }
+
+ @Override
+ public void init(ServletConfig servletConfig) throws ServletException {
+ super.init(servletConfig);
+ resources = proxyDirContext;
+ }
+
+ @Override
+ protected String getRelativePath(HttpServletRequest request) {
+ String path = request.getPathInfo();
+ if (path == null || path.length() == 0) {
+ path = "/";
+ }
+ return path;
+ }
+}
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java
new file mode 100644
index 0000000000..d1a2e5d283
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java
@@ -0,0 +1,701 @@
+/*
+ * 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 org.apache.tuscany.sca.http.tomcat;
+
+import java.beans.PropertyChangeListener;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.AccessController;
+import java.security.KeyStore;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.Embedded;
+import org.apache.coyote.http11.Http11Protocol;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.mapper.MappingData;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.host.http.DefaultResourceServlet;
+import org.apache.tuscany.sca.host.http.SecurityContext;
+import org.apache.tuscany.sca.host.http.ServletHost;
+import org.apache.tuscany.sca.host.http.ServletMappingException;
+import org.apache.tuscany.sca.work.WorkScheduler;
+
+/**
+ * A Tomcat based implementation of ServletHost.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TomcatServer implements ServletHost, LifeCycleListener {
+ private static final Logger logger = Logger.getLogger(TomcatServer.class.getName());
+
+ private WorkScheduler workScheduler;
+ private Embedded embedded;
+ private Map<Integer, Port> ports = new HashMap<Integer, Port>();
+ private String contextPath = "/";
+ private int defaultPort = 8080;
+ private int defaultSSLPort = 8443;
+
+ private final class TuscanyLoader implements Loader {
+ private final ClassLoader tccl;
+ private boolean delegate;
+ private boolean reloadable;
+ private Container container;
+ private List<String> repos = new ArrayList<String>();
+ private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
+
+ private TuscanyLoader(ClassLoader tccl) {
+ this.tccl = tccl;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void addRepository(String repository) {
+ repos.add(repository);
+ }
+
+ public void backgroundProcess() {
+ }
+
+ public String[] findRepositories() {
+ return repos.toArray(new String[repos.size()]);
+ }
+
+ public Container getContainer() {
+ return container;
+ }
+
+ public boolean getDelegate() {
+ return delegate;
+ }
+
+ public String getInfo() {
+ return "Tuscany Loader for Embedded Tomcat";
+ }
+
+ public boolean getReloadable() {
+ return reloadable;
+ }
+
+ public boolean modified() {
+ return false;
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ public void setContainer(Container container) {
+ this.container = container;
+ }
+
+ public void setDelegate(boolean delegate) {
+ this.delegate = delegate;
+ }
+
+ public void setReloadable(boolean reloadable) {
+ this.reloadable = reloadable;
+ }
+
+ public ClassLoader getClassLoader() {
+ return tccl;
+ }
+ }
+
+ /**
+ * Represents a port and the server that serves it.
+ */
+ private class Port {
+ private Engine engine;
+ private Host host;
+ private Connector connector;
+
+ private Port(Engine engine, Host host, Connector connector) {
+ this.engine = engine;
+ this.host = host;
+ this.connector = connector;
+ }
+
+ public Engine getEngine() {
+ return engine;
+ }
+
+ public Host getHost() {
+ return host;
+ }
+
+ public Connector getConnector() {
+ return connector;
+ }
+ }
+
+ /**
+ * Constructs a new embedded Tomcat server.
+ *
+ */
+ public TomcatServer(ExtensionPointRegistry registry, Map<String, String> attributes) {
+ UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class);
+ this.workScheduler = utilities.getUtility(WorkScheduler.class);
+ String port = attributes.get("defaultPort");
+ if (port != null) {
+ defaultPort = Integer.parseInt(port);
+ }
+ String sslPort = attributes.get("defaultSSLPort");
+ if (sslPort != null) {
+ defaultPort = Integer.parseInt(sslPort);
+ }
+ if (attributes.containsKey("contextPath")) {
+ contextPath = attributes.get("contextPath");
+ }
+ }
+
+ protected TomcatServer(WorkScheduler workScheduler) {
+ this.workScheduler = workScheduler;
+ }
+
+ public void setDefaultPort(int port) {
+ defaultPort = port;
+ }
+
+ public int getDefaultPort() {
+ return defaultPort;
+ }
+
+ /**
+ * Stop all the started servers.
+ */
+ public void stop() {
+ if (embedded != null) {
+ try {
+ // embedded.stop();
+ embedded.destroy();
+ for (Port port : ports.values()) {
+ port.connector.stop();
+ }
+ } catch (LifecycleException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+
+ public synchronized String addServletMapping(String suri, Servlet servlet) {
+ init();
+ URI uri = URI.create(suri);
+
+ // Get the URI scheme and port
+ String scheme = uri.getScheme();
+ if (scheme == null) {
+ scheme = "http";
+ }
+ final int portNumber = (uri.getPort() == -1 ? defaultPort : uri.getPort());
+
+ // Get the port object associated with the given port number
+ Port port = ports.get(portNumber);
+ if (port == null) {
+
+ port = createInstance(scheme, portNumber);
+ ports.put(portNumber, port);
+ }
+
+ // Register the Servlet mapping
+ String path = uri.getPath();
+
+ if (!path.startsWith("/")) {
+ path = '/' + path;
+ }
+
+ if (!path.startsWith(contextPath)) {
+ path = contextPath + path;
+ }
+
+ ServletWrapper wrapper;
+ if (servlet instanceof DefaultResourceServlet) {
+ String defaultServletPath = path;
+
+ // Optimize the handling of resource requests, use the Tomcat default Servlet
+ // instead of our default resource Servlet
+ if (defaultServletPath.endsWith("*")) {
+ defaultServletPath = defaultServletPath.substring(0, defaultServletPath.length() - 1);
+ }
+ if (defaultServletPath.endsWith("/")) {
+ defaultServletPath = defaultServletPath.substring(0, defaultServletPath.length() - 1);
+ }
+ DefaultResourceServlet resourceServlet = (DefaultResourceServlet)servlet;
+ TomcatDefaultServlet defaultServlet =
+ new TomcatDefaultServlet(defaultServletPath, resourceServlet.getDocumentRoot());
+ wrapper = new ServletWrapper(defaultServlet);
+
+ } else {
+ wrapper = new ServletWrapper(servlet);
+ }
+ Context context = port.getHost().map(path);
+ wrapper.setName(path);
+ wrapper.addMapping(path);
+ context.addChild(wrapper);
+ context.addServletMapping(path, path);
+ port.getConnector().getMapper().addWrapper("localhost", "", path, wrapper);
+
+ // Initialize the Servlet
+ try {
+ wrapper.initServlet();
+ } catch (ServletException e) {
+ throw new ServletMappingException(e);
+ }
+
+ // Compute the complete URL
+ String host;
+ try {
+ host = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ host = "localhost";
+ }
+ URL addedURL;
+ try {
+ addedURL = new URL(scheme, host, portNumber, path);
+ } catch (MalformedURLException e) {
+ throw new ServletMappingException(e);
+ }
+ logger.info("Added Servlet mapping: " + addedURL);
+ return addedURL.toString();
+ }
+
+ private Port createInstance(String scheme, final int portNumber) {
+ Port port;
+ // Create an engine
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ final Engine engine = AccessController.doPrivileged(new PrivilegedAction<Engine>() {
+ public Engine run() {
+ return embedded.createEngine();
+ }
+ });
+
+ ((StandardEngine)engine).setBaseDir("");
+ engine.setDefaultHost("localhost");
+ engine.setName("engine/" + portNumber);
+
+ // Create a host
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ final Host host = AccessController.doPrivileged(new PrivilegedAction<Host>() {
+ public Host run() {
+ Host host = embedded.createHost("localhost", "");
+ engine.addChild(host);
+ return host;
+ }
+ });
+
+ // Create the root context
+ Context context = embedded.createContext("", "");
+ final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ context.setLoader(new TuscanyLoader(tccl));
+ // context.setParentClassLoader(tccl.getParent());
+ ContextConfig config = new ContextConfig();
+ ((Lifecycle)context).addLifecycleListener(config);
+ host.addChild(context);
+
+ embedded.addEngine(engine);
+
+ // Install an HTTP connector
+
+ Connector connector;
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ try {
+ final String protocol = scheme;
+ connector = AccessController.doPrivileged(new PrivilegedExceptionAction<Connector>() {
+ public Connector run() throws Exception {
+ Connector customConnector = new Connector();
+ customConnector.setPort(portNumber);
+
+ if ("https".equalsIgnoreCase(protocol)) {
+ configureSSL(customConnector);
+ ((Http11Protocol)customConnector.getProtocolHandler()).setSSLEnabled(true);
+ }
+ return customConnector;
+ }
+
+ private void configureSSL(Connector customConnector) {
+ String trustStore = System.getProperty("javax.net.ssl.trustStore");
+ String trustStorePass = System.getProperty("javax.net.ssl.trustStorePassword");
+ String keyStore = System.getProperty("javax.net.ssl.keyStore");
+ String keyStorePass = System.getProperty("javax.net.ssl.keyStorePassword");
+
+ customConnector.setProperty("protocol", "TLS");
+
+ customConnector.setProperty("keystore", keyStore);
+ customConnector.setProperty("keypass", keyStorePass);
+ String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
+ String trustStoreType =
+ System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
+ customConnector.setProperty("keytype", keyStoreType);
+ customConnector.setProperty("trusttype", trustStoreType);
+ customConnector.setProperty("truststore", trustStore);
+ customConnector.setProperty("trustpass", trustStorePass);
+
+ customConnector.setProperty("clientauth", "false");
+ customConnector.setProtocol("HTTP/1.1");
+ customConnector.setScheme(protocol);
+ customConnector.setProperty("backlog", "10");
+ customConnector.setSecure(true);
+ }
+ });
+ } catch (Exception e) {
+ throw new ServletMappingException(e);
+ }
+
+ embedded.addConnector(connector);
+ try {
+ connector.start();
+ } catch (LifecycleException e) {
+ throw new ServletMappingException(e);
+ }
+
+ // Keep track of the running server
+ port = new Port(engine, host, connector);
+ return port;
+ }
+
+ private synchronized void init() {
+ if (embedded != null) {
+ return;
+ }
+ embedded = new Embedded();
+ embedded.setAwait(true);
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws LifecycleException {
+ embedded.start();
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ // throw (LifecycleException)e.getException();
+ throw new ServletMappingException(e);
+ }
+ }
+
+ public URL getURLMapping(String suri) throws ServletMappingException {
+ URI uri = URI.create(suri);
+
+ // Get the URI scheme and port
+ String scheme = uri.getScheme();
+ if (scheme == null) {
+ scheme = "http";
+ }
+ int portNumber = uri.getPort();
+ if (portNumber == -1) {
+ portNumber = defaultPort;
+ }
+
+ // Get the host
+ String host;
+ try {
+ host = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ host = "localhost";
+ }
+
+ // Construct the URL
+ String path = uri.getPath();
+
+ if (!path.startsWith("/")) {
+ path = '/' + path;
+ }
+
+ if (!path.startsWith(contextPath)) {
+ path = contextPath + path;
+ }
+ URL url;
+ try {
+ url = new URL(scheme, host, portNumber, path);
+ } catch (MalformedURLException e) {
+ throw new ServletMappingException(e);
+ }
+ return url;
+ }
+
+ public Servlet getServletMapping(String suri) throws ServletMappingException {
+
+ if (suri == null) {
+ return null;
+ }
+
+ URI uri = URI.create(suri);
+
+ // Get the URI port
+ int portNumber = uri.getPort();
+ if (portNumber == -1) {
+ portNumber = defaultPort;
+ }
+
+ // Get the port object associated with the given port number
+ Port port = ports.get(portNumber);
+ if (port == null) {
+ return null;
+ }
+
+ String mapping = uri.getPath();
+
+ if (!mapping.startsWith("/")) {
+ mapping = '/' + mapping;
+ }
+
+ if (!mapping.startsWith(contextPath)) {
+ mapping = contextPath + mapping;
+ }
+
+ final Context context = port.getHost().map(mapping);
+ final MappingData md = new MappingData();
+ final MessageBytes mb = MessageBytes.newInstance();
+ mb.setString(mapping);
+ try {
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ context.getMapper().map(mb, md);
+ return null;
+ }
+ });
+ } catch (Exception e) {
+ return null;
+ }
+ if (md.wrapper instanceof ServletWrapper) {
+ ServletWrapper servletWrapper = (ServletWrapper)md.wrapper;
+ return servletWrapper.getServlet();
+ } else {
+ return null;
+ }
+ }
+
+ public synchronized Servlet removeServletMapping(String suri) {
+ URI uri = URI.create(suri);
+
+ // Get the URI port
+ int portNumber = uri.getPort();
+ if (portNumber == -1) {
+ portNumber = defaultPort;
+ }
+
+ // Get the port object associated with the given port number
+ Port port = ports.get(portNumber);
+ if (port == null) {
+ throw new IllegalStateException("No servlet registered at this URI: " + suri);
+ }
+
+ String mapping = uri.getPath();
+
+ if (!mapping.startsWith("/")) {
+ mapping = '/' + mapping;
+ }
+
+ if (!mapping.startsWith(contextPath)) {
+ mapping = contextPath + mapping;
+ }
+
+ final Context context = port.getHost().map(mapping);
+ final MappingData md = new MappingData();
+ final MessageBytes mb = MessageBytes.newInstance();
+ mb.setString(mapping);
+ try {
+ // Allow privileged access to read properties. Requires PropertiesPermission read in
+ // security policy.
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ context.getMapper().map(mb, md);
+ return null;
+ }
+ });
+ } catch (Exception e) {
+ return null;
+ }
+ if (md.wrapper instanceof ServletWrapper) {
+ ServletWrapper servletWrapper = (ServletWrapper)md.wrapper;
+
+ port.getConnector().getMapper().removeWrapper("localhost", "", mapping);
+
+ try {
+ context.removeServletMapping(mapping);
+ } catch (NegativeArraySizeException e) {
+ // JIRA TUSCANY-1599
+ // FIXME Looks like a bug in Tomcat when removing the last
+ // Servlet in the list, catch the exception for now as it doesn't
+ // seem harmful, will find a better solution for the next release
+ }
+ context.removeChild(servletWrapper);
+ try {
+ servletWrapper.destroyServlet();
+ } catch (Exception ex) {
+ // Hack to handle destruction of Servlets without Servlet context
+ }
+
+ logger.info("Removed Servlet mapping: " + suri);
+
+ // Stop the port if there's no servlets on it anymore
+ String[] contextNames = port.getConnector().getMapper().getContextNames();
+ if (contextNames == null || contextNames.length == 0) {
+ try {
+ port.getConnector().stop();
+ ((StandardEngine)port.getEngine()).stop();
+ embedded.removeEngine(port.getEngine());
+ embedded.removeConnector(port.getConnector());
+ ports.remove(portNumber);
+ } catch (LifecycleException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ return servletWrapper.getServlet();
+ } else {
+ logger.warning("Trying to Remove servlet mapping: " + mapping + " where mapping is not registered");
+ return null;
+ }
+ }
+
+ public RequestDispatcher getRequestDispatcher(String suri) throws ServletMappingException {
+ //FIXME implement this later
+ return null;
+ }
+
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ public void setContextPath(String path) {
+ this.contextPath = path;
+ }
+
+ public void start() {
+
+ }
+
+ public String addServletMapping(String uri, Servlet servlet, SecurityContext securityContext)
+ throws ServletMappingException {
+ return addServletMapping(uri, servlet);
+ }
+
+ public String getName() {
+ return "tomcat";
+ }
+
+ public URL getURLMapping(String arg0, SecurityContext arg1) {
+ return null;
+ }
+
+ public void setAttribute(String arg0, Object arg1) {
+ }
+
+ private URL map(String suri, SecurityContext securityContext, boolean resolve) throws ServletMappingException {
+ URI uri = URI.create(suri);
+
+ // Get the URI scheme and port
+ String scheme = null;
+ if (securityContext != null && securityContext.isSSLEnabled()) {
+ scheme = "https";
+ } else {
+ scheme = uri.getScheme();
+ if (scheme == null) {
+ scheme = "http";
+ }
+ }
+
+ int portNumber = uri.getPort();
+ if (portNumber == -1) {
+ if ("http".equals(scheme)) {
+ portNumber = defaultPort;
+ } else {
+ portNumber = defaultSSLPort;
+ }
+ }
+
+ // Get the host
+ String host = uri.getHost();
+ if (host == null) {
+ host = "0.0.0.0";
+ if (resolve) {
+ try {
+ host = InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ host = "localhost";
+ }
+ }
+ }
+
+ // Construct the URL
+ String path = uri.getPath();
+
+ if (!path.startsWith("/")) {
+ path = '/' + path;
+ }
+
+ if (!path.startsWith(contextPath)) {
+ path = contextPath + path;
+ }
+
+ URL url;
+ try {
+ url = new URL(scheme, host, portNumber, path);
+ } catch (MalformedURLException e) {
+ throw new ServletMappingException(e);
+ }
+ return url;
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ if (ports.isEmpty()) {
+ return null;
+ } else {
+ Port port = ports.values().iterator().next();
+ return port.getHost().map(getContextPath()).getServletContext();
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/resources/META-INF/services/org.apache.tuscany.sca.host.http.ServletHost b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/resources/META-INF/services/org.apache.tuscany.sca.host.http.ServletHost
new file mode 100644
index 0000000000..00350bbf6b
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/main/resources/META-INF/services/org.apache.tuscany.sca.host.http.ServletHost
@@ -0,0 +1,18 @@
+# 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.
+# Implementation class for the ServletHost
+org.apache.tuscany.sca.http.tomcat.TomcatServer;name=tomcat,ranking=50,defaultPort=8085
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/java/org/apache/tuscany/sca/http/tomcat/TomcatServerTestCase.java b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/java/org/apache/tuscany/sca/http/tomcat/TomcatServerTestCase.java
new file mode 100644
index 0000000000..4bcf0623e8
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/java/org/apache/tuscany/sca/http/tomcat/TomcatServerTestCase.java
@@ -0,0 +1,372 @@
+/*
+ * 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 org.apache.tuscany.sca.http.tomcat;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.URL;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.host.http.DefaultResourceServlet;
+import org.apache.tuscany.sca.host.http.ExtensibleServletHost;
+import org.apache.tuscany.sca.host.http.ServletHost;
+import org.apache.tuscany.sca.work.NotificationListener;
+import org.apache.tuscany.sca.work.WorkScheduler;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class TomcatServerTestCase extends TestCase {
+
+ private static final String REQUEST1_HEADER =
+ "GET /foo HTTP/1.0\n" + "Host: localhost\n"
+ + "Content-Type: text/xml\n"
+ + "Connection: close\n"
+ + "Content-Length: ";
+ private static final String REQUEST1_CONTENT = "";
+ private static final String REQUEST1 =
+ REQUEST1_HEADER + REQUEST1_CONTENT.getBytes().length + "\n\n" + REQUEST1_CONTENT;
+
+ private static final String REQUEST2_HEADER =
+ "GET /webcontent/test.html HTTP/1.0\n" + "Host: localhost\n"
+ + "Content-Type: text/xml\n"
+ + "Connection: close\n"
+ + "Content-Length: ";
+ private static final String REQUEST2_CONTENT = "";
+ private static final String REQUEST2 =
+ REQUEST2_HEADER + REQUEST2_CONTENT.getBytes().length + "\n\n" + REQUEST2_CONTENT;
+
+ private static final int HTTP_PORT = 8085;
+
+ private WorkScheduler workScheduler = new WorkScheduler() {
+
+ public <T extends Runnable> void scheduleWork(T work) {
+ Thread thread = new Thread(work);
+ thread.start();
+ }
+
+ public <T extends Runnable> void scheduleWork(T work, NotificationListener<T> listener) {
+ scheduleWork(work);
+ }
+
+ public void destroy() {
+ }
+ };
+
+ /**
+ * Verifies requests are properly routed according to the Servlet mapping
+ */
+ public void testRegisterServletMapping() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ ServletContext servletContext = service.getServletContext();
+ Assert.assertNotNull(servletContext);
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ service.stop();
+ assertTrue(servlet.invoked);
+ }
+
+ /**
+ * Verifies requests are properly routed according to the Servlet mapping
+ */
+ public void testRegisterServletMappingSSL() throws Exception {
+ System.setProperty("javax.net.ssl.keyStore", "target/test-classes/tuscany.keyStore");
+ System.setProperty("javax.net.ssl.keyStorePassword", "apache");
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ try {
+ service.addServletMapping("https://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ } finally {
+ System.clearProperty("javax.net.ssl.keyStore");
+ System.clearProperty("javax.net.ssl.keyStorePassword");
+ }
+ System.setProperty("javax.net.ssl.trustStore", "target/test-classes/tuscany.keyStore");
+ System.setProperty("javax.net.ssl.trustStorePassword", "apache");
+ URL url = new URL("https://127.0.0.1:8085/foo");
+ HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
+ conn.setHostnameVerifier(new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }}
+ );
+
+ conn.connect();
+ read(conn.getInputStream());
+
+ service.stop();
+ assertTrue(servlet.invoked);
+
+ }
+ /**
+ * Verifies that Servlets can be registered with multiple ports
+ */
+ public void testRegisterMultiplePorts() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/", servlet);
+ TestServlet servlet2 = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + (HTTP_PORT + 1) + "/", servlet2);
+ {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ }
+ {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT + 1);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ }
+
+ service.stop();
+ assertTrue(servlet.invoked);
+ assertTrue(servlet2.invoked);
+ }
+
+ public void testUnregisterMapping() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ service.removeServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo");
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ service.stop();
+ assertFalse(servlet.invoked);
+
+ }
+
+ public void testRegisterUnregisterMapping() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ }
+ assertTrue(servlet.invoked);
+ service.removeServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo");
+ {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ }
+ servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ }
+ assertTrue(servlet.invoked);
+ service.stop();
+ }
+
+ public void testRequestSession() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ TestServlet servlet = new TestServlet();
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/foo", servlet);
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ read(client);
+ service.stop();
+ assertTrue(servlet.invoked);
+ assertNotNull(servlet.sessionId);
+ }
+
+ public void testRestart() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ service.stop();
+ service.stop();
+ }
+
+ public void testNoMappings() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+ Exception ex = null;
+ try {
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST1.getBytes());
+ os.flush();
+ } catch (Exception e) {
+ ex = e;
+ }
+ assertNotNull(ex);
+ service.stop();
+ }
+
+ public void testResourceServlet() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+
+ String documentRoot = getClass().getClassLoader().getResource("content/test.html").toString();
+ documentRoot = documentRoot.substring(0, documentRoot.lastIndexOf('/'));
+ DefaultResourceServlet resourceServlet = new DefaultResourceServlet(documentRoot);
+ TestResourceServlet servlet = new TestResourceServlet(resourceServlet);
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/webcontent/*", servlet);
+
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST2.getBytes());
+ os.flush();
+
+ String document = read(client);
+ assertTrue(document.indexOf("<body><p>hello</body>") != -1);
+
+ service.stop();
+ }
+
+ public void testDefaultServlet() throws Exception {
+ TomcatServer service = new TomcatServer(workScheduler);
+
+ String documentRoot = getClass().getClassLoader().getResource("content/test.html").toString();
+ documentRoot = documentRoot.substring(0, documentRoot.lastIndexOf('/'));
+ DefaultResourceServlet resourceServlet = new DefaultResourceServlet(documentRoot);
+ service.addServletMapping("http://127.0.0.1:" + HTTP_PORT + "/webcontent/*", resourceServlet);
+
+ Socket client = new Socket("127.0.0.1", HTTP_PORT);
+ OutputStream os = client.getOutputStream();
+ os.write(REQUEST2.getBytes());
+ os.flush();
+
+ String document = read(client);
+ assertTrue(document.indexOf("<body><p>hello</body>") != -1);
+
+ service.stop();
+ }
+
+ private static String read(Socket socket) throws IOException {
+ InputStream is = socket.getInputStream();
+ return read(is);
+ }
+
+ private static String read(InputStream is) throws IOException {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(is));
+ StringBuffer sb = new StringBuffer();
+ String str;
+ while ((str = reader.readLine()) != null) {
+ sb.append(str);
+ }
+ return sb.toString();
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ }
+ private class TestServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ boolean invoked;
+ String sessionId;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ invoked = true;
+ sessionId = req.getSession().getId();
+ OutputStream writer = resp.getOutputStream();
+ try {
+ writer.write("result".getBytes());
+ } finally {
+ writer.close();
+ }
+ }
+
+ }
+
+ private class TestResourceServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private HttpServlet delegate;
+
+ public TestResourceServlet(HttpServlet delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ delegate.init();
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init();
+ delegate.init(config);
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ delegate.service(req, resp);
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ delegate.destroy();
+ }
+ }
+
+ @Test
+ public void testDiscovery() {
+ ExtensionPointRegistry registry = new DefaultExtensionPointRegistry();
+ ExtensibleServletHost host = ExtensibleServletHost.getInstance(registry);
+ ServletHost me = host.getDefaultServletHost();
+ Assert.assertEquals("tomcat", me.getName());
+ Assert.assertEquals(8085, me.getDefaultPort());
+ registry.stop();
+ }
+}
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/content/test.html b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/content/test.html
new file mode 100644
index 0000000000..f4b79d7f01
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/content/test.html
@@ -0,0 +1,21 @@
+<html>
+<!--
+ * 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.
+-->
+<body><p>hello</body>
+</html> \ No newline at end of file
diff --git a/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/tuscany.keyStore b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/tuscany.keyStore
new file mode 100644
index 0000000000..7ea23f7ff4
--- /dev/null
+++ b/sandbox/sebastien/java/shell/contrib/modules/host-tomcat/src/test/resources/tuscany.keyStore
Binary files differ