4242import org .scijava .event .ContextDisposingEvent ;
4343import org .scijava .event .EventHandler ;
4444import org .scijava .event .EventService ;
45+ import org .scijava .log .LogService ;
4546import org .scijava .plugin .Parameter ;
4647import org .scijava .plugin .PluginIndex ;
4748import org .scijava .service .Service ;
@@ -62,10 +63,11 @@ public class Context implements Disposable {
6263
6364 /**
6465 * System property indicating whether the context should fail fast when
65- * is attempts to instantiate a required service which is invalid or missing.
66+ * attempting to instantiate a required service which is invalid or missing.
6667 * If this property is set to "false" then the context creation will attempt
6768 * to continue even when a required service cannot be instantiated. Otherwise,
68- * the constructor will throw an {@link IllegalArgumentException} in that situation.
69+ * the constructor will throw an {@link IllegalArgumentException} in that
70+ * situation.
6971 */
7072 public static final String STRICT_PROPERTY = "scijava.context.strict" ;
7173
@@ -77,6 +79,23 @@ public class Context implements Disposable {
7779 /** Master index of all plugins known to the application context. */
7880 private final PluginIndex pluginIndex ;
7981
82+ /**
83+ * Whether context creation and injection should behave strictly, failing fast
84+ * when attempting to instantiate a required service which is invalid or
85+ * missing.
86+ * <ul>
87+ * <li>If the flag is false, then the context creation will attempt to
88+ * continue even when a required service cannot be instantiated. Otherwise,
89+ * the constructor will throw an {@link IllegalArgumentException} in that
90+ * situation.</li>
91+ * <li>If this flag is false, then a call to {@link Context#inject(Object)}
92+ * will attempt to catch any errors that occur during context injection
93+ * (notably: {@link NoClassDefFoundError} when scanning for event handler
94+ * methods), logging them as errors.</li>
95+ * </ul>
96+ */
97+ private boolean strict ;
98+
8099 /**
81100 * Creates a new SciJava application context with all available services.
82101 *
@@ -239,6 +258,8 @@ public Context(final Collection<Class<? extends Service>> serviceClasses,
239258 this .pluginIndex = pluginIndex == null ? new PluginIndex () : pluginIndex ;
240259 this .pluginIndex .discover ();
241260
261+ setStrict (strict );
262+
242263 final ServiceHelper serviceHelper =
243264 new ServiceHelper (this , serviceClasses , strict );
244265 serviceHelper .loadServices ();
@@ -254,6 +275,14 @@ public PluginIndex getPluginIndex() {
254275 return pluginIndex ;
255276 }
256277
278+ public boolean isStrict () {
279+ return strict ;
280+ }
281+
282+ public void setStrict (final boolean strict ) {
283+ this .strict = strict ;
284+ }
285+
257286 /**
258287 * Gets the service of the given class.
259288 *
@@ -329,46 +358,14 @@ public Service getService(final String className) {
329358 */
330359 public void inject (final Object o ) {
331360 // iterate over all @Parameter annotated fields
332- final List <Field > fields =
333- ClassUtils .getAnnotatedFields (o .getClass (), Parameter .class );
361+ final List <Field > fields = getParameterFields (o );
334362 for (final Field f : fields ) {
335- f .setAccessible (true ); // expose private fields
336-
337- final Class <?> type = f .getType ();
338- if (Service .class .isAssignableFrom (type )) {
339- final Service existingService = (Service ) ClassUtils .getValue (f , o );
340- if (existingService != null ) {
341- throw new IllegalStateException ("Context already injected: " +
342- f .getDeclaringClass ().getName () + "#" + f .getName ());
343- }
344-
345- // populate Service parameter
346- @ SuppressWarnings ("unchecked" )
347- final Class <? extends Service > serviceType =
348- (Class <? extends Service >) type ;
349- final Service service = getService (serviceType );
350- if (service == null && f .getAnnotation (Parameter .class ).required ()) {
351- throw new IllegalArgumentException (
352- createMissingServiceMessage (serviceType ));
353- }
354- ClassUtils .setValue (f , o , service );
355- }
356- else if (Context .class .isAssignableFrom (type ) && type .isInstance (this )) {
357- final Context existingContext = (Context ) ClassUtils .getValue (f , o );
358- if (existingContext != null ) {
359- throw new IllegalStateException ("Context already injected: " +
360- f .getDeclaringClass ().getName () + "#" + f .getName ());
361- }
362-
363- // populate Context parameter
364- ClassUtils .setValue (f , o , this );
365- }
363+ inject (f , o );
366364 }
367365
368366 // NB: Subscribe to all events handled by this object.
369367 // This greatly simplifies event handling.
370- final EventService eventService = getService (EventService .class );
371- if (eventService != null ) eventService .subscribe (o );
368+ subscribeToEvents (o );
372369 }
373370
374371 // -- Disposable methods --
@@ -403,6 +400,75 @@ public static List<Class<? extends Service>> serviceClassList(
403400
404401 // -- Helper methods --
405402
403+ private List <Field > getParameterFields (Object o ) {
404+ try {
405+ return ClassUtils .getAnnotatedFields (o .getClass (), Parameter .class );
406+ }
407+ catch (final Throwable t ) {
408+ handleSafely (t );
409+ }
410+ return Collections .emptyList ();
411+ }
412+
413+ private void inject (final Field f , final Object o ) {
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 );
435+ }
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 );
445+ }
446+ }
447+ catch (final Throwable t ) {
448+ handleSafely (t );
449+ }
450+ }
451+
452+ private void subscribeToEvents (final Object 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 );
470+ }
471+
406472 private String createMissingServiceMessage (
407473 final Class <? extends Service > serviceType )
408474 {
0 commit comments