Skip to content

Commit f892ca9

Browse files
committed
Merge pull request #103 from scijava/usage-statistics
Usage statistics service
2 parents 858afc8 + 3d396bf commit f892ca9

17 files changed

+724
-42
lines changed

src/main/java/org/scijava/Context.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ public Context(final boolean empty) {
117117
* </p>
118118
*
119119
* @param serviceClasses A list of types that implement the {@link Service}
120-
* interface (e.g., {@code DisplayService.class}).
120+
* interface (e.g., {@code DisplayService.class}). Compatible
121+
* services will be loaded in the order given,
122+
* <em>regardless of their relative priorities</em>.
121123
* @see #Context(Collection, PluginIndex, boolean)
122124
* @throws ClassCastException If any of the given arguments do not implement
123125
* the {@link Service} interface.
@@ -132,6 +134,8 @@ public Context(@SuppressWarnings("rawtypes") final Class... serviceClasses) {
132134
*
133135
* @param serviceClasses A collection of types that implement the
134136
* {@link Service} interface (e.g., {@code DisplayService.class}).
137+
* Compatible services will be loaded according to the order of the
138+
* collection, <em>regardless of their relative priorities</em>.
135139
* @see #Context(Collection, PluginIndex, boolean)
136140
*/
137141
public Context(final Collection<Class<? extends Service>> serviceClasses) {
@@ -144,8 +148,10 @@ public Context(final Collection<Class<? extends Service>> serviceClasses) {
144148
*
145149
* @param serviceClasses A collection of types that implement the
146150
* {@link Service} interface (e.g., {@code DisplayService.class}).
151+
* Compatible services will be loaded according to the order of the
152+
* collection, <em>regardless of their relative priorities</em>.
147153
* @param strict Whether context creation will fail fast when there is
148-
* is an error instantiating a required service.
154+
* an error instantiating a required service.
149155
* @see #Context(Collection, PluginIndex, boolean)
150156
*/
151157
public Context(final Collection<Class<? extends Service>> serviceClasses,
@@ -155,10 +161,10 @@ public Context(final Collection<Class<? extends Service>> serviceClasses,
155161
}
156162

157163
/**
158-
* Creates a new SciJava application with the specified PluginIndex. This
159-
* allows a base set of available plugins to be defined, and is useful when
160-
* plugins that would not be returned by the {@link PluginIndex}'s
161-
* {@link org.scijava.plugin.PluginFinder} are desired.
164+
* Creates a new SciJava application context with all available services from
165+
* the specified PluginIndex. This allows a base set of available plugins to
166+
* be defined, and is useful when plugins that would not be returned by the
167+
* {@link PluginIndex}'s {@link org.scijava.plugin.PluginFinder} are desired.
162168
*
163169
* @param pluginIndex The plugin index to use when discovering and indexing
164170
* plugins. If you wish to completely control how services are
@@ -181,6 +187,8 @@ public Context(final PluginIndex pluginIndex) {
181187
*
182188
* @param serviceClasses A collection of types that implement the
183189
* {@link Service} interface (e.g., {@code DisplayService.class}).
190+
* Compatible services will be loaded according to the order of the
191+
* collection, <em>regardless of their relative priorities</em>.
184192
* @param pluginIndex The plugin index to use when discovering and indexing
185193
* plugins. If you wish to completely control how services are
186194
* discovered (i.e., use your own
@@ -212,14 +220,16 @@ public Context(final Collection<Class<? extends Service>> serviceClasses,
212220
*
213221
* @param serviceClasses A collection of types that implement the
214222
* {@link Service} interface (e.g., {@code DisplayService.class}).
223+
* Compatible services will be loaded according to the order of the
224+
* collection, <em>regardless of their relative priorities</em>.
215225
* @param pluginIndex The plugin index to use when discovering and indexing
216226
* plugins. If you wish to completely control how services are
217227
* discovered (i.e., use your own
218228
* {@link org.scijava.plugin.PluginFinder} implementation), then you
219229
* can pass a custom {@link PluginIndex} here. Passing null will
220230
* result in a default plugin index being constructed and used.
221231
* @param strict Whether context creation will fail fast when there is
222-
* is an error instantiating a required service.
232+
* an error instantiating a required service.
223233
*/
224234
public Context(final Collection<Class<? extends Service>> serviceClasses,
225235
final PluginIndex pluginIndex, final boolean strict)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2014 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava;
33+
34+
/**
35+
* An object whose location is defined by a URL string.
36+
*
37+
* @author Curtis Rueden
38+
*/
39+
public interface Locatable {
40+
41+
/** Gets the URL string defining the object's location. */
42+
String getLocation();
43+
44+
}

src/main/java/org/scijava/command/CommandInfo.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.scijava.Identifiable;
4646
import org.scijava.InstantiableException;
4747
import org.scijava.ItemVisibility;
48+
import org.scijava.Locatable;
4849
import org.scijava.ValidityProblem;
4950
import org.scijava.event.EventService;
5051
import org.scijava.module.Module;
@@ -76,7 +77,7 @@
7677
* commands and the rich {@link Module} interface.
7778
*/
7879
public class CommandInfo extends PluginInfo<Command> implements ModuleInfo,
79-
Identifiable
80+
Identifiable, Locatable
8081
{
8182

8283
/** Wrapped {@link PluginInfo}, if any. */
@@ -422,6 +423,18 @@ public String getIdentifier() {
422423
return sb.toString();
423424
}
424425

426+
// -- Locatable methods --
427+
428+
@Override
429+
public String getLocation() {
430+
try {
431+
return ClassUtils.getLocation(loadDelegateClass()).toExternalForm();
432+
}
433+
catch (final ClassNotFoundException exc) {
434+
return null;
435+
}
436+
}
437+
425438
// -- Helper methods --
426439

427440
/**

src/main/java/org/scijava/event/DefaultEventService.java

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.scijava.service.AbstractService;
4949
import org.scijava.service.Service;
5050
import org.scijava.thread.ThreadService;
51+
import org.scijava.util.ClassUtils;
5152

5253
/**
5354
* Default service for publishing and subscribing to SciJava events.
@@ -63,11 +64,6 @@ public class DefaultEventService extends AbstractService implements
6364
/**
6465
* The default event service's priority.
6566
* <p>
66-
* It is paramount that the event service be created and initialized before
67-
* other services. Any service created before the event service may not have
68-
* its event handling methods registered correctly.
69-
* </p>
70-
* <p>
7167
* Alternative event service implementations that wish to prioritize
7268
* themselves above this one can still ensure preferential usage via
7369
* {@code priority = DefaultEventService.PRIORITY + 1} or similar.
@@ -101,7 +97,16 @@ public <E extends SciJavaEvent> void publishLater(final E e) {
10197
public List<EventSubscriber<?>> subscribe(final Object o) {
10298
final List<EventSubscriber<?>> subscribers =
10399
new ArrayList<EventSubscriber<?>>();
104-
subscribeRecursively(subscribers, o.getClass(), o);
100+
final List<Method> eventHandlers =
101+
ClassUtils.getAnnotatedMethods(o.getClass(), EventHandler.class);
102+
for (final Method m : eventHandlers) {
103+
final Class<? extends SciJavaEvent> eventClass = getEventClass(m);
104+
if (eventClass == null) {
105+
log.warn("Invalid EventHandler method: " + m);
106+
continue;
107+
}
108+
subscribers.add(subscribe(eventClass, o, m));
109+
}
105110
return subscribers;
106111
}
107112

@@ -144,30 +149,6 @@ public void dispose() {
144149

145150
// -- Helper methods --
146151

147-
/**
148-
* Recursively scans for @{@link EventHandler} annotated methods, and
149-
* subscribes them to the event service.
150-
*/
151-
private void subscribeRecursively(
152-
final List<EventSubscriber<?>> subscribers, final Class<?> type,
153-
final Object o)
154-
{
155-
if (type == null || type == Object.class) return;
156-
for (final Method m : type.getDeclaredMethods()) {
157-
final EventHandler ann = m.getAnnotation(EventHandler.class);
158-
if (ann == null) continue; // not an event handler method
159-
160-
final Class<? extends SciJavaEvent> eventClass = getEventClass(m);
161-
if (eventClass == null) {
162-
log.warn("Invalid EventHandler method: " + m);
163-
continue;
164-
}
165-
166-
subscribers.add(subscribe(eventClass, o, m));
167-
}
168-
subscribeRecursively(subscribers, type.getSuperclass(), o);
169-
}
170-
171152
private <E extends SciJavaEvent> void subscribe(final Class<E> c,
172153
final EventSubscriber<E> subscriber)
173154
{

src/main/java/org/scijava/module/AbstractModuleInfo.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939

4040
import org.scijava.AbstractUIDetails;
4141
import org.scijava.Identifiable;
42+
import org.scijava.Locatable;
4243
import org.scijava.ValidityProblem;
4344
import org.scijava.event.EventService;
4445
import org.scijava.module.event.ModulesUpdatedEvent;
46+
import org.scijava.util.ClassUtils;
4547
import org.scijava.util.ConversionUtils;
4648

4749
/**
@@ -54,7 +56,7 @@
5456
* @author Curtis Rueden
5557
*/
5658
public abstract class AbstractModuleInfo extends AbstractUIDetails implements
57-
ModuleInfo, Identifiable
59+
ModuleInfo, Identifiable, Locatable
5860
{
5961

6062
/** Table of inputs, keyed on name. */
@@ -170,6 +172,21 @@ public String getIdentifier() {
170172
return "module:" + getDelegateClassName();
171173
}
172174

175+
// -- Locatable methods --
176+
177+
@Override
178+
public String getLocation() {
179+
// NB: By default, we use the location of the delegate class.
180+
// If the same delegate class is used for more than one module, though,
181+
// it may need to override this method to indicate a different location.
182+
try {
183+
return ClassUtils.getLocation(loadDelegateClass()).toExternalForm();
184+
}
185+
catch (final ClassNotFoundException exc) {
186+
return null;
187+
}
188+
}
189+
173190
// -- Internal methods --
174191

175192
/**

src/main/java/org/scijava/module/DefaultModuleService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public Module createModule(final ModuleInfo info) {
152152
return module;
153153
}
154154
catch (final ModuleException exc) {
155-
log.error("Cannot create module: " + info.getDelegateClassName());
155+
log.error("Cannot create module: " + info.getDelegateClassName(), exc);
156156
}
157157
return null;
158158
}

src/main/java/org/scijava/object/DefaultObjectService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
package org.scijava.object;
3333

34-
3534
import java.util.List;
3635

3736
import org.scijava.event.EventHandler;

src/main/java/org/scijava/script/ScriptInfo.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,13 @@ public String getIdentifier() {
278278
return "script:" + path;
279279
}
280280

281+
// -- Locatable methods --
282+
283+
@Override
284+
public String getLocation() {
285+
return new File(path).toURI().normalize().toString();
286+
}
287+
281288
// -- Helper methods --
282289

283290
private void parseParam(final String param) throws ScriptException {

src/main/java/org/scijava/service/ServiceHelper.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.scijava.AbstractContextual;
4242
import org.scijava.Context;
4343
import org.scijava.Optional;
44+
import org.scijava.event.EventHandler;
4445
import org.scijava.event.EventService;
4546
import org.scijava.log.LogService;
4647
import org.scijava.log.StderrLogService;
@@ -305,6 +306,11 @@ private <S extends Service> S createServiceRecursively(final Class<S> c)
305306
final Double priority = classPoolMap.get(c);
306307
if (priority != null) service.setPriority(priority);
307308

309+
// NB: If there are any @EventHandler annotated methods, we treat the
310+
// EventService as a required dependency, _unless_ there is also an
311+
// EventService field annotated with @Parameter(required = false).
312+
boolean eventServiceRequired = true;
313+
308314
// populate service parameters
309315
final List<Field> fields =
310316
ClassUtils.getAnnotatedFields(c, Parameter.class);
@@ -332,10 +338,18 @@ private <S extends Service> S createServiceRecursively(final Class<S> c)
332338
// recursively obtain needed service
333339
final boolean required = f.getAnnotation(Parameter.class).required();
334340
s = loadService(serviceType, required);
341+
// NB: Remember when there is an optional EventService parameter.
342+
if (s instanceof EventService) eventServiceRequired = required;
335343
}
336344
ClassUtils.setValue(f, service, s);
337345
}
338346

347+
// check for event handlers
348+
if (!ClassUtils.getAnnotatedMethods(c, EventHandler.class).isEmpty()) {
349+
// NB: There are @EventHandler methods; we need an EventService.
350+
loadService(EventService.class, eventServiceRequired);
351+
}
352+
339353
service.initialize();
340354
service.registerEventHandlers();
341355
return service;

0 commit comments

Comments
 (0)