You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/01/23 15:36:13 UTC

[isis] 02/02: ISIS-1846 major refactoring: introduces _Reflect (internal API)

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch ISIS-1846_internal_utils
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 3bb368394cb0dc7b6a651212a7e5aa07bc16a238
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Jan 23 16:35:15 2018 +0100

    ISIS-1846 major refactoring: introduces _Reflect (internal API)
    
    all reflection use-cases are now contained within internal API,
    no more dependency on org.reflections apart from unittestsupport and
    applib itself;
---
 core/applib/pom.xml                                |  20 ++-
 .../java/org/apache/isis/applib/AppManifest.java   | 120 +----------------
 .../isis/applib/internal/context/_Contexts.java    |  72 ++++++++++
 .../isis/applib/internal/context/package-info.java |  28 ++++
 .../applib/internal/exceptions/_Exceptions.java    |   2 +-
 .../isis/applib/internal/reflection/_Reflect.java  |  72 ++++++++++
 .../internal/reflection/_Reflect_Discovery.java    |  85 ++++++++++++
 .../internal/reflection/_Reflect_Manifest.java     | 147 +++++++++++++++++++++
 .../applib/internal/reflection/package-info.java   |  28 ++++
 .../ClassDiscoveryServiceUsingReflections.java     |  35 +----
 core/pom.xml                                       |  21 ++-
 .../services/ServicesInstallerFromAnnotation.java  |  36 +++--
 .../IsisComponentProvider.java                     |  38 +++---
 .../objectstore/jdo/service/RegisterEntities.java  |  39 ++----
 core/unittestsupport/pom.xml                       |   1 +
 15 files changed, 521 insertions(+), 223 deletions(-)

diff --git a/core/applib/pom.xml b/core/applib/pom.xml
index 8ba1faa..b8dc99d 100644
--- a/core/applib/pom.xml
+++ b/core/applib/pom.xml
@@ -146,14 +146,32 @@
             <groupId>org.apache.isis.core</groupId>
             <artifactId>isis-core-unittestsupport</artifactId>
             <scope>test</scope>
+            <exclusions>
+            	<exclusion>
+            	    <!-- whatever unittestsupport is dependent on, it should not be transitive  -->
+            		<groupId>org.reflections</groupId>
+            		<artifactId>reflections</artifactId>
+            	</exclusion>
+            </exclusions>
         </dependency>
 
-        <!-- as used by ClassDiscoveryServiceUsingReflections -->
+        <!-- as used by internal API, 
+        	also provides com.google.code.findbugs/annotations @Nullable, 
+        	but not transitive -->
         <dependency>
             <groupId>org.reflections</groupId>
             <artifactId>reflections</artifactId>
+            <!-- we enforce usage of internal reflective API, 
+       			only applib is allowed to access reflections directly -->
+            <optional>true</optional> 
         </dependency>
 
+		<!-- provides @Nullable -->
+        <dependency>
+        	<groupId>com.google.code.findbugs</groupId>
+        	<artifactId>annotations</artifactId>
+        </dependency>
+        
     </dependencies>
 
 </project>
diff --git a/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java b/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
index e6703ea..cfab0aa 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/AppManifest.java
@@ -19,10 +19,6 @@
 
 package org.apache.isis.applib;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -31,11 +27,6 @@ import java.util.Set;
 
 import javax.jdo.annotations.PersistenceCapable;
 
-import com.google.common.collect.Lists;
-
-import org.reflections.vfs.SystemDir;
-import org.reflections.vfs.Vfs;
-
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.fixturescripts.FixtureScript;
 
