Skip to content

Commit e4ae9ee

Browse files
ctruedenclaude
andcommitted
Add test for singleton plugin injecting its managing service
Regression test for the circular reference scenario where a SingletonPlugin has an @parameter for its managing SingletonService. Historically the service's not-yet-complete initialization could prevent the parameter from being filled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 041acb4 commit e4ae9ee

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

src/test/java/org/scijava/plugin/SingletonServiceTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static org.junit.Assert.assertNotNull;
3535
import static org.junit.Assert.assertNotSame;
3636
import static org.junit.Assert.assertNull;
37+
import static org.junit.Assert.assertSame;
3738
import static org.junit.Assert.assertTrue;
3839

3940
import java.util.List;
@@ -249,6 +250,49 @@ public void testListenToRemove() {
249250
.getInstances().size());
250251
}
251252

253+
/**
254+
* Tests that a {@link SingletonPlugin} with an {@code @Parameter} for its
255+
* managing {@link SingletonService} has that parameter successfully injected.
256+
* This is a circular reference scenario: the service instantiates plugins
257+
* during initialization, and those plugins in turn require the service itself
258+
* to be injected. Historically SciJava Common had trouble with this pattern.
259+
*/
260+
@Test
261+
public void testSingletonPluginInjectsManagingService() {
262+
final Context ctx = new Context(PluginService.class, FooService.class);
263+
try {
264+
final FooService fooService = ctx.getService(FooService.class);
265+
266+
final List<FooPlugin> instances = fooService.getInstances();
267+
assertEquals(1, instances.size());
268+
269+
final FooPlugin fooPlugin = instances.get(0);
270+
assertNotNull("FooService was not injected into FooPlugin",
271+
fooPlugin.fooService);
272+
assertSame(fooService, fooPlugin.fooService);
273+
}
274+
finally {
275+
ctx.dispose();
276+
}
277+
}
278+
279+
@Plugin(type = FooPlugin.class)
280+
public static class FooPlugin extends AbstractRichPlugin implements
281+
SingletonPlugin
282+
{
283+
284+
@Parameter
285+
FooService fooService;
286+
}
287+
288+
public static class FooService extends AbstractSingletonService<FooPlugin> {
289+
290+
@Override
291+
public Class<FooPlugin> getPluginType() {
292+
return FooPlugin.class;
293+
}
294+
}
295+
252296
@Plugin(type = DummyPlugin.class)
253297
public static class DummyPlugin extends AbstractRichPlugin implements
254298
SingletonPlugin

0 commit comments

Comments
 (0)