You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2013/09/16 15:14:56 UTC

svn commit: r1523633 - in /tomcat/trunk: java/org/apache/catalina/loader/ java/org/apache/catalina/webresources/ test/org/apache/catalina/webresources/ test/webresources/

Author: markt
Date: Mon Sep 16 13:14:56 2013
New Revision: 1523633

URL: http://svn.apache.org/r1523633
Log:
Add support for a custom URL protocol "war" that is used when accessing
JARs inside packed WAR files as the jar protocol does not support
nesting.

Added:
    tomcat/trunk/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java
    tomcat/trunk/java/org/apache/catalina/webresources/WarURLConnection.java
    tomcat/trunk/java/org/apache/catalina/webresources/WarURLStreamHandler.java
    tomcat/trunk/test/org/apache/catalina/webresources/TestWarURLConnection.java
    tomcat/trunk/test/webresources/war-url-connection.war
Modified:
    tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
    tomcat/trunk/java/org/apache/catalina/webresources/JarWarResource.java
    tomcat/trunk/java/org/apache/catalina/webresources/StandardRoot.java

Modified: tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=1523633&r1=1523632&r2=1523633&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java (original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoader.java Mon Sep 16 13:14:56 2013
@@ -65,6 +65,7 @@ import org.apache.catalina.LifecycleList
 import org.apache.catalina.LifecycleState;
 import org.apache.catalina.WebResource;
 import org.apache.catalina.WebResourceRoot;
+import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.IntrospectionUtils;
 import org.apache.tomcat.util.res.StringManager;
@@ -1750,6 +1751,8 @@ public class WebappClassLoader
         // Clear the classloader reference in the VM's bean introspector
         java.beans.Introspector.flushCaches();
 
+        // Clear any custom URLStreamHandlers
+        TomcatURLStreamHandlerFactory.release(this);
     }
 
 

Modified: tomcat/trunk/java/org/apache/catalina/webresources/JarWarResource.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/JarWarResource.java?rev=1523633&r1=1523632&r2=1523633&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/JarWarResource.java (original)
+++ tomcat/trunk/java/org/apache/catalina/webresources/JarWarResource.java Mon Sep 16 13:14:56 2013
@@ -46,7 +46,7 @@ public class JarWarResource extends Abst
         super(root, webAppPath, jarEntry);
         this.base = base;
         this.archivePath = archivePath;
