users@jersey.java.net

Re: [Jersey] Simplifying Jersey life-cycle and IoC integration

From: Martin Grotzke <martin.grotzke_at_freiheit.com>
Date: Mon, 27 Oct 2008 14:08:33 +0100

On Mon, 2008-10-27 at 12:40 +0100, Paul Sandoz wrote:
> On Oct 27, 2008, at 12:14 PM, Martin Grotzke wrote:
>
> > Hi Paul,
> >
> > sounds good to me. Do you plan to change the spring-stuff together
> > with
> > the api/interface changes, or can I help in this area?
> >
>
> Yes, just committed the code :-) no test failures.
Great! I'll have a look at your implementation this evening - to keep in
touch :)


> I don't recall if
> we have any tests for AOP-based stuff, if we do not it might be useful
> to add something.
IIRC we don't have any test related to this, as this is a feature of
spring and we shouldn't need to test this.

However, we could add a sample that shows how spring-AOP can be used
(using the ResourceContext to get subresource instances). As soon as I
find the time I can do this.


>
> One area that could be improved is this:
>
> if (springContext.isSingleton(beanName)) {
> return new
> SpringManagedComponentProvider(ComponentScope.Singleton, beanName, c);
> } else if (springContext.isPrototype(beanName)) {
> return new
> SpringManagedComponentProvider(ComponentScope.PerRequest, beanName, c);
> } else {
> return new
> SpringManagedComponentProvider(ComponentScope.Undefined, beanName, c);
> }
>
> For the ComponentScope.PerRequest we could support Spring "prototype"
> or the Spring "per-request" scope. I seem to recall that you defined a
> mapping from Jersey scopes and Spring scopes that allowed for the "per-
> request" inclusion. Was that right?
Correct, it looked like this:

    enum SupportedSpringScopes {

        /**
         * Maps the spring scope "singleton" to the {_at_link SingletonProvider}
         */
        SINGLETON("singleton", SingletonProvider.class),
        /**
         * Maps the spring scope "prototype" to the {_at_link PerRequestProvider}
         */
        PROTOTYPE("prototype", PerRequestProvider.class),
        /**
         * Maps the spring scope "request" to the {_at_link PerRequestProvider}
         */
        REQUEST("request", PerRequestProvider.class),
        /**
         * Maps the spring scope "session" to the {_at_link PerSessionProvider}
         */
        SESSION("session", PerSessionProvider.class);

        private final String _springScope;

        private final Class<? extends ResourceProvider> _resourceProviderClass;

        private SupportedSpringScopes(String springScope,
                Class<? extends ResourceProvider> resourceProviderClass) {
            _springScope = springScope;
            _resourceProviderClass = resourceProviderClass;
        }

        /**
         * Get the resource provider.
         *
         * @return the resource provider.
         */
        public Class<? extends ResourceProvider> getResourceProviderClass() {
            return _resourceProviderClass;
        }

        /**
         * Get the Spring scope.
         *
         * @return the Spring scope.
         */
        public String getSpringScope() {
            return _springScope;
        }

        /**
         * Get the default support scope.
         *
         * @return the default supported scope.
         */
        public static SupportedSpringScopes defaultSpringScope() {
            return SINGLETON;
        }

        /**
         * Select the matching supported scope item.
         *
         * @param springScope the Spring scope
         *
         * @return the matching supported scope, or <code>null</code> if
         * the scope could not be matched.
         */
        public static SupportedSpringScopes valueOfSpringScope(String springScope) {
            for (SupportedSpringScopes scope : values()) {
                if (scope.getSpringScope().equals(springScope)) {
                    return scope;
                }
            }
            return null;
        }

    }

It could be used like this:

final Scope scope = resourceClass.getAnnotation(Scope.class);
if (scope != null) {
    final SupportedSpringScopes springScope = SupportedSpringScopes.valueOfSpringScope(scope.value());
    if (springScope != null) {
        resourceProviderClass = springScope.getResourceProviderClass();
    } else {
        throw new RuntimeException("No jersey lifecycle annotation specified on" +
                " resource class " + resourceClass.getName() +
                " and also no valid spring scope (valid scopes: " + SupportedSpringScopes.getSpringScopesAsCSV() + ")");
    }
} else {
    resourceProviderClass = SupportedSpringScopes.defaultSpringScope().getResourceProviderClass();
}



