users@jersey.java.net

Re: [Jersey] Using Guice with JAX-RS

From: Gili <cowwoc_at_bbs.darktech.org>
Date: Mon, 6 Oct 2008 13:01:40 -0700 (PDT)

Hi Paul,

It is my understanding that if (a class contains no Jersey scope) *and* (is
annotated using Guice's @Inject *or* has a default constructor), then Guice
should be able to construct the class or throw a fatal exception. If a class
contains Jersey scope then Guice may fail to construct it and fall back on
Jersey's built-in constructors.

As far as I understand it, there are 4 possible use-cases:

1) Component annotated with a Jersey scope but no Guice scope
2) Component annotated with a Guice scope but no Jersey scope
3) Component annotated with neither type of scopes
4) Component annotationed with both types of scopes

And here is how I propose handling them:

1) Jersey's @ResourceFactory delegates to a built-in resource provider.
This, in turn, delegates to GuiceComponentProvider. If it returns a null
value Jersey will construct the object itself.

Please note that there is no way to check that the underlying Guice scope
will be the same as the Jersey scope in Guice 1.0. In 2.0 we'll at least be
able to check against the built-in scopes.

2) Jersey will ask Guice to inject the class and fail if Guice doesn't know
how.
3) Same as #1 except that the default scope is used.
4) GuiceComponentProvider will throw a fatal error.

To that end, please review the following implementation and tell me what you
think (I've dropped the GuiceResourceProvider):


/**
 * Integrate Guice into Jersey-based servlets.
 *
 * @see https://jersey.dev.java.net/servlets/ReadMsg?list=users&msgNo=1326
 * @author Gili Tzabari
 */
public class GuiceServlet extends ServletContainer
{
        private static final long serialVersionUID = 0L;
        private static Injector injector;

        @Override
        protected void initiate(ResourceConfig resourceConfig, WebApplication
webapp)
        {
                try
                {
                        ServletConfig servletConfig = getServletConfig();
                        String moduleName = servletConfig.getInitParameter("moduleClass");
                        if (moduleName == null)
                        {
                                throw new IllegalArgumentException("GuiceServlet requires init parameter
\"moduleClass\" that " +
                                        "specifies the fully-qualified path of the Guice module");
                        }
                        Module module = (Module) Class.forName(moduleName).newInstance();
                        GuiceServlet.injector = Guice.createInjector(module);
                        webapp.initiate(resourceConfig, new GuiceComponentProvider(injector));
                }
                catch (ClassNotFoundException e)
                {
                        throw new RuntimeException(e);
                }
                catch (InstantiationException e)
                {
                        throw new RuntimeException(e);
                }
                catch (IllegalAccessException e)
                {
                        throw new RuntimeException(e);
                }
        }

        /**
         * Returns the Guice injector.
         *
         * @return the Guice injector
         */
        public static Injector getInjector()
        {
                assert (injector != null): "getInjector() called before
initiate(ResourceConfig, WebApplication)";
                return injector;
        }
}

/**
 * Uses Guice to inject classes, fields but not method parameters.
 *
 * @author Gili Tzabari
 */
public class GuiceComponentProvider implements ComponentProvider
{
        private final Injector injector;

        /**
         * Creates a new GuiceComponentProvider.
         *
         * @param injector the Guice injector
         */
        public GuiceComponentProvider(Injector injector)
        {
                this.injector = injector;
        }

        @Override
        public <T> T getInjectableInstance(T instance)
        {
                return instance;
        }

        @Override
        public <T> T getInstance(Scope scope, Class<T> clazz)
                throws InstantiationException, IllegalAccessException
        {
                if (scope == Scope.PerRequest && clazz.getAnnotation(PerRequest.class) ==
null)
                {
                        // The default scope is PerRequest, but no annotation was used
                        scope = scope.Undefined;
                }
                if (scope != Scope.Undefined)
                {
                        // Disallow the mixing of Jersey and Guice scopes even if they don't
conflict because we have no way
                        // of knowing about all possible scopes (they are extensible)
                        for (Annotation annotation: clazz.getAnnotations())
                        {
                                if (annotation.annotationType().getAnnotation(ScopeAnnotation.class) !=
null)
                                {
                                        // All Guice scopes are annotated with @ScopeAnnotation
                                        throw new IllegalArgumentException(clazz.getName() + " may not define
both a Jersey scope (" +
                                                scope + ") and a Guice scope (" +
annotation.annotationType().getName() + ")");
                                }
                        }
                }

                try
                {
                        return injector.getInstance(clazz);
                }
                catch (RuntimeException e)
                {
                        // WORKAROUND: Guice 1.0 doesn't allow us to catch
ConfigurationException:
                        // http://code.google.com/p/google-guice/issues/detail?id=68

                        for (Constructor<?> constructor: clazz.getConstructors())
                        {
                                if (constructor.getParameterTypes().length == 0 ||
constructor.getAnnotation(Inject.class) != null)
                                {
                                        // Guice should have been able to inject this class, so we fail
                                        throw e;
                                }
                        }

                        // Assume that Jersey asked Guice to construct internal classes it had no
way of knowing about. Let
                        // Jersey construct the class itself.
                        return null;
                }
        }

        @Override
        public <T> T getInstance(Scope scope, Constructor<T> constructor, Object[]
parameters)
                throws InstantiationException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException
        {
                return getInstance(scope, constructor.getDeclaringClass());
        }

        @Override
        public <T> T getInstance(ComponentContext context, Scope scope, Class<T>
clazz)
                throws InstantiationException, IllegalAccessException
        {
                return getInstance(scope, clazz);
        }

        @Override
        public void inject(Object instance)
        {
                injector.injectMembers(instance);
        }
}


/**
 * Uses Guice to inject {_at_code @Named} method parameters.
 *
 * @author Gili Tzabari
 */
@Provider
public class GuiceNamedInjectable implements InjectableProvider<Named, Type>
{
        @Override
        public Scope getScope()
        {
                return Scope.Undefined;
        }

        @Override
        public Injectable getInjectable(ComponentContext injectionContext, final
Named named,
                final Type userObject)
        {
                return new Injectable()
                {
                        @Override
                        public Object getValue(HttpContext context)
                        {
                                return GuiceServlet.getInjector().getInstance(Key.get(userObject,
named));
                        }
                };
        }
}


Thank you,
Gili
-- 
View this message in context: http://n2.nabble.com/Using-Guice-with-JAX-RS-tp1127230p1301798.html
Sent from the Jersey mailing list archive at Nabble.com.