@@ -163,7 +154,7 @@ public interface AppManifest {
     /**
      * Holds the set of domain services, persistent entities and fixture scripts.services
      */
-    public static class Registry {
+    public final static class Registry {
 
         public final static List<String> FRAMEWORK_PROVIDED_SERVICES = Collections.unmodifiableList(Arrays.asList(
                 "org.apache.isis.applib",
@@ -237,115 +228,6 @@ public interface AppManifest {
         }
         //endregion
 
-        //region > urlTypes
-        public List<Vfs.UrlType> getUrlTypes() {
-            final List<Vfs.UrlType> urlTypes = Lists.newArrayList();
-            urlTypes.add(new EmptyIfFileEndingsUrlType(".pom", ".jnilib", "QTJava.zip"));
-            urlTypes.add(new JettyConsoleUrlType());
-            urlTypes.addAll(Arrays.asList(Vfs.DefaultUrlTypes.values()));
-
-            return urlTypes;
-        }
-        //endregion
-
-        private static class EmptyIfFileEndingsUrlType implements Vfs.UrlType {
-
-            private final List<String> fileEndings;
-
-            private EmptyIfFileEndingsUrlType(final String... fileEndings) {
-                this.fileEndings = Lists.newArrayList(fileEndings);
-            }
-
-            public boolean matches(URL url) {
-                final String protocol = url.getProtocol();
-                final String externalForm = url.toExternalForm();
-                if (!protocol.equals("file")) {
-                    return false;
-                }
-                for (String fileEnding : fileEndings) {
-                    if (externalForm.endsWith(fileEnding))
-                        return true;
-                }
-                return false;
-            }
-
-            public Vfs.Dir createDir(final URL url) throws Exception {
-                return emptyVfsDir(url);
-            }
-
-            private static Vfs.Dir emptyVfsDir(final URL url) {
-                return new Vfs.Dir() {
-                    @Override
-                    public String getPath() {
-                        return url.toExternalForm();
-                    }
-
-                    @Override
-                    public Iterable<Vfs.File> getFiles() {
-                        return Collections.emptyList();
-                    }
-
-                    @Override
-                    public void close() {
-                        //
-                    }
-                };
-            }
-        }
-
-        private static class JettyConsoleUrlType implements Vfs.UrlType {
-            public boolean matches(URL url) {
-                final String protocol = url.getProtocol();
-                final String externalForm = url.toExternalForm();
-                final boolean matches = protocol.equals("file") && externalForm.contains("jetty-console") && externalForm.contains("-any-") && externalForm.endsWith("webapp/WEB-INF/classes/");
-                return matches;
-            }
-
-            public Vfs.Dir createDir(final URL url) throws Exception {
-                return new SystemDir(getFile(url));
-            }
-
-            /**
-             * try to get {@link java.io.File} from url
-             *
-             * <p>
-             *     Copied from {@link Vfs} (not publicly accessible)
-             * </p>
-             */
-            static java.io.File getFile(URL url) {
-                java.io.File file;
-                String path;
-
-                try {
-                    path = url.toURI().getSchemeSpecificPart();
-                    if ((file = new java.io.File(path)).exists()) return file;
-                } catch (URISyntaxException e) {
-                }
-
-                try {
-                    path = URLDecoder.decode(url.getPath(), "UTF-8");
-                    if (path.contains(".jar!")) path = path.substring(0, path.lastIndexOf(".jar!") + ".jar".length());
-                    if ((file = new java.io.File(path)).exists()) return file;
-
-                } catch (UnsupportedEncodingException e) {
-                }
-
-                try {
-                    path = url.toExternalForm();
-                    if (path.startsWith("jar:")) path = path.substring("jar:".length());
-                    if (path.startsWith("file:")) path = path.substring("file:".length());
-                    if (path.contains(".jar!")) path = path.substring(0, path.indexOf(".jar!") + ".jar".length());
-                    if ((file = new java.io.File(path)).exists()) return file;
-
-                    path = path.replace("%20", " ");
-                    if ((file = new java.io.File(path)).exists()) return file;
-
-                } catch (Exception e) {
-                }
-
-                return null;
-            }
-        }
     }
 
     public static class Util {
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/context/_Contexts.java b/core/applib/src/main/java/org/apache/isis/applib/internal/context/_Contexts.java
new file mode 100644
index 0000000..72c75ed
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/context/_Contexts.java
@@ -0,0 +1,72 @@
+/*
+ *  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.isis.applib.internal.context;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * Entry point for providing application scoped contexts.
+ * </p>
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/> 
+ * These may be changed or removed without notice!
+ * </p>
+ * @since 2.0.0
+ */
+public final class _Contexts {
+
+	private _Contexts(){}
+	
+	public interface Context {
+		public ClassLoader getDefaultClassLoader();
+	}
+
+	private final static Map<Long, Context> contextMap = new HashMap<Long, _Contexts.Context>(); 	
+	
+	/**
+	 * TODO this is just a stub yet
+	 * @return the current context
+	 * 
+	 * @throws IllegalStateException if there is more than one context available, 
+	 * use {@link #get(long)} instead
+	 */
+	public static Context current() {
+		return new Context() {
+			@Override
+			public ClassLoader getDefaultClassLoader() {
+				return Thread.currentThread().getContextClassLoader();
+			}
+		};
+	}
+	
+	/**
+	 * Get a context by id
+	 * @param contextId
+	 * @return
+	 */
+	public static Context get(long contextId) {
+		return contextMap.get(contextId);
+	}
+	
+	
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/context/package-info.java b/core/applib/src/main/java/org/apache/isis/applib/internal/context/package-info.java
new file mode 100644
index 0000000..84caa68
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/context/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  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.
+ */
+/**
+ * <h1>Internal API</h1>
+ * Internal classes, contributing to the internal proprietary API. 
+ * These may be changed or removed without notice!
+ * <p>
+ * <b>WARNING</b>: 
+ * Do NOT use any of the classes provided by this package!
+ * </p>
+ */
+package org.apache.isis.applib.internal.context;
\ No newline at end of file
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/exceptions/_Exceptions.java b/core/applib/src/main/java/org/apache/isis/applib/internal/exceptions/_Exceptions.java
index 7905f27..b503853 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/internal/exceptions/_Exceptions.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/exceptions/_Exceptions.java
@@ -29,7 +29,7 @@ import org.apache.isis.applib.internal.base._NullSafe;
 /**
  * <h1>- internal use only -</h1>
  * <p>
- * A collection of framework internal exceptions.
+ * A collection of framework internal exceptions and exception related idioms.
  * </p>
  * <p>
  * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/> 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect.java b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect.java
new file mode 100644
index 0000000..01b6fd6
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect.java
@@ -0,0 +1,72 @@
+/*
+ *  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.isis.applib.internal.reflection;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.isis.applib.internal.context._Contexts;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.util.ClasspathHelper;
+
+/**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * Java reflective utilities.
+ * </p>
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/> 
+ * These may be changed or removed without notice!
+ * </p>
+ * @since 2.0.0
+ */
+public final class _Reflect {
+
+	private _Reflect(){}
+	
+	// -- REFLECTIVE CLASS DISCOVERY
+	
+	public static interface Discovery {
+		public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation);
+		public <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type);
+	}
+
+	public static Discovery discover(String packageNamePrefix) {
+		_Reflect_Manifest.prepareDiscovery();
+		return _Reflect_Discovery.of(packageNamePrefix);
+	}
+	
+	public static Discovery discover(List<String> packageNamePrefixes) {
+		_Reflect_Manifest.prepareDiscovery();
+		return _Reflect_Discovery.of(packageNamePrefixes);
+	}
+	
+	public static Discovery discoverFullscan(String packageNamePrefix) {
+		_Reflect_Manifest.prepareDiscovery();
+		return _Reflect_Discovery.of(
+				ClasspathHelper.forClassLoader(_Contexts.current().getDefaultClassLoader()),
+				ClasspathHelper.forClass(Object.class),
+				ClasspathHelper.forPackage(packageNamePrefix),
+				new SubTypesScanner(false)
+		);
+	}
+	
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Discovery.java b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Discovery.java
new file mode 100644
index 0000000..9059e05
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Discovery.java
@@ -0,0 +1,85 @@
+package org.apache.isis.applib.internal.reflection;
+
+import java.lang.annotation.Annotation;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.constraints.NotNull;
+
+import org.apache.isis.applib.internal.base._NullSafe;
+import org.apache.isis.applib.internal.reflection._Reflect.Discovery;
+import org.reflections.Reflections;
+
+/**
+ * 
+ * package private mixin for utility class {@link _Reflect}
+ *
+ */
+class _Reflect_Discovery implements _Reflect.Discovery {
+	
+	private final Reflections reflections;
+	
+	// -- CONSTRUCTORS
+	
+	public static Discovery of(List<String> packagePrefixes) {
+		return new _Reflect_Discovery(packagePrefixes);
+	}
+
+	public static Discovery of(String packageNamePrefix) {
+		return new _Reflect_Discovery(packageNamePrefix);
+	}
+	
+	public static Discovery of(final Object... params) {
+		return new _Reflect_Discovery(params);
+	}
+	
+	// -- HIDDEN CONSTRUCTOR
+	
+	private _Reflect_Discovery(final Object... params) {
+		this.reflections = new Reflections(params);
+	}
+	
+	// -- IMPLEMENTATION
+
+	@Override @NotNull
+	public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
+		
+		Objects.requireNonNull(annotation);
+
+		// ensure unique entries
+		return streamTypesAnnotatedWith(annotation).collect(Collectors.toCollection(HashSet::new));
+	}
+
+	@Override @NotNull
+	public <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type) {
+		
+		Objects.requireNonNull(type);
+		
+		// ensure unique entries
+		return streamSubTypesOf(type).collect(Collectors.toCollection(HashSet::new));
+	}
+	
+	// -- HELPER
+	
+	
+	private Stream<Class<?>> streamTypesAnnotatedWith(Class<? extends Annotation> annotation) {
+		
+		// ensure non-null elements
+		return _NullSafe.stream(reflections.getTypesAnnotatedWith(annotation))
+				.filter(_NullSafe::isPresent);
+	}
+
+	private <T> Stream<Class<? extends T>> streamSubTypesOf(final Class<T> type) {
+		
+		// ensure non-null elements
+		return _NullSafe.stream(reflections.getSubTypesOf(type))
+				.filter(_NullSafe::isPresent);
+	}
+
+
+	
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Manifest.java b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Manifest.java
new file mode 100644
index 0000000..5deadce
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/_Reflect_Manifest.java
@@ -0,0 +1,147 @@
+package org.apache.isis.applib.internal.reflection;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.reflections.vfs.SystemDir;
+import org.reflections.vfs.Vfs;
+
+import com.google.common.collect.Lists;
+
+/**
+ * 
+ * package private mixin for utility class {@link _Reflect}
+ *
+ */
+class _Reflect_Manifest {
+
+	/*
+	 * If this static reference survives ApplicationScope life-cycles, thats ok.
+	 * List once initialized is quasi immutable. 
+	 */
+	private final static List<Vfs.UrlType> urlTypes = new ArrayList<>();
+	
+	public static void prepareDiscovery() {
+		Vfs.setDefaultURLTypes(getUrlTypes());
+	}
+
+	// -- 
+
+	private static List<Vfs.UrlType> getUrlTypes() {
+		
+		if(urlTypes.isEmpty()) {
+			urlTypes.add(new EmptyIfFileEndingsUrlType(".pom", ".jnilib", "QTJava.zip"));
+			urlTypes.add(new JettyConsoleUrlType());
+			urlTypes.addAll(Arrays.asList(Vfs.DefaultUrlTypes.values()));
+		}
+
+		return urlTypes;
+	}
+
+	// -- HELPER
+
+	private static class EmptyIfFileEndingsUrlType implements Vfs.UrlType {
+
+		private final List<String> fileEndings;
+
+		private EmptyIfFileEndingsUrlType(final String... fileEndings) {
+			this.fileEndings = Lists.newArrayList(fileEndings);
+		}
+
+		public boolean matches(URL url) {
+			final String protocol = url.getProtocol();
+			final String externalForm = url.toExternalForm();
+			if (!protocol.equals("file")) {
+				return false;
+			}
+			for (String fileEnding : fileEndings) {
+				if (externalForm.endsWith(fileEnding))
+					return true;
+			}
+			return false;
+		}
+
+		public Vfs.Dir createDir(final URL url) throws Exception {
+			return emptyVfsDir(url);
+		}
+
+		private static Vfs.Dir emptyVfsDir(final URL url) {
+			return new Vfs.Dir() {
+				@Override
+				public String getPath() {
+					return url.toExternalForm();
+				}
+
+				@Override
+				public Iterable<Vfs.File> getFiles() {
+					return Collections.emptyList();
+				}
+
+				@Override
+				public void close() {
+					//
+				}
+			};
+		}
+	}
+
+	private static class JettyConsoleUrlType implements Vfs.UrlType {
+		public boolean matches(URL url) {
+			final String protocol = url.getProtocol();
+			final String externalForm = url.toExternalForm();
+			final boolean matches = protocol.equals("file") && externalForm.contains("jetty-console") && externalForm.contains("-any-") && externalForm.endsWith("webapp/WEB-INF/classes/");
+			return matches;
+		}
+
+		public Vfs.Dir createDir(final URL url) throws Exception {
+			return new SystemDir(getFile(url));
+		}
+
+		/**
+		 * try to get {@link java.io.File} from url
+		 *
+		 * <p>
+		 *     Copied from {@link Vfs} (not publicly accessible)
+		 * </p>
+		 */
+		static java.io.File getFile(URL url) {
+			java.io.File file;
+			String path;
+
+			try {
+				path = url.toURI().getSchemeSpecificPart();
+				if ((file = new java.io.File(path)).exists()) return file;
+			} catch (URISyntaxException e) {
+			}
+
+			try {
+				path = URLDecoder.decode(url.getPath(), "UTF-8");
+				if (path.contains(".jar!")) path = path.substring(0, path.lastIndexOf(".jar!") + ".jar".length());
+				if ((file = new java.io.File(path)).exists()) return file;
+
+			} catch (UnsupportedEncodingException e) {
+			}
+
+			try {
+				path = url.toExternalForm();
+				if (path.startsWith("jar:")) path = path.substring("jar:".length());
+				if (path.startsWith("file:")) path = path.substring("file:".length());
+				if (path.contains(".jar!")) path = path.substring(0, path.indexOf(".jar!") + ".jar".length());
+				if ((file = new java.io.File(path)).exists()) return file;
+
+				path = path.replace("%20", " ");
+				if ((file = new java.io.File(path)).exists()) return file;
+
+			} catch (Exception e) {
+			}
+
+			return null;
+		}
+	}
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/package-info.java b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/package-info.java
new file mode 100644
index 0000000..49537d2
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/internal/reflection/package-info.java
@@ -0,0 +1,28 @@
+/*
+ *  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.
+ */
+/**
+ * <h1>Internal API</h1>
+ * Internal classes, contributing to the internal proprietary API. 
+ * These may be changed or removed without notice!
+ * <p>
+ * <b>WARNING</b>: 
+ * Do NOT use any of the classes provided by this package!
+ * </p>
+ */
+package org.apache.isis.applib.internal.reflection;
\ No newline at end of file
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/classdiscovery/ClassDiscoveryServiceUsingReflections.java b/core/applib/src/main/java/org/apache/isis/applib/services/classdiscovery/ClassDiscoveryServiceUsingReflections.java
index 2ce5197..ff48a8e 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/classdiscovery/ClassDiscoveryServiceUsingReflections.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/classdiscovery/ClassDiscoveryServiceUsingReflections.java
@@ -19,20 +19,16 @@
 package org.apache.isis.applib.services.classdiscovery;
 
 import java.util.Collections;
-import java.util.List;
 import java.util.Set;
 
-import org.reflections.Reflections;
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.util.ClasspathHelper;
-import org.reflections.vfs.Vfs;
-
 import org.apache.isis.applib.AbstractService;
 import org.apache.isis.applib.AppManifest;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.fixturescripts.FixtureScript;
+import org.apache.isis.applib.internal.base._Casts;
+import org.apache.isis.applib.internal.reflection._Reflect;
 
 /**
  * This utility service supports the dynamic discovery of classes from the classpath.  One service that uses this
@@ -52,43 +48,24 @@ public class ClassDiscoveryServiceUsingReflections
 extends AbstractService 
 implements ClassDiscoveryService {
 
-
 	@Programmatic
 	@Override
-	public <T> Set<Class<? extends T>> findSubTypesOfClasses(Class<T> type, String packagePrefix) {
+	public <T> Set<Class<? extends T>> findSubTypesOfClasses(Class<T> type, String packageNamePrefix) {
 
 		if(type == FixtureScript.class) {
 			return getFixtureScriptTypes();
 		}
 
 		// no appManifest or not asking for FixtureScripts
-		Vfs.setDefaultURLTypes(getUrlTypes());
-
-		final Reflections reflections = new Reflections(
-				ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader()),
-				ClasspathHelper.forClass(Object.class),
-				ClasspathHelper.forPackage(packagePrefix),
-				new SubTypesScanner(false)
-				);
-		return reflections.getSubTypesOf(type);
-	}
-
-	// //////////////////////////////////////
-
-	/**
-	 * Has <tt>public</tt> visibility only so can be reused by other services (including Isis runtime itself).
-	 */
-	public static List<Vfs.UrlType> getUrlTypes() {
-		return AppManifest.Registry.instance().getUrlTypes();
+		return _Reflect.discoverFullscan(packageNamePrefix).getSubTypesOf(type);
 	}
 
 	// -- HELPER
 
-	@SuppressWarnings({ "unchecked", "rawtypes" })
 	private static <T> Set<Class<? extends T>> getFixtureScriptTypes() {
-		Set fixtureScriptTypes = AppManifest.Registry.instance().getFixtureScriptTypes();
+		Set<?> fixtureScriptTypes = AppManifest.Registry.instance().getFixtureScriptTypes();
 		if (fixtureScriptTypes != null) {
-			return fixtureScriptTypes;
+			return _Casts.uncheckedCast(fixtureScriptTypes);
 		}
 		return Collections.emptySet();
 	}
diff --git a/core/pom.xml b/core/pom.xml
index 967ff80..3a5ef0b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -116,6 +116,7 @@
         <objenesis.version>2.4</objenesis.version>
         <javassist.version>3.20.0-GA</javassist.version>
         <reflections.version>0.9.10</reflections.version>
+        <findbugs-annotations.version>2.0.1</findbugs-annotations.version>
 
         <commons-logging.version>1.2</commons-logging.version>
         <commons-cli.version>1.3.1</commons-cli.version>
@@ -1108,6 +1109,19 @@ ${license.additional-notes}
                 <type>test-jar</type>
                 <scope>test</scope>
             </dependency>
+            <!-- provides @Nullable -->
+	        <dependency>
+	        	<groupId>com.google.code.findbugs</groupId>
+	        	<artifactId>annotations</artifactId>
+	        	<version>${findbugs-annotations.version}</version>
+	        </dependency>
+            
+            <!--  reflections/applib -->
+            <dependency>
+                <groupId>org.reflections</groupId>
+                <artifactId>reflections</artifactId>
+                <version>${reflections.version}</version>
+            </dependency>
 
             <!-- schema -->
             <dependency>
@@ -1392,13 +1406,6 @@ ${license.additional-notes}
                 <version>${picocontainer.version}</version>
             </dependency>
 
-            <!--  Reflection/metadata -->
-            <dependency>
-                <groupId>org.reflections</groupId>
-                <artifactId>reflections</artifactId>
-                <version>${reflections.version}</version>
-            </dependency>
-
             <!-- Bytecode libraries -->
             <dependency>
                 <groupId>org.objenesis</groupId>
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
index 5a2ca5a..1d0a0f3 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/ServicesInstallerFromAnnotation.java
@@ -19,6 +19,9 @@
 
 package org.apache.isis.core.runtime.services;
 
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.not;
+
 import java.lang.reflect.Modifier;
 import java.util.List;
 import java.util.Set;
@@ -27,6 +30,17 @@ import java.util.SortedSet;
 
 import javax.annotation.PreDestroy;
 
+import org.apache.isis.applib.AppManifest;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.internal.reflection._Reflect;
+import org.apache.isis.applib.internal.reflection._Reflect.Discovery;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceMenuOrder;
+import org.apache.isis.core.metamodel.util.DeweyOrderComparator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
@@ -36,22 +50,6 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import org.reflections.Reflections;
-import org.reflections.vfs.Vfs;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.AppManifest;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.DomainServiceLayout;
-import org.apache.isis.applib.services.classdiscovery.ClassDiscoveryServiceUsingReflections;
-import org.apache.isis.core.commons.config.IsisConfigurationDefault;
-import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceMenuOrder;
-import org.apache.isis.core.metamodel.util.DeweyOrderComparator;
-
-import static com.google.common.base.Predicates.and;
-import static com.google.common.base.Predicates.not;
-
 public class ServicesInstallerFromAnnotation extends ServicesInstallerAbstract {
 
     //region > constants
@@ -213,9 +211,9 @@ public class ServicesInstallerFromAnnotation extends ServicesInstallerAbstract {
         Set<Class<?>> domainServiceTypes = AppManifest.Registry.instance().getDomainServiceTypes();
         if(domainServiceTypes == null) {
             // if no appManifest
-            Vfs.setDefaultURLTypes(ClassDiscoveryServiceUsingReflections.getUrlTypes());
-            final Reflections reflections = new Reflections(packagePrefixList);
-            domainServiceTypes = reflections.getTypesAnnotatedWith(DomainService.class);
+        	final Discovery discovery = _Reflect.discover(packagePrefixList);
+        	
+        	domainServiceTypes = discovery.getTypesAnnotatedWith(DomainService.class);
         }
 
         final List<Class<?>> domainServiceClasses = Lists.newArrayList(Iterables.filter(domainServiceTypes, instantiatable()));
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
index 890e831..70c0acb 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/systemusinginstallers/IsisComponentProvider.java
@@ -28,23 +28,14 @@ import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import javax.jdo.annotations.PersistenceCapable;
 
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import org.reflections.Reflections;
-import org.reflections.vfs.Vfs;
-
 import org.apache.isis.applib.AppManifest;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Mixin;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.fixturescripts.FixtureScript;
-import org.apache.isis.applib.services.classdiscovery.ClassDiscoveryServiceUsingReflections;
+import org.apache.isis.applib.internal.reflection._Reflect;
+import org.apache.isis.applib.internal.reflection._Reflect.Discovery;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.config.IsisConfigurationDefault;
 import org.apache.isis.core.commons.factory.InstanceUtil;
@@ -67,6 +58,13 @@ import org.apache.isis.objectstore.jdo.service.RegisterEntities;
 import org.apache.isis.progmodels.dflt.JavaReflectorHelper;
 import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5;
 
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
 /**
  * 
  */
@@ -137,24 +135,23 @@ public abstract class IsisComponentProvider {
         moduleAndFrameworkPackages.addAll(AppManifest.Registry.FRAMEWORK_PROVIDED_SERVICES);
         Iterables.addAll(moduleAndFrameworkPackages, modulePackages);
 
-        Vfs.setDefaultURLTypes(ClassDiscoveryServiceUsingReflections.getUrlTypes());
-
-        final Reflections reflections = new Reflections(moduleAndFrameworkPackages);
+        final Discovery discovery = _Reflect.discover(moduleAndFrameworkPackages);
 
-        final Set<Class<?>> domainServiceTypes = reflections.getTypesAnnotatedWith(DomainService.class);
-        final Set<Class<?>> persistenceCapableTypes = reflections.getTypesAnnotatedWith(PersistenceCapable.class);
-        final Set<Class<? extends FixtureScript>> fixtureScriptTypes = reflections.getSubTypesOf(FixtureScript.class);
+        final Set<Class<?>> domainServiceTypes = discovery.getTypesAnnotatedWith(DomainService.class);
+        final Set<Class<?>> persistenceCapableTypes = discovery.getTypesAnnotatedWith(PersistenceCapable.class);
+        final Set<Class<? extends FixtureScript>> fixtureScriptTypes = discovery.getSubTypesOf(FixtureScript.class);
 
         final Set<Class<?>> mixinTypes = Sets.newHashSet();
-        mixinTypes.addAll(reflections.getTypesAnnotatedWith(Mixin.class));
+        mixinTypes.addAll(discovery.getTypesAnnotatedWith(Mixin.class));
 
-        final Set<Class<?>> domainObjectTypes = reflections.getTypesAnnotatedWith(DomainObject.class);
+        final Set<Class<?>> domainObjectTypes = discovery.getTypesAnnotatedWith(DomainObject.class);
         mixinTypes.addAll(
                 domainObjectTypes.stream()
                         .filter(input -> input.getAnnotation(DomainObject.class).nature() == Nature.MIXIN)
                         .collect(Collectors.toList())
         );
-
+        
+        
         // add in any explicitly registered services...
         domainServiceTypes.addAll(appManifest.getAdditionalServices());
 
@@ -176,6 +173,7 @@ public abstract class IsisComponentProvider {
         registry.setPersistenceCapableTypes(within(packagesWithDotSuffix, persistenceCapableTypes));
         registry.setFixtureScriptTypes(within(packagesWithDotSuffix, fixtureScriptTypes));
         registry.setMixinTypes(within(packagesWithDotSuffix, mixinTypes));
+
     }
 
     static <T> Set<Class<? extends T>> within(
diff --git a/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/service/RegisterEntities.java b/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/service/RegisterEntities.java
index 538b5b1..55598c8 100644
--- a/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/service/RegisterEntities.java
+++ b/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/service/RegisterEntities.java
@@ -25,22 +25,21 @@ import java.util.Set;
 
 import javax.jdo.annotations.PersistenceCapable;
 
+import org.apache.isis.applib.AppManifest;
+import org.apache.isis.applib.internal.reflection._Reflect;
+import org.apache.isis.applib.internal.reflection._Reflect.Discovery;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
-import org.reflections.Reflections;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.AppManifest;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-
 public class RegisterEntities {
 
     @SuppressWarnings("unused")
@@ -96,11 +95,13 @@ public class RegisterEntities {
         final Set<Class<?>> persistenceCapableTypes = Sets.newLinkedHashSet();
         final List<String> domPackages = parseDomPackages(packagePrefixes);
         for (final String packageName : domPackages) {
-            Reflections reflections = new Reflections(packageName);
+        	
+        	final Discovery dicovery = _Reflect.discover(packageName);
+        	
             final Set<Class<?>> entityTypesInPackage =
-                    reflections.getTypesAnnotatedWith(PersistenceCapable.class);
+            		dicovery.getTypesAnnotatedWith(PersistenceCapable.class);
 
-            if(!entitiesIn(entityTypesInPackage)) {
+            if(entityTypesInPackage.isEmpty()) {
                 throw new IllegalArgumentException(String.format(
                         "Bad configuration.\n\nCould not locate any @PersistenceCapable entities in package '%s'\n" +
                                 "Check value of '%s' key in WEB-INF/*.properties\n",
@@ -147,22 +148,6 @@ public class RegisterEntities {
         };
     }
 
-    private static boolean entitiesIn(Set<Class<?>> entityTypes) {
-        return Iterables.filter(entityTypes, notNullClass()).iterator().hasNext();
-    }
-
-    /**
-     * {@link Reflections} seems to return a set with 1 null element if none can be found, so we ignore these.
-     */
-    private static <T> Predicate<T> notNullClass() {
-        return new Predicate<T>() {
-            @Override
-            public boolean apply(T input) {
-                return input != null;
-            }
-        };
-    }
-
     // //////////////////////////////////////
 
     SpecificationLoader getSpecificationLoader() {
diff --git a/core/unittestsupport/pom.xml b/core/unittestsupport/pom.xml
index 6f8c59b..72894a8 100644
--- a/core/unittestsupport/pom.xml
+++ b/core/unittestsupport/pom.xml
@@ -87,6 +87,7 @@
                         <artifactId>xml-apis</artifactId>
                     </exclusion>
                 </exclusions>
+                <optional>true</optional>
             </dependency>
 
 	</dependencies>

-- 
To stop receiving notification emails like this one, please contact
ahuber@apache.org.