Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/main/java/org/scijava/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ private static String getStaticVersion() {

/** Index of the application context's services. */
private final ServiceIndex serviceIndex;

/** Helper class for loading services. */
private final ServiceHelper serviceHelper;

/** Master index of all plugins known to the application context. */
private final PluginIndex pluginIndex;
Expand Down Expand Up @@ -166,8 +169,7 @@ public Context(final Collection<Class<? extends Service>> serviceClasses) {
pom = POM.getPOM(Context.class, "org.scijava", "scijava-common");
manifest = Manifest.getManifest(Context.class);

final ServiceHelper serviceHelper =
new ServiceHelper(this, serviceClasses);
serviceHelper = new ServiceHelper(this, serviceClasses);
serviceHelper.loadServices();
}

Expand Down Expand Up @@ -244,7 +246,15 @@ public PluginIndex getPluginIndex() {

/** Gets the service of the given class. */
public <S extends Service> S getService(final Class<S> c) {
return serviceIndex.getService(c);
S service = serviceIndex.getService(c);

if (service == null && serviceHelper != null &&
serviceHelper.canLoadLazy())
{
service = serviceHelper.loadService(c);
}

return service;
}

/** Gets the service of the given class name (useful for scripts). */
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/scijava/plugin/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@
* </p>
*/
boolean headless() default false;

/**
* When true, if this plugin is an {@link org.scijava.service.Service},
* the context will not try to load this service unless explicitly requested.
* <p>
* NB: Annotating a service field the {@link org.scijava.plugin.Parameter}
* annotation will cause that service to be loaded.
* </p>
*/
boolean lazy() default false;

/** Defines a function that is called to initialize the plugin in some way. */
String initializer() default "";
Expand Down
91 changes: 72 additions & 19 deletions src/main/java/org/scijava/service/ServiceHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public class ServiceHelper extends AbstractContextual {
/** Classes to instantiate as services. */
private final List<Class<? extends Service>> serviceClasses;

/** Class list of lazy services. */
private final List<Class<? extends Service>> lazyPoolList;

/** Whether this ServiceHelper will load lazy services. */
private boolean loadLazy;

/**
* Creates a new service helper for discovering and instantiating services.
*
Expand All @@ -87,12 +93,13 @@ public ServiceHelper(final Context context) {
* @param serviceClasses The service classes to instantiate.
*/
public ServiceHelper(final Context context,
final Collection<Class<? extends Service>> serviceClasses)
final Collection<Class<? extends Service>> serviceClasses)
{
setContext(context);
classPoolMap = new HashMap<Class<? extends Service>, Double>();
classPoolList = new ArrayList<Class<? extends Service>>();
findServiceClasses(classPoolMap, classPoolList);
lazyPoolList = new ArrayList<Class<? extends Service>>();
findServiceClasses(classPoolMap, classPoolList, lazyPoolList);
this.serviceClasses = new ArrayList<Class<? extends Service>>();
if (serviceClasses == null) {
// load all discovered services
Expand All @@ -102,6 +109,8 @@ public ServiceHelper(final Context context,
// load only the services that were explicitly specified
this.serviceClasses.addAll(serviceClasses);
}

loadLazy = false;
}

// -- ServiceHelper methods --
Expand All @@ -115,8 +124,12 @@ public void loadServices() {
loadService(serviceClass);
}
final EventService eventService =
getContext().getService(EventService.class);
getContext().getService(EventService.class);
if (eventService != null) eventService.publish(new ServicesLoadedEvent());

// All non-lazy services should be loaded at this point,
// so lazy services can now be loaded
loadLazy = true;
}

/**
Expand All @@ -129,18 +142,18 @@ public void loadServices() {
*/
public <S extends Service> S loadService(final Class<S> c) {
// if a compatible service already exists, return it
final S service = getContext().getServiceIndex().getService(c);
S service = getContext().getServiceIndex().getService(c);
if (service != null) return service;

// scan the class pool for a suitable match
for (final Class<? extends Service> serviceClass : classPoolList) {
if (c.isAssignableFrom(serviceClass)) {
// found a match; now instantiate it
@SuppressWarnings("unchecked")
final S result = (S) createExactService(serviceClass);
return result;
}
}
service = this.<S>searchListForService(c, classPoolList);

// scan the lazy class pool for a suitable match if necessary
if (service == null && canLoadLazy())
service = this.<S>searchListForService(c, lazyPoolList);

// found a match, return it.
if (service != null) return service;

return createExactService(c);
}
Expand All @@ -164,12 +177,23 @@ public <S extends Service> S createExactService(final Class<S> c) {
}
return null;
}

/**
* Returns whether or not lazy services will be loaded by this ServiceHelper.
* {@link #loadServices()} should be run once before any lazy services
* can be loaded.
*
* @return true if this ServiceHelper will load lazy services
*/
public boolean canLoadLazy() {
return loadLazy ;
}

// -- Helper methods --

/** Instantiates a service using the given constructor. */
private <S extends Service> S createService(final Class<S> c)
throws InstantiationException, IllegalAccessException
throws InstantiationException, IllegalAccessException
{
final S service = c.newInstance();
service.setContext(getContext());
Expand All @@ -180,7 +204,7 @@ private <S extends Service> S createService(final Class<S> c)

// populate service parameters
final List<Field> fields =
ClassUtils.getAnnotatedFields(c, Parameter.class);
ClassUtils.getAnnotatedFields(c, Parameter.class);
for (final Field f : fields) {
f.setAccessible(true); // expose private fields

Expand All @@ -202,21 +226,51 @@ private <S extends Service> S createService(final Class<S> c)
return service;
}

/**
* Iterates over the provided list, looking for classes
* that can be cast to the specified baseClass, and attempting
* to instantiate these classes until successful or the list is exhausted.
*/
@SuppressWarnings("unchecked")
private <S extends Service> S searchListForService(
Class<? extends Service> baseClass,
List<Class<? extends Service>> serviceList)
{
for (Class<? extends Service> testClass : serviceList) {
if (baseClass.isAssignableFrom(testClass)) {
// found a match; now instantiate it
return (S) createExactService(testClass);
}
}

return null;
}

/** Asks the plugin index for all available service implementations. */
private void findServiceClasses(
final Map<Class<? extends Service>, Double> serviceMap,
final List<Class<? extends Service>> serviceList)
final Map<Class<? extends Service>, Double> serviceMap,
final List<Class<? extends Service>> serviceList,
List<Class<? extends Service>> lazyServiceList)
{
// ask the plugin index for the (sorted) list of available services
final List<PluginInfo<Service>> services =
getContext().getPluginIndex().getPlugins(Service.class);
getContext().getPluginIndex().getPlugins(Service.class);

for (final PluginInfo<Service> info : services) {
try {
final Class<? extends Service> c = info.loadClass();
final double priority = info.getPriority();
serviceMap.put(c, priority);
serviceList.add(c);

// If the service is annotated as lazy, add it to the lazy list
// for later loading. Otherwise, it can be added to the list of
// available services immediately.
if (info.getAnnotation().lazy()) {
lazyServiceList.add(c);
}
else {
serviceList.add(c);
}
}
catch (final Throwable e) {
error("Invalid service: " + info, e);
Expand All @@ -241,5 +295,4 @@ private void debug(final String msg) {
final LogService log = getContext().getService(LogService.class);
if (log != null) log.debug(msg);
}

}