>
> BTW the new approach should be a little more efficient as the spring
> bean name does not need to be derived each and every time an instance
> is requested.
Great!

Cheers,
Martin


>
> Paul.
>
> > Cheers,
> > Martin
> >
> >
> > On Thu, 2008-10-23 at 12:15 +0200, Paul Sandoz wrote:
> >> Hi,
> >>
> >> It is clear that the integration of IoC frameworks is more
> >> complicated
> >> that it should be, especially when the Jersey life-cycle annotations
> >> need to sync up with the IoC life-cycles.
> >>
> >> I propose the following simplifications:
> >>
> >> 1) Jersey should get out of the way when an IoC integrates such that
> >> Jersey always defers the IoC life-cycle
> >> mechanisms of the IoC. The Jersey specific life-cycle
> >> annotations should only be used when an IoC such as
> >> Spring, Guice or WebBeans is not integrated. Of course this does
> >> not mean that someone could write their
> >> own special IoC that re-used Jersey's life-cycle support (e.g.
> >> for proxies of components) or an IoC integration
> >> could provide some support for transitioning to the IoC.
> >>
> >> 2) Jersey should never defer to an IoC (if present) for the
> >> instantiation of internal Jersey components.
> >>
> >> 3) Unify ResourceProvider/ResourceProviderFactory with
> >> ComponentProvider and clearly differentiate
> >> between Jersey and IoC component providers.
> >>
> >> The proposed interfaces for 3) are at the end of the email. There are
> >> specifics to IoC integration that Jersey needs to know if it has to
> >> perform injection of Jersey specific artifacts (if the IoC does not
> >> support this). The interface IoCComponentProvider represents those
> >> specifics.
> >>
> >> The Jersey life-cycle annotations will declare specific
> >> ComponentProvider classes.
> >>
> >> @Target({ElementType.TYPE})
> >> @Retention(RetentionPolicy.RUNTIME)
> >> @Documented
> >> @ResourceProvider(PerRequestComponentProvider.class)
> >> public @interface PerRequest {}
> >>
> >> This should also make the integration with an IoC a little more
> >> efficient since validation can be performed once by the
> >> ComponentProvider and not each and every time an instance is
> >> requested.
> >>
> >> Paul.
> >>
> >> /**
> >> * A factory for obtaining component providers
> >> */
> >> interface ComponentProviderFactory<C extends ComponentProvider> {
> >>
> >> /**
> >> * Get the component provider for a class.
> >> *
> >> * @param c the class
> >> * @return the component provider for the class
> >> */
> >> C getComponentProvider(Class c);
> >>
> >> /**
> >> * Get the component provider for a class.
> >> *
> >> * @param cc the component context to obtain annotations and
> >> * annotated object (if present).
> >> * @param c the class
> >> * @return the component provider for the class
> >> */
> >> C getComponentProvider(ComponentContext cc, Class c);
> >> }
> >>
> >>
> >> /**
> >> * Provide instances of a component.
> >> *
> >> */
> >> interface ComponentProvider {
> >> /**
> >> * Get the instance.
> >> *
> >> * @return the instance.
> >> */
> >> Object getInstance();
> >> }
> >>
> >> /**
> >> * An IoC-specific factory for obtaining IoC-specific component
> >> providers.
> >> *
> >> */
> >> interface IoCComponentProviderFactory extends
> >> ComponentProviderFactory<IoCComponentProvider> {
> >> }
> >>
> >>
> >> /**
> >> * Provide instances of a IoC-specfic component.
> >> *
> >> */
> >> interface IoCComponentProvider extends ComponentProvider {
> >> /**
> >> * Get the scope.
> >> * <p>
> >> * The Scope is required so that Jersey can correctly inject on
> >> an instance
> >> * returned from an application specfic component provider.
> >> *
> >> * @return the scope
> >> */
> >> Scope getScope();
> >>
> >> /**
> >> * Get the injectable instance that Jersey can perform field and
> >> method
> >> * setter injection on.
> >> *
> >> * @param o the instance returned from getInstance.
> >> * @return the injectable instance. May be the same as o. If null
> >> * then Jersey will perform no injection.
> >> */
> >> Object getInjectableInstance(Object o);
> >> }
> >>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> >> For additional commands, e-mail: users-help_at_jersey.dev.java.net
> >
> > --
> > Martin Grotzke
> > http://www.javakaffee.de/blog/
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net

-- 
Martin Grotzke
http://www.javakaffee.de/blog/