Skip to content

Commit 70c50f4

Browse files
committed
Merge branch 'command-context-injection'
This branch fixes a serious flaw in how Service and Context parameters of Commands were being handled. It also provides a unit test for the new parameter validater function mechanism, which doubles as a test for the fixed Service parameter injection behavior. This branch is dedicated to Richard Domander.
2 parents 199360e + 1ada80f commit 70c50f4

File tree

7 files changed

+81
-29
lines changed

7 files changed

+81
-29
lines changed

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import java.util.Map;
4242

4343
import org.scijava.Cancelable;
44-
import org.scijava.Context;
4544
import org.scijava.InstantiableException;
4645
import org.scijava.ItemIO;
4746
import org.scijava.ItemVisibility;
@@ -55,7 +54,6 @@
5554
import org.scijava.plugin.Parameter;
5655
import org.scijava.plugin.Plugin;
5756
import org.scijava.plugin.PluginInfo;
58-
import org.scijava.service.Service;
5957
import org.scijava.util.ClassUtils;
6058
import org.scijava.util.StringMaker;
6159

@@ -451,11 +449,6 @@ private void checkFields(final Class<?> type) {
451449
for (final Field f : fields) {
452450
f.setAccessible(true); // expose private fields
453451

454-
// NB: Skip types handled by the application framework itself.
455-
// I.e., these parameters get injected by Context#inject(Object).
456-
if (Service.class.isAssignableFrom(f.getType())) continue;
457-
if (Context.class.isAssignableFrom(f.getType())) continue;
458-
459452
final Parameter param = f.getAnnotation(Parameter.class);
460453

461454
boolean valid = true;

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.scijava.InstantiableException;
4040
import org.scijava.NullContextException;
4141
import org.scijava.module.AbstractModule;
42-
import org.scijava.module.MethodCallException;
4342
import org.scijava.module.Module;
4443
import org.scijava.module.ModuleException;
4544
import org.scijava.module.ModuleInfo;
@@ -144,13 +143,6 @@ public void cancel() {
144143
previewPlugin.cancel();
145144
}
146145

147-
@Override
148-
public void initialize() throws MethodCallException {
149-
// NB: Inject the context into the command before initializing.
150-
getContext().inject(command);
151-
super.initialize();
152-
}
153-
154146
@Override
155147
public CommandInfo getInfo() {
156148
return info;

src/main/java/org/scijava/module/process/GatewayPreprocessor.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
*
5353
* @author Curtis Rueden
5454
*/
55-
@Plugin(type = PreprocessorPlugin.class,
56-
priority = Priority.VERY_HIGH_PRIORITY)
55+
@Plugin(type = PreprocessorPlugin.class, //
56+
priority = 2 * Priority.VERY_HIGH_PRIORITY)
5757
public class GatewayPreprocessor extends AbstractPreprocessorPlugin {
5858

5959
@Parameter
@@ -87,22 +87,22 @@ private <G extends Gateway> void setGatewayValue(final Context context,
8787
try {
8888
gateway = type.getConstructor(Context.class).newInstance(context);
8989
}
90-
catch (IllegalArgumentException exc) {
90+
catch (final IllegalArgumentException exc) {
9191
exception = exc;
9292
}
93-
catch (SecurityException exc) {
93+
catch (final SecurityException exc) {
9494
exception = exc;
9595
}
96-
catch (InstantiationException exc) {
96+
catch (final InstantiationException exc) {
9797
exception = exc;
9898
}
99-
catch (IllegalAccessException exc) {
99+
catch (final IllegalAccessException exc) {
100100
exception = exc;
101101
}
102-
catch (InvocationTargetException exc) {
102+
catch (final InvocationTargetException exc) {
103103
exception = exc;
104104
}
105-
catch (NoSuchMethodException exc) {
105+
catch (final NoSuchMethodException exc) {
106106
exception = exc;
107107
}
108108
if (exception != null) {

src/main/java/org/scijava/module/process/SaveInputsPreprocessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
* <p>
4444
* This preprocessor runs late in the chain, giving other preprocessors every
4545
* chance to populate the inputs first. In particular, it executes after the
46-
* {@link org.scijava.widget.InputHarvester} has run, so that user-specified values
47-
* are persisted for next time.
46+
* {@link org.scijava.widget.InputHarvester} has run, so that user-specified
47+
* values are persisted for next time.
4848
* </p>
4949
*
5050
* @author Curtis Rueden

src/main/java/org/scijava/module/process/ServicePreprocessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
*
6262
* @author Curtis Rueden
6363
*/
64-
@Plugin(type = PreprocessorPlugin.class,
65-
priority = Priority.VERY_HIGH_PRIORITY)
64+
@Plugin(type = PreprocessorPlugin.class, //
65+
priority = 2 * Priority.VERY_HIGH_PRIORITY)
6666
public class ServicePreprocessor extends AbstractPreprocessorPlugin {
6767

6868
// -- ModuleProcessor methods --

src/main/java/org/scijava/module/process/ValidityPreprocessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
* @author Curtis Rueden
4545
*/
4646
@Plugin(type = PreprocessorPlugin.class,
47-
priority = Priority.VERY_HIGH_PRIORITY + 1)
47+
priority = 3 * Priority.VERY_HIGH_PRIORITY)
4848
public class ValidityPreprocessor extends AbstractPreprocessorPlugin {
4949

5050
// -- ModuleProcessor methods --

src/test/java/org/scijava/command/CommandModuleTest.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,24 @@
3333

3434
import static org.junit.Assert.assertEquals;
3535
import static org.junit.Assert.assertFalse;
36+
import static org.junit.Assert.assertNotNull;
3637
import static org.junit.Assert.assertTrue;
3738

3839
import java.util.concurrent.ExecutionException;
3940

4041
import org.junit.Test;
4142
import org.scijava.Cancelable;
4243
import org.scijava.Context;
44+
import org.scijava.ItemIO;
45+
import org.scijava.Priority;
46+
import org.scijava.log.LogService;
4347
import org.scijava.module.Module;
48+
import org.scijava.module.ModuleItem;
4449
import org.scijava.module.process.AbstractPreprocessorPlugin;
4550
import org.scijava.module.process.PreprocessorPlugin;
4651
import org.scijava.plugin.Parameter;
4752
import org.scijava.plugin.Plugin;
53+
import org.scijava.service.Service;
4854

4955
/** Regression tests for {@link CommandModule}. */
5056
public class CommandModuleTest {
@@ -64,7 +70,6 @@ public void testCancelable() throws InterruptedException, ExecutionException {
6470
assertFalse(crow.isCanceled());
6571
}
6672

67-
6873
@Test
6974
public void testNotCancelable() throws InterruptedException,
7075
ExecutionException
@@ -96,6 +101,17 @@ public void testDefaultValues() {
96101
assertEquals(null, info.getInput("thing").getDefaultValue());
97102
}
98103

104+
@Test
105+
public void testValidation() throws InterruptedException, ExecutionException {
106+
final Context context = new Context(CommandService.class);
107+
final CommandService commandService = context.service(CommandService.class);
108+
109+
final CommandModule module = //
110+
commandService.run(CommandWithValidation.class, true).get();
111+
assertNotNull(module.getInput("stuff"));
112+
assertEquals("success", module.getOutput("result"));
113+
}
114+
99115
// -- Helper classes --
100116

101117
/** A command which implements {@link Cancelable}. */
@@ -168,4 +184,55 @@ public void run() {
168184
time = 0;
169185
}
170186
}
187+
188+
/** A command which validates an input. */
189+
@Plugin(type = Command.class)
190+
public static class CommandWithValidation extends ContextCommand {
191+
192+
@Parameter
193+
private LogService log;
194+
195+
@Parameter(validater = "validateStuff")
196+
private Stuff stuff;
197+
198+
@Parameter(type = ItemIO.OUTPUT)
199+
private String result = "default";
200+
201+
@SuppressWarnings("unused")
202+
private void validateStuff() {
203+
final StringBuilder sb = new StringBuilder();
204+
if (log == null) sb.append("[null-log] ");
205+
if (stuff == null) sb.append("[null-stuff] ");
206+
result = sb.length() == 0 ? "success" : sb.toString();
207+
}
208+
209+
@Override
210+
public void run() {
211+
if (!result.equals("success")) result += " failure";
212+
}
213+
}
214+
215+
/**
216+
* Preprocessor to inject {@link Stuff} instances very early. But (in theory)
217+
* not as early as {@link Service} and {@link Context} parameters get
218+
* populated.
219+
*/
220+
@Plugin(type = PreprocessorPlugin.class,
221+
priority = Priority.VERY_HIGH_PRIORITY)
222+
public static class StuffPreprocessor extends AbstractPreprocessorPlugin {
223+
224+
@Override
225+
public void process(final Module module) {
226+
for (final ModuleItem<?> input : module.getInfo().inputs()) {
227+
if (Stuff.class.isAssignableFrom(input.getType())) {
228+
module.setInput(input.getName(), new Stuff());
229+
module.resolveInput(input.getName());
230+
}
231+
}
232+
}
233+
234+
}
235+
236+
/** Placeholder class, for type safety. */
237+
public static class Stuff {}
171238
}

0 commit comments

Comments
 (0)