classscanner: prever Streams over Iterables
Project: http://git-wip-us.apache.org/repos/asf/zest-java/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-java/commit/3acd801d Tree: http://git-wip-us.apache.org/repos/asf/zest-java/tree/3acd801d Diff: http://git-wip-us.apache.org/repos/asf/zest-java/diff/3acd801d Branch: refs/heads/develop Commit: 3acd801d4a51ca16a0db4b91531842db81261633 Parents: 611ef1b Author: Paul Merlin <[email protected]> Authored: Mon Nov 28 11:12:55 2016 +0100 Committer: Paul Merlin <[email protected]> Committed: Mon Nov 28 11:28:41 2016 +0100 ---------------------------------------------------------------------- .../org/apache/zest/bootstrap/ClassScanner.java | 160 +++++++------------ .../apache/zest/bootstrap/ClassScannerTest.java | 14 +- .../builder/ApplicationBuilderTest.java | 12 +- .../runtime/query/IterableQuerySourceTest.java | 8 +- .../library/rest/common/ValueAssembler.java | 12 +- .../server/assembler/RestServerAssembler.java | 32 ++-- 6 files changed, 98 insertions(+), 140 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/core/bootstrap/src/main/java/org/apache/zest/bootstrap/ClassScanner.java ---------------------------------------------------------------------- diff --git a/core/bootstrap/src/main/java/org/apache/zest/bootstrap/ClassScanner.java b/core/bootstrap/src/main/java/org/apache/zest/bootstrap/ClassScanner.java index 7b8cd6b..c792048 100644 --- a/core/bootstrap/src/main/java/org/apache/zest/bootstrap/ClassScanner.java +++ b/core/bootstrap/src/main/java/org/apache/zest/bootstrap/ClassScanner.java @@ -25,47 +25,46 @@ import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.net.URL; import java.security.CodeSource; +import java.util.Collections; +import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; -import org.apache.zest.functional.Iterables; - -import static org.apache.zest.functional.Iterables.filter; -import static org.apache.zest.functional.Iterables.flatten; -import static org.apache.zest.functional.Iterables.flattenIterables; -import static org.apache.zest.functional.Iterables.iterable; -import static org.apache.zest.functional.Iterables.map; +import java.util.stream.Stream; /** * Scan classpath for classes that matches given criteria. Useful for automated assemblies with lots of similar classes. */ public class ClassScanner { + private static final ValidClass VALID_CLASS_PREDICATE = new ValidClass(); + /** * Get all classes from the same package of the given class, and recursively in all subpackages. * <p> * This only works if the seed class is loaded from a file: URL. Jar files are possible as well. Abstract classes - * are not included in the results. For further filtering use e.g. Iterables.filter. + * are not included in the results. For further filtering use e.g. Stream.filter. * </p> * @param seedClass starting point for classpath scanning * - * @return iterable of all concrete classes in the same package as the seedclass, and also all classes in subpackages. + * @return Stream of all concrete classes in the same package as the seedclass, and also all classes in subpackages. */ - public static Iterable<Class<?>> findClasses( final Class<?> seedClass ) + public static Stream<? extends Class<?>> findClasses( final Class<?> seedClass ) { CodeSource codeSource = seedClass.getProtectionDomain().getCodeSource(); if( codeSource == null ) { - return Iterables.empty(); + return Stream.of(); } URL location = codeSource.getLocation(); if( !location.getProtocol().equals( "file" ) ) { - throw new IllegalArgumentException( "Can only enumerate classes from file system locations. URL is:" + location ); + throw new IllegalArgumentException( + "Can only enumerate classes from file system locations. URL is:" + location ); } final File file; @@ -75,7 +74,8 @@ public class ClassScanner } catch( URISyntaxException e ) { - throw new IllegalArgumentException( "The file location of codebase is invalid. Can not convert to URI. URL is:" + location ); + throw new IllegalArgumentException( + "The file location of codebase is invalid. Can not convert to URI. URL is:" + location ); } if( file.getName().endsWith( ".jar" ) ) @@ -85,38 +85,27 @@ public class ClassScanner final String packageName = seedClass.getPackage().getName().replace( '.', '/' ); JarFile jarFile = new JarFile( file ); - Iterable<JarEntry> entries = Iterables.iterable( jarFile.entries() ); + List<JarEntry> entries = Collections.list( jarFile.entries() ); try { - return Iterables.toList( filter( new ValidClass(), - map( new Function<JarEntry, Class<?>>() - { - @Override - public Class apply( JarEntry jarEntry ) - { - String name = jarEntry.getName(); - name = name.substring( 0, name.length() - 6 ); - name = name.replace( '/', '.' ); - try - { - return seedClass.getClassLoader().loadClass( name ); - } - catch( ClassNotFoundException e ) - { - return null; - } - } - } - , filter( new Predicate<JarEntry>() - { - @Override - public boolean test( JarEntry jarEntry ) - { - return jarEntry.getName() - .startsWith( packageName ) && jarEntry.getName() - .endsWith( ".class" ); - } - }, entries ) ) ) ); + return entries.stream() + .filter( jarEntry -> jarEntry.getName().startsWith( packageName ) + && jarEntry.getName().endsWith( ".class" ) ) + .map( jarEntry -> + { + String name = jarEntry.getName(); + name = name.substring( 0, name.length() - 6 ); + name = name.replace( '/', '.' ); + try + { + return seedClass.getClassLoader().loadClass( name ); + } + catch( ClassNotFoundException e ) + { + return null; + } + } ) + .filter( VALID_CLASS_PREDICATE ); } finally { @@ -131,34 +120,22 @@ public class ClassScanner else { final File path = new File( file, seedClass.getPackage().getName().replace( '.', File.separatorChar ) ); - Iterable<File> files = findFiles( path, new Predicate<File>() - { - @Override - public boolean test( File file ) - { - return file.getName().endsWith( ".class" ); - } - } ); - - return filter( new ValidClass(), - map( new Function<File, Class<?>>() - { - @Override - public Class<?> apply( File f ) - { - String fileName = f.getAbsolutePath().substring( file.toString().length() + 1 ); - fileName = fileName.replace( File.separatorChar, '.' ) - .substring( 0, fileName.length() - 6 ); - try - { - return seedClass.getClassLoader().loadClass( fileName ); - } - catch( ClassNotFoundException e ) - { - return null; - } - } - }, files ) ); + Stream<File> classFiles = findFiles( path, candidate -> candidate.getName().endsWith( ".class" ) ); + return classFiles + .map( classFile -> + { + String fileName = classFile.getAbsolutePath().substring( file.toString().length() + 1 ); + fileName = fileName.replace( File.separatorChar, '.' ).substring( 0, fileName.length() - 6 ); + try + { + return seedClass.getClassLoader().loadClass( fileName ); + } + catch( ClassNotFoundException e ) + { + return null; + } + } ) + .filter( VALID_CLASS_PREDICATE ); } } @@ -175,35 +152,21 @@ public class ClassScanner public static Predicate<Class<?>> matches( String regex ) { final Pattern pattern = Pattern.compile( regex ); - - return new Predicate<Class<?>>() - { - @Override - public boolean test( Class<?> aClass ) - { - return pattern.matcher( aClass.getName() ).matches(); - } - }; + return aClass -> pattern.matcher( aClass.getName() ).matches(); } - private static Iterable<File> findFiles( File directory, final Predicate<File> filter ) + private static Stream<File> findFiles( File directory, final Predicate<File> filter ) { - return flatten( filter( filter, iterable( directory.listFiles() ) ), - flattenIterables( map( new Function<File, Iterable<File>>() - { - @Override - public Iterable<File> apply( File file ) - { - return findFiles( file, filter ); - } - }, filter( new Predicate<File>() - { - @Override - public boolean test( File file ) - { - return file.isDirectory(); - } - }, iterable( directory.listFiles() ) ) ) ) ); + File[] listedFiles = directory.listFiles(); + if( listedFiles == null ) + { + return Stream.of(); + } + return Stream.concat( Stream.of( listedFiles ).filter( filter ), + Stream.of( listedFiles ) + .filter( File::isDirectory ) + .map( dir -> findFiles( dir, filter ) ) + .flatMap( Function.identity() ) ); } private static class ValidClass @@ -212,7 +175,8 @@ public class ClassScanner @Override public boolean test( Class<?> item ) { - return ( item.isInterface() || !Modifier.isAbstract( item.getModifiers() ) ) && ( !item.isEnum() && !item.isAnonymousClass() ); + return ( item.isInterface() || !Modifier.isAbstract( item.getModifiers() ) ) + && ( !item.isEnum() && !item.isAnonymousClass() ); } } } http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/core/bootstrap/src/test/java/org/apache/zest/bootstrap/ClassScannerTest.java ---------------------------------------------------------------------- diff --git a/core/bootstrap/src/test/java/org/apache/zest/bootstrap/ClassScannerTest.java b/core/bootstrap/src/test/java/org/apache/zest/bootstrap/ClassScannerTest.java index 0a9932b..bb38ff5 100644 --- a/core/bootstrap/src/test/java/org/apache/zest/bootstrap/ClassScannerTest.java +++ b/core/bootstrap/src/test/java/org/apache/zest/bootstrap/ClassScannerTest.java @@ -19,16 +19,14 @@ */ package org.apache.zest.bootstrap; +import org.apache.zest.api.activation.ActivationException; +import org.apache.zest.bootstrap.somepackage.Test2Value; import org.apache.zest.bootstrap.unitofwork.DefaultUnitOfWorkAssembler; import org.junit.Assert; import org.junit.Test; -import org.apache.zest.api.activation.ActivationException; -import org.apache.zest.bootstrap.somepackage.Test2Value; -import org.apache.zest.functional.Iterables; import static org.apache.zest.bootstrap.ClassScanner.findClasses; import static org.apache.zest.bootstrap.ClassScanner.matches; -import static org.apache.zest.functional.Iterables.filter; /** * Test and showcase of the ClassScanner assembly utility. @@ -48,10 +46,8 @@ public class ClassScannerTest new DefaultUnitOfWorkAssembler().assemble( module ); // Find all classes starting from TestValue, but include only the ones that are named *Value - for( Class aClass : filter( matches( ".*Value" ), findClasses( TestValue.class ) ) ) - { - module.values( aClass ); - } + findClasses( TestValue.class ).filter( matches( ".*Value" ) ) + .forEach( module::values ); } }; @@ -62,6 +58,6 @@ public class ClassScannerTest @Test public void testClassScannerJar() { - Assert.assertEquals( 185, Iterables.count( findClasses( Test.class ) ) ); + Assert.assertEquals( 185, findClasses( Test.class ).count() ); } } http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/core/bootstrap/src/test/java/org/apache/zest/bootstrap/builder/ApplicationBuilderTest.java ---------------------------------------------------------------------- diff --git a/core/bootstrap/src/test/java/org/apache/zest/bootstrap/builder/ApplicationBuilderTest.java b/core/bootstrap/src/test/java/org/apache/zest/bootstrap/builder/ApplicationBuilderTest.java index fea47d4..a07d92a 100644 --- a/core/bootstrap/src/test/java/org/apache/zest/bootstrap/builder/ApplicationBuilderTest.java +++ b/core/bootstrap/src/test/java/org/apache/zest/bootstrap/builder/ApplicationBuilderTest.java @@ -22,8 +22,6 @@ package org.apache.zest.bootstrap.builder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import org.json.JSONException; -import org.junit.Test; import org.apache.zest.api.activation.ActivationException; import org.apache.zest.api.mixin.Mixins; import org.apache.zest.api.structure.Application; @@ -31,12 +29,15 @@ import org.apache.zest.api.structure.Module; import org.apache.zest.bootstrap.Assembler; import org.apache.zest.bootstrap.AssemblyException; import org.apache.zest.bootstrap.ModuleAssembly; +import org.json.JSONException; +import org.junit.Test; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; +import static java.util.stream.Collectors.toList; import static org.apache.zest.bootstrap.ClassScanner.findClasses; import static org.apache.zest.bootstrap.ClassScanner.matches; import static org.apache.zest.functional.Iterables.filter; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; public class ApplicationBuilderTest { @@ -48,7 +49,8 @@ public class ApplicationBuilderTest builder.withLayer( "layer1" ).using( "layer2" ).using( "layer3" ); builder.withLayer( "layer2" ); builder.withLayer( "layer3" ).withModule( "test module" ). - withAssemblers( filter( matches( ".*ServiceAssembler" ), findClasses( getClass() ) ) ); + withAssemblers( filter( matches( ".*ServiceAssembler" ), + findClasses( getClass() ).collect( toList() ) ) ); Application application = builder.newApplication(); Module module = application.findModule( "layer3", "test module" ); TestService service = module.findService( TestService.class ).get(); http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/core/runtime/src/test/java/org/apache/zest/runtime/query/IterableQuerySourceTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/apache/zest/runtime/query/IterableQuerySourceTest.java b/core/runtime/src/test/java/org/apache/zest/runtime/query/IterableQuerySourceTest.java index 2b35c48..d36e659 100644 --- a/core/runtime/src/test/java/org/apache/zest/runtime/query/IterableQuerySourceTest.java +++ b/core/runtime/src/test/java/org/apache/zest/runtime/query/IterableQuerySourceTest.java @@ -81,15 +81,11 @@ public class IterableQuerySourceTest { SingletonAssembler assembler = new SingletonAssembler() { + @Override public void assemble( ModuleAssembly module ) throws AssemblyException { - Iterable<Class<?>> entities = ClassScanner.findClasses( DomainEntity.class ); - - for( Class entity : entities ) - { - module.entities( entity ); - } + ClassScanner.findClasses( DomainEntity.class ).forEach( module::entities ); module.values( ContactsValue.class, ContactValue.class ); new EntityTestAssembler().assemble( module ); http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/libraries/rest-common/src/main/java/org/apache/zest/library/rest/common/ValueAssembler.java ---------------------------------------------------------------------- diff --git a/libraries/rest-common/src/main/java/org/apache/zest/library/rest/common/ValueAssembler.java b/libraries/rest-common/src/main/java/org/apache/zest/library/rest/common/ValueAssembler.java index f8a301e..8cebeba 100644 --- a/libraries/rest-common/src/main/java/org/apache/zest/library/rest/common/ValueAssembler.java +++ b/libraries/rest-common/src/main/java/org/apache/zest/library/rest/common/ValueAssembler.java @@ -26,9 +26,8 @@ import org.apache.zest.bootstrap.Assembler; import org.apache.zest.bootstrap.AssemblyException; import org.apache.zest.bootstrap.ModuleAssembly; -import static org.apache.zest.api.util.Classes.*; -import static org.apache.zest.bootstrap.ClassScanner.*; -import static org.apache.zest.functional.Iterables.*; +import static org.apache.zest.api.util.Classes.isAssignableFrom; +import static org.apache.zest.bootstrap.ClassScanner.findClasses; /** * Assembler for all REST values. @@ -40,9 +39,8 @@ public class ValueAssembler public void assemble( ModuleAssembly module ) throws AssemblyException { - for( Class<?> aClass : filter( isAssignableFrom( ValueComposite.class ), findClasses( Resource.class ) )) - { - module.values( aClass ).visibleIn( Visibility.application ); - } + findClasses( Resource.class ).filter( isAssignableFrom( ValueComposite.class ) ) + .forEach( resourceType -> module.values( resourceType ) + .visibleIn( Visibility.application ) ); } } http://git-wip-us.apache.org/repos/asf/zest-java/blob/3acd801d/libraries/rest-server/src/main/java/org/apache/zest/library/rest/server/assembler/RestServerAssembler.java ---------------------------------------------------------------------- diff --git a/libraries/rest-server/src/main/java/org/apache/zest/library/rest/server/assembler/RestServerAssembler.java b/libraries/rest-server/src/main/java/org/apache/zest/library/rest/server/assembler/RestServerAssembler.java index a9ee8ac..d51c8d5 100644 --- a/libraries/rest-server/src/main/java/org/apache/zest/library/rest/server/assembler/RestServerAssembler.java +++ b/libraries/rest-server/src/main/java/org/apache/zest/library/rest/server/assembler/RestServerAssembler.java @@ -23,6 +23,7 @@ package org.apache.zest.library.rest.server.assembler; import freemarker.template.Configuration; import freemarker.template.Version; import java.lang.reflect.Modifier; +import java.util.List; import java.util.Properties; import java.util.function.Predicate; import org.apache.velocity.app.VelocityEngine; @@ -42,11 +43,11 @@ import org.apache.zest.library.rest.server.restlet.responsewriter.DefaultRespons import org.apache.zest.library.rest.server.spi.ResponseWriter; import org.restlet.service.MetadataService; +import static java.util.stream.Collectors.toList; import static org.apache.zest.api.util.Classes.hasModifier; import static org.apache.zest.api.util.Classes.isAssignableFrom; import static org.apache.zest.bootstrap.ImportedServiceDeclaration.INSTANCE; import static org.apache.zest.bootstrap.ImportedServiceDeclaration.NEW_OBJECT; -import static org.apache.zest.functional.Iterables.filter; /** * JAVADOC @@ -66,7 +67,7 @@ public class RestServerAssembler VelocityEngine velocity = new VelocityEngine( props ); module.importedServices( VelocityEngine.class ) - .importedBy( INSTANCE ).setMetaInfo( velocity ); + .importedBy( INSTANCE ).setMetaInfo( velocity ); } catch( Exception e ) { @@ -83,28 +84,29 @@ public class RestServerAssembler module.importedServices( MetadataService.class ); module.importedServices( ResponseWriterDelegator.class ) - .identifiedBy( "responsewriterdelegator" ) - .importedBy( NEW_OBJECT ) - .visibleIn( Visibility.layer ); + .identifiedBy( "responsewriterdelegator" ) + .importedBy( NEW_OBJECT ) + .visibleIn( Visibility.layer ); module.objects( ResponseWriterDelegator.class ); module.importedServices( RequestReaderDelegator.class ) - .identifiedBy( "requestreaderdelegator" ) - .importedBy( NEW_OBJECT ) - .visibleIn( Visibility.layer ); + .identifiedBy( "requestreaderdelegator" ) + .importedBy( NEW_OBJECT ) + .visibleIn( Visibility.layer ); module.objects( RequestReaderDelegator.class ); - module.importedServices( InteractionConstraintsService.class ). - importedBy( NewObjectImporter.class ). - visibleIn( Visibility.application ); + module.importedServices( InteractionConstraintsService.class ) + .importedBy( NewObjectImporter.class ) + .visibleIn( Visibility.application ); module.objects( InteractionConstraintsService.class ); // Standard response writers - Iterable<Class<?>> writers = ClassScanner.findClasses( DefaultResponseWriter.class ); - Predicate<Class<?>> responseWriterClass = isAssignableFrom( ResponseWriter.class ); + Predicate<Class<?>> isResponseWriterClass = isAssignableFrom( ResponseWriter.class ); Predicate<Class<?>> isNotAnAbstract = hasModifier( Modifier.ABSTRACT ).negate(); - Iterable<Class<?>> candidates = filter( isNotAnAbstract.and( responseWriterClass ), writers ); - for( Class<?> responseWriter : candidates ) + List<? extends Class<?>> responseWriters = ClassScanner.findClasses( DefaultResponseWriter.class ) + .filter( isNotAnAbstract.and( isResponseWriterClass ) ) + .collect( toList() ); + for( Class<?> responseWriter : responseWriters ) { module.objects( responseWriter ); }
