From e447fa23f6d880d7ffbda6782077be7284ef85f1 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Fri, 30 Jul 2021 10:33:55 -0500 Subject: [PATCH 1/8] Remove complex Discoverer methods --- .../org/scijava/ops/discovery/Discoverer.java | 4 -- .../ops/discovery/StaticDiscoverer.java | 28 ------------- .../ops/engine/impl/DefaultOpEnvironment.java | 34 +++++++++++---- .../ops/engine/impl/DefaultOpService.java | 41 +------------------ 4 files changed, 28 insertions(+), 79 deletions(-) diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java index 52dcdc50c..9dcbfe14e 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java +++ b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java @@ -7,8 +7,4 @@ public interface Discoverer { List> implementingClasses(Class c); - List implementingInstances(Class c, Class[] constructorClasses, Object[] constructorArgs); - - List> implementationsOf(Class c); - } diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java index d6038f6e9..e71395ed7 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java +++ b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java @@ -27,32 +27,4 @@ public List> implementingClasses(Class c) { .collect(Collectors.toList()); } - @Override - public List implementingInstances(Class c, - Class[] constructorClasses, Object[] constructorArgs) - { - return implementingClasses(c).stream() // - .map(cls -> classToObjectOrNull(cls, constructorClasses, constructorArgs)) // - .filter(o -> o != null) // - .collect(Collectors.toList()); - } - - private T classToObjectOrNull(Class c, Class[] constructorClasses, Object[] constructorArgs) { - try { - return c.getDeclaredConstructor(constructorClasses).newInstance( - constructorArgs); - } - catch (Throwable t) - { - return null; - } - } - - @Override - public List> implementationsOf(Class c) { - return implementingClasses(c).stream() // - .map(cls -> new Implementation<>(cls, c, names.get(cls))) // - .collect(Collectors.toList()); - } - } diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java index c8a153870..b5ad3ed97 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java @@ -53,6 +53,7 @@ import org.scijava.ops.api.OpCandidate.StatusCode; import org.scijava.ops.api.OpDependencyMember; import org.scijava.ops.api.OpEnvironment; +import org.scijava.ops.api.OpHistory; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpRef; @@ -92,7 +93,7 @@ */ public class DefaultOpEnvironment implements OpEnvironment { - private final Discoverer discoverer; + private final List discoverers; private OpMatcher matcher; @@ -137,8 +138,17 @@ public class DefaultOpEnvironment implements OpEnvironment { */ private Hints environmentHints = null; - public DefaultOpEnvironment(final Discoverer d, final TypeService typeService, final LogService log, final OpHistoryService history, final List infoGenerators) { - this.discoverer = d; + public DefaultOpEnvironment(final TypeService typeService, final LogService log, final OpHistoryService history, final List infoGenerators, final List d) { + this.discoverers = d; + this.typeService = typeService; + this.log = log; + this.history = history; + this.infoGenerators = infoGenerators; + matcher = new DefaultOpMatcher(); + } + + public DefaultOpEnvironment(final TypeService typeService, final LogService log, final OpHistoryService history, final List infoGenerators, final Discoverer... d) { + this.discoverers = Arrays.asList(d); this.typeService = typeService; this.log = log; this.history = history; @@ -486,11 +496,19 @@ private List resolveOpDependencies(OpCandidate candidate, Hi private void initWrappers() { wrappers = new HashMap<>(); - Class[] constructorClasses = {}; - Object[] constructorObjects = {}; - for (OpWrapper wrapper : discoverer.implementingInstances(OpWrapper.class, constructorClasses, constructorObjects)) { - wrappers.put(wrapper.type(), wrapper); - } + for (Discoverer d : discoverers) + for (Class cls : d.implementingClasses(OpWrapper.class)) + { + OpWrapper wrapper; + try { + wrapper = cls.getDeclaredConstructor().newInstance(); + wrappers.put(wrapper.type(), wrapper); + } + catch (Throwable t) + { + log.warn("OpWrapper " + cls + " not instantiated. Due to " + t); + } + } } /** diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java index 3a5cf2dfb..8c8cf3359 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java @@ -95,8 +95,8 @@ private synchronized void initEnv() { List infoGenerators = Arrays.asList( new PluginBasedClassOpInfoGenerator(plugins), new PluginBasedOpCollectionInfoGenerator(plugins)); - env = new DefaultOpEnvironment(new PluginBasedDiscoverer(context()), types, - log, history, infoGenerators); + env = new DefaultOpEnvironment(types, log, history, + infoGenerators, new PluginBasedDiscoverer(context())); } } @@ -122,36 +122,6 @@ public List> implementingClasses(Class c) { .filter(cls -> cls != null).collect(Collectors.toList()); } - @Override - @SuppressWarnings("unchecked") - public List implementingInstances(Class c, - Class[] constructorClasses, Object[] constructorArgs) - { - if (!SciJavaPlugin.class.isAssignableFrom(c)) { - throw new UnsupportedOperationException( - "Current discovery mechanism tied to SciJava Context; only able to search for SciJavaPlugins"); - } - List instances = p.createInstancesOfType( - (Class) c); - return instances.stream().map(instance -> (T) instance).collect(Collectors - .toList()); - } - - @Override - @SuppressWarnings("unchecked") - public List> implementationsOf(Class c) { - if (!SciJavaPlugin.class.isAssignableFrom(c)) { - throw new UnsupportedOperationException( - "Current discovery mechanism tied to SciJava Context; only able to search for SciJavaPlugins"); - } - List> instances = p.getPluginsOfType( - (Class) c); - return instances.stream() // - .map(instance -> makeDiscoveryOrNull(c, instance)) // - .filter(d -> d.implementation() != null) // - .collect(Collectors.toList()); - } - @SuppressWarnings("unchecked") private Class makeClassOrNull(@SuppressWarnings("unused") Class type, PluginInfo instance) @@ -164,11 +134,4 @@ private Class makeClassOrNull(@SuppressWarnings("unused") Class type, } } - private Implementation makeDiscoveryOrNull(Class type, - PluginInfo instance) - { - return new Implementation<>(makeClassOrNull(type, instance), type, instance - .getName()); - } - } From 52ca72f58dd42d0481e6121dd973efbb899ef95c Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Fri, 30 Jul 2021 15:36:55 -0500 Subject: [PATCH 2/8] Create SciJava Ops ServiceLoader module Adds a Discoverer responsible for looking up implementations via ServiceLoader --- pom.xml | 1 + .../org/scijava/ops/discovery/Discoverer.java | 2 +- .../ops/discovery/StaticDiscoverer.java | 2 +- .../ops/engine/impl/ClassOpInfoGenerator.java | 38 +++ .../ops/engine/impl/DefaultOpService.java | 12 +- ...or.java => OpCollectionInfoGenerator.java} | 41 +-- .../impl/PluginBasedClassOpInfoGenerator.java | 42 ---- scijava/scijava-ops-serviceloader/.gitignore | 2 + scijava/scijava-ops-serviceloader/LICENSE.txt | 24 ++ scijava/scijava-ops-serviceloader/README.md | 5 + .../bin/generate.groovy | 238 ++++++++++++++++++ scijava/scijava-ops-serviceloader/pom.xml | 128 ++++++++++ .../src/main/java/module-info.java | 12 + .../ops/serviceloader/ServiceBasedAdder.java | 19 ++ .../ServiceLoaderDiscoverer.java | 19 ++ .../ServiceLoaderDiscovererTest.java | 22 ++ .../java/org/scijava/ops/spi/OpClass.java | 23 ++ 17 files changed, 563 insertions(+), 67 deletions(-) create mode 100644 scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java rename scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/{PluginBasedOpCollectionInfoGenerator.java => OpCollectionInfoGenerator.java} (55%) delete mode 100644 scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java create mode 100644 scijava/scijava-ops-serviceloader/.gitignore create mode 100644 scijava/scijava-ops-serviceloader/LICENSE.txt create mode 100644 scijava/scijava-ops-serviceloader/README.md create mode 100644 scijava/scijava-ops-serviceloader/bin/generate.groovy create mode 100644 scijava/scijava-ops-serviceloader/pom.xml create mode 100644 scijava/scijava-ops-serviceloader/src/main/java/module-info.java create mode 100644 scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedAdder.java create mode 100644 scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java create mode 100644 scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java create mode 100644 scijava/scijava-ops-spi/src/main/java/org/scijava/ops/spi/OpClass.java diff --git a/pom.xml b/pom.xml index c8fee2714..e6d4035d8 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ scijava/scijava-ops-api scijava/scijava-ops-discovery scijava/scijava-ops-engine + scijava/scijava-ops-serviceloader scijava/scijava-ops-spi scijava/scijava-struct scijava/scijava-taglets diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java index 9dcbfe14e..b9bf5764f 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java +++ b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java @@ -5,6 +5,6 @@ public interface Discoverer { - List> implementingClasses(Class c); + List> implementingClasses(Class c); } diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java index e71395ed7..014ff23f6 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java +++ b/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java @@ -20,7 +20,7 @@ public void register(Class c, String name) { @SuppressWarnings("unchecked") @Override - public List> implementingClasses(Class c) { + public List> implementingClasses(Class c) { return names.keySet().stream() // .filter(cls -> cls.isAssignableFrom(c)) // .map(cls -> (Class) cls) // diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java new file mode 100644 index 000000000..7cbc00731 --- /dev/null +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java @@ -0,0 +1,38 @@ + +package org.scijava.ops.engine.impl; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.scijava.ops.api.OpInfo; +import org.scijava.ops.api.OpInfoGenerator; +import org.scijava.ops.api.OpUtils; +import org.scijava.ops.discovery.Discoverer; +import org.scijava.ops.engine.matcher.impl.OpClassInfo; +import org.scijava.ops.spi.Op; +import org.scijava.plugin.Plugin; + +public class ClassOpInfoGenerator implements OpInfoGenerator { + + private final List discoverers; + + public ClassOpInfoGenerator(Discoverer... d) { + this.discoverers = Arrays.asList(d); + } + + @Override + public List generateInfos() { + List infos = discoverers.stream() // + .flatMap(d -> d.implementingClasses(Op.class).stream()) // + .filter(cls -> cls.getAnnotation(Plugin.class) != null) // + .map(cls -> { + Plugin p = cls.getAnnotation(Plugin.class); + String[] parsedOpNames = OpUtils.parseOpNames(p.name()); + return new OpClassInfo(cls, parsedOpNames); + }) // + .collect(Collectors.toList()); + return infos; + } + +} diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java index 8c8cf3359..0a59dcbc6 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java @@ -40,7 +40,6 @@ import org.scijava.ops.api.OpEnvironment; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.discovery.Discoverer; -import org.scijava.ops.discovery.Implementation; import org.scijava.ops.engine.OpHistoryService; import org.scijava.ops.engine.OpService; import org.scijava.plugin.Plugin; @@ -88,15 +87,14 @@ public OpEnvironment env() { private synchronized void initEnv() { if (env != null) return; - PluginService plugins = context().getService(PluginService.class); LogService log = context().getService(LogService.class); TypeService types = context().getService(TypeService.class); OpHistoryService history = context().getService(OpHistoryService.class); + Discoverer d = new PluginBasedDiscoverer(context()); List infoGenerators = Arrays.asList( - new PluginBasedClassOpInfoGenerator(plugins), - new PluginBasedOpCollectionInfoGenerator(plugins)); - env = new DefaultOpEnvironment(types, log, history, - infoGenerators, new PluginBasedDiscoverer(context())); + new ClassOpInfoGenerator(d), + new OpCollectionInfoGenerator(d)); + env = new DefaultOpEnvironment(types, log, history, infoGenerators, d); } } @@ -110,7 +108,7 @@ public PluginBasedDiscoverer(Context ctx) { @Override @SuppressWarnings("unchecked") - public List> implementingClasses(Class c) { + public List> implementingClasses(Class c) { if (!SciJavaPlugin.class.isAssignableFrom(c)) { throw new UnsupportedOperationException( "Current discovery mechanism tied to SciJava Context; only able to search for SciJavaPlugins"); diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedOpCollectionInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java similarity index 55% rename from scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedOpCollectionInfoGenerator.java rename to scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java index c4334cf05..dc0b7e6f9 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedOpCollectionInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java @@ -4,37 +4,41 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.scijava.InstantiableException; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpUtils; +import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.matcher.impl.OpFieldInfo; import org.scijava.ops.engine.matcher.impl.OpMethodInfo; import org.scijava.ops.spi.OpCollection; import org.scijava.ops.spi.OpField; import org.scijava.ops.spi.OpMethod; -import org.scijava.plugin.PluginInfo; -import org.scijava.plugin.PluginService; +import org.scijava.plugin.Plugin; import org.scijava.util.ClassUtils; -public class PluginBasedOpCollectionInfoGenerator implements OpInfoGenerator { +public class OpCollectionInfoGenerator implements OpInfoGenerator { - private final PluginService service; + private final List discoverers; - public PluginBasedOpCollectionInfoGenerator(PluginService service) { - this.service = service; + public OpCollectionInfoGenerator(Discoverer... d) { + this.discoverers = Arrays.asList(d); } @Override public List generateInfos() { - List infos = new ArrayList<>(); - for (PluginInfo info : service.getPluginsOfType(OpCollection.class) ) { + List infos = discoverers.stream() // + .flatMap(d -> d.implementingClasses(OpCollection.class).stream()) // + .filter(cls -> cls.getAnnotation(Plugin.class) != null) // + .map(cls -> { try { - Class c = info.loadClass(); - final List fields = ClassUtils.getAnnotatedFields(c, OpField.class); + List collectionInfos = new ArrayList<>(); + final List fields = ClassUtils.getAnnotatedFields(cls, OpField.class); Object instance = null; for (Field field : fields) { final boolean isStatic = Modifier.isStatic(field.getModifiers()); @@ -43,20 +47,25 @@ public List generateInfos() { } String unparsedOpNames = field.getAnnotation(OpField.class).names(); String[] parsedOpNames = OpUtils.parseOpNames(unparsedOpNames); - infos.add(new OpFieldInfo(isStatic ? null : instance, field, + collectionInfos.add(new OpFieldInfo(isStatic ? null : instance, field, parsedOpNames)); } - final List methods = ClassUtils.getAnnotatedMethods(c, OpMethod.class); + final List methods = ClassUtils.getAnnotatedMethods(cls, OpMethod.class); for (final Method method: methods) { String unparsedOpNames = method.getAnnotation(OpMethod.class).names(); String[] parsedOpNames = OpUtils.parseOpNames(unparsedOpNames); - infos.add(new OpMethodInfo(method, parsedOpNames)); + collectionInfos.add(new OpMethodInfo(method, parsedOpNames)); } - } catch (InstantiationException | IllegalAccessException | InstantiableException exc) { + return collectionInfos; + } catch (InstantiationException | IllegalAccessException exc) { // TODO: Consider how best to handle this. - exc.printStackTrace(); + return null; } - } + + }) // + .filter(list -> list!= null) // + .flatMap(list -> list.stream()) // + .collect(Collectors.toList()); return infos; } diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java deleted file mode 100644 index 8bc55e2fb..000000000 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.scijava.ops.engine.impl; - -import java.util.ArrayList; -import java.util.List; - -import org.scijava.InstantiableException; -import org.scijava.ops.api.OpInfo; -import org.scijava.ops.api.OpInfoGenerator; -import org.scijava.ops.api.OpUtils; -import org.scijava.ops.engine.matcher.impl.OpClassInfo; -import org.scijava.ops.spi.Op; -import org.scijava.plugin.PluginInfo; -import org.scijava.plugin.PluginService; - - -public class PluginBasedClassOpInfoGenerator implements OpInfoGenerator { - - private final PluginService service; - - public PluginBasedClassOpInfoGenerator(PluginService service) { - this.service = service; - } - - @Override - public List generateInfos() { - List infos = new ArrayList<>(); - for (PluginInfo info : service.getPluginsOfType(Op.class) ) { - Class opClass; - try { - opClass = info.loadClass(); - String[] parsedOpNames = OpUtils.parseOpNames(info.getName()); - infos.add(new OpClassInfo(opClass, parsedOpNames)); - } - catch (InstantiableException exc) { - exc.printStackTrace(); - } - - } - return infos; - } - -} diff --git a/scijava/scijava-ops-serviceloader/.gitignore b/scijava/scijava-ops-serviceloader/.gitignore new file mode 100644 index 000000000..00d2ab71d --- /dev/null +++ b/scijava/scijava-ops-serviceloader/.gitignore @@ -0,0 +1,2 @@ +/.apt_generated/ +/.apt_generated_tests/ diff --git a/scijava/scijava-ops-serviceloader/LICENSE.txt b/scijava/scijava-ops-serviceloader/LICENSE.txt new file mode 100644 index 000000000..ef17d5e7a --- /dev/null +++ b/scijava/scijava-ops-serviceloader/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2016 - 2019, SciJava Ops developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/scijava/scijava-ops-serviceloader/README.md b/scijava/scijava-ops-serviceloader/README.md new file mode 100644 index 000000000..a0fddd805 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/README.md @@ -0,0 +1,5 @@ +# SciJava Discovery: A library for the abstraction of service discovery mechanisms + +This module provides the `Discoverer` interface, an abstraction for service discovery. + +WIP \ No newline at end of file diff --git a/scijava/scijava-ops-serviceloader/bin/generate.groovy b/scijava/scijava-ops-serviceloader/bin/generate.groovy new file mode 100644 index 000000000..3e26050d9 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/bin/generate.groovy @@ -0,0 +1,238 @@ +#!/usr/bin/env groovy + +/* + * #%L + * SciJava Operations: a framework for reusable algorithms. + * %% + * Copyright (C) 2018 SciJava developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +debug = System.getenv('DEBUG') +def debug(msg) { + if (debug) System.err.println("[DEBUG] $msg") +} + +@Grab('org.apache.velocity:velocity:1.7') +import org.apache.velocity.app.VelocityEngine + +// TODO: Get path to Groovy script and make these dirs relative to that. +templateDirectory = 'templates' +outputDirectory = 'src' + +knownFiles = new java.util.HashSet(); + +/* Gets the last modified timestamp for the given file. */ +def timestamp(dir, file) { + if (file == null) return Long.MAX_VALUE; + file = new java.io.File(dir, file); + knownFiles.add(file); + return file.lastModified(); +} + +/* Processes a template using Apache Velocity. */ +def processTemplate(engine, context, templateFile, outFilename) { + debug("processTemplate('$engine', '$context', '$templateFile', '$outFilename')") + + if (outFilename == null) return; // nothing to do + + // create output directory if it does not already exist + outFile = new java.io.File(outputDirectory, outFilename); + knownFiles.add(outFile); + if (outFile.getParentFile() != null) outFile.getParentFile().mkdirs(); + + // apply the template and write out the result + t = engine.getTemplate(templateFile); + writer = new StringWriter(); + t.merge(context, writer); + out = new PrintWriter(outFile, "UTF-8"); + out.print(writer.toString()); + out.close(); +} + +/* Evaluates a string using Groovy. */ +def parseValue(sh, translationsFile, key, expression) { + try { + result = sh.evaluate(expression) + sh.setVariable(key, result) + return result + } + catch (groovy.lang.GroovyRuntimeException e) { + print("[WARNING] $translationsFile: " + + "key '$key' has unparseable value: " + e.getMessage()); + } +} + +/* Reads a translations File */ +def readTranslation(engine, globalContext, reader, templateSubdirectory, templateFile, translationsFile, isInclude){ + sh = new groovy.lang.GroovyShell(); + for (;;) { + // read the line + line = reader.readLine(); + + if (line == null) break; + // check if the line starts a new section + if (line.startsWith("[") && line.endsWith("]")) { + // if we are parsing a .include file, return when we hit any sections + if(isInclude){ + println("[WARNING] $translationsFile: Section definition in .include file. Ending processing of $translationsFile"); + return context; + } + // write out the previous file + processTemplate(engine, context, templateFile, outputFilename); + + // start a new file + outputFilename = line.substring(1, line.length() - 1); + if (!templateDirectory.equals(templateSubdirectory)) { + subPath = templateSubdirectory.substring(templateDirectory.length() + 1); + outputFilename = "$subPath/$outputFilename"; + } + context = new org.apache.velocity.VelocityContext(globalContext); + continue; + } + + // ignore blank lines + trimmedLine = line.trim(); + if (trimmedLine.isEmpty()) continue; + + // ignore comments + if (trimmedLine.startsWith("#")) continue; + + // include any global files + if (trimmedLine.startsWith(".include")){ + includeFile = line.substring(9); + if(includeFile.startsWith("templates")){ + includeSubdirectory = includeFile.substring(0, includeFile.lastIndexOf("/")) + includeFile = includeFile.substring(includeFile.lastIndexOf("/")) + } + else{ + includeSubdirectory = templateSubdirectory + } + globalReader = new java.io.BufferedReader(new java.io.FileReader("$includeSubdirectory/$includeFile")); + encapsulatedContext = new org.apache.velocity.VelocityContext(context) + context = readTranslation(engine, encapsulatedContext, globalReader, templateSubdirectory, templateFile, includeFile, true) + continue; + } + + if (!line.contains('=')) { + print("[WARNING] $translationsFile: Ignoring spurious line: $line"); + continue; + } + + int idx = line.indexOf('='); + key = line.substring(0, idx).trim(); + value = line.substring(idx + 1); + + if (value.trim().equals('```')) { + // multi-line value + builder = new StringBuilder(); + for (;;) { + line = reader.readLine(); + if (line == null) { + throw new RuntimeException("Unfinished value: " + builder.toString()); + } + if (line.equals('```')) { + break; + } + if (builder.length() > 0) { + builder.append("\n"); + } + builder.append(line); + } + value = builder.toString(); + } + + context.put(key, parseValue(sh, translationsFile, key, value)); + } + + return context; +} + +/* + * Translates a template into many files in the outputDirectory, + * given a translations file in INI style; e.g.: + * + * [filename1] + * variable1 = value1 + * variable2 = value2 + * ... + * [filename2] + * variable1 = value3 + * variable2 = value4 + * ... + */ +def translate(templateSubdirectory, templateFile, translationsFile) { + debug("translate('$templateSubdirectory', '$templateFile', '$translationsFile')") + + // initialize the Velocity engine + engine = new org.apache.velocity.app.VelocityEngine(); + p = new java.util.Properties(); + // fail if template uses an invalid expression; e.g., an undefined variable + p.setProperty("runtime.references.strict", "true"); + // tell Velocity where the templates are located + p.setProperty("file.resource.loader.path", "$templateSubdirectory"); + // tell Velocity to log to stderr rather than to a velocity.log file + p.setProperty(org.apache.velocity.runtime.RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, + "org.apache.velocity.runtime.log.SystemLogChute"); + engine.init(p); + + // read translation lines + outputFilename = null; + context = globalContext = new org.apache.velocity.VelocityContext(); + reader = new java.io.BufferedReader(new java.io.FileReader("$templateSubdirectory/$translationsFile")); + + readTranslation(engine, context, reader, templateSubdirectory, templateFile, translationsFile, false); + + reader.close(); + + // process the template + processTemplate(engine, context, templateFile, outputFilename); +} + +/* Recursively translates all templates in the given directory. */ +def translateDirectory(templateSubdirectory) { + debug("translateDirectory('$templateSubdirectory')") + + for (file in new java.io.File(templateSubdirectory).listFiles()) { + if (file.isDirectory()) { + // process subdirectories recursively + translateDirectory(file.getPath()); + } + else { + // process Velocity template files only + name = file.getName(); + if (!name.endsWith('.vm')) continue; + prefix = name.substring(0, name.lastIndexOf('.')); + translate(templateSubdirectory, name, prefix + '.list'); + } + } +} + +try { + translateDirectory(templateDirectory); +} +catch (Throwable t) { + t.printStackTrace(System.err); + throw t; +} diff --git a/scijava/scijava-ops-serviceloader/pom.xml b/scijava/scijava-ops-serviceloader/pom.xml new file mode 100644 index 000000000..c09568d91 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + + + org.scijava + scijava-incubator + 0-SNAPSHOT + ../.. + + + scijava-ops-serviceloader + + SciJava Ops Service Loader + SciJava Operations Service Loader: A ServiceLoader based implementation of SciJava Ops Discovery. + https://github.com/scijava/scijava-ops-serviceloader + 2021 + + SciJava + https://scijava.org/ + + + + Simplified BSD License + repo + + + + + + ctrueden + Curtis Rueden + https://imagej.net/User:Rueden + + founder + lead + reviewer + support + maintainer + + + + gselzer + Gabriel Selzer + + founder + developer + debugger + reviewer + support + + + + + + Christian Dietz + https://imagej.net/User:Dietzc + + founder + + + dietzc + + + + David Kolb + + founder + + + Treiblesschorle + + + + + + + Image.sc Forum + https://forum.image.sc/tags/scijava-ops-serviceloader + + + + + scm:git:git://github.com/scijava/incubator + scm:git:git@github.com:scijava/incubator + HEAD + https://github.com/scijava/incubator + + + GitHub Issues + https://github.com/scijava/scijava-ops-serviceloader/issues + + + Travis CI + https://travis-ci.com/scijava/incubator + + + + org.scijava.ops.serviceloader.Main + org.scijava.ops.serviceloader + + bsd_2 + SciJava developers. + + ${scijava.allowedDuplicateClasses},com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.* + ${scijava-ops-serviceloader.allowedDuplicateClasses} + + + + org.scijava + scijava-ops-spi + ${project.version} + + + org.scijava + scijava-ops-discovery + ${project.version} + + + + junit + junit + test + + + diff --git a/scijava/scijava-ops-serviceloader/src/main/java/module-info.java b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java new file mode 100644 index 000000000..07901e1a4 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java @@ -0,0 +1,12 @@ + +module org.scijava.ops.serviceloader { + + exports org.scijava.ops.serviceloader; + + requires org.scijava.ops.spi; + requires org.scijava.ops.discovery; + + uses org.scijava.ops.spi.Op; + provides org.scijava.ops.spi.Op with org.scijava.ops.serviceloader.ServiceBasedAdder; + +} diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedAdder.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedAdder.java new file mode 100644 index 000000000..2d309f549 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedAdder.java @@ -0,0 +1,19 @@ + +package org.scijava.ops.serviceloader; + +import java.util.function.BiFunction; + +import org.scijava.ops.spi.Op; +import org.scijava.ops.spi.OpClass; + +@OpClass(names = "math.add") +public class ServiceBasedAdder implements BiFunction, + Op +{ + + @Override + public Double apply(Number t, Number u) { + return t.doubleValue() + u.doubleValue(); + } + +} diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java new file mode 100644 index 000000000..5a4821ce9 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java @@ -0,0 +1,19 @@ +package org.scijava.ops.serviceloader; + +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Collectors; + +import org.scijava.ops.discovery.Discoverer; + +public class ServiceLoaderDiscoverer implements Discoverer { + + @SuppressWarnings("unchecked") + @Override + public List> implementingClasses(Class c) { + ServiceLoader loader = ServiceLoader.load(c); + return loader.stream().map(p -> (Class) p.get().getClass()) // + .collect(Collectors.toList()); + } + +} diff --git a/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java new file mode 100644 index 000000000..aa6ebf4dd --- /dev/null +++ b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java @@ -0,0 +1,22 @@ +package org.scijava.ops.serviceloader; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.scijava.ops.discovery.Discoverer; +import org.scijava.ops.spi.Op; + +public class ServiceLoaderDiscovererTest { + + @Test + public void testServiceLoader() { + Discoverer d = new ServiceLoaderDiscoverer(); + List> implementingClasses = d.implementingClasses(Op.class); + Assert.assertTrue(implementingClasses.contains(ServiceBasedAdder.class)); + Assert.assertEquals(implementingClasses.size(), 1); + } + + +} + diff --git a/scijava/scijava-ops-spi/src/main/java/org/scijava/ops/spi/OpClass.java b/scijava/scijava-ops-spi/src/main/java/org/scijava/ops/spi/OpClass.java new file mode 100644 index 000000000..7e6e33b41 --- /dev/null +++ b/scijava/scijava-ops-spi/src/main/java/org/scijava/ops/spi/OpClass.java @@ -0,0 +1,23 @@ +package org.scijava.ops.spi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.scijava.Priority; + +/** Annotates an op declared as a field in an {@link OpCollection}. */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface OpClass { + + String names(); + + // the names of the parameters (inputs and outputs) that will appear in a call + // to help(). + String[] params() default ""; + + double priority() default Priority.NORMAL; + +} From 140fdeb982612c1ccc6cca79ba531fba8dee9d2f Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 2 Aug 2021 10:31:13 -0500 Subject: [PATCH 3/8] Use ServiceLoader to load Ops/OpCollections --- scijava/scijava-ops-engine/pom.xml | 5 ++ .../src/main/java/module-info.java | 2 + .../ops/engine/copy/CopyOpCollection.java | 4 +- .../ops/engine/impl/DefaultOpService.java | 10 ++-- .../OpClassBasedClassOpInfoGenerator.java | 39 +++++++++++++++ .../impl/OpCollectionInfoGenerator.java | 1 - ...a => PluginBasedClassOpInfoGenerator.java} | 4 +- .../ops/engine/copy/CopyOpCollectionTest.java | 47 +++++++++++++++++++ .../templates/main/java/module-info.vm | 2 + .../src/main/java/module-info.java | 8 +++- .../ServiceBasedMultipliers.java | 15 ++++++ .../ServiceLoaderDiscoverer.java | 13 ++++- .../ServiceLoaderDiscovererTest.java | 11 ++++- 13 files changed, 146 insertions(+), 15 deletions(-) create mode 100644 scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java rename scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/{ClassOpInfoGenerator.java => PluginBasedClassOpInfoGenerator.java} (84%) create mode 100644 scijava/scijava-ops-engine/src/test/java/org/scijava/ops/engine/copy/CopyOpCollectionTest.java create mode 100644 scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedMultipliers.java diff --git a/scijava/scijava-ops-engine/pom.xml b/scijava/scijava-ops-engine/pom.xml index 334c4bcca..91089c183 100644 --- a/scijava/scijava-ops-engine/pom.xml +++ b/scijava/scijava-ops-engine/pom.xml @@ -136,6 +136,11 @@ scijava-ops-discovery ${project.version} + + org.scijava + scijava-ops-serviceloader + ${project.version} + org.scijava scijava-struct diff --git a/scijava/scijava-ops-engine/src/main/java/module-info.java b/scijava/scijava-ops-engine/src/main/java/module-info.java index 40e4e563e..ce28ccfdd 100644 --- a/scijava/scijava-ops-engine/src/main/java/module-info.java +++ b/scijava/scijava-ops-engine/src/main/java/module-info.java @@ -38,6 +38,7 @@ requires org.scijava.struct; requires org.scijava.ops.api; requires org.scijava.ops.discovery; + requires org.scijava.ops.serviceloader; requires org.scijava.ops.spi; requires org.scijava.types; requires javassist; @@ -45,4 +46,5 @@ requires therapi.runtime.javadoc; uses javax.annotation.processing.Processor; + provides org.scijava.ops.spi.OpCollection with org.scijava.ops.engine.copy.CopyOpCollection; } diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/copy/CopyOpCollection.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/copy/CopyOpCollection.java index ed692dd1c..f268b311d 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/copy/CopyOpCollection.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/copy/CopyOpCollection.java @@ -4,10 +4,8 @@ import org.scijava.function.Computers; import org.scijava.ops.spi.OpCollection; import org.scijava.ops.spi.OpField; -import org.scijava.plugin.Plugin; -@Plugin(type = OpCollection.class) -public class CopyOpCollection { +public class CopyOpCollection implements OpCollection{ @OpField(names = "cp, copy", priority = Priority.LOW, params = "array, arrayCopy") public static final Computers.Arity1 copyPrimitiveDoubleArray = (from, to) -> { diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java index 0a59dcbc6..a7e746047 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java @@ -42,6 +42,7 @@ import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.OpHistoryService; import org.scijava.ops.engine.OpService; +import org.scijava.ops.serviceloader.ServiceLoaderDiscoverer; import org.scijava.plugin.Plugin; import org.scijava.plugin.PluginInfo; import org.scijava.plugin.PluginService; @@ -90,11 +91,12 @@ private synchronized void initEnv() { LogService log = context().getService(LogService.class); TypeService types = context().getService(TypeService.class); OpHistoryService history = context().getService(OpHistoryService.class); - Discoverer d = new PluginBasedDiscoverer(context()); + Discoverer d1 = new PluginBasedDiscoverer(context()); + Discoverer d2 = new ServiceLoaderDiscoverer(); List infoGenerators = Arrays.asList( - new ClassOpInfoGenerator(d), - new OpCollectionInfoGenerator(d)); - env = new DefaultOpEnvironment(types, log, history, infoGenerators, d); + new PluginBasedClassOpInfoGenerator(d1, d2), + new OpCollectionInfoGenerator(d1, d2)); + env = new DefaultOpEnvironment(types, log, history, infoGenerators, d1, d2); } } diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java new file mode 100644 index 000000000..c1854cab1 --- /dev/null +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java @@ -0,0 +1,39 @@ + +package org.scijava.ops.engine.impl; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.scijava.ops.api.OpInfo; +import org.scijava.ops.api.OpInfoGenerator; +import org.scijava.ops.api.OpUtils; +import org.scijava.ops.discovery.Discoverer; +import org.scijava.ops.engine.matcher.impl.OpClassInfo; +import org.scijava.ops.spi.Op; +import org.scijava.ops.spi.OpClass; +import org.scijava.plugin.Plugin; + +public class OpClassBasedClassOpInfoGenerator implements OpInfoGenerator { + + private final List discoverers; + + public OpClassBasedClassOpInfoGenerator(Discoverer... d) { + this.discoverers = Arrays.asList(d); + } + + @Override + public List generateInfos() { + List infos = discoverers.stream() // + .flatMap(d -> d.implementingClasses(Op.class).stream()) // + .filter(cls -> cls.getAnnotation(OpClass.class) != null) // + .map(cls -> { + OpClass p = cls.getAnnotation(OpClass.class); + String[] parsedOpNames = OpUtils.parseOpNames(p.names()); + return new OpClassInfo(cls, parsedOpNames); + }) // + .collect(Collectors.toList()); + return infos; + } + +} diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java index dc0b7e6f9..07699caea 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java @@ -34,7 +34,6 @@ public OpCollectionInfoGenerator(Discoverer... d) { public List generateInfos() { List infos = discoverers.stream() // .flatMap(d -> d.implementingClasses(OpCollection.class).stream()) // - .filter(cls -> cls.getAnnotation(Plugin.class) != null) // .map(cls -> { try { List collectionInfos = new ArrayList<>(); diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java similarity index 84% rename from scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java rename to scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java index 7cbc00731..ad50c8103 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/ClassOpInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java @@ -13,11 +13,11 @@ import org.scijava.ops.spi.Op; import org.scijava.plugin.Plugin; -public class ClassOpInfoGenerator implements OpInfoGenerator { +public class PluginBasedClassOpInfoGenerator implements OpInfoGenerator { private final List discoverers; - public ClassOpInfoGenerator(Discoverer... d) { + public PluginBasedClassOpInfoGenerator(Discoverer... d) { this.discoverers = Arrays.asList(d); } diff --git a/scijava/scijava-ops-engine/src/test/java/org/scijava/ops/engine/copy/CopyOpCollectionTest.java b/scijava/scijava-ops-engine/src/test/java/org/scijava/ops/engine/copy/CopyOpCollectionTest.java new file mode 100644 index 000000000..7e23215e5 --- /dev/null +++ b/scijava/scijava-ops-engine/src/test/java/org/scijava/ops/engine/copy/CopyOpCollectionTest.java @@ -0,0 +1,47 @@ +/* + * #%L + * SciJava Operations: a framework for reusable algorithms. + * %% + * Copyright (C) 2016 - 2019 SciJava Ops developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.ops.engine.copy; + +import org.junit.Assert; +import org.junit.Test; +import org.scijava.ops.engine.AbstractTestEnvironment; + +public class CopyOpCollectionTest extends AbstractTestEnvironment { + + @Test + public void testCopyOp() { + double[] input = {1, 2, 3}; + double[] output = {4, 5, 6}; + ops.op("cp").input(input).output(output).compute(); + + Assert.assertArrayEquals(input, output, 0.); + } + +} diff --git a/scijava/scijava-ops-engine/templates/main/java/module-info.vm b/scijava/scijava-ops-engine/templates/main/java/module-info.vm index 089320bc7..a218221fb 100644 --- a/scijava/scijava-ops-engine/templates/main/java/module-info.vm +++ b/scijava/scijava-ops-engine/templates/main/java/module-info.vm @@ -27,6 +27,7 @@ module org.scijava.ops.engine { requires org.scijava.struct; requires org.scijava.ops.api; requires org.scijava.ops.discovery; + requires org.scijava.ops.serviceloader; requires org.scijava.ops.spi; requires org.scijava.types; requires javassist; @@ -34,4 +35,5 @@ module org.scijava.ops.engine { requires therapi.runtime.javadoc; uses javax.annotation.processing.Processor; + provides org.scijava.ops.spi.OpCollection with org.scijava.ops.engine.copy.CopyOpCollection; } diff --git a/scijava/scijava-ops-serviceloader/src/main/java/module-info.java b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java index 07901e1a4..0589eeaba 100644 --- a/scijava/scijava-ops-serviceloader/src/main/java/module-info.java +++ b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java @@ -7,6 +7,12 @@ requires org.scijava.ops.discovery; uses org.scijava.ops.spi.Op; - provides org.scijava.ops.spi.Op with org.scijava.ops.serviceloader.ServiceBasedAdder; + uses org.scijava.ops.spi.OpCollection; + + provides org.scijava.ops.spi.Op with + org.scijava.ops.serviceloader.ServiceBasedAdder; + + provides org.scijava.ops.spi.OpCollection with + org.scijava.ops.serviceloader.ServiceBasedMultipliers; } diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedMultipliers.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedMultipliers.java new file mode 100644 index 000000000..b5c2baa71 --- /dev/null +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceBasedMultipliers.java @@ -0,0 +1,15 @@ + +package org.scijava.ops.serviceloader; + +import java.util.function.BiFunction; + +import org.scijava.ops.spi.OpCollection; +import org.scijava.ops.spi.OpField; + +public class ServiceBasedMultipliers implements OpCollection { + + @OpField(names = "math.multiply") + public final BiFunction fieldMultiplier = (in1, + in2) -> in1.doubleValue() * in2.doubleValue(); + +} diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java index 5a4821ce9..a72ac9a93 100644 --- a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java @@ -1,6 +1,9 @@ + package org.scijava.ops.serviceloader; +import java.util.Collections; import java.util.List; +import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.stream.Collectors; @@ -11,9 +14,15 @@ public class ServiceLoaderDiscoverer implements Discoverer { @SuppressWarnings("unchecked") @Override public List> implementingClasses(Class c) { - ServiceLoader loader = ServiceLoader.load(c); - return loader.stream().map(p -> (Class) p.get().getClass()) // + // TODO: can we first check that THIS module uses c? + try { + ServiceLoader loader = ServiceLoader.load(c); + return loader.stream().map(p -> (Class) p.get().getClass()) // .collect(Collectors.toList()); + } + catch (ServiceConfigurationError e) { + return Collections.emptyList(); + } } } diff --git a/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java index aa6ebf4dd..9975e4a7e 100644 --- a/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java +++ b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java @@ -6,17 +6,24 @@ import org.junit.Test; import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.spi.Op; +import org.scijava.ops.spi.OpCollection; public class ServiceLoaderDiscovererTest { @Test - public void testServiceLoader() { + public void testServiceLoaderWithOps() { Discoverer d = new ServiceLoaderDiscoverer(); List> implementingClasses = d.implementingClasses(Op.class); Assert.assertTrue(implementingClasses.contains(ServiceBasedAdder.class)); Assert.assertEquals(implementingClasses.size(), 1); } - + @Test + public void testServiceLoaderWithOpCollections() { + Discoverer d = new ServiceLoaderDiscoverer(); + List> implementingClasses = d.implementingClasses(OpCollection.class); + Assert.assertTrue(implementingClasses.contains(ServiceBasedMultipliers.class)); + Assert.assertEquals(implementingClasses.size(), 1); + } } From b44cc06a896074003f5bd35837aaf9b076c115cb Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 2 Aug 2021 13:18:37 -0500 Subject: [PATCH 4/8] Explicitly check for declared usage of c --- scijava/scijava-ops-serviceloader/README.md | 5 ++--- .../serviceloader/ServiceLoaderDiscoverer.java | 17 ++++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/scijava/scijava-ops-serviceloader/README.md b/scijava/scijava-ops-serviceloader/README.md index a0fddd805..d31d30f56 100644 --- a/scijava/scijava-ops-serviceloader/README.md +++ b/scijava/scijava-ops-serviceloader/README.md @@ -1,5 +1,4 @@ -# SciJava Discovery: A library for the abstraction of service discovery mechanisms - -This module provides the `Discoverer` interface, an abstraction for service discovery. +# SciJava Ops Service Loader: A [`ServiceLoader`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) +This module provides an implementation of `Discoverer` WIP \ No newline at end of file diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java index a72ac9a93..bea3070d5 100644 --- a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java @@ -14,15 +14,14 @@ public class ServiceLoaderDiscoverer implements Discoverer { @SuppressWarnings("unchecked") @Override public List> implementingClasses(Class c) { - // TODO: can we first check that THIS module uses c? - try { - ServiceLoader loader = ServiceLoader.load(c); - return loader.stream().map(p -> (Class) p.get().getClass()) // - .collect(Collectors.toList()); - } - catch (ServiceConfigurationError e) { - return Collections.emptyList(); - } + // If we cannot use c, we cannot find any implementations + Module thisModule = this.getClass().getModule(); + if (!thisModule.canUse(c)) return Collections.emptyList(); + + // If we can use c, look up the implementations + ServiceLoader loader = ServiceLoader.load(c); + return loader.stream().map(p -> (Class) p.get().getClass()) // + .collect(Collectors.toList()); } } From d8d8b247876efad7032282e2de088b083a777808 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 2 Aug 2021 14:24:20 -0500 Subject: [PATCH 5/8] Remove Ops connotation from Scijava Discovery It is unfortunate to make this change on this branch, but after writing SciJava Ops ServiceLoader, it became clear that SciJava Ops Discovery has nothing to do with Ops. Thus it becomes SciJava Discovery (without the "Ops). SciJava Ops ServiceLoader, however, needs the Ops name because of its module-info.java --- pom.xml | 4 ++-- scijava/scijava-discovery/.gitignore | 2 ++ .../LICENSE.txt | 0 .../README.md | 0 .../bin/generate.groovy | 0 .../pom.xml | 21 +++++++++---------- .../src/main/java/module-info.java | 5 +++++ .../org/scijava}/discovery/Discoverer.java | 2 +- .../scijava}/discovery/Implementation.java | 2 +- .../scijava}/discovery/StaticDiscoverer.java | 2 +- scijava/scijava-ops-discovery/.gitignore | 3 +-- .../src/main/java/module-info.java | 5 ----- scijava/scijava-ops-engine/pom.xml | 2 +- .../src/main/java/module-info.java | 2 +- .../ops/engine/impl/DefaultOpEnvironment.java | 3 +-- .../ops/engine/impl/DefaultOpService.java | 2 +- .../OpClassBasedClassOpInfoGenerator.java | 3 +-- .../impl/OpCollectionInfoGenerator.java | 4 +--- .../impl/PluginBasedClassOpInfoGenerator.java | 2 +- .../templates/main/java/module-info.vm | 2 +- scijava/scijava-ops-serviceloader/README.md | 6 ++++-- scijava/scijava-ops-serviceloader/pom.xml | 2 +- .../src/main/java/module-info.java | 2 +- .../ServiceLoaderDiscoverer.java | 3 +-- .../ServiceLoaderDiscovererTest.java | 2 +- 25 files changed, 39 insertions(+), 42 deletions(-) create mode 100644 scijava/scijava-discovery/.gitignore rename scijava/{scijava-ops-discovery => scijava-discovery}/LICENSE.txt (100%) rename scijava/{scijava-ops-discovery => scijava-discovery}/README.md (100%) rename scijava/{scijava-ops-discovery => scijava-discovery}/bin/generate.groovy (100%) rename scijava/{scijava-ops-discovery => scijava-discovery}/pom.xml (73%) create mode 100644 scijava/scijava-discovery/src/main/java/module-info.java rename scijava/{scijava-ops-discovery/src/main/java/org/scijava/ops => scijava-discovery/src/main/java/org/scijava}/discovery/Discoverer.java (71%) rename scijava/{scijava-ops-discovery/src/main/java/org/scijava/ops => scijava-discovery/src/main/java/org/scijava}/discovery/Implementation.java (89%) rename scijava/{scijava-ops-discovery/src/main/java/org/scijava/ops => scijava-discovery/src/main/java/org/scijava}/discovery/StaticDiscoverer.java (90%) delete mode 100644 scijava/scijava-ops-discovery/src/main/java/module-info.java diff --git a/pom.xml b/pom.xml index e6d4035d8..1d37d9336 100644 --- a/pom.xml +++ b/pom.xml @@ -47,13 +47,13 @@ imagej/imagej-ops2 imagej/imagej-testutil - scijava/scijava-persist + scijava/scijava-discovery scijava/scijava-function scijava/scijava-ops-api - scijava/scijava-ops-discovery scijava/scijava-ops-engine scijava/scijava-ops-serviceloader scijava/scijava-ops-spi + scijava/scijava-persist scijava/scijava-struct scijava/scijava-taglets scijava/scijava-testutil diff --git a/scijava/scijava-discovery/.gitignore b/scijava/scijava-discovery/.gitignore new file mode 100644 index 000000000..00d2ab71d --- /dev/null +++ b/scijava/scijava-discovery/.gitignore @@ -0,0 +1,2 @@ +/.apt_generated/ +/.apt_generated_tests/ diff --git a/scijava/scijava-ops-discovery/LICENSE.txt b/scijava/scijava-discovery/LICENSE.txt similarity index 100% rename from scijava/scijava-ops-discovery/LICENSE.txt rename to scijava/scijava-discovery/LICENSE.txt diff --git a/scijava/scijava-ops-discovery/README.md b/scijava/scijava-discovery/README.md similarity index 100% rename from scijava/scijava-ops-discovery/README.md rename to scijava/scijava-discovery/README.md diff --git a/scijava/scijava-ops-discovery/bin/generate.groovy b/scijava/scijava-discovery/bin/generate.groovy similarity index 100% rename from scijava/scijava-ops-discovery/bin/generate.groovy rename to scijava/scijava-discovery/bin/generate.groovy diff --git a/scijava/scijava-ops-discovery/pom.xml b/scijava/scijava-discovery/pom.xml similarity index 73% rename from scijava/scijava-ops-discovery/pom.xml rename to scijava/scijava-discovery/pom.xml index b55aca60c..e79586d2d 100644 --- a/scijava/scijava-ops-discovery/pom.xml +++ b/scijava/scijava-discovery/pom.xml @@ -11,11 +11,11 @@ ../.. - scijava-ops-discovery + scijava-discovery - SciJava Ops Discovery - SciJava Operations Discovery: Discovery mechanisms used by the SciJava Operations framework. - https://github.com/scijava/scijava-ops-discovery + SciJava Discovery + SciJava Discovery: Discovery mechanisms used by the SciJava Framework. + https://github.com/scijava/scijava-discovery 2021 SciJava @@ -78,7 +78,7 @@ Image.sc Forum - https://forum.image.sc/tags/scijava-ops-discovery + https://forum.image.sc/tags/scijava-discovery @@ -90,7 +90,7 @@ GitHub Issues - https://github.com/scijava/scijava-ops-discovery/issues + https://github.com/scijava/scijava-discovery/issues Travis CI @@ -98,14 +98,13 @@ - org.scijava.ops.discovery.Main - org.scijava.ops.discovery + org.scijava.discovery.Main + org.scijava.discovery bsd_2 SciJava developers. - - ${scijava.allowedDuplicateClasses},com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.* - ${scijava-ops-discovery.allowedDuplicateClasses} + ${scijava.allowedDuplicateClasses},com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.* + ${scijava-discovery.allowedDuplicateClasses} diff --git a/scijava/scijava-discovery/src/main/java/module-info.java b/scijava/scijava-discovery/src/main/java/module-info.java new file mode 100644 index 000000000..10b0759ef --- /dev/null +++ b/scijava/scijava-discovery/src/main/java/module-info.java @@ -0,0 +1,5 @@ +module org.scijava.discovery { + + exports org.scijava.discovery; + +} diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/Discoverer.java similarity index 71% rename from scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java rename to scijava/scijava-discovery/src/main/java/org/scijava/discovery/Discoverer.java index b9bf5764f..e851d17c4 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Discoverer.java +++ b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/Discoverer.java @@ -1,5 +1,5 @@ -package org.scijava.ops.discovery; +package org.scijava.discovery; import java.util.List; diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Implementation.java b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/Implementation.java similarity index 89% rename from scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Implementation.java rename to scijava/scijava-discovery/src/main/java/org/scijava/discovery/Implementation.java index ed7b2d434..a42ae5986 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/Implementation.java +++ b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/Implementation.java @@ -1,4 +1,4 @@ -package org.scijava.ops.discovery; +package org.scijava.discovery; import java.lang.reflect.Type; diff --git a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/StaticDiscoverer.java similarity index 90% rename from scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java rename to scijava/scijava-discovery/src/main/java/org/scijava/discovery/StaticDiscoverer.java index 014ff23f6..ca931326c 100644 --- a/scijava/scijava-ops-discovery/src/main/java/org/scijava/ops/discovery/StaticDiscoverer.java +++ b/scijava/scijava-discovery/src/main/java/org/scijava/discovery/StaticDiscoverer.java @@ -1,5 +1,5 @@ -package org.scijava.ops.discovery; +package org.scijava.discovery; import java.util.HashMap; import java.util.List; diff --git a/scijava/scijava-ops-discovery/.gitignore b/scijava/scijava-ops-discovery/.gitignore index 00d2ab71d..ae3c17260 100644 --- a/scijava/scijava-ops-discovery/.gitignore +++ b/scijava/scijava-ops-discovery/.gitignore @@ -1,2 +1 @@ -/.apt_generated/ -/.apt_generated_tests/ +/bin/ diff --git a/scijava/scijava-ops-discovery/src/main/java/module-info.java b/scijava/scijava-ops-discovery/src/main/java/module-info.java deleted file mode 100644 index e1803e6dd..000000000 --- a/scijava/scijava-ops-discovery/src/main/java/module-info.java +++ /dev/null @@ -1,5 +0,0 @@ -module org.scijava.ops.discovery { - - exports org.scijava.ops.discovery; - -} diff --git a/scijava/scijava-ops-engine/pom.xml b/scijava/scijava-ops-engine/pom.xml index 91089c183..6d6a4eda0 100644 --- a/scijava/scijava-ops-engine/pom.xml +++ b/scijava/scijava-ops-engine/pom.xml @@ -133,7 +133,7 @@ org.scijava - scijava-ops-discovery + scijava-discovery ${project.version} diff --git a/scijava/scijava-ops-engine/src/main/java/module-info.java b/scijava/scijava-ops-engine/src/main/java/module-info.java index ce28ccfdd..f8a8e7538 100644 --- a/scijava/scijava-ops-engine/src/main/java/module-info.java +++ b/scijava/scijava-ops-engine/src/main/java/module-info.java @@ -34,10 +34,10 @@ requires java.desktop; requires org.scijava; + requires org.scijava.discovery; requires org.scijava.function; requires org.scijava.struct; requires org.scijava.ops.api; - requires org.scijava.ops.discovery; requires org.scijava.ops.serviceloader; requires org.scijava.ops.spi; requires org.scijava.types; diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java index b5ad3ed97..3572075f0 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpEnvironment.java @@ -47,18 +47,17 @@ import java.util.stream.Collectors; import org.scijava.Priority; +import org.scijava.discovery.Discoverer; import org.scijava.log.LogService; import org.scijava.ops.api.Hints; import org.scijava.ops.api.OpCandidate; import org.scijava.ops.api.OpCandidate.StatusCode; import org.scijava.ops.api.OpDependencyMember; import org.scijava.ops.api.OpEnvironment; -import org.scijava.ops.api.OpHistory; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpRef; import org.scijava.ops.api.OpWrapper; -import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.BaseOpHints.Adaptation; import org.scijava.ops.engine.BaseOpHints.DependencyMatching; import org.scijava.ops.engine.BaseOpHints.Simplification; diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java index a7e746047..c307de205 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java @@ -35,11 +35,11 @@ import org.scijava.Context; import org.scijava.InstantiableException; +import org.scijava.discovery.Discoverer; import org.scijava.log.LogService; import org.scijava.ops.api.OpBuilder; import org.scijava.ops.api.OpEnvironment; import org.scijava.ops.api.OpInfoGenerator; -import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.OpHistoryService; import org.scijava.ops.engine.OpService; import org.scijava.ops.serviceloader.ServiceLoaderDiscoverer; diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java index c1854cab1..14ec2ad33 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpClassBasedClassOpInfoGenerator.java @@ -5,14 +5,13 @@ import java.util.List; import java.util.stream.Collectors; +import org.scijava.discovery.Discoverer; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpUtils; -import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.matcher.impl.OpClassInfo; import org.scijava.ops.spi.Op; import org.scijava.ops.spi.OpClass; -import org.scijava.plugin.Plugin; public class OpClassBasedClassOpInfoGenerator implements OpInfoGenerator { diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java index 07699caea..b780a942c 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/OpCollectionInfoGenerator.java @@ -8,17 +8,15 @@ import java.util.List; import java.util.stream.Collectors; -import org.scijava.InstantiableException; +import org.scijava.discovery.Discoverer; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpUtils; -import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.matcher.impl.OpFieldInfo; import org.scijava.ops.engine.matcher.impl.OpMethodInfo; import org.scijava.ops.spi.OpCollection; import org.scijava.ops.spi.OpField; import org.scijava.ops.spi.OpMethod; -import org.scijava.plugin.Plugin; import org.scijava.util.ClassUtils; diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java index ad50c8103..7c8124af2 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/PluginBasedClassOpInfoGenerator.java @@ -5,10 +5,10 @@ import java.util.List; import java.util.stream.Collectors; +import org.scijava.discovery.Discoverer; import org.scijava.ops.api.OpInfo; import org.scijava.ops.api.OpInfoGenerator; import org.scijava.ops.api.OpUtils; -import org.scijava.ops.discovery.Discoverer; import org.scijava.ops.engine.matcher.impl.OpClassInfo; import org.scijava.ops.spi.Op; import org.scijava.plugin.Plugin; diff --git a/scijava/scijava-ops-engine/templates/main/java/module-info.vm b/scijava/scijava-ops-engine/templates/main/java/module-info.vm index a218221fb..6960c0f66 100644 --- a/scijava/scijava-ops-engine/templates/main/java/module-info.vm +++ b/scijava/scijava-ops-engine/templates/main/java/module-info.vm @@ -23,10 +23,10 @@ module org.scijava.ops.engine { requires java.desktop; requires org.scijava; + requires org.scijava.discovery; requires org.scijava.function; requires org.scijava.struct; requires org.scijava.ops.api; - requires org.scijava.ops.discovery; requires org.scijava.ops.serviceloader; requires org.scijava.ops.spi; requires org.scijava.types; diff --git a/scijava/scijava-ops-serviceloader/README.md b/scijava/scijava-ops-serviceloader/README.md index d31d30f56..d6aca9e3f 100644 --- a/scijava/scijava-ops-serviceloader/README.md +++ b/scijava/scijava-ops-serviceloader/README.md @@ -1,4 +1,6 @@ # SciJava Ops Service Loader: A [`ServiceLoader`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) -This module provides an implementation of `Discoverer` +This module provides an implementation of `Discoverer` that is powered by the `ServiceLoader` from Java's Module System. -WIP \ No newline at end of file +## What can `ServiceLoaderDiscoverer` discover? + +`ServiceLoaderDiscoverer` can discover implementations of *any* interface declared to be `use`d in its `module-info.java`. Given a \ No newline at end of file diff --git a/scijava/scijava-ops-serviceloader/pom.xml b/scijava/scijava-ops-serviceloader/pom.xml index c09568d91..03d3f1cda 100644 --- a/scijava/scijava-ops-serviceloader/pom.xml +++ b/scijava/scijava-ops-serviceloader/pom.xml @@ -115,7 +115,7 @@ org.scijava - scijava-ops-discovery + scijava-discovery ${project.version} diff --git a/scijava/scijava-ops-serviceloader/src/main/java/module-info.java b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java index 0589eeaba..753123255 100644 --- a/scijava/scijava-ops-serviceloader/src/main/java/module-info.java +++ b/scijava/scijava-ops-serviceloader/src/main/java/module-info.java @@ -4,7 +4,7 @@ exports org.scijava.ops.serviceloader; requires org.scijava.ops.spi; - requires org.scijava.ops.discovery; + requires org.scijava.discovery; uses org.scijava.ops.spi.Op; uses org.scijava.ops.spi.OpCollection; diff --git a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java index bea3070d5..f0ad681e3 100644 --- a/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java +++ b/scijava/scijava-ops-serviceloader/src/main/java/org/scijava/ops/serviceloader/ServiceLoaderDiscoverer.java @@ -3,11 +3,10 @@ import java.util.Collections; import java.util.List; -import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.stream.Collectors; -import org.scijava.ops.discovery.Discoverer; +import org.scijava.discovery.Discoverer; public class ServiceLoaderDiscoverer implements Discoverer { diff --git a/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java index 9975e4a7e..e22dce445 100644 --- a/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java +++ b/scijava/scijava-ops-serviceloader/src/test/java/org/scijava/ops/serviceloader/ServiceLoaderDiscovererTest.java @@ -4,7 +4,7 @@ import org.junit.Assert; import org.junit.Test; -import org.scijava.ops.discovery.Discoverer; +import org.scijava.discovery.Discoverer; import org.scijava.ops.spi.Op; import org.scijava.ops.spi.OpCollection; From edd739734eaedcbb266c8331bf4b69b9cd910c28 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 2 Aug 2021 15:14:56 -0500 Subject: [PATCH 6/8] Improve README --- scijava/scijava-ops-serviceloader/README.md | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/scijava/scijava-ops-serviceloader/README.md b/scijava/scijava-ops-serviceloader/README.md index d6aca9e3f..0a48dfba3 100644 --- a/scijava/scijava-ops-serviceloader/README.md +++ b/scijava/scijava-ops-serviceloader/README.md @@ -1,6 +1,26 @@ -# SciJava Ops Service Loader: A [`ServiceLoader`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) +# SciJava Ops Service Loader: A [`ServiceLoader`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) extension of SciJava Discovery This module provides an implementation of `Discoverer` that is powered by the `ServiceLoader` from Java's Module System. ## What can `ServiceLoaderDiscoverer` discover? -`ServiceLoaderDiscoverer` can discover implementations of *any* interface declared to be `use`d in its `module-info.java`. Given a \ No newline at end of file +`ServiceLoaderDiscoverer` can discover implementations of *any* interface declared to be `use`d in its `module-info.java`. Although we limit the `use`d interfaces to those defined in SciJava Ops SPI (this is, after all, an Ops project), the process is the same for any other interface. + +# I wrote an implementation, how can I make it discoverable via `ServiceLoaderDiscoverer`? + +Suppose your module `com.example.foo` contains an implementation `com.example.foo.FooOp` of `org.scijava.ops.spi.Op`, and you wise to make `FooOp` discoverable from `ServiceLoaderDiscoverer`. Make sure to: + +* Declare SciJava Ops Service Loader as a dependency; if using Maven, it looks like this: + +```java + + org.scijava + scijava-ops-serviceLoader + 0-SNAPSHOT + Date: Mon, 2 Aug 2021 16:07:34 -0500 Subject: [PATCH 7/8] Use OpClassBasedClassOpInfoGenerator Man, that's a mouthful --- .../main/java/org/scijava/ops/engine/impl/DefaultOpService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java index c307de205..63ef7b2aa 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/impl/DefaultOpService.java @@ -95,6 +95,7 @@ private synchronized void initEnv() { Discoverer d2 = new ServiceLoaderDiscoverer(); List infoGenerators = Arrays.asList( new PluginBasedClassOpInfoGenerator(d1, d2), + new OpClassBasedClassOpInfoGenerator(d1, d2), new OpCollectionInfoGenerator(d1, d2)); env = new DefaultOpEnvironment(types, log, history, infoGenerators, d1, d2); } From f08cb580f9828a0a3297a7c1270b1f2697f12b46 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 2 Aug 2021 16:08:04 -0500 Subject: [PATCH 8/8] Add proof-of-concept for OpClass Service ops --- scijava/scijava-ops-engine/src/main/java/module-info.java | 1 + .../src/main/java/org/scijava/ops/engine/stats/Mean.java | 6 +++--- .../scijava-ops-engine/templates/main/java/module-info.vm | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scijava/scijava-ops-engine/src/main/java/module-info.java b/scijava/scijava-ops-engine/src/main/java/module-info.java index f8a8e7538..915e545ff 100644 --- a/scijava/scijava-ops-engine/src/main/java/module-info.java +++ b/scijava/scijava-ops-engine/src/main/java/module-info.java @@ -47,4 +47,5 @@ uses javax.annotation.processing.Processor; provides org.scijava.ops.spi.OpCollection with org.scijava.ops.engine.copy.CopyOpCollection; + provides org.scijava.ops.spi.Op with org.scijava.ops.engine.stats.Mean.MeanFunction; } diff --git a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/stats/Mean.java b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/stats/Mean.java index a6a304e96..212f770df 100644 --- a/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/stats/Mean.java +++ b/scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/stats/Mean.java @@ -4,13 +4,13 @@ import java.util.function.Function; import org.scijava.ops.spi.Op; +import org.scijava.ops.spi.OpClass; import org.scijava.ops.spi.OpDependency; -import org.scijava.plugin.Plugin; public class Mean { - @Plugin(type = Op.class, name = "stats.mean") - public static class MeanFunction implements Function, O>{ + @OpClass(names = "stats.mean") + public static class MeanFunction implements Function, O>, Op{ @OpDependency(name = "math.add") Function, O> sumFunc; diff --git a/scijava/scijava-ops-engine/templates/main/java/module-info.vm b/scijava/scijava-ops-engine/templates/main/java/module-info.vm index 6960c0f66..5229b4638 100644 --- a/scijava/scijava-ops-engine/templates/main/java/module-info.vm +++ b/scijava/scijava-ops-engine/templates/main/java/module-info.vm @@ -36,4 +36,5 @@ module org.scijava.ops.engine { uses javax.annotation.processing.Processor; provides org.scijava.ops.spi.OpCollection with org.scijava.ops.engine.copy.CopyOpCollection; + provides org.scijava.ops.spi.Op with org.scijava.ops.engine.stats.Mean.MeanFunction; }