-        this.baseUrl = "jar:" + baseUrl;
+        this.baseUrl = "jar:war:" + baseUrl + "^/" + archivePath;
 
         String resourceName = resource.getName();
         if (resourceName.charAt(resourceName.length() - 1) == '/') {

Modified: tomcat/trunk/java/org/apache/catalina/webresources/StandardRoot.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/StandardRoot.java?rev=1523633&r1=1523632&r2=1523633&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/StandardRoot.java (original)
+++ tomcat/trunk/java/org/apache/catalina/webresources/StandardRoot.java Mon Sep 16 13:14:56 2013
@@ -420,6 +420,10 @@ public class StandardRoot extends Lifecy
     protected void initInternal() throws LifecycleException {
         super.initInternal();
 
+        // Ensure support for jar:war:file:/ URKLs will be available (required
+        // for resource JARs in packed WAR files).
+        TomcatURLStreamHandlerFactory.register();
+
         if (context == null) {
             throw new IllegalStateException(
                     sm.getString("standardRoot.noContext"));

Added: tomcat/trunk/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java?rev=1523633&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java (added)
+++ tomcat/trunk/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java Mon Sep 16 13:14:56 2013
@@ -0,0 +1,119 @@
+/*
+ * 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.catalina.webresources;
+
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+// TODO Add hook to enable user registered factories to be unloaded on web
+// application stop.
+public class TomcatURLStreamHandlerFactory implements URLStreamHandlerFactory{
+
+    private static final String WAR_PROTOCOL = "war";
+
+    // Singleton instance
+    private static TomcatURLStreamHandlerFactory instance =
+            new TomcatURLStreamHandlerFactory();
+
+    /**
+     * Obtain a reference to the singleton instance,
+     */
+    public static TomcatURLStreamHandlerFactory getInstance() {
+        return instance;
+    }
+
+
+    // List of factories for application defined stream handler factories.
+    private List<URLStreamHandlerFactory> userFactories =
+            new CopyOnWriteArrayList<>();
+
+
+    /**
+     * Register this factory with the JVM. May be called more than once. The
+     * implementation ensures that registration only occurs once.
+     */
+    public static void register() {
+        // Calling this method loads this class which in turn triggers all the
+        // necessary registration.
+    }
+
+
+    /**
+     * Since the JVM only allows a single call to
+     * {@link URL#setURLStreamHandlerFactory(URLStreamHandlerFactory)} and
+     * Tomcat needs to register a handler, provide a mechanism to allow
+     * applications to registertheir own handlers.
+     */
+    public static void addUserFactory(URLStreamHandlerFactory factory) {
+        instance.userFactories.add(factory);
+    }
+
+
+    /**
+     * Release references to any user provided factories that have been loaded
+     * using the provided class loader. Called during web application stop to
+     * prevent memory leaks.
+     */
+    public static void release(ClassLoader classLoader) {
+        Iterator<URLStreamHandlerFactory> iter = instance.userFactories.iterator();
+        while (iter.hasNext()) {
+            ClassLoader factoryLoader = iter.next().getClass().getClassLoader();
+            while (factoryLoader != null) {
+                if (classLoader.equals(factoryLoader)) {
+                    iter.remove();
+                    break;
+                }
+                factoryLoader = factoryLoader.getParent();
+            }
+        }
+    }
+
+
+    private TomcatURLStreamHandlerFactory() {
+        // Hide default constructor
+        // Singleton pattern to ensure there is only one instance of this
+        // factory
+        URL.setURLStreamHandlerFactory(this);
+    }
+
+
+    @Override
+    public URLStreamHandler createURLStreamHandler(String protocol) {
+
+        // Tomcat's handler always takes priority so applications can't override
+        // it.
+        if (WAR_PROTOCOL.equals(protocol)) {
+            return new WarURLStreamHandler();
+        }
+
+        // Application handlers
+        for (URLStreamHandlerFactory factory : userFactories) {
+            URLStreamHandler handler =
+                factory.createURLStreamHandler(protocol);
+            if (handler != null) {
+                return handler;
+            }
+        }
+
+        // Unknown protocol
+        return null;
+    }
+}

Added: tomcat/trunk/java/org/apache/catalina/webresources/WarURLConnection.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/WarURLConnection.java?rev=1523633&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/WarURLConnection.java (added)
+++ tomcat/trunk/java/org/apache/catalina/webresources/WarURLConnection.java Mon Sep 16 13:14:56 2013
@@ -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.catalina.webresources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+
+public class WarURLConnection extends URLConnection {
+
+    private final URLConnection innerJarUrlConnection;
+    private boolean connected;
+
+    protected WarURLConnection(URL url) throws IOException {
+        super(url);
+        URL innerJarUrl = new URL(url.getFile());
+        innerJarUrlConnection = innerJarUrl.openConnection();
+    }
+
+    @Override
+    public void connect() throws IOException {
+        if (!connected) {
+            innerJarUrlConnection.connect();
+            connected = true;
+        }
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        connect();
+        return innerJarUrlConnection.getInputStream();
+    }
+}

Added: tomcat/trunk/java/org/apache/catalina/webresources/WarURLStreamHandler.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/WarURLStreamHandler.java?rev=1523633&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/WarURLStreamHandler.java (added)
+++ tomcat/trunk/java/org/apache/catalina/webresources/WarURLStreamHandler.java Mon Sep 16 13:14:56 2013
@@ -0,0 +1,43 @@
+/*
+ * 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.catalina.webresources;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+public class WarURLStreamHandler extends URLStreamHandler{
+
+    @Override
+    protected void parseURL(URL u, String spec, int start, int limit) {
+        // Need to make this look like a JAR URL for the WAR file
+        // Assumes that the spec is absolute and starts war:file:/...
+
+        // Only the path needs to be changed
+        String path = "jar:" + spec.substring(4);
+        path = path.replaceFirst("\\^/", "!/");
+
+        setURL(u, u.getProtocol(), "", -1, null, null,
+                path, null, null);
+    }
+
+    @Override
+    protected URLConnection openConnection(URL u) throws IOException {
+        return new WarURLConnection(u);
+    }
+}

Added: tomcat/trunk/test/org/apache/catalina/webresources/TestWarURLConnection.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/webresources/TestWarURLConnection.java?rev=1523633&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/webresources/TestWarURLConnection.java (added)
+++ tomcat/trunk/test/org/apache/catalina/webresources/TestWarURLConnection.java Mon Sep 16 13:14:56 2013
@@ -0,0 +1,50 @@
+/*
+ * 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.catalina.webresources;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestWarURLConnection {
+
+    @Before
+    public void register() {
+        TomcatURLStreamHandlerFactory.register();
+    }
+
+
+    @Test
+    public void testContentLength() throws Exception {
+        File f = new File("test/webresources/war-url-connection.war");
+        String fileUrl = f.toURI().toURL().toString();
+
+        URL indexHtmlUrl = new URL("jar:war:" + fileUrl +
+                "^/WEB-INF/lib/test.jar!/META-INF/resources/index.html");
+
+        URLConnection urlConn = indexHtmlUrl.openConnection();
+        urlConn.connect();
+
+        int size = urlConn.getContentLength();
+
+        Assert.assertEquals(137, size);
+    }
+}

Added: tomcat/trunk/test/webresources/war-url-connection.war
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/webresources/war-url-connection.war?rev=1523633&view=auto
==============================================================================
Files tomcat/trunk/test/webresources/war-url-connection.war (added) and tomcat/trunk/test/webresources/war-url-connection.war Mon Sep 16 13:14:56 2013 differ



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org