Skip to content

Commit c9830fb

Browse files
committed
Context: try hard not to die in non-strict mode
When Context#inject(Object) is called, there are all kinds of crazy things that can potentially happen, particularly class loading failures (e.g., NoClassDefFoundError) due to missing dependencies when scanning the given object's fields and methods. When the context is in non-strict mode, we want to try very hard not to throw an exception, but instead simply log an error, when something goes wrong during the injection. And we want the injection to proceed tenaciously, so that if something goes wrong at one stage, other stages still might complete successfully. This commit is best viewed with git's "-b" flag.
1 parent 070f04d commit c9830fb

File tree

1 file changed

+57
-30
lines changed

1 file changed

+57
-30
lines changed

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

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.scijava.event.ContextDisposingEvent;
4343
import org.scijava.event.EventHandler;
4444
import org.scijava.event.EventService;
45+
import org.scijava.log.LogService;
4546
import org.scijava.plugin.Parameter;
4647
import org.scijava.plugin.PluginIndex;
4748
import org.scijava.service.Service;
@@ -400,46 +401,72 @@ public static List<Class<? extends Service>> serviceClassList(
400401
// -- Helper methods --
401402

402403
private List<Field> getParameterFields(Object o) {
403-
return ClassUtils.getAnnotatedFields(o.getClass(), Parameter.class);
404+
try {
405+
return ClassUtils.getAnnotatedFields(o.getClass(), Parameter.class);
406+
}
407+
catch (final Throwable t) {
408+
handleSafely(t);
409+
}
410+
return Collections.emptyList();
404411
}
405412

406413
private void inject(final Field f, final Object o) {
407-
f.setAccessible(true); // expose private fields
408-
409-
final Class<?> type = f.getType();
410-
if (Service.class.isAssignableFrom(type)) {
411-
final Service existingService = (Service) ClassUtils.getValue(f, o);
412-
if (existingService != null) {
413-
throw new IllegalStateException("Context already injected: " +
414-
f.getDeclaringClass().getName() + "#" + f.getName());
414+
try {
415+
f.setAccessible(true); // expose private fields
416+
417+
final Class<?> type = f.getType();
418+
if (Service.class.isAssignableFrom(type)) {
419+
final Service existingService = (Service) ClassUtils.getValue(f, o);
420+
if (existingService != null) {
421+
throw new IllegalStateException("Context already injected: " +
422+
f.getDeclaringClass().getName() + "#" + f.getName());
423+
}
424+
425+
// populate Service parameter
426+
@SuppressWarnings("unchecked")
427+
final Class<? extends Service> serviceType =
428+
(Class<? extends Service>) type;
429+
final Service service = getService(serviceType);
430+
if (service == null && f.getAnnotation(Parameter.class).required()) {
431+
throw new IllegalArgumentException(
432+
createMissingServiceMessage(serviceType));
433+
}
434+
ClassUtils.setValue(f, o, service);
415435
}
416-
417-
// populate Service parameter
418-
@SuppressWarnings("unchecked")
419-
final Class<? extends Service> serviceType =
420-
(Class<? extends Service>) type;
421-
final Service service = getService(serviceType);
422-
if (service == null && f.getAnnotation(Parameter.class).required()) {
423-
throw new IllegalArgumentException(
424-
createMissingServiceMessage(serviceType));
436+
else if (Context.class.isAssignableFrom(type) && type.isInstance(this)) {
437+
final Context existingContext = (Context) ClassUtils.getValue(f, o);
438+
if (existingContext != null) {
439+
throw new IllegalStateException("Context already injected: " +
440+
f.getDeclaringClass().getName() + "#" + f.getName());
441+
}
442+
443+
// populate Context parameter
444+
ClassUtils.setValue(f, o, this);
425445
}
426-
ClassUtils.setValue(f, o, service);
427446
}
428-
else if (Context.class.isAssignableFrom(type) && type.isInstance(this)) {
429-
final Context existingContext = (Context) ClassUtils.getValue(f, o);
430-
if (existingContext != null) {
431-
throw new IllegalStateException("Context already injected: " +
432-
f.getDeclaringClass().getName() + "#" + f.getName());
433-
}
434-
435-
// populate Context parameter
436-
ClassUtils.setValue(f, o, this);
447+
catch (final Throwable t) {
448+
handleSafely(t);
437449
}
438450
}
439451

440452
private void subscribeToEvents(final Object o) {
441-
final EventService eventService = getService(EventService.class);
442-
if (eventService != null) eventService.subscribe(o);
453+
try {
454+
final EventService eventService = getService(EventService.class);
455+
if (eventService != null) eventService.subscribe(o);
456+
}
457+
catch (final Throwable t) {
458+
handleSafely(t);
459+
}
460+
}
461+
462+
private void handleSafely(final Throwable t) {
463+
if (isStrict()) {
464+
// NB: Only rethrow unchecked exceptions.
465+
if (t instanceof RuntimeException) throw (RuntimeException) t;
466+
if (t instanceof Error) throw (Error) t;
467+
}
468+
final LogService log = getService(LogService.class);
469+
if (log != null) log.error(t);
443470
}
444471

445472
private String createMissingServiceMessage(

0 commit comments

Comments
 (0)