Exception Handling for Injection Interceptor

Remember my post “Circular Injection of Util Classes in EJB 3.0“? There I offered a some kind of ugly solution to the circular dependency problem for managed classes in Java EE 5. In a preceding post (Circular Dependencies of Session Beans) I grumbled about the exception handling in jBoss-5.1. It lets you alone with a meaningless error message and you have to guess what the problem is. Unfortunately my own code presented in the former post is even worse, since it logs problems but ignores them. It wasn’t mentioned for productive use, but it was annoying to me, so here is a little tune up adding exception handling and readable error messages.

Have a look at the class InjectionInterceptor in the post “Circular Injection of Util Classes in EJB 3.0“. The inner class InjectionWalker calls itself recursively and shares the loaded util classes of the surrounding class. We add a new parameter to the constructor of the inner class of type InjectionWalker called parent. At recursion level null (InjectionInterceptor#postConstruct()) null is handed in. On every deeper recursion level the current walker is passed (this). A toString() method is added, that contains the injection hierarchy in a preorder like manner (this.parent != null ? this.parent + "->" : "") + this.target.getClass().getSimpleName(); (e.g. “MyBean->MyFooUtil->MyBarUtil”). Furthermore we create a new exception type InjectionException:

@ApplicationException
public class InjectionException extends RuntimeException {

	private static final long serialVersionUID = -5583296470309401600L;
	
	public InjectionException(String message) {
		super(message);
	}
	
	public InjectionException(String message, Throwable cause) {
		super(message, cause);
	}
	
}

In order to avoid “Caused by” feasts in the recursive calls, a check for injection exceptions is added being thrown upwards without change. The resulting inner class looks like this:

private class InjectionWalker {
	private final Set<Field> fields;
	private final Set<Field> injectedFields;
	private final Object target;
	private final InjectionWalker parent;

	public InjectionWalker(final Object target, final InjectionWalker parent) {
		this.target = target;
		this.fields = new HashSet<Field>(Arrays.asList(target.getClass().getFields()));
		this.injectedFields = new HashSet<Field>((int)(1.5*fields.size()));
		this.parent = parent;
		this.stringRepresentation = (this.parent != null ? this.parent + "->" : "") + this.target.getClass().getSimpleName();
	}

	public void inject() {
		for(final Field field : this.fields) {
		
			if(field.isAnnotationPresent(Inject.class)) {
				
				for(final Object bean : InjectionInterceptor.this.beans) {
					
					if(field.getType().isAssignableFrom(bean.getClass())) {
						try {
							field.set(this.target, bean);
							this.injectedFields.add(field);
						}
						catch (Exception e) {
							throw new InjectionException(this + "-InjectionWalker#inject(): target: " +
									this.target + ", field: " + field.getName(), e);
						}
					}
				}
				for(final Map.Entry<Class<?>, Object> utilEntry : InjectionInterceptor.this.utils.entrySet()) {
					
					if(field.getType().isAssignableFrom(utilEntry.getKey())) {
						try {
							if(utilEntry.getValue() == null) {
								final Object utilInstance = utilEntry.getKey().newInstance();
								// prevents endless loops, because of circular dependencies between util classes.
								utilEntry.setValue(utilInstance);
								final InjectionWalker injectionWalker = new InjectionWalker(utilInstance, this);
								InjectionInterceptor.this.injectionWalkers.add(injectionWalker);
								injectionWalker.inject();
							}
							field.set(this.target, utilEntry.getValue());
							this.injectedFields.add(field);
						}
						catch(InjectionException e) {
							throw e;
						}
						catch (Exception e) {
							throw new InjectionException(this + "-InjectionWalker#inject(): target: " +
									this.target + ", field: " + field.getName(), e);
						}
					}
				}
				if (!this.injectedFields.contains(field)) {
					throw new InjectionException(this + "-InjectionWalker#inject(): injection failed - no implementation found: " + 
							field.getDeclaringClass().getSimpleName() + "#" + field.getName());
				}
			}
		}
	}
	
	private final String stringRepresentation;
	@Override
	public String toString() {
		return stringRepresentation;
	}

	public void clear() {
		if(this.injectedFields != null) {
			for(final Field field : this.injectedFields) {
				try {
					field.set(target, null);
				}
				catch(Throwable t) {
					log.fatal(this + "-InjectionWalker#clear(): target: " + this.target + ", field: " + field.getName(), t);
				}
			}
		}
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Captcha * Time limit is exhausted. Please reload CAPTCHA.