You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/10/05 23:42:31 UTC
git commit: Split the guts of ClassNameLocator into a new
ClasspathScanner service
Updated Branches:
refs/heads/5.4-js-rewrite 0eaeb8bb5 -> c8fd64807
Split the guts of ClassNameLocator into a new ClasspathScanner service
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/c8fd6480
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/c8fd6480
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/c8fd6480
Branch: refs/heads/5.4-js-rewrite
Commit: c8fd648077b8be794ac78f03c666c4b8576c2869
Parents: 0eaeb8b
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Fri Oct 5 14:39:54 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Fri Oct 5 14:39:54 2012 -0700
----------------------------------------------------------------------
.../hibernate/HibernateSessionSourceImplTest.java | 11 +-
.../internal/services/ClassNameLocatorImpl.java | 318 ++-------------
.../internal/services/ClasspathScannerImpl.java | 314 ++++++++++++++
.../tapestry5/ioc/services/ClassNameLocator.java | 3 +-
.../tapestry5/ioc/services/ClasspathMatcher.java | 38 ++
.../tapestry5/ioc/services/ClasspathScanner.java | 38 ++
.../tapestry5/ioc/services/TapestryIOCModule.java | 1 +
.../ioc/specs/ClassNameLocatorImplSpec.groovy | 82 ++--
8 files changed, 481 insertions(+), 324 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-hibernate-core/src/test/java/org/apache/tapestry5/internal/hibernate/HibernateSessionSourceImplTest.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate-core/src/test/java/org/apache/tapestry5/internal/hibernate/HibernateSessionSourceImplTest.java b/tapestry-hibernate-core/src/test/java/org/apache/tapestry5/internal/hibernate/HibernateSessionSourceImplTest.java
index 515c076..0ea7d6a 100644
--- a/tapestry-hibernate-core/src/test/java/org/apache/tapestry5/internal/hibernate/HibernateSessionSourceImplTest.java
+++ b/tapestry-hibernate-core/src/test/java/org/apache/tapestry5/internal/hibernate/HibernateSessionSourceImplTest.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import org.apache.tapestry5.hibernate.HibernateConfigurer;
import org.apache.tapestry5.hibernate.HibernateEntityPackageManager;
import org.apache.tapestry5.hibernate.HibernateSessionSource;
import org.apache.tapestry5.ioc.internal.services.ClassNameLocatorImpl;
+import org.apache.tapestry5.ioc.internal.services.ClasspathScannerImpl;
import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.test.IOCTestCase;
@@ -47,11 +48,15 @@ public class HibernateSessionSourceImplTest extends IOCTestCase
HibernateEntityPackageManager packageManager = newMock(HibernateEntityPackageManager.class);
TestBase.expect(packageManager.getPackageNames()).andReturn(packageNames);
+ ClasspathScannerImpl scanner = new ClasspathScannerImpl(new ClasspathURLConverterImpl());
+ ClassNameLocatorImpl classNameLocator = new ClassNameLocatorImpl(scanner);
+
List<HibernateConfigurer> filters = Arrays.asList(new DefaultHibernateConfigurer(true),
- new PackageNameHibernateConfigurer(packageManager, new ClassNameLocatorImpl(
- new ClasspathURLConverterImpl())));
+ new PackageNameHibernateConfigurer(packageManager, classNameLocator));
+
replay();
+
HibernateSessionSource source = new HibernateSessionSourceImpl(log, filters);
Session session = source.create();
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassNameLocatorImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassNameLocatorImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassNameLocatorImpl.java
index 3738f83..18fb90d 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassNameLocatorImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassNameLocatorImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2010 The Apache Software Foundation
+// Copyright 2007, 2008, 2010, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,328 +14,84 @@
package org.apache.tapestry5.ioc.internal.services;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.func.F;
+import org.apache.tapestry5.func.Mapper;
import org.apache.tapestry5.ioc.services.ClassNameLocator;
-import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
-import org.apache.tapestry5.ioc.util.Stack;
+import org.apache.tapestry5.ioc.services.ClasspathMatcher;
+import org.apache.tapestry5.ioc.services.ClasspathScanner;
-import java.io.*;
-import java.net.JarURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
+import java.io.IOException;
import java.util.Collection;
-import java.util.Enumeration;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import java.util.regex.Pattern;
public class ClassNameLocatorImpl implements ClassNameLocator
{
- private static final String CLASS_SUFFIX = ".class";
- public static final String PACKAGE_INFO = "package-info.class";
-
- private final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-
- private final ClasspathURLConverter converter;
+ private final ClasspathScanner scanner;
// This matches normal class files but not inner class files (which contain a '$'.
private final Pattern CLASS_NAME_PATTERN = Pattern.compile("^\\p{javaJavaIdentifierStart}[\\p{javaJavaIdentifierPart}&&[^\\$]]*\\.class$", Pattern.CASE_INSENSITIVE);
- private final Pattern FOLDER_NAME_PATTERN = Pattern.compile("^\\p{javaJavaIdentifierStart}[\\p{javaJavaIdentifierPart}]*$", Pattern.CASE_INSENSITIVE);
-
- static class Queued
- {
- final URL packageURL;
-
- final String packagePath;
-
- public Queued(final URL packageURL, final String packagePath)
- {
- this.packageURL = packageURL;
- this.packagePath = packagePath;
- }
- }
-
- public ClassNameLocatorImpl(ClasspathURLConverter converter)
- {
- this.converter = converter;
- }
-
/**
- * Synchronization should not be necessary, but perhaps the underlying ClassLoader's are sensitive to threading.
+ * Matches paths that are classes, but not for inner classes, or the package-info.class psuedo-class (used for package-level annotations).
*/
- public synchronized Collection<String> locateClassNames(String packageName)
- {
- String packagePath = packageName.replace('.', '/') + "/";
-
- try
- {
-
- return findClassesWithinPath(packagePath);
-
- } catch (IOException ex)
- {
- throw new RuntimeException(ex);
- }
- }
-
- private Collection<String> findClassesWithinPath(String packagePath) throws IOException
- {
- Collection<String> result = CollectionFactory.newList();
-
- Enumeration<URL> urls = contextClassLoader.getResources(packagePath);
-
- while (urls.hasMoreElements())
- {
- URL url = urls.nextElement();
-
- URL converted = converter.convert(url);
-
- scanURL(packagePath, result, converted);
- }
-
- return result;
- }
-
- private void scanURL(String packagePath, Collection<String> componentClassNames, URL url) throws IOException
+ private final ClasspathMatcher CLASS_NAME_MATCHER = new ClasspathMatcher()
{
- URLConnection connection = url.openConnection();
-
- JarFile jarFile;
-
- if (connection instanceof JarURLConnection)
- {
- jarFile = ((JarURLConnection) connection).getJarFile();
- } else
- {
- jarFile = getAlternativeJarFile(url);
- }
-
- if (jarFile != null)
- {
- scanJarFile(packagePath, componentClassNames, jarFile);
- } else if (supportsDirStream(url))
+ @Override
+ public boolean matches(String packagePath, String fileName)
{
- Stack<Queued> queue = CollectionFactory.newStack();
-
- queue.push(new Queued(url, packagePath));
-
- while (!queue.isEmpty())
+ if (!CLASS_NAME_PATTERN.matcher(fileName).matches())
{
- Queued queued = queue.pop();
-
- scanDirStream(queued.packagePath, queued.packageURL, componentClassNames, queue);
- }
- } else
- {
- // Try scanning file system.
- String packageName = packagePath.replace("/", ".");
- if (packageName.endsWith("."))
- {
- packageName = packageName.substring(0, packageName.length() - 1);
+ return false;
}
- scanDir(packageName, new File(url.getFile()), componentClassNames);
- }
-
- }
-
- /**
- * 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
- {
- InternalUtils.close(is);
- }
- }
-
- private void scanDirStream(String packagePath, URL packageURL, Collection<String> componentClassNames,
- Stack<Queued> queue) throws IOException
- {
- InputStream is;
-
- try
- {
- is = new BufferedInputStream(packageURL.openStream());
- } catch (FileNotFoundException ex)
- {
- // This can happen for certain application servers (JBoss 4.0.5 for example), that
- // export part of the exploded WAR for deployment, but leave part (WEB-INF/classes)
- // unexploded.
-
- return;
- }
- Reader reader = new InputStreamReader(is);
- LineNumberReader lineReader = new LineNumberReader(reader);
+ // Filter out inner classes.
- String packageName = null;
-
- try
- {
- while (true)
+ if (fileName.contains("$") || fileName.equals("package-info.class"))
{
- String line = lineReader.readLine();
-
- if (line == null) break;
-
- if (CLASS_NAME_PATTERN.matcher(line).matches())
- {
- if (packageName == null)
- {
- packageName = packagePath.replace('/', '.');
- }
-
- // packagePath ends with '/', packageName ends with '.'
-
- String fileName = line.substring(0, line.length() - CLASS_SUFFIX.length());
-
- if (!fileName.equals("package-info"))
- {
- String fullClassName = packageName + fileName;
-
- componentClassNames.add(fullClassName);
- }
-
- continue;
- }
-
- // This should match just directories. It may also match files that have no extension;
- // when we read those, none of the lines should look like class files.
-
- if (FOLDER_NAME_PATTERN.matcher(line).matches())
- {
- URL newURL = new URL(packageURL.toExternalForm() + line + "/");
- String newPackagePath = packagePath + line + "/";
-
- queue.push(new Queued(newURL, newPackagePath));
- }
+ return false;
}
- lineReader.close();
- lineReader = null;
- } finally
- {
- InternalUtils.close(lineReader);
- }
-
- }
-
- private void scanJarFile(String packagePath, Collection<String> componentClassNames, JarFile jarFile)
- {
- Enumeration<JarEntry> e = jarFile.entries();
-
- while (e.hasMoreElements())
- {
- String name = e.nextElement().getName();
-
- if (!name.startsWith(packagePath)) continue;
-
-
- int lastSlashx = name.lastIndexOf('/');
-
- String fileName = name.substring(lastSlashx + 1);
-
- if (isClassName(fileName))
- {
-
- // Strip off .class and convert the slashes back to periods.
- String className =
- name.substring(0, lastSlashx + 1).replace('/', '.') +
- fileName.substring(0, fileName.length() - CLASS_SUFFIX.length());
-
-
- componentClassNames.add(className);
- }
+ return true;
}
- }
+ };
/**
- * 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 classes.
- * @param componentClassNames List of class names that have been found.
+ * Maps a path name ("foo/bar/Baz.class") to a class name ("foo.bar.Baz").
*/
- private void scanDir(String packageName, File dir, Collection<String> componentClassNames)
+ private final Mapper<String, String> CLASS_NAME_MAPPER = new Mapper<String, String>()
{
- if (dir.exists() && dir.isDirectory())
+ @Override
+ public String map(String element)
{
- for (File file : dir.listFiles())
- {
- String fileName = file.getName();
- if (file.isDirectory())
- {
- scanDir(packageName + "." + fileName, file, componentClassNames);
- }
- // https://issues.apache.org/jira/browse/TAP5-1737
- // Use of package-info.java leaves these package-info.class files around.
- else if (isClassName(fileName))
- {
- String className = packageName + "." + fileName.substring(0,
- fileName.length() - CLASS_SUFFIX.length());
- componentClassNames.add(className);
- }
- }
+ return element.substring(0, element.length() - 6).replace('/', '.');
}
- }
+ };
+
- private boolean isClassName(String fileName)
+ public ClassNameLocatorImpl(ClasspathScanner scanner)
{
- return fileName.endsWith(CLASS_SUFFIX) && !fileName.equals(PACKAGE_INFO) && !fileName.contains("$");
+ this.scanner = scanner;
}
/**
- * 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
+ * Synchronization should not be necessary, but perhaps the underlying ClassLoader's are sensitive to threading.
*/
- private JarFile getAlternativeJarFile(URL url) throws IOException
+ public synchronized Collection<String> locateClassNames(String packageName)
{
- String urlFile = url.getFile();
- // Trim off any suffix - which is prefixed by "!/" on Weblogic
- int separatorIndex = urlFile.indexOf("!/");
+ String packagePath = packageName.replace('.', '/') + "/";
- // OK, didn't find that. Try the less safe "!", used on OC4J
- if (separatorIndex == -1)
+ try
{
- separatorIndex = urlFile.indexOf('!');
- }
+ Collection<String> matches = scanner.scan(packagePath, CLASS_NAME_MATCHER);
- 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 F.flow(matches).map(CLASS_NAME_MAPPER).toSet();
- return new JarFile(jarFileUrl);
+ } catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
}
-
- return null;
}
+
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClasspathScannerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClasspathScannerImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClasspathScannerImpl.java
new file mode 100644
index 0000000..c006c0f
--- /dev/null
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClasspathScannerImpl.java
@@ -0,0 +1,314 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.ClasspathMatcher;
+import org.apache.tapestry5.ioc.services.ClasspathScanner;
+import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
+import org.apache.tapestry5.ioc.util.Stack;
+
+import java.io.*;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Pattern;
+
+public class ClasspathScannerImpl implements ClasspathScanner
+{
+ private final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ private final ClasspathURLConverter converter;
+
+ private final Pattern FOLDER_NAME_PATTERN = Pattern.compile("^\\p{javaJavaIdentifierStart}[\\p{javaJavaIdentifierPart}]*$", Pattern.CASE_INSENSITIVE);
+
+ public ClasspathScannerImpl(ClasspathURLConverter converter)
+ {
+ this.converter = converter;
+ }
+
+ /**
+ * Scans the indicated package path for matches.
+ *
+ * @param packagePath
+ * a package path (like a package name, but using '/' instead of '.', and ending with '/')
+ * @param matcher
+ * passed a resource path from the package (or a sub-package), returns true if the provided
+ * path should be included in the returned collection
+ * @return collection of matching paths, in no specified order
+ * @throws java.io.IOException
+ */
+ public Set<String> scan(String packagePath, ClasspathMatcher matcher) throws IOException
+ {
+ assert packagePath != null && packagePath.endsWith("/");
+ assert matcher != null;
+
+ return new Job(matcher).findMatches(packagePath);
+ }
+
+ /**
+ * Check whether container supports opening a stream on a dir/package to get a list of its contents.
+ */
+ private boolean supportsDirStream(URL packageURL)
+ {
+ InputStream is = null;
+
+ try
+ {
+ is = packageURL.openStream();
+
+ return true;
+ } catch (FileNotFoundException ex)
+ {
+ return false;
+ } catch (IOException ex)
+ {
+ return false;
+ } finally
+ {
+ InternalUtils.close(is);
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ static class Queued
+ {
+ final URL packageURL;
+
+ final String packagePath;
+
+ public Queued(final URL packageURL, final String packagePath)
+ {
+ this.packageURL = packageURL;
+ this.packagePath = packagePath;
+ }
+ }
+
+ class Job
+ {
+ final ClasspathMatcher matcher;
+
+ final Set<String> matches = CollectionFactory.newSet();
+
+ final Stack<Queued> queue = CollectionFactory.newStack();
+
+ Job(ClasspathMatcher matcher)
+ {
+ this.matcher = matcher;
+ }
+
+ Set<String> findMatches(String packagePath) throws IOException
+ {
+
+ Enumeration<URL> urls = contextClassLoader.getResources(packagePath);
+
+ while (urls.hasMoreElements())
+ {
+ URL url = urls.nextElement();
+
+ URL converted = converter.convert(url);
+
+ scanURL(packagePath, converted);
+
+ while (!queue.isEmpty())
+ {
+ Queued queued = queue.pop();
+
+ scanDirStream(queued.packagePath, queued.packageURL);
+ }
+ }
+
+ return matches;
+ }
+
+ void scanURL(String packagePath, URL url) throws IOException
+ {
+ URLConnection connection = url.openConnection();
+
+ JarFile jarFile;
+
+ if (connection instanceof JarURLConnection)
+ {
+ jarFile = ((JarURLConnection) connection).getJarFile();
+ } else
+ {
+ jarFile = getAlternativeJarFile(url);
+ }
+
+ if (jarFile != null)
+ {
+ scanJarFile(packagePath, jarFile);
+ } else if (supportsDirStream(url))
+ {
+ queue.push(new Queued(url, packagePath));
+ } else
+ {
+ // Try scanning file system.
+
+ scanDir(packagePath, new File(url.getFile()));
+ }
+
+ }
+
+ /**
+ * Scan a dir for classes. Will recursively look in the supplied directory and all sub directories.
+ *
+ * @param packagePath
+ * Name of package that this directory corresponds to.
+ * @param packageDir
+ * Dir to scan for classes.
+ */
+ private void scanDir(String packagePath, File packageDir)
+ {
+ if (packageDir.exists() && packageDir.isDirectory())
+ {
+ for (File file : packageDir.listFiles())
+ {
+ String fileName = file.getName();
+
+ if (file.isDirectory())
+ {
+ // TODO: A second queue instead of recursion.
+
+ scanDir(packagePath + fileName + "/", file);
+ }
+
+ if (matcher.matches(packagePath, fileName))
+ {
+ matches.add(packagePath + fileName);
+ }
+ }
+ }
+ }
+
+ private void scanDirStream(String packagePath, URL packageURL) throws IOException
+ {
+ InputStream is;
+
+ try
+ {
+ is = new BufferedInputStream(packageURL.openStream());
+ } catch (FileNotFoundException ex)
+ {
+ // This can happen for certain application servers (JBoss 4.0.5 for example), that
+ // export part of the exploded WAR for deployment, but leave part (WEB-INF/classes)
+ // unexploded.
+
+ return;
+ }
+
+ Reader reader = new InputStreamReader(is);
+ LineNumberReader lineReader = new LineNumberReader(reader);
+
+ try
+ {
+ while (true)
+ {
+ String line = lineReader.readLine();
+
+ if (line == null) break;
+
+ if (matcher.matches(packagePath, line))
+ {
+ matches.add(packagePath + line);
+ } else
+ {
+
+ // This should match just directories. It may also match files that have no extension;
+ // when we read those, none of the lines should look like class files.
+
+ if (FOLDER_NAME_PATTERN.matcher(line).matches())
+ {
+ URL newURL = new URL(packageURL.toExternalForm() + line + "/");
+ String newPackagePath = packagePath + line + "/";
+
+ queue.push(new Queued(newURL, newPackagePath));
+ }
+ }
+ }
+ lineReader.close();
+ lineReader = null;
+ } finally
+ {
+ InternalUtils.close(lineReader);
+ }
+
+ }
+
+ private void scanJarFile(String packagePath, JarFile jarFile)
+ {
+ Enumeration<JarEntry> e = jarFile.entries();
+
+ while (e.hasMoreElements())
+ {
+ String name = e.nextElement().getName();
+
+ if (!name.startsWith(packagePath)) continue;
+
+ int lastSlashx = name.lastIndexOf('/');
+
+ String filePackagePath = name.substring(0, lastSlashx + 1);
+ String fileName = name.substring(lastSlashx + 1);
+
+ if (matcher.matches(filePackagePath, fileName))
+ {
+ matches.add(name);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassNameLocator.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassNameLocator.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassNameLocator.java
index 3c9ded8..8b22404 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassNameLocator.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClassNameLocator.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.Collection;
* Scans the classpath for top-level classes within particular packages.
*
* @see org.apache.tapestry5.ioc.services.ClasspathURLConverter
+ * @see ClasspathScanner
*/
public interface ClassNameLocator
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathMatcher.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathMatcher.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathMatcher.java
new file mode 100644
index 0000000..27a23c5
--- /dev/null
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathMatcher.java
@@ -0,0 +1,38 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Used to determine which files will be included in the set of matches paths within a particular
+ * package.
+ *
+ * @see ClasspathScanner
+ * @since 5.4
+ */
+public interface ClasspathMatcher
+{
+ /**
+ * Invoked for each located file, to determine if it belongs. May be passed file names
+ * that are actually nested folders. Typically, an implementation determined what matches
+ * based on a file extension of naming pattern.
+ *
+ * @param packagePath
+ * package path containing the file, ending with '/'
+ * @param fileName
+ * name of file within the package
+ * @return true to include, false to exclude
+ */
+ boolean matches(String packagePath, String fileName);
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathScanner.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathScanner.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathScanner.java
new file mode 100644
index 0000000..e308374
--- /dev/null
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ClasspathScanner.java
@@ -0,0 +1,38 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.ioc.services;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Used to scan a portion of the classpath for files that match a particular pattern, defined by a {@link ClasspathMatcher}.
+ *
+ * @since 5.4
+ */
+public interface ClasspathScanner
+{
+ /**
+ * Perform a scan of the indicated package path and any nested packages.
+ *
+ * @param packagePath
+ * defines the root of the search as a path, e.g., "org/apache/tapestry5/" not "org.apache.tapestry5"
+ * @param matcher
+ * passed each potential match to determine which are included in the final result
+ * @return matching paths based on the search and the matcher
+ * @throws IOException
+ */
+ Set<String> scan(String packagePath, ClasspathMatcher matcher) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
index d0e0c52..3bd7a6d 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
@@ -65,6 +65,7 @@ public final class TapestryIOCModule
binder.bind(Runnable.class, RegistryStartup.class).withSimpleId();
binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class).preventReloading();
binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
+ binder.bind(ClasspathScanner.class, ClasspathScannerImpl.class);
binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c8fd6480/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
index 106cb29..f90ff2c 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
@@ -1,76 +1,80 @@
package ioc.specs
import org.apache.tapestry5.ioc.internal.services.ClassNameLocatorImpl
+import org.apache.tapestry5.ioc.internal.services.ClasspathScannerImpl
import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl
import org.apache.tapestry5.ioc.services.ClassNameLocator
+import org.apache.tapestry5.ioc.services.ClasspathScanner
import spock.lang.Specification
class ClassNameLocatorImplSpec extends Specification {
- ClassNameLocator locator = new ClassNameLocatorImpl(new ClasspathURLConverterImpl());
+ ClasspathScanner scanner = new ClasspathScannerImpl(new ClasspathURLConverterImpl())
- def assertInList(classNames, packageName, String... expectedNames) {
+ ClassNameLocator locator = new ClassNameLocatorImpl(scanner);
- expectedNames.each { name ->
- String qualifiedName = "${packageName}.${name}"
+ def assertInList(classNames, packageName, String... expectedNames) {
- assert classNames.contains(qualifiedName), "[$qualifiedName] not present in ${classNames.join(', ')}."
+ expectedNames.each { name ->
+ String qualifiedName = "${packageName}.${name}"
+
+ assert classNames.contains(qualifiedName), "[$qualifiedName] not present in ${classNames.join(', ')}."
+ }
}
- }
- def assertNotInList(classNames, packageName, String... expectedNames) {
+ def assertNotInList(classNames, packageName, String... expectedNames) {
- expectedNames.each { name ->
- String qualifiedName = "${packageName}.${name}"
+ expectedNames.each { name ->
+ String qualifiedName = "${packageName}.${name}"
- assert !classNames.contains(qualifiedName), "[$qualifiedName] should not be present in ${classNames.join(', ')}."
+ assert !classNames.contains(qualifiedName), "[$qualifiedName] should not be present in ${classNames.join(', ')}."
+ }
}
- }
- def "locate classes inside a JAR file on the classpath"() {
+ def "locate classes inside a JAR file on the classpath"() {
- expect:
+ expect:
- assertInList locator.locateClassNames("javax.inject"),
- "javax.inject",
- "Inject", "Named", "Singleton"
- }
+ assertInList locator.locateClassNames("javax.inject"),
+ "javax.inject",
+ "Inject", "Named", "Singleton"
+ }
- def "can locate classes inside a subpackage, inside a classpath JAR file"() {
+ def "can locate classes inside a subpackage, inside a classpath JAR file"() {
- expect:
+ expect:
- assertInList locator.locateClassNames("org.slf4j"),
- "org.slf4j",
- "spi.MDCAdapter"
- }
+ assertInList locator.locateClassNames("org.slf4j"),
+ "org.slf4j",
+ "spi.MDCAdapter"
+ }
- def "can locate classes in local folder, but exclude inner classes"() {
+ def "can locate classes in local folder, but exclude inner classes"() {
- def packageName = "org.apache.tapestry5.ioc.services"
+ def packageName = "org.apache.tapestry5.ioc.services"
- when:
+ when:
- def names = locator.locateClassNames packageName
+ def names = locator.locateClassNames packageName
- then:
+ then:
- assertInList names, packageName, "SymbolSource", "TapestryIOCModule"
+ assertInList names, packageName, "SymbolSource", "TapestryIOCModule"
- assertNotInList names, packageName, 'TapestryIOCMOdules$1'
- }
+ assertNotInList names, packageName, 'TapestryIOCMOdules$1'
+ }
- def "can locate classes in subpackage of local folders"() {
- def packageName = "org.apache.tapestry5"
+ def "can locate classes in subpackage of local folders"() {
+ def packageName = "org.apache.tapestry5"
- when:
+ when:
- def names = locator.locateClassNames packageName
+ def names = locator.locateClassNames packageName
- then:
+ then:
- assertInList names, packageName, "ioc.Orderable", "ioc.services.ChainBuilder"
- assertNotInList names, packageName, 'services.TapestryIOCModule$1'
- }
+ assertInList names, packageName, "ioc.Orderable", "ioc.services.ChainBuilder"
+ assertNotInList names, packageName, 'services.TapestryIOCModule$1'
+ }
}