You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2007/04/17 02:27:33 UTC

svn commit: r529455 - /tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java

Author: hlship
Date: Mon Apr 16 17:27:32 2007
New Revision: 529455

URL: http://svn.apache.org/viewvc?view=rev&rev=529455
Log:
TAPESTRY-1333: Cannot bundle component classes & templates in WEB-INF/classes when deploying to Tomcat (& JBoss)

Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java?view=diff&rev=529455&r1=529454&r2=529455
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ClassNameLocatorImpl.java Mon Apr 16 17:27:32 2007
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.services;
 
 import java.io.BufferedInputStream;
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -27,6 +28,7 @@
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 import org.apache.tapestry.internal.TapestryInternalUtils;
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
@@ -93,30 +95,78 @@
     {
         URLConnection connection = url.openConnection();
 
+        JarFile jarFile;
+
         if (connection instanceof JarURLConnection)
         {
-            scanJarConnection(packagePath, componentClassNames, (JarURLConnection) connection);
-            return;
+            jarFile = ((JarURLConnection) connection).getJarFile();
+        }
+        else
+        {
+            jarFile = getAlternativeJarFile(url);
         }
 
-        // Otherwise, we're forced to assume that it is a file: URL for files in the user's
-        // workspace.
+        if (jarFile != null)
+        {
+            scanJarFile(packagePath, componentClassNames, jarFile);
+        }
+        else if (supportsDirStream(url))
+        {
+            Stack<Queued> queue = CollectionFactory.newStack();
 
-        Stack<Queued> queue = CollectionFactory.newStack();
+            queue.push(new Queued(url, packagePath));
 
-        queue.push(new Queued(url, packagePath));
+            while (!queue.isEmpty())
+            {
+                Queued queued = queue.pop();
 
-        while (!queue.isEmpty())
+                scanDirStream(queued._packagePath, queued._packageURL, componentClassNames, queue);
+            }
+        }
+        else
         {
-            Queued queued = queue.pop();
-
-            scan(queued._packagePath, queued._packageURL, componentClassNames, queue);
+            // Try scanning file system.
+            String packageName = packagePath.replace("/", ".");
+            if (packageName.endsWith("."))
+            {
+                packageName = packageName.substring(0, packageName.length() - 1);
+            }
+            scanDir(packageName, new File(url.getFile()), componentClassNames);
         }
 
     }
 
-    private void scan(String packagePath, URL packageURL, Collection<String> componentClassNames,
-            Stack<Queued> queue) throws IOException
+    /**
+     * Check whether container supports opening a stream on a dir/package to get a list of its
+     * contents.
+     * 
+     * @param packageURL
+     * @return
+     */
+    private boolean supportsDirStream(URL packageURL)
+    {
+        InputStream is = null;
+        try
+        {
+            is = packageURL.openStream();
+            return true;
+        }
+        catch (FileNotFoundException ex)
+        {
+            return false;
+        }
+        catch (IOException e)
+        {
+            return false;
+        }
+        finally
+        {
+            TapestryInternalUtils.close(is);
+        }
+    }
+
+    private void scanDirStream(String packagePath, URL packageURL,
+            Collection<String> componentClassNames, Stack<Queued> queue) throws IOException
     {
         InputStream is = null;
 
@@ -184,10 +234,10 @@
 
     }
 
-    private void scanJarConnection(String packagePath, Collection<String> componentClassNames,
-            JarURLConnection connection) throws IOException
+    private void scanJarFile(String packagePath, Collection<String> componentClassNames,
+            JarFile jarFile) throws IOException
     {
-        Enumeration<JarEntry> e = connection.getJarFile().entries();
+        Enumeration<JarEntry> e = jarFile.entries();
 
         while (e.hasMoreElements())
         {
@@ -207,6 +257,74 @@
 
             componentClassNames.add(className);
         }
+    }
+
+    /**
+     * Scan a dir for classes. Will recursively look in the supplied directory and all sub
+     * directories.
+     * 
+     * @param packageName
+     *            Name of package that this directory corresponds to.
+     * @param dir
+     *            Dir to scan for clases.
+     * @param componentClassNames
+     *            List of class names that have been found.
+     */
+    private void scanDir(String packageName, File dir, Collection<String> componentClassNames)
+    {
+        if (dir.exists() && dir.isDirectory())
+        {
+            for (File file : dir.listFiles())
+            {
+                String fileName = file.getName();
+                if (file.isDirectory())
+                {
+                    scanDir(packageName + "." + fileName, file, componentClassNames);
+                }
+                else if (fileName.endsWith(CLASS_SUFFIX))
+                {
+                    String className = packageName + "."
+                            + fileName.substring(0, fileName.length() - CLASS_SUFFIX.length());
+                    componentClassNames.add(className);
+                }
+            }
+        }
+    }
+
+    /**
+     * For URLs to JARs that do not use JarURLConnection - allowed by the servlet spec - attempt to
+     * produce a JarFile object all the same. Known servlet engines that function like this include
+     * Weblogic and OC4J. This is not a full solution, since an unpacked WAR or EAR will not have
+     * JAR "files" as such.
+     * 
+     * @param url
+     *            URL of jar
+     * @return JarFile or null
+     * @throws java.io.IOException
+     *             If error occurs creating jar file
+     */
+    private JarFile getAlternativeJarFile(URL url) throws IOException
+    {
+        String urlFile = url.getFile();
+        // Trim off any suffix - which is prefixed by "!/" on Weblogic
+        int separatorIndex = urlFile.indexOf("!/");
+
+        // OK, didn't find that. Try the less safe "!", used on OC4J
+        if (separatorIndex == -1)
+        {
+            separatorIndex = urlFile.indexOf('!');
+        }
+        if (separatorIndex != -1)
+        {
+            String jarFileUrl = urlFile.substring(0, separatorIndex);
+            // And trim off any "file:" prefix.
+            if (jarFileUrl.startsWith("file:"))
+            {
+                jarFileUrl = jarFileUrl.substring("file:".length());
+            }
+            return new JarFile(jarFileUrl);
+        }
+        return null;
     }
 
 }