summaryrefslogtreecommitdiffstats
path: root/sca-java-2.x/trunk
diff options
context:
space:
mode:
Diffstat (limited to 'sca-java-2.x/trunk')
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/testing/helloworld-service-contribution/src/main/java/testing/HelloworldService.java3
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyContextConfig.java183
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyHost.java167
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/tomcat/foo/TuscanyTomcatNode.java74
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/Installer.java26
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/InstallerServlet.java4
-rw-r--r--sca-java-2.x/trunk/distribution/tomcat/tomcat-war/src/main/webapp/installer.jsp34
7 files changed, 393 insertions, 98 deletions
diff --git a/sca-java-2.x/trunk/distribution/tomcat/testing/helloworld-service-contribution/src/main/java/testing/HelloworldService.java b/sca-java-2.x/trunk/distribution/tomcat/testing/helloworld-service-contribution/src/main/java/testing/HelloworldService.java
index 65e95f275c..eec9d63da8 100644
--- a/sca-java-2.x/trunk/distribution/tomcat/testing/helloworld-service-contribution/src/main/java/testing/HelloworldService.java
+++ b/sca-java-2.x/trunk/distribution/tomcat/testing/helloworld-service-contribution/src/main/java/testing/HelloworldService.java
@@ -18,6 +18,9 @@
*/
package testing;
+import org.oasisopen.sca.annotation.Remotable;
+
+@Remotable
public interface HelloworldService {
String sayHello(String name);
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyContextConfig.java b/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyContextConfig.java
new file mode 100644
index 0000000000..5e7905d246
--- /dev/null
+++ b/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyContextConfig.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tomcat;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.catalina.Host;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.ExpandWar;
+
+public class TuscanyContextConfig extends ContextConfig{
+
+ /**
+ * Return the location of the default deployment descriptor
+ *
+ * Override the super class method to use the Tuscany specific
+ * default web.xml which has the Tuscany listener and filer config
+ */
+ @Override
+ public String getDefaultWebXml() {
+ if( defaultWebXml == null ) {
+ defaultWebXml="conf/tuscany-web.xml";
+ }
+ return (this.defaultWebXml);
+ }
+
+ /**
+ * Adjust docBase.
+ *
+ * This is cutNpaste of the Tomcat method but changed on the lines marked with // TUSCANY:
+ * to override the default that only works for file names ending with .war
+ *
+ */
+ @Override
+ protected void fixDocBase()
+ throws IOException {
+
+ Host host = (Host) context.getParent();
+ String appBase = host.getAppBase();
+
+ boolean unpackWARs = true;
+ if (host instanceof StandardHost) {
+ unpackWARs = ((StandardHost) host).isUnpackWARs()
+ && ((StandardContext) context).getUnpackWAR();
+ }
+
+ File canonicalAppBase = new File(appBase);
+ if (canonicalAppBase.isAbsolute()) {
+ canonicalAppBase = canonicalAppBase.getCanonicalFile();
+ } else {
+ canonicalAppBase =
+ new File(System.getProperty("catalina.base"), appBase)
+ .getCanonicalFile();
+ }
+
+ String docBase = context.getDocBase();
+ if (docBase == null) {
+ // Trying to guess the docBase according to the path
+ String path = context.getPath();
+ if (path == null) {
+ return;
+ }
+ if (path.equals("")) {
+ docBase = "ROOT";
+ } else {
+ if (path.startsWith("/")) {
+ docBase = path.substring(1);
+ } else {
+ docBase = path;
+ }
+ }
+ }
+
+ File file = new File(docBase);
+ if (!file.isAbsolute()) {
+ docBase = (new File(canonicalAppBase, docBase)).getPath();
+ } else {
+ docBase = file.getCanonicalPath();
+ }
+ file = new File(docBase);
+ String origDocBase = docBase;
+
+ String contextPath = context.getPath();
+ if (contextPath.equals("")) {
+ contextPath = "ROOT";
+ } else {
+ if (contextPath.lastIndexOf('/') > 0) {
+ contextPath = "/" + contextPath.substring(1).replace('/','#');
+ }
+ }
+ // TUSCANY: update from .war to also support .jar and .zip SCA contributions
+ if ((docBase.toLowerCase().endsWith(".war") || docBase.toLowerCase().endsWith(".jar")||docBase.toLowerCase().endsWith(".zip")) && !file.isDirectory() && unpackWARs) {
+ URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
+ docBase = ExpandWar.expand(host, war, contextPath);
+ file = new File(docBase);
+ docBase = file.getCanonicalPath();
+ if (context instanceof StandardContext) {
+ ((StandardContext) context).setOriginalDocBase(origDocBase);
+ }
+ } else {
+ File docDir = new File(docBase);
+ if (!docDir.exists()) {
+ // TUSCANY: update from .war to also support .jar and .zip SCA contributions
+ File warFile = new File(docBase + ".war");
+ if (warFile.exists()) {
+ if (unpackWARs) {
+ URL war =
+ new URL("jar:" + warFile.toURI().toURL() + "!/");
+ docBase = ExpandWar.expand(host, war, contextPath);
+ file = new File(docBase);
+ docBase = file.getCanonicalPath();
+ } else {
+ docBase = warFile.getCanonicalPath();
+ }
+ } else {
+ warFile = new File(docBase + ".jar");
+ if (warFile.exists()) {
+ if (unpackWARs) {
+ URL war =
+ new URL("jar:" + warFile.toURI().toURL() + "!/");
+ docBase = ExpandWar.expand(host, war, contextPath);
+ file = new File(docBase);
+ docBase = file.getCanonicalPath();
+ } else {
+ docBase = warFile.getCanonicalPath();
+ }
+ } else {
+ warFile = new File(docBase + ".zip");
+ if (warFile.exists()) {
+ if (unpackWARs) {
+ URL war =
+ new URL("jar:" + warFile.toURI().toURL() + "!/");
+ docBase = ExpandWar.expand(host, war, contextPath);
+ file = new File(docBase);
+ docBase = file.getCanonicalPath();
+ } else {
+ docBase = warFile.getCanonicalPath();
+ }
+ }
+ }
+ }
+ if (context instanceof StandardContext) {
+ ((StandardContext) context).setOriginalDocBase(origDocBase);
+ }
+ }
+ }
+
+ if (docBase.startsWith(canonicalAppBase.getPath())) {
+ docBase = docBase.substring(canonicalAppBase.getPath().length());
+ docBase = docBase.replace(File.separatorChar, '/');
+ if (docBase.startsWith("/")) {
+ docBase = docBase.substring(1);
+ }
+ } else {
+ docBase = docBase.replace(File.separatorChar, '/');
+ }
+
+ context.setDocBase(docBase);
+
+ }
+
+}
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyHost.java b/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyHost.java
new file mode 100644
index 0000000000..d6042c5902
--- /dev/null
+++ b/sca-java-2.x/trunk/distribution/tomcat/tomcat-hook/src/main/java/org/apache/tuscany/sca/tomcat/TuscanyHost.java
@@ -0,0 +1,167 @@
+/*
+ * 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.tomcat;
+
+import java.io.File;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.startup.HostConfig;
+
+/**
+ * A Tuscany customized HostConfig that adds support for SCA contributions
+ * to be deployed along with the usual .war files.
+ */
+public class TuscanyHost extends HostConfig {
+
+ protected File scaBase = null;
+
+ @Override
+ protected void deployApps() {
+
+ File appBase = appBase();
+ File configBase = configBase();
+ // Deploy XML descriptors from configBase
+ deployDescriptors(configBase, configBase.list());
+ // Deploy WARs, and loop if additional descriptors are found
+ deployWARs(appBase, appBase.list());
+ // TUSCANY: Deploy any SCA contibutions
+ deploySCAContributions(appBase, appBase.list());
+ // Deploy expanded folders
+ deployDirectories(appBase, appBase.list());
+ }
+
+ protected void deploySCAContributions(File appBase, String[] files) {
+ if (files == null)
+ return;
+
+ for (int i = 0; i < files.length; i++) {
+
+ File scafile = new File(appBase, files[i]);
+
+ if (scafile.isFile() && isSCAContribution(scafile)) {
+
+ // Calculate the context path and make sure it is unique
+ String contextPath = "/" + files[i].replace('#','/');
+ int period = contextPath.lastIndexOf(".");
+ if (period >= 0)
+ contextPath = contextPath.substring(0, period);
+
+ if (isServiced(contextPath))
+ continue;
+
+ String file = files[i];
+
+ deploySCAContribution(contextPath, scafile, file);
+
+ }
+
+ }
+
+ }
+
+ protected String tuscanyContextClass = "org.apache.tuscany.sca.tomcat.TuscanyContextConfig";
+
+ protected void deploySCAContribution(String contextPath, File dir, String file) {
+ if (deploymentExists(contextPath))
+ return;
+
+ DeployedApplication deployedApp = new DeployedApplication(contextPath);
+
+ // Deploy the application in this WAR file
+ if(log.isInfoEnabled())
+ log.info("Deploying SCA contibution: " + file);
+
+ // Populate redeploy resources with the WAR file
+ deployedApp.redeployResources.put(dir.getAbsolutePath(), new Long(dir.lastModified()));
+
+ try {
+ Context context = (Context) Class.forName(contextClass).newInstance();
+ if (context instanceof Lifecycle) {
+
+ // Tuscany: change to use the Tuscany ContextConfig class
+ Class clazz = Class.forName(tuscanyContextClass);
+
+ LifecycleListener listener =
+ (LifecycleListener) clazz.newInstance();
+ ((Lifecycle) context).addLifecycleListener(listener);
+ }
+ context.setPath(contextPath);
+ context.setDocBase(file);
+
+ host.addChild(context);
+ // If we're unpacking WARs, the docBase will be mutated after
+ // starting the context
+ if (unpackWARs && (context.getDocBase() != null)) {
+ String name = null;
+ String path = context.getPath();
+ if (path.equals("")) {
+ name = "ROOT";
+ } else {
+ if (path.startsWith("/")) {
+ name = path.substring(1);
+ } else {
+ name = path;
+ }
+ }
+ name = name.replace('/', '#');
+ File docBase = new File(name);
+ if (!docBase.isAbsolute()) {
+ docBase = new File(appBase(), name);
+ }
+ deployedApp.redeployResources.put(docBase.getAbsolutePath(),
+ new Long(docBase.lastModified()));
+ addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
+ } else {
+ addWatchedResources(deployedApp, null, context);
+ }
+ } catch (Throwable t) {
+ log.error(sm.getString("hostConfig.deployJar.error", file), t);
+ }
+
+ deployed.put(contextPath, deployedApp);
+ }
+
+ protected boolean isSCAContribution(File file) {
+ ZipFile zip = null;
+ ZipEntry entry = null;
+ try {
+ try {
+ zip = new ZipFile(file);
+ entry = zip.getEntry("META-INF/sca-contribution.xml");
+ } catch (Exception e) {
+ }
+
+ return (entry != null);
+
+ } finally {
+ if (zip != null) {
+ try {
+ zip.close();
+ } catch (Throwable t) {
+ }
+ }
+ }
+ }
+
+}
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/tomcat/foo/TuscanyTomcatNode.java b/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/tomcat/foo/TuscanyTomcatNode.java
deleted file mode 100644
index 972aafd8b6..0000000000
--- a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/tomcat/foo/TuscanyTomcatNode.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.tomcat.foo;
-
-import java.io.File;
-import java.net.MalformedURLException;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-
-import org.apache.tuscany.sca.node.Contribution;
-import org.apache.tuscany.sca.node.Node;
-import org.apache.tuscany.sca.node.NodeFactory;
-import org.apache.tuscany.sca.war.Installer;
-
-public class TuscanyTomcatNode implements ServletContextListener {
-
- protected Node tomcatNode;
-
- public void contextInitialized(ServletContextEvent arg0) {
- if (!Installer.isTuscanyHookRunning()) {
- return;
- }
-
- // TODO: this relys on the location of webapp folder, find way to get actual catalina base
- File tomcatBase = new File(arg0.getServletContext().getRealPath("/")).getParentFile().getParentFile();
-
- File contributionDir = new File(tomcatBase, "sca-contributions");
- if (!contributionDir.exists()) {
- return;
- }
- File[] contributionFiles = contributionDir.listFiles();
- if (contributionFiles.length < 1) {
- return;
- }
-
- Contribution[] nodeContributions = new Contribution[contributionFiles.length];
- for (int i = 0; i<contributionFiles.length; i++) {
- try {
- nodeContributions[i] =
- new Contribution(contributionFiles[i].getName(), contributionFiles[i].toURI().toURL().toString());
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
- }
-
- tomcatNode = NodeFactory.newInstance().createNode(nodeContributions);
- tomcatNode.start();
- }
-
- public void contextDestroyed(ServletContextEvent arg0) {
- if (tomcatNode != null) {
- tomcatNode.stop();
- }
- }
-
-}
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/Installer.java b/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/Installer.java
index 2251a2e833..40651050fa 100644
--- a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/Installer.java
+++ b/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/Installer.java
@@ -29,7 +29,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.util.Properties;
import org.apache.tuscany.sca.tomcat.TuscanyLifecycleListener;
import org.codehaus.swizzle.stream.DelimitedTokenReplacementInputStream;
@@ -72,10 +71,10 @@ public class Installer {
return status;
}
- public boolean install(boolean singleton) {
+ public boolean install() {
try {
- doInstall(singleton);
+ doInstall();
status = "Install successful, Tomcat restart required.";
restartRequired = true;
return true;
@@ -95,8 +94,7 @@ public class Installer {
try {
doUnintsall();
- status =
- "Tuscany removed from server.xml, please restart Tomcat and manually remove Tuscany jars from Tomcat lib";
+ status = "Tuscany removed from server.xml, please restart Tomcat and manually remove Tuscany jars from Tomcat lib";
restartRequired = true;
return true;
@@ -123,7 +121,7 @@ public class Installer {
}
- private boolean doInstall(boolean singleton) {
+ private boolean doInstall() {
// First verify all the file locations are as expected
if (!tuscanyWAR.exists()) {
throw new IllegalStateException("Tuscany war missing: " + tuscanyWAR.getAbsolutePath());
@@ -154,20 +152,6 @@ public class Installer {
// Copy tuscany-tomcat jar from the tuscany webapp web-inf/lib to Tomcat server/lib
copyFile(tuscanyTomcatJar, new File(serverLib, tuscanyTomcatJar.getName()));
- if (singleton) {
- try {
- // Write out a property file
- File propFile = new File(tuscanyWAR, "tuscany.properties");
- FileOutputStream os = new FileOutputStream(propFile);
- Properties props = new Properties();
- props.put("singleton", "true");
- props.store(os, "Apache Tuscany properties for Tomcat");
- os.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
// Add Tuscany LifecycleListener to Tomcat server.xml
updateServerXml(serverXml);
@@ -186,7 +170,7 @@ public class Installer {
static final String tuscanyListener =
"\r\n" + " <!-- Tuscany plugin for Tomcat -->\r\n"
- + "<Listener className=\"org.apache.tuscany.sca.tomcat.TuscanyLifecycleListener\" />";
+ + " <Listener className=\"org.apache.tuscany.sca.tomcat.TuscanyLifecycleListener\" />";
private void updateServerXml(File serverXmlFile) {
String serverXML = readAll(serverXmlFile);
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/InstallerServlet.java b/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/InstallerServlet.java
index a7e5c7e3d7..efcc2711f3 100644
--- a/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/InstallerServlet.java
+++ b/sca-java-2.x/trunk/distribution/tomcat/tomcat-servlet/src/main/java/org/apache/tuscany/sca/war/InstallerServlet.java
@@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
public class InstallerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
+
private transient ServletConfig servletConfig;
private transient Installer installer;
@@ -56,8 +57,7 @@ public class InstallerServlet extends HttpServlet {
protected void doIt(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
if ("Install".equalsIgnoreCase(req.getParameter("action"))) {
- String singleton = req.getParameter("singleton");
- installer.install(singleton!=null && singleton.equalsIgnoreCase("true"));
+ installer.install();
} else if ("Uninstall".equalsIgnoreCase(req.getParameter("action"))) {
installer.uninstall();
}
diff --git a/sca-java-2.x/trunk/distribution/tomcat/tomcat-war/src/main/webapp/installer.jsp b/sca-java-2.x/trunk/distribution/tomcat/tomcat-war/src/main/webapp/installer.jsp
index a6c3e4acaa..355976676b 100644
--- a/sca-java-2.x/trunk/distribution/tomcat/tomcat-war/src/main/webapp/installer.jsp
+++ b/sca-java-2.x/trunk/distribution/tomcat/tomcat-war/src/main/webapp/installer.jsp
@@ -48,7 +48,6 @@
<B>Install Tuscany</B><BR>
To install Tuscany into Tomcat, click:
<form action='installer' method='post'>
- <input type='checkbox' name='singleton' value='true'>The Tuscany runtime is shared by all web applications if checked.<p>
<input type='submit' name='action' value='Install'>
</form>
<BR>
@@ -70,5 +69,38 @@
<% }%>
</B>
+
+ <p>
+ <h2>What does this do?</h2>
+
+ This Tuscany install makes two updates to Tomcat:
+ <UL>
+ <LI>copies the jars from the tomcat-lib directory in this webapp into the Tomcat lib directory
+ <LI>updates the Tomcat conf/server.xml to include a &lt;Listener&gt; definition for Tuscany
+ </UL>
+ Those changes cause the Tuscany listener to be called at Tomcat startup and that locates all the Host
+ defiinitions and patches them to use the TuscanyStandardContext. This enables support for deploying
+ SCA enabled webapps and plain SCA jar, zip, or folder contributions to the Host in the same way
+ that .war files are deployed.
+ <p>
+ The SCA domain used for running the contributions defaults to "vm:default". This can be configured
+ by using an initilization parameter, most easiliy by defining that parameter in a context.xml file.
+ The Tomcat file conf/context.xml file defines the global defaults so that can be used for setting the
+ deafult domain for all SCA contributions. Individual contributions can also use their own context.xml
+ files to override that default.
+ See the <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html">Tomcat doc</a> for more information on using context.xml files.
+ <p>
+ An example of setting the domain as a context.xml parameter:
+ <br>&lt;Context&gt;
+ <br>. . .
+ <br>&lt;Parameter name="org.apache.tuscany.sca.defaultDomainURI" value="tribes:myDomain" override="false"/&gt;
+ <br>. . .
+ <br>&lt;/Context&gt;
+ <p>
+ For more information visit the Tuscany website page on <a href="http://tuscany.apache.org/tuscany-tomcat-distribution.html">Tomcat Integration</a>.
+ <p>
+ Note also that this is work in progress so is liable to change as Tuscany 2.0 is developed. Feedback is welcome and appreciated so if you've any comments or requests on this Tomcat integration please email <href="mailto:dev@tuscany.apache.org">dev@tuscany.apache.org</a>.
+ <p>
+
</body>
</html>