Package com.hubspot.dropwizard.guice

Source Code of com.hubspot.dropwizard.guice.GuiceComponentProviderFactory$GuiceManagedComponentProvider

package com.hubspot.dropwizard.guice;

import com.google.inject.*;
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.*;

import javax.ws.rs.WebApplicationException;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* The Guice-based {@link com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory}.
*
* @author Gili Tzabari
* @author Paul Sandoz
* @author Charlie Groves
*/
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) {

    if (injector == null) {
      throw new NullPointerException("Guice Injector can not be null!");
    }

    this.injector = injector;
    register(config, injector);
  }

  /**
   * Registers any Guice-bound providers or root resources.
   *
   * @param config the resource configuration
   * @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.log(Level.INFO, "Registering {0} as a provider class", c.getName());
            config.getClasses().add(c);
          } else if (ResourceConfig.isRootResourceClass(c)) {
            LOGGER.log(Level.INFO, "Registering {0} as a root resource class", c.getName());
            config.getClasses().add(c);
          }

        }
      }
      injector = injector.getParent();
    }
  }

  @Override
  public IoCComponentProvider getComponentProvider(Class<?> c) {
    return getComponentProvider(null, c);
  }

  @Override
  public IoCComponentProvider getComponentProvider(ComponentContext cc, Class<?> clazz) {
    if (LOGGER.isLoggable(Level.FINE)) {
      LOGGER.log(Level.FINE, "getComponentProvider({0})", 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.log(Level.INFO, "Binding {0} to GuiceInstantiatedComponentProvider", clazz.getName());
            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.log(Level.INFO, "Binding {0} to GuiceInjectedComponentProvider", clazz.getName());
        return new GuiceInjectedComponentProvider(injector);
      } else {
        return null;
      }
    }

    ComponentScope componentScope = getComponentScope(key, i);
    LOGGER.log(Level.INFO, "Binding {0} to GuiceManagedComponentProvider with the scope \"{1}\"",
               new Object[]{clazz.getName(), componentScope});
    return new GuiceManagedComponentProvider(i, componentScope, clazz);
  }

  private ComponentScope getComponentScope(Key<?> key, Injector i) {
    return i.getBinding(key).acceptScopingVisitor(new BindingScopingVisitor<ComponentScope>() {

      @Override
      public ComponentScope visitEagerSingleton() {
        return ComponentScope.Singleton;
      }

      @Override
      public ComponentScope visitScope(Scope theScope) {
        ComponentScope cs = scopeMap.get(theScope);
        return (cs != null) ? cs : ComponentScope.Undefined;
      }

      @Override
      public ComponentScope visitScopeAnnotation(Class scopeAnnotation) {
        // This method is not invoked for Injector bindings
        throw new UnsupportedOperationException();
      }

      @Override
      public ComponentScope visitNoScoping() {
        return ComponentScope.PerRequest;
      }
    });
  }

  private Injector findInjector(Key<?> key) {
    Injector i = injector;
    while (i != null) {
      if (i.getBindings().containsKey(key)) {
        return i;
      }

      i = i.getParent();
    }
    return null;
  }

  /**
   * Determine if a class is an implicit Guice component that can be
   * instantiated 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
   * instantiated 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.getDeclaredConstructors()) {
      if (isInjectable(con)) {
        return true;
      }
    }

    return false;
  }

  /**
   * Determine if a class uses field or method injection via Guice
   * using the {@code 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 (isInjectable(m)) {
        return true;
      }
    }

    for (Field f : c.getDeclaredFields()) {
      if (isInjectable(f)) {
        return true;
      }
    }

    return !c.equals(Object.class) && isGuiceFieldOrMethodInjected(c.getSuperclass());
  }

  private static boolean isInjectable(AnnotatedElement element) {
    return (element.isAnnotationPresent(com.google.inject.Inject.class)
      || element.isAnnotationPresent(javax.inject.Inject.class));
  }

  /**
   * 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;
    }

    @Override
    public Object getInstance() {
      throw new IllegalStateException();
    }

    @Override
    public Object proxy(Object o) {
      try {
        injector.injectMembers(o);
      } catch (ProvisionException e) {
        if (e.getCause() instanceof WebApplicationException) {
          throw (WebApplicationException)e.getCause();
        }
        throw e;
      }
      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
    @Override
    public Object getInjectableInstance(Object o) {
      return o;
    }

    @Override
    public Object getInstance() {
      try {
        return injector.getInstance(clazz);
      } catch (ProvisionException e) {
        if (e.getCause() instanceof WebApplicationException) {
          throw (WebApplicationException)e.getCause();
        }
        throw e;
      }
    }
  }

  /**
   * 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;
    }

    @Override
    public ComponentScope getScope() {
      return scope;
    }
  }
}
TOP

Related Classes of com.hubspot.dropwizard.guice.GuiceComponentProviderFactory$GuiceManagedComponentProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.