Skip to content

Commit 2cb16a6

Browse files
committed
Add default behavior for service accessors
It is easy and reasonable to fall back to asking the context directly for the service. However, there is a downside: services which do not override these methods to return their injected field values may have subtle bugs relating to service dependencies and initialization order. For example, InputService has a method EventService, which is intended to never return null. In other words: it is intended that InputService implementations (at least by default) _depend_ on the EventService. If you write: Context ctx = new Context(InputService.class); When DefaultInputService is on the classpath, it will be instantiated, its @parameter EventService field will be noted, and the highest-priority concrete EventService implementation (i.e., DefaultEventService) will then be recursively instantiated and initialized, and so forth. So you will end up with a Context containing a DefaultInputService _and_ all of its service dependencies. But if DefaultInputService neglects to declare an @parameter EventService, the way the service loader is coded right now, the EventService dependency will not be noticed, and the Context will contain only a DefaultInputService, whose eventService() method will return null, resulting in NPE when calling other methods which use it. There is one vital reason to define these default method behaviors anyway, though: to avoid breaking SPI compatibility with downstream components. For example, the net.imagej:imagej-legacy component implements a LegacyConsoleService (ConsoleService is a SingletonService), which was forced to implement the new objectService() method when upgrading its version of scijava-common. Unfortunately, the ImageJ Updater (net.imagej:imagej-updater) has a serious design flaw whereby it updates itself and its dependencies (including scijava-common) first, without updating any downstream libraries (e.g., imagej-legacy). So we end up with a situation where scijava-common is at 2.58.2, after the addition of the newly required objectService() method, while imagej-legacy remains at its old version which does not implement that method. And the Context fails to spin up. Adding these default method behaviors is a simple way around the issue. We should consider whether to remove these default implementations in SJC3. And we should definitely consider how best to fix SciJava Common so that downstream JAR skew cannot taint the Context startup so easily. But for now, it is safest to provide these methods, in case of JAR skew.
1 parent fd0a289 commit 2cb16a6

File tree

7 files changed

+33
-11
lines changed

7 files changed

+33
-11
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@
6464
*/
6565
public interface CommandService extends PTService<Command>, SciJavaService {
6666

67-
EventService eventService();
67+
default EventService eventService() {
68+
return context().getService(EventService.class);
69+
}
6870

69-
ModuleService moduleService();
71+
default ModuleService moduleService() {
72+
return context().getService(ModuleService.class);
73+
}
7074

7175
/** Gets the list of all available {@link Command}s). */
7276
List<CommandInfo> getCommands();

src/main/java/org/scijava/display/DisplayService.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,17 @@
5050
*/
5151
public interface DisplayService extends SciJavaService {
5252

53-
EventService eventService();
53+
default EventService eventService() {
54+
return context().getService(EventService.class);
55+
}
5456

55-
ObjectService objectService();
57+
default ObjectService objectService() {
58+
return context().getService(ObjectService.class);
59+
}
5660

57-
PluginService pluginService();
61+
default PluginService pluginService() {
62+
return context().getService(PluginService.class);
63+
}
5864

5965
/** Gets the currently active display (of any Display type). */
6066
Display<?> getActiveDisplay();

src/main/java/org/scijava/input/InputService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
*/
4646
public interface InputService extends SciJavaService {
4747

48-
EventService eventService();
48+
default EventService eventService() {
49+
return context().getService(EventService.class);
50+
}
4951

5052
InputModifiers getModifiers();
5153

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
*/
4444
public interface ObjectService extends SciJavaService {
4545

46-
EventService eventService();
46+
default EventService eventService() {
47+
return context().getService(EventService.class);
48+
}
4749

4850
/** Gets the index of available objects. */
4951
ObjectIndex<Object> getIndex();

src/main/java/org/scijava/platform/PlatformService.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ public interface PlatformService extends SingletonService<Platform>,
5252
SciJavaService
5353
{
5454

55-
EventService eventService();
55+
default EventService eventService() {
56+
return context().getService(EventService.class);
57+
}
5658

57-
CommandService commandService();
59+
default CommandService commandService() {
60+
return context().getService(CommandService.class);
61+
}
5862

5963
/** Gets the platform handlers applicable to this platform. */
6064
List<Platform> getTargetPlatforms();

src/main/java/org/scijava/plugin/PTService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ public interface PTService<PT extends SciJavaPlugin> extends Service {
8787
* Gets the service responsible for discovering and managing this service's
8888
* plugins.
8989
*/
90-
PluginService pluginService();
90+
default PluginService pluginService() {
91+
return context().getService(PluginService.class);
92+
}
9193

9294
/** Gets the type of plugins managed by this service. */
9395
Class<PT> getPluginType();

src/main/java/org/scijava/plugin/SingletonService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ public interface SingletonService<PT extends SingletonPlugin> extends
5757
{
5858

5959
/** Gets the {@link ObjectService} upon which this service depends. */
60-
ObjectService objectService();
60+
default ObjectService objectService() {
61+
return context().getService(ObjectService.class);
62+
}
6163

6264
/**
6365
* Gets the list of plugin instances. There will be one singleton instance for

0 commit comments

Comments
 (0)