diff options
Diffstat (limited to 'branches/sca-java-1.0/modules/host-tomcat/src/main/java/org')
4 files changed, 580 insertions, 0 deletions
diff --git a/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/ServletWrapper.java new file mode 100644 index 0000000000..b1dd818371 --- /dev/null +++ b/branches/sca-java-1.0/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/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java new file mode 100644 index 0000000000..4b48458a56 --- /dev/null +++ b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatDefaultServlet.java @@ -0,0 +1,99 @@ +/* + * 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.URL; +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; + +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 = 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 { + return url.openStream(); + } + }; + } catch (MalformedURLException e) { + throw new NamingException(e.toString()); + } + } + }; + + 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/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java new file mode 100644 index 0000000000..bcd4d18daa --- /dev/null +++ b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/TomcatServer.java @@ -0,0 +1,374 @@ +/* + * 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.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +import org.apache.catalina.Context; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardEngine; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.startup.ContextConfig; +import org.apache.coyote.http11.Http11Protocol; +import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.mapper.MappingData; +import org.apache.tomcat.util.net.JIoEndpoint; +import org.apache.tuscany.sca.host.http.DefaultResourceServlet; +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$ + */ +@SuppressWarnings("deprecation") +public class TomcatServer implements ServletHost { + private final static Logger logger = Logger.getLogger(TomcatServer.class.getName()); + + private static final int DEFAULT_PORT = 8080; + + /** + * Represents a port and the server that serves it. + */ + private class Port { + private StandardEngine engine; + private StandardHost host; + private Connector connector; + + private Port(StandardEngine engine, StandardHost host, Connector connector) { + this.engine = engine; + this.host = host; + this.connector = connector; + } + + public StandardEngine getEngine() { + return engine; + } + + public StandardHost getHost() { + return host; + } + + public Connector getConnector() { + return connector; + } + } + + private Map<Integer, Port> ports = new HashMap<Integer, Port>(); + + private WorkScheduler workScheduler; + + /** + * Constructs a new embedded Tomcat server. + * + * @param workScheduler the WorkScheduler to use to process requests. + */ + public TomcatServer(WorkScheduler workScheduler) { + this.workScheduler = workScheduler; + } + + /** + * Stop all the started servers. + */ + public void stop() throws ServletMappingException { + if (!ports.isEmpty()) { + try { + Set<Entry<Integer, Port>> entries = new HashSet<Entry<Integer, Port>>(ports.entrySet()); + for (Entry<Integer, Port> entry: entries) { + entry.getValue().getConnector().stop(); + entry.getValue().getEngine().stop(); + ports.remove(entry.getKey()); + } + } catch (Exception e) { + throw new ServletMappingException(e); + } + } + } + + public void addServletMapping(String suri, Servlet servlet) { + 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 = DEFAULT_PORT; + } + + // Get the port object associated with the given port number + Port port = ports.get(portNumber); + if (port == null) { + + // Create an engine + StandardEngine engine = new StandardEngine(); + engine.setBaseDir(""); + engine.setDefaultHost("localhost"); + engine.setName("engine/" + portNumber); + + // Create a host + StandardHost host = new StandardHost(); + host.setAppBase(""); + host.setName("localhost"); + engine.addChild(host); + + // Create the root context + StandardContext context = new StandardContext(); + context.setParentClassLoader(Thread.currentThread().getContextClassLoader()); + context.setDocBase(""); + context.setPath(""); + ContextConfig config = new ContextConfig(); + ((Lifecycle)context).addLifecycleListener(config); + host.addChild(context); + + // Install an HTTP connector + Connector connector; + try { + engine.start(); + connector = new CustomConnector(); + connector.setPort(portNumber); + connector.setContainer(engine); + connector.initialize(); + connector.start(); + } catch (Exception e) { + throw new ServletMappingException(e); + } + + // Keep track of the running server + port = new Port(engine, host, connector); + ports.put(portNumber, port); + } + + // Register the servlet mapping + String path = uri.getPath(); + if (!path.startsWith("/")) { + path = '/' + 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); + } + + URI addedURI = URI.create(scheme + "://localhost:" + portNumber + path); + logger.info("Added Servlet mapping: " + addedURI); + } + + public Servlet getServletMapping(String suri) throws ServletMappingException { + URI uri = URI.create(suri); + + // Get the URI port + int portNumber = uri.getPort(); + if (portNumber == -1) { + portNumber = DEFAULT_PORT; + } + + // Get the port object associated with the given port number + Port port = ports.get(portNumber); + if (port == null) { + return null; + } + + String mapping = uri.getPath(); + Context context = port.getHost().map(mapping); + MappingData md = new MappingData(); + MessageBytes mb = MessageBytes.newInstance(); + mb.setString(mapping); + try { + context.getMapper().map(mb, md); + } catch (Exception e) { + return null; + } + if (md.wrapper instanceof ServletWrapper) { + ServletWrapper servletWrapper = (ServletWrapper)md.wrapper; + return servletWrapper.getServlet(); + } else { + return null; + } + } + + public Servlet removeServletMapping(String suri) { + URI uri = URI.create(suri); + + // Get the URI port + int portNumber = uri.getPort(); + if (portNumber == -1) { + portNumber = DEFAULT_PORT; + } + + // 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(); + Context context = port.getHost().map(mapping); + MappingData md = new MappingData(); + MessageBytes mb = MessageBytes.newInstance(); + mb.setString(mapping); + try { + context.getMapper().map(mb, md); + } catch (Exception e) { + return null; + } + if (md.wrapper instanceof ServletWrapper) { + ServletWrapper servletWrapper = (ServletWrapper)md.wrapper; + 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); + servletWrapper.destroyServlet(); + return servletWrapper.getServlet(); + } else { + return null; + } + } + + public RequestDispatcher getRequestDispatcher(String suri) throws ServletMappingException { + //FIXME implement this later + return null; + } + + public String getContextPath() { + return "/"; + } + + /** + * A custom connector that uses our WorkScheduler to schedule + * worker threads. + */ + private class CustomConnector extends Connector { + + private class CustomHttpProtocolHandler extends Http11Protocol { + + /** + * An Executor wrappering our WorkScheduler + */ + private class WorkSchedulerExecutor implements Executor { + public void execute(Runnable command) { + workScheduler.scheduleWork(command); + } + } + + /** + * A custom Endpoint that waits for its acceptor thread to + * terminate before stopping. + */ + private class CustomEndpoint extends JIoEndpoint { + private Thread acceptorThread; + + private class CustomAcceptor extends Acceptor { + CustomAcceptor() { + super(); + } + } + + @Override + public void start() throws Exception { + if (!initialized) + init(); + if (!running) { + running = true; + paused = false; + acceptorThread = new Thread(new CustomAcceptor(), getName() + "-Acceptor-" + 0); + acceptorThread.setPriority(threadPriority); + acceptorThread.setDaemon(daemon); + acceptorThread.start(); + } + } + + @Override + public void stop() { + super.stop(); + try { + acceptorThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public int getCurrentThreadsBusy() { + return 0; + } + } + + CustomHttpProtocolHandler() { + endpoint = new CustomEndpoint(); + endpoint.setExecutor(new WorkSchedulerExecutor()); + } + } + + CustomConnector() throws Exception { + protocolHandler = new CustomHttpProtocolHandler(); + } + } + +} diff --git a/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/module/TomcatRuntimeModuleActivator.java b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/module/TomcatRuntimeModuleActivator.java new file mode 100644 index 0000000000..9309e89d60 --- /dev/null +++ b/branches/sca-java-1.0/modules/host-tomcat/src/main/java/org/apache/tuscany/sca/http/tomcat/module/TomcatRuntimeModuleActivator.java @@ -0,0 +1,49 @@ +/* + * 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.module; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.ModuleActivator; +import org.apache.tuscany.sca.host.http.ServletHostExtensionPoint; +import org.apache.tuscany.sca.http.tomcat.TomcatServer; +import org.apache.tuscany.sca.work.WorkScheduler; + +/** + * @version $Rev$ $Date$ + */ +public class TomcatRuntimeModuleActivator implements ModuleActivator { + + private TomcatServer server; + + public void start(ExtensionPointRegistry extensionPointRegistry) { + + // Register a Tomcat servlet host + ServletHostExtensionPoint servletHosts = + extensionPointRegistry.getExtensionPoint(ServletHostExtensionPoint.class); + WorkScheduler workScheduler = extensionPointRegistry.getExtensionPoint(WorkScheduler.class); + server = new TomcatServer(workScheduler); + servletHosts.addServletHost(server); + } + + public void stop(ExtensionPointRegistry registry) { + server.stop(); + } + +} |