package org.jboss.seam.ioc.guice; import org.jboss.seam.Component; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.util.Reflections; import org.jboss.seam.util.Strings; import org.jboss.seam.annotations.intercept.AroundInvoke; import org.jboss.seam.annotations.intercept.Interceptor; import org.jboss.seam.core.Expressions; import org.jboss.seam.intercept.InvocationContext; import org.jboss.seam.intercept.AbstractInterceptor; import org.jboss.seam.log.Log; import org.jboss.seam.log.Logging; import com.google.inject.Injector; import com.google.inject.Inject; import java.lang.reflect.Field; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * Triggers Guice injection on a Seam component. * * @author Pawel Wrzeszcz (pwrzeszcz [at] jboss . org) * @author Tomasz Szymanski (tszymanski [at] jboss.org) */ @Interceptor public class GuiceInterceptor extends AbstractInterceptor { private static final Log log = Logging.getLog(GuiceInterceptor.class); private static final long serialVersionUID = -6716553117162905303L; private static final String GUICE_COMPONENT_FIELDS_MAP = "org.jboss.seam.GuiceComponentFieldsMap"; private transient Injector defaultInjector = null; @AroundInvoke public Object aroundInvoke(InvocationContext invocationContext) throws Exception { inject(invocationContext.getTarget()); Object result = invocationContext.proceed(); disinject(invocationContext.getTarget()); return result; } private void inject(Object target) { if (log.isTraceEnabled()) { log.trace("Injecting members of component '#0'", getComponent().getName()); } getGuiceInjector().injectMembers(target); } private void disinject(Object target) throws Exception { for (Field guiceField : getGuiceAnnotatedFields()) { if (!guiceField.isAccessible()) { guiceField.setAccessible(true); } Reflections.set(guiceField, target, null); } } /** * @return a Guice injector for the current component */ private Injector getGuiceInjector() { final String expr; Guice guice = getComponent().getBeanClass().getAnnotation(Guice.class); if (guice != null) { expr = guice.value(); } else { expr = null; } // Optimize lookups for default injector return Strings.isEmpty(expr) ? getCachedDefaultInjector() : getInjectorByName(expr); } private Injector getCachedDefaultInjector() { if (defaultInjector == null) { GuiceInit init = (GuiceInit) Component.getInstance(GuiceInit.class); if (init != null) { defaultInjector = init.getInjector(); } if (defaultInjector == null) { throw new IllegalStateException("Default Guice injector not specified."); } } return defaultInjector; } private static Injector getInjectorByName(final String expr) { Object result; if (expr.startsWith("#")) { result = Expressions.instance().createValueExpression(expr).getValue(); } else { result = Component.getInstance(expr); } if (!(result instanceof Injector)) { throw new IllegalArgumentException("Expression '" + expr + "' does not evaluate to a Guice injector."); } return (Injector) result; } /** * @return a collection of all component fields injected by Guice (annotated with the @Inject annotation) */ private Collection<Field> getGuiceAnnotatedFields() { final Map<Class,Collection<Field>> fieldsMap = getGuiceComponentFieldsMap(); Collection<Field> annotatedFields = fieldsMap.get(getComponent().getBeanClass()); if (annotatedFields == null) { annotatedFields = Reflections.getFields(getComponent().getBeanClass(), Inject.class); fieldsMap.put(getComponent().getBeanClass(), annotatedFields); } return annotatedFields; } /** * @return a cache that stores fields annotated with the @Inject annotation for the Guice component classes */ @SuppressWarnings("unchecked") private Map<Class,Collection<Field>> getGuiceComponentFieldsMap() { if (Contexts.getApplicationContext().get(GUICE_COMPONENT_FIELDS_MAP) == null) { Contexts.getApplicationContext().set(GUICE_COMPONENT_FIELDS_MAP, new HashMap<Class, Collection<Field>>()); } return (Map<Class, Collection<Field>>) Contexts.getApplicationContext().get(GUICE_COMPONENT_FIELDS_MAP); } public boolean isInterceptorEnabled() { return true; } }