users@jersey.java.net

Re: [Jersey] Using Guice with JAX-RS

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 07 Oct 2008 12:08:55 +0200

Hi,

You should not have to check for the PerRequest annotation. It should
be sufficient to use the "scope" that Jersey is requesting. It seems
to me there are some cases that may be reliably consistent to check:

1) scope == Scope.PerRequest and the class is not annotated Guice's
@Singleton or @SessionScoped
      or an annotation that is annotated with Guice's @ScopeAnnotation.

2) scope == Scope.PerRequest and the class is annotated with Guice's
@RequestScoped
     My guess is that should work fine as from Jerseys perspective a
new instance will
     be used for the period of the request/response.

3) scope == Scope.Singleton and the class is annotated with Guice's
@Singleton.


It is frustrating that one cannot tell Guice which constructor to use
with a list of constructor parameters, where null values mean Guice
should determine the parameters.

I think we should review this whole area for Jersey 1.0.1. For example
for performance reasons it might be preferable to for the
ComponentProvider to return something that obtains the instance, that
way configuration checking does not need to be performed each and
every time.

Paul.

On Oct 6, 2008, at 10:01 PM, Gili wrote:

>
> 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.
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>