Skip to content

Commit 5e7cdbd

Browse files
gselzerctrueden
authored andcommitted
Add ModuleErroredEvent to convey thrown exceptions
1 parent 74518c5 commit 5e7cdbd

File tree

4 files changed

+212
-29
lines changed

4 files changed

+212
-29
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111

1212
<artifactId>scijava-common</artifactId>
13-
<version>2.92.1-SNAPSHOT</version>
13+
<version>2.93.0-SNAPSHOT</version>
1414

1515
<name>SciJava Common</name>
1616
<description>SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.</description>

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

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.scijava.event.EventService;
4040
import org.scijava.log.LogService;
4141
import org.scijava.module.event.ModuleCanceledEvent;
42+
import org.scijava.module.event.ModuleErroredEvent;
4243
import org.scijava.module.event.ModuleExecutedEvent;
4344
import org.scijava.module.event.ModuleExecutingEvent;
4445
import org.scijava.module.event.ModuleFinishedEvent;
@@ -144,36 +145,42 @@ public void run() {
144145

145146
final String title = module.getInfo().getTitle();
146147

147-
// announce start of execution process
148-
if (ss != null) ss.showStatus("Running command: " + title);
149-
if (es != null) es.publish(new ModuleStartedEvent(module));
150-
151-
// execute preprocessors
152-
final ModulePreprocessor canceler = preProcess();
153-
if (canceler != null) {
154-
// module execution was canceled by preprocessor
155-
final String reason = canceler.getCancelReason();
156-
cancel(reason);
157-
cleanupAndBroadcastCancelation(title, reason);
158-
return;
148+
try {
149+
// announce start of execution process
150+
if (ss != null) ss.showStatus("Running command: " + title);
151+
if (es != null) es.publish(new ModuleStartedEvent(module));
152+
153+
// execute preprocessors
154+
final ModulePreprocessor canceler = preProcess();
155+
if (canceler != null) {
156+
// module execution was canceled by preprocessor
157+
final String reason = canceler.getCancelReason();
158+
cancel(reason);
159+
cleanupAndBroadcastCancelation(title, reason);
160+
return;
161+
}
162+
163+
// execute module
164+
if (es != null) es.publish(new ModuleExecutingEvent(module));
165+
module.run();
166+
if (isCanceled()) {
167+
// module execution was canceled by the module itself
168+
cleanupAndBroadcastCancelation(title, getCancelReason());
169+
return;
170+
}
171+
if (es != null) es.publish(new ModuleExecutedEvent(module));
172+
173+
// execute postprocessors
174+
postProcess();
175+
176+
// announce completion of execution process
177+
if (es != null) es.publish(new ModuleFinishedEvent(module));
178+
if (ss != null) ss.showStatus("Command finished: " + title);
159179
}
160-
161-
// execute module
162-
if (es != null) es.publish(new ModuleExecutingEvent(module));
163-
module.run();
164-
if (isCanceled()) {
165-
// module execution was canceled by the module itself
166-
cleanupAndBroadcastCancelation(title, getCancelReason());
167-
return;
180+
catch (final Throwable t) {
181+
cleanupAndBroadcastException(title, t);
182+
throw t;
168183
}
169-
if (es != null) es.publish(new ModuleExecutedEvent(module));
170-
171-
// execute postprocessors
172-
postProcess();
173-
174-
// announce completion of execution process
175-
if (es != null) es.publish(new ModuleFinishedEvent(module));
176-
if (ss != null) ss.showStatus("Command finished: " + title);
177184
}
178185

179186
// -- Helper methods --
@@ -190,6 +197,16 @@ private void cleanupAndBroadcastCancelation(final String title,
190197
}
191198
}
192199

200+
private void cleanupAndBroadcastException(final String title,
201+
final Throwable t)
202+
{
203+
if (es != null) es.publish(new ModuleErroredEvent(module, t));
204+
if (ss != null) {
205+
ss.showStatus("Module errored: " + title);
206+
if (t != null) ss.warn(t.getMessage());
207+
}
208+
}
209+
193210
private boolean isCanceled() {
194211
return module instanceof Cancelable && ((Cancelable) module).isCanceled();
195212
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2023 SciJava developers.
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
30+
package org.scijava.module.event;
31+
32+
import org.scijava.module.Module;
33+
34+
/**
35+
* An event indicating a module execution has thrown an exception.
36+
*
37+
* @author Gabriel Selzer
38+
*/
39+
public class ModuleErroredEvent extends ModuleExecutionEvent {
40+
41+
private final Throwable exc;
42+
43+
public ModuleErroredEvent(final Module module, final Throwable exc) {
44+
super(module);
45+
this.exc = exc;
46+
}
47+
48+
public Throwable getException() {
49+
return exc;
50+
}
51+
52+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*-
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2023 SciJava developers.
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
30+
package org.scijava.module.event;
31+
32+
import static org.junit.Assert.assertThrows;
33+
import static org.junit.Assert.assertTrue;
34+
35+
import org.junit.Before;
36+
import org.junit.Test;
37+
import org.scijava.Context;
38+
import org.scijava.event.EventHandler;
39+
import org.scijava.event.EventService;
40+
import org.scijava.module.AbstractModule;
41+
import org.scijava.module.AbstractModuleInfo;
42+
import org.scijava.module.Module;
43+
import org.scijava.module.ModuleException;
44+
import org.scijava.module.ModuleInfo;
45+
import org.scijava.module.ModuleService;
46+
47+
/**
48+
* Tests {@link ModuleErroredEvent} behavior.
49+
*
50+
* @author Gabriel Selzer
51+
*/
52+
public class ModuleErroredEventTest {
53+
54+
private EventService es;
55+
private ModuleService module;
56+
57+
@Before
58+
public void setUp() {
59+
Context ctx = new Context();
60+
es = ctx.getService(EventService.class);
61+
module = ctx.getService(ModuleService.class);
62+
}
63+
64+
@Test
65+
public void testModuleErroredEvent() {
66+
67+
// Must be a final boolean array to be included in the below closure
68+
final boolean[] caughtException = { false };
69+
70+
// Add a new EventHandler to change our state
71+
es.subscribe(new Object() {
72+
73+
@EventHandler
74+
void onEvent(final ModuleErroredEvent e) {
75+
caughtException[0] = true;
76+
}
77+
});
78+
79+
// Run the module, ensure we get the exception
80+
assertThrows(Exception.class, //
81+
() -> module.run(new TestModuleInfo(), false).get());
82+
assertTrue(caughtException[0]);
83+
}
84+
85+
static class TestModuleInfo extends AbstractModuleInfo {
86+
87+
@Override
88+
public String getDelegateClassName() {
89+
return this.getClass().getName();
90+
}
91+
92+
@Override
93+
public Class<?> loadDelegateClass() throws ClassNotFoundException {
94+
return this.getClass();
95+
}
96+
97+
@Override
98+
public Module createModule() throws ModuleException {
99+
ModuleInfo thisInfo = this;
100+
return new AbstractModule() {
101+
102+
@Override
103+
public ModuleInfo getInfo() {
104+
return thisInfo;
105+
}
106+
107+
@Override
108+
public void run() {
109+
throw new RuntimeException("Yay!");
110+
}
111+
};
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)