/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.jersey.guice.spi.container; import com.google.inject.ConfigurationException; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Scope; import com.google.inject.Scopes; import com.google.inject.spi.BindingScopingVisitor; import com.sun.jersey.api.core.ResourceConfig; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.core.spi.component.ComponentScope; import com.sun.jersey.core.spi.component.ioc.IoCComponentProvider; import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory; import com.sun.jersey.core.spi.component.ioc.IoCInstantiatedComponentProvider; import com.sun.jersey.core.spi.component.ioc.IoCManagedComponentProvider; import com.sun.jersey.core.spi.component.ioc.IoCProxiedComponentProvider; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * The Guice-based {@link IoCComponentProviderFactory}. * * @author Gili Tzabari * @author Paul Sandoz */ public class GuiceComponentProviderFactory implements IoCComponentProviderFactory { private static final Logger LOGGER = Logger.getLogger(GuiceComponentProviderFactory.class.getName()); private final Map<Scope, ComponentScope> scopeMap = createScopeMap(); private final Injector injector; /** * Creates a new GuiceComponentProviderFactory. * * @param config the resource configuration * @param injector the Guice injector */ public GuiceComponentProviderFactory(ResourceConfig config, Injector injector) { this.injector = injector; register(config, injector); } /** * Registers any Guice-bound providers or root resources. * * @param config the resource config * @param injector the Guice injector */ private void register(ResourceConfig config, Injector injector) { while (injector != null) { for (Key<?> key : injector.getBindings().keySet()) { Type type = key.getTypeLiteral().getType(); if (type instanceof Class) { Class<?> c = (Class) type; if (ResourceConfig.isProviderClass(c)) { LOGGER.info("Registering " + c.getName() + " as a provider class"); config.getClasses().add(c); } else if (ResourceConfig.isRootResourceClass(c)) { LOGGER.info("Registering " + c.getName() + " as a root resource class"); config.getClasses().add(c); } } } injector = injector.getParent(); } } public IoCComponentProvider getComponentProvider(Class c) { return getComponentProvider(null, c); } public IoCComponentProvider getComponentProvider(ComponentContext cc, Class clazz) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("getComponentProvider(" + clazz.getName() + ")"); } Key<?> key = Key.get(clazz); Injector i = findInjector(key); // If there is no explicit binding if (i == null) { // If @Inject is explicitly declared on constructor if (isGuiceConstructorInjected(clazz)) { try { // If a binding is possible if (injector.getBinding(key) != null) { LOGGER.info("Binding " + clazz.getName() + " to GuiceInstantiatedComponentProvider"); return new GuiceInstantiatedComponentProvider(injector, clazz); } } catch (ConfigurationException e) { // The class cannot be injected. // For example, the constructor might contain parameters that // cannot be injected LOGGER.log(Level.SEVERE, "Cannot bind " + clazz.getName(), e); // Guice should have picked this up. We fail-fast to prevent // Jersey from trying to handle injection. throw e; } // If @Inject is declared on field or method } else if (isGuiceFieldOrMethodInjected(clazz)) { LOGGER.info("Binding " + clazz.getName() + " to GuiceInjectedComponentProvider"); return new GuiceInjectedComponentProvider(injector); } else { return null; } } ComponentScope componentScope = getComponentScope(key, i); LOGGER.info("Binding " + clazz.getName() + " to GuiceManagedComponentProvider with the scope \"" + componentScope + "\""); return new GuiceManagedComponentProvider(i, componentScope, clazz); } private ComponentScope getComponentScope(Key<?> key, Injector i) { final Scope[] scope = new Scope[1]; i.getBinding(key).acceptScopingVisitor(new BindingScopingVisitor<Void>() { public Void visitEagerSingleton() { scope[0] = Scopes.SINGLETON; return null; } public Void visitScope(Scope theScope) { scope[0] = theScope; return null; } public Void visitScopeAnnotation(Class scopeAnnotation) { // This method is not invoked for Injector bindings throw new UnsupportedOperationException(); } public Void visitNoScoping() { scope[0] = Scopes.NO_SCOPE; return null; } }); assert (scope[0] != null); return getComponentScope(scope[0]); } private Injector findInjector(Key<?> key) { Injector i = injector; while (i != null) { if (i.getBindings().containsKey(key)) return i; i = i.getParent(); } return null; } /** * Converts a Guice scope to Jersey scope. * * @param scope the guice scope * @return the Jersey scope */ private ComponentScope getComponentScope(Scope scope) { ComponentScope cs = scopeMap.get(scope); return (cs != null) ? cs : ComponentScope.Undefined; } /** * Determine if a class is an implicit Guice component that can be * instatiated by Guice and the life-cycle managed by Jersey. * * @param c the class. * @return true if the class is an implicit Guice component. * @deprecated see {@link #isGuiceConstructorInjected(java.lang.Class) } */ @Deprecated public boolean isImplicitGuiceComponent(Class<?> c) { return isGuiceConstructorInjected(c); } /** * Determine if a class is an implicit Guice component that can be * instatiated by Guice and the life-cycle managed by Jersey. * * @param c the class. * @return true if the class is an implicit Guice component. */ public boolean isGuiceConstructorInjected(Class<?> c) { for (Constructor<?> con : c.getConstructors()) { if (con.isAnnotationPresent(Inject.class)) return true; } return false; } /** * Determine if a class uses field or method injection via Guice * using the {@link Inject} annotation * * @param c the class. * @return true if the class is an implicit Guice component. */ public boolean isGuiceFieldOrMethodInjected(Class<?> c) { for (Method m : c.getDeclaredMethods()) { if (m.isAnnotationPresent(Inject.class)) return true; } for (Field f : c.getDeclaredFields()) { if (f.isAnnotationPresent(Inject.class)) return true; } if (!c.equals(Object.class)) { return isGuiceFieldOrMethodInjected(c.getSuperclass()); } return false; } /** * Maps a Guice scope to a Jersey scope. * * @return the map */ public Map<Scope, ComponentScope> createScopeMap() { Map<Scope, ComponentScope> result = new HashMap<Scope, ComponentScope>(); result.put(Scopes.SINGLETON, ComponentScope.Singleton); result.put(Scopes.NO_SCOPE, ComponentScope.PerRequest); return result; } private static class GuiceInjectedComponentProvider implements IoCProxiedComponentProvider { private final Injector injector; public GuiceInjectedComponentProvider(Injector injector) { this.injector = injector; } public Object getInstance() { throw new IllegalStateException(); } public Object proxy(Object o) { injector.injectMembers(o); return o; } } /** * Guice injects instances while Jersey manages their scope. * * @author Gili Tzabari */ private static class GuiceInstantiatedComponentProvider implements IoCInstantiatedComponentProvider { private final Injector injector; private final Class<?> clazz; /** * Creates a new GuiceManagedComponentProvider. * * @param injector the injector * @param clazz the class */ public GuiceInstantiatedComponentProvider(Injector injector, Class<?> clazz) { this.injector = injector; this.clazz = clazz; } public Class<?> getInjectableClass(Class<?> c) { return c.getSuperclass(); } // IoCInstantiatedComponentProvider public Object getInjectableInstance(Object o) { return o; } public Object getInstance() { return injector.getInstance(clazz); } } /** * Guice injects instances and manages their scope. * * @author Gili Tzabari */ private static class GuiceManagedComponentProvider extends GuiceInstantiatedComponentProvider implements IoCManagedComponentProvider { private final ComponentScope scope; /** * Creates a new GuiceManagedComponentProvider. * * @param injector the injector * @param scope the Jersey scope * @param clazz the class */ public GuiceManagedComponentProvider(Injector injector, ComponentScope scope, Class<?> clazz) { super(injector, clazz); this.scope = scope; } public ComponentScope getScope() { return scope; } } }