@Override public DioritePlugin loadPlugin(final File file) throws PluginException { try { final PluginClassLoader classLoader = new PluginClassLoader(file); final ConfigurationBuilder config = new ConfigurationBuilder(); config.setClassLoaders(new PluginClassLoader[] {classLoader}); config.setUrls(ClasspathHelper.forClassLoader(classLoader)); final Reflections ref = new Reflections(config); final Set<Class<?>> annotated = ref.getTypesAnnotatedWith(Plugin.class); if (annotated.isEmpty()) { throw new PluginException("Plugin annotation doesn't found!"); } if (annotated.size() > 1) { throw new PluginException("Plugin has more than one main class!"); } final Class<?> mainClass = annotated.iterator().next(); if (!DioritePlugin.class.isAssignableFrom(mainClass)) { throw new PluginException("Main class must extend PluginMainClass!"); } final DioritePlugin dioritePlugin = (DioritePlugin) mainClass.newInstance(); final Plugin pluginDescription = mainClass.getAnnotation(Plugin.class); if (ServerImpl.getInstance().getPluginManager().getPlugin(pluginDescription.name()) != null) { throw new PluginException("Plugin " + pluginDescription.name() + " is arleady loaded!"); } dioritePlugin.init( classLoader, this, dioritePlugin, pluginDescription.name(), pluginDescription.version(), pluginDescription.author(), pluginDescription.description(), pluginDescription.website()); System.out.println( "Loading " + pluginDescription.name() + " v" + pluginDescription.version() + " by " + pluginDescription.author() + " from file " + file.getName()); dioritePlugin.onLoad(); return dioritePlugin; } catch (final InstantiationException | IllegalAccessException | MalformedURLException e) { throw new PluginException("Exception while loading plugin from file " + file.getName(), e); } }
@Programmatic @Override public <T> Set<Class<? extends T>> findSubTypesOfClasses(Class<T> type) { Vfs.setDefaultURLTypes(getUrlTypes()); final Reflections reflections = new Reflections( ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader()), ClasspathHelper.forClass(Object.class), new SubTypesScanner(false)); return reflections.getSubTypesOf(type); }
/* * Scan the classpath looking for JAX-RS Application sub-classes */ private static Set<Class<? extends Application>> findJaxrsApplicationClasses() { logger.info("Scanning classpath to find JAX-RS Application classes"); final Collection<URL> systemPropertyURLs = ClasspathHelper.forJavaClassPath(); final Collection<URL> classLoaderURLs = ClasspathHelper.forClassLoader(); Set<URL> classpathURLs = new HashSet<URL>(); copyValidClasspathEntries(systemPropertyURLs, classpathURLs); copyValidClasspathEntries(classLoaderURLs, classpathURLs); logger.debug("Classpath URLs to be scanned: " + classpathURLs); Reflections reflections = new Reflections(classpathURLs, new SubTypesScanner()); return reflections.getSubTypesOf(Application.class); }
private void guess( Map<String, DefaultCoreExtension> extensions, DefaultCoreExtensionRepository repository) { Set<ExtensionDependency> dependencies = new HashSet<ExtensionDependency>(); for (DefaultCoreExtension coreExtension : extensions.values()) { for (ExtensionDependency dependency : coreExtension.getDependencies()) { dependencies.add(dependency); } } // Normalize and guess Map<String, Object[]> fileNames = new HashMap<String, Object[]>(); Map<String, Object[]> guessedArtefacts = new HashMap<String, Object[]>(); Set<URL> urls = ClasspathHelper.forClassLoader(); for (URL url : urls) { try { String path = url.toURI().getPath(); String filename = path.substring(path.lastIndexOf('/') + 1); String type = null; int extIndex = filename.lastIndexOf('.'); if (extIndex != -1) { type = filename.substring(extIndex + 1); filename = filename.substring(0, extIndex); } int index; if (!filename.endsWith(SNAPSHOTSUFFIX)) { index = filename.lastIndexOf('-'); } else { index = filename.lastIndexOf('-', filename.length() - SNAPSHOTSUFFIX.length()); } if (index != -1) { fileNames.put(filename, new Object[] {url}); String artefactname = filename.substring(0, index); String version = filename.substring(index + 1); guessedArtefacts.put(artefactname, new Object[] {version, url, type}); } } catch (Exception e) { this.logger.warn("Failed to parse resource name [" + url + "]", e); } } // Try to resolve version no easy to find from the pom.xml try { for (DefaultCoreExtension coreExtension : extensions.values()) { String artifactId = getArtifactId(coreExtension); Object[] artefact = guessedArtefacts.get(artifactId); if (artefact != null) { if (coreExtension.getId().getVersion().getValue().charAt(0) == '$') { coreExtension.setId( new ExtensionId(coreExtension.getId().getId(), (String) artefact[0])); coreExtension.setGuessed(true); } if (coreExtension.getType().charAt(0) == '$') { coreExtension.setType((String) artefact[2]); coreExtension.setGuessed(true); } } } // Add dependencies that does not provide proper pom.xml resource and can't be found in the // classpath for (ExtensionDependency extensionDependency : dependencies) { Dependency dependency = (Dependency) extensionDependency.getProperty(MavenCoreExtensionDependency.PKEY_MAVEN_DEPENDENCY); if (dependency == null) { dependency = toDependency( extensionDependency.getId(), extensionDependency.getVersionConstraint().getValue(), null); } String dependencyId = dependency.getGroupId() + ':' + dependency.getArtifactId(); DefaultCoreExtension coreExtension = extensions.get(dependencyId); if (coreExtension == null) { String dependencyFileName = dependency.getArtifactId() + '-' + dependency.getVersion(); if (dependency.getClassifier() != null) { dependencyFileName += '-' + dependency.getClassifier(); dependencyId += ':' + dependency.getClassifier(); } Object[] filenameArtifact = fileNames.get(dependencyFileName); Object[] guessedArtefact = guessedArtefacts.get(dependency.getArtifactId()); if (filenameArtifact != null) { coreExtension = new DefaultCoreExtension( repository, (URL) filenameArtifact[0], new ExtensionId(dependencyId, dependency.getVersion()), packagingToType(dependency.getType())); coreExtension.setGuessed(true); } else if (guessedArtefact != null) { coreExtension = new DefaultCoreExtension( repository, (URL) guessedArtefact[1], new ExtensionId(dependencyId, (String) guessedArtefact[0]), packagingToType(dependency.getType())); coreExtension.setGuessed(true); } if (coreExtension != null) { extensions.put(dependencyId, coreExtension); } } } } catch (Exception e) { this.logger.warn("Failed to guess extra information about some extensions", e); } }
/** @author Nils Olsson */ public final class TestExecutor implements Executor { private static final Reflections reflections = new Reflections( new ConfigurationBuilder() .addUrls(filter(ClasspathHelper.forJavaClassPath(), ClasspathHelper.forClassLoader())) .addScanners(new SubTypesScanner(), new TypeAnnotationsScanner())); private static Collection<URL> filter(Collection<URL> classPath, Collection<URL> classLoader) { Reflections.log = null; List<URL> urls = new ArrayList<>(), filteredUrls = new ArrayList<>(); urls.addAll(classPath); urls.addAll(classLoader); for (URL url : urls) { if (!filteredUrls.contains(url) && new File(url.getFile()).exists()) { filteredUrls.add(url); } } return filteredUrls; } private final Configuration configuration; private final MachineConfiguration machineConfiguration; private final Map<Context, MachineException> failures = new HashMap<>(); private final Machine machine; private Result result; public TestExecutor(Configuration configuration) { this.configuration = configuration; this.machineConfiguration = createMachineConfiguration(AnnotationUtils.findTests(reflections)); this.machine = createMachine(machineConfiguration); } public TestExecutor(Class<?>... tests) { this.configuration = new Configuration(); this.machineConfiguration = createMachineConfiguration(Arrays.asList(tests)); this.machine = createMachine(machineConfiguration); } public TestExecutor(Context... contexts) { this.configuration = new Configuration(); this.machineConfiguration = new MachineConfiguration(); this.machine = new SimpleMachine(contexts); } public TestExecutor(Collection<Context> contexts) { this.configuration = new Configuration(); this.machineConfiguration = new MachineConfiguration(); this.machine = new SimpleMachine(contexts); } @Override public Machine getMachine() { return machine; } private MachineConfiguration createMachineConfiguration(Collection<Class<?>> testClasses) { MachineConfiguration machineConfiguration = new MachineConfiguration(); for (Class<?> testClass : testClasses) { GraphWalker annotation = testClass.getAnnotation(GraphWalker.class); if (isTestIncluded(annotation, testClass.getName())) { ContextConfiguration contextConfiguration = new ContextConfiguration(); contextConfiguration.setTestClass(testClass); machineConfiguration.addContextConfiguration(contextConfiguration); } } return machineConfiguration; } private Collection<Context> createContexts(MachineConfiguration machineConfiguration) { Set<Context> contexts = new HashSet<>(); for (ContextConfiguration contextConfiguration : machineConfiguration.getContextConfigurations()) { Context context = createContext(contextConfiguration.getTestClass()); configureContext(context); contexts.add(context); } return contexts; } private Context createContext(Class<?> testClass) { try { return (Context) testClass.newInstance(); } catch (Throwable e) { throw new TestExecutionException("Failed to create context"); } } private void configureContext(Context context) { Set<Model> models = AnnotationUtils.getAnnotations(context.getClass(), Model.class); GraphWalker annotation = context.getClass().getAnnotation(GraphWalker.class); if (!models.isEmpty()) { Path path = Paths.get(models.iterator().next().file()); ContextFactoryScanner.get(reflections, path).create(path, context); } if (!"".equals(annotation.value())) { context.setPathGenerator(GeneratorFactory.parse(annotation.value())); } else { context.setPathGenerator(PathGeneratorFactory.createPathGenerator(annotation)); } if (!"".equals(annotation.start())) { context.setNextElement(getElement(context.getModel(), annotation.start())); } } private Machine createMachine(MachineConfiguration machineConfiguration) { Collection<Context> contexts = createContexts(machineConfiguration); Machine machine = new SimpleMachine(contexts); for (Context context : machine.getContexts()) { if (context instanceof Observer) { machine.addObserver((Observer) context); } } return machine; } @Override public MachineConfiguration getMachineConfiguration() { return machineConfiguration; } @Override public Result execute() { return execute(false); } @Override public Result execute(boolean ignoreErrors) { result = new Result(machine.getContexts().size()); executeAnnotation(BeforeExecution.class, machine); try { Context context = null; while (machine.hasNextStep()) { if (null != context) { executeAnnotation(BeforeElement.class, context); } context = machine.getNextStep(); executeAnnotation(AfterElement.class, context); } } catch (MachineException e) { failures.put(e.getContext(), e); } executeAnnotation(AfterExecution.class, machine); updateResult(result, machine); if (!ignoreErrors && !failures.isEmpty()) { throw new TestExecutionException("Test execution contains failures"); } return result; } @Override public Result getResult() { return result; } private void updateResult(Result result, Machine machine) { int completed = 0, failed = 0, notExecuted = 0, incomplete = 0; for (Context context : machine.getContexts()) { switch (context.getExecutionStatus()) { case COMPLETED: { completed++; } break; case FAILED: { failed++; } break; case NOT_EXECUTED: { notExecuted++; } break; case EXECUTING: { incomplete++; } } } result.setCompletedCount(completed); result.setFailedCount(failed); result.setNotExecutedCount(notExecuted); result.setIncompleteCount(incomplete); for (MachineException exception : getFailures()) { result.addError(getStackTrace(exception.getCause())); } } private String getStackTrace(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer, true)); return writer.getBuffer().toString(); } private boolean isTestIncluded(GraphWalker annotation, String name) { boolean belongsToGroup = false; for (String group : annotation.groups()) { for (String definedGroups : configuration.getGroups()) { if (SelectorUtils.match(definedGroups, group)) { belongsToGroup = true; break; } } } if (belongsToGroup) { for (String exclude : configuration.getExcludes()) { if (SelectorUtils.match(exclude, name)) { return false; } } for (String include : configuration.getIncludes()) { if (SelectorUtils.match(include, name)) { return true; } } } return false; } private Element getElement(RuntimeModel model, String name) { List<Element> elements = model.findElements(name); if (null == elements || 0 == elements.size()) { throw new TestExecutionException("Start element not found"); } if (1 < elements.size()) { throw new TestExecutionException("Ambiguous start element defined"); } return elements.get(0); } private void executeAnnotation(Class<? extends Annotation> annotation, Machine machine) { for (Context context : machine.getContexts()) { executeAnnotation(annotation, context); } } private void executeAnnotation(Class<? extends Annotation> annotation, Context context) { AnnotationUtils.execute(annotation, context); } @Override public boolean isFailure(Context context) { return failures.containsKey(context); } @Override public MachineException getFailure(Context context) { return failures.get(context); } @Override public Collection<MachineException> getFailures() { return failures.values(); } public void reportResults(File file, Date startTime, Properties properties) { new XMLReportGenerator(startTime, properties).writeReport(file, this); if (0 < getFailures().size()) { throw new TestExecutionException( MessageFormat.format( "There are test failures.\n\n Please refer to {0} for the individual test results.", file.getAbsolutePath())); } } }
@Override public void beforeStart(final Application application) { final Reflections reflections = new Reflections( new ConfigurationBuilder() .filterInputsBy(new FilterBuilder.Exclude(FilterBuilder.prefix("com.google"))) .addUrls(ClasspathHelper.forClassLoader(application.classloader())) .addScanners(new SubTypesScanner())); // automatic Guice module detection Set<Class<? extends AbstractModule>> guiceModules = reflections.getSubTypesOf(AbstractModule.class); for (Class<? extends Module> moduleClass : guiceModules) { try { if (!moduleClass.isAnonymousClass()) { modules.add(moduleClass.newInstance()); } } catch (InstantiationException e) { throw Throwables.propagate(e); } catch (IllegalAccessException e) { throw Throwables.propagate(e); } } modules.add( new AbstractModule() { @Override protected void configure() { bind(Application.class).toInstance(application); bind(Reflections.class).toInstance(reflections); Names.bindProperties( this.binder(), fromKeys( application.configuration().keys(), new Function<String, String>() { @Override public String apply(String key) { // remove after https://play.lighthouseapp.com/projects/82401/tickets/372 is // fixed if (key.contains("akka")) return null; return application.configuration().getString(key); } })); for (Class<? extends Controller> controllerClass : reflections.getSubTypesOf(Controller.class)) { requestStaticInjection(controllerClass); } // bind all services Multibinder<Service> serviceBinder = Multibinder.newSetBinder(binder(), Service.class); for (Class<? extends Service> serviceImplClass : reflections.getSubTypesOf(AbstractService.class)) { serviceBinder.addBinding().to(serviceImplClass).asEagerSingleton(); } for (Class<? extends Service> serviceImplClass : reflections.getSubTypesOf(AbstractIdleService.class)) { serviceBinder.addBinding().to(serviceImplClass).asEagerSingleton(); } for (Class<? extends Service> serviceImplClass : reflections.getSubTypesOf(AbstractExecutionThreadService.class)) { serviceBinder.addBinding().to(serviceImplClass).asEagerSingleton(); } // bind actor - todo use reflections for this // start/stop services after injection and on shutdown of the Play app bindListener( MoreMatchers.subclassesOf(Service.class), new TypeListener() { @Override public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) { typeEncounter.register( new InjectionListener<I>() { @Override public void afterInjection(final I i) { onStartListeners.add( new OnStartListener() { @Override public void onApplicationStart( Application application, Injector injector) { Logger.info(String.format("Starting %s", i.toString())); ((Service) i).start(); onStopListeners.add( new OnStopListener() { @Override public void onApplicationStop(Application application) { Logger.info(String.format("Stopping %s", i.toString())); ((Service) i).stop(); } }); } }); } }); } }); } }); }