Package br.gov.frameworkdemoiselle.internal.context

Source Code of br.gov.frameworkdemoiselle.internal.context.CustomContextCounter

package br.gov.frameworkdemoiselle.internal.context;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.spi.Context;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.BeanManager;

import org.slf4j.Logger;

import br.gov.frameworkdemoiselle.DemoiselleException;
import br.gov.frameworkdemoiselle.annotation.StaticScoped;
import br.gov.frameworkdemoiselle.internal.producer.LoggerProducer;
import br.gov.frameworkdemoiselle.util.Beans;
import br.gov.frameworkdemoiselle.util.ResourceBundle;

/**
* <p>
* Manage custom contexts relevant to Demoiselle operations.
* </p>
* <p>
* When starting, the ContextManager must be initialized by calling {@link #initialize(AfterBeanDiscovery event)} inside
* any methods observing the {@link AfterBeanDiscovery} event. Upon initialization a {@link StaticContext} will be
* created to handle {@link StaticScoped} beans (but not activated, you must call
* {@link #activate(Class customContextClass, Class scope)} to activate this context).
* </p>
* <p>
* If an extension wants to manage another custom context, it must first call
* {@link #add(CustomContext context, AfterBeanDiscovery event)} to add it's context to the list of managed contexts and
* then call {@link #activate(Class customContextClass, Class scope)} whenever it wants to activate this added context
* (contexts added through the {@link #add(CustomContext context, AfterBeanDiscovery event)} method are also not
* activated upon adding).
* </p>
*
* @author serpro
*/
public final class ContextManager {

  private static List<CustomContextCounter> contexts = Collections
      .synchronizedList(new ArrayList<CustomContextCounter>());

  private static boolean initialized = false;

  private static transient ResourceBundle bundle;

  private static Logger logger;

  private ContextManager() {
  }

  /**
   * <p>
   * Initializes this manager and adds the {@link StaticContext} context to the list of managed contexts. Other
   * contexts must be added before they can be activated.
   * </p>
   * <p>
   * It's OK to call this method multiple times, it will be initialized only once.
   * </p>
   *
   * @param event
   *            The CDI event indicating all beans have been discovered.
   */
  public static void initialize(AfterBeanDiscovery event) {
    if (initialized) {
      return;
    }

    add(new StaticContext(), event);
    initialized = true;
  }

  /**
   * <p>
   * Adds a context to the list of managed contexts.
   * </p>
   * <p>
   * A context added through this method will be deactivated before management can start. Only after calling
   * {@link #activate(Class customContextClass, Class scope)} the context will be activated.
   * </p>
   * <p>
   * Trying to add a context already managed will result in this method call being ignored.
   * </p>
   *
   * @param context
   *            The context to be added
   * @param event
   *            The CDI event indicating all beans have been discovered.
   */
  public static void add(CustomContext context, AfterBeanDiscovery event) {
    for (CustomContextCounter contextCounter : contexts) {
      if (contextCounter.isSame(context.getClass(), context.getScope())) {

        ContextManager.getLogger().trace(
            ContextManager.getBundle().getString("bootstrap-context-already-managed",
                context.getClass().getCanonicalName(), context.getScope().getCanonicalName()));

        return;
      }
    }

    ContextManager.getLogger().trace(
        ContextManager.getBundle().getString("bootstrap-context-added", context.getClass().getCanonicalName(),
            context.getScope().getCanonicalName()));

    context.setActive(false);
    event.addContext(context);
    contexts.add(new CustomContextCounter(context));
  }

  /**
   * <p>
   * Activates a managed context.
   * </p>
   * <p>
   * To be activated, a context must fulfill the following requisites:
   * <ul>
   * <li>Must be managed by this class (be of type {@link StaticScoped} or be added with
   * {@link #add(CustomContext context, AfterBeanDiscovery event)})</li>
   * <li>Must be of a scope not already attached to another active context</li>
   * </ul>
   * </p>
   *
   * @param customContextClass
   *            Type of context to activate
   * @param scope
   *            The scope to activate this context for
   * @return <code>true</code> if there is a managed context of the provided type and scope and no other context is
   *         active for the provided scope, <code>false</code> if there is a managed context of the provided type and
   *         scope but another context is active for the provided scope.
   * @throws DemoiselleException
   *             if there is no managed context of the provided type and scope.
   */
  public static synchronized void activate(Class<? extends CustomContext> customContextClass,
      Class<? extends Annotation> scope) {
    if (!initialized) {
      throw new DemoiselleException(getBundle().getString("custom-context-manager-not-initialized"));
    }

    for (CustomContextCounter context : contexts) {
      if (context.isSame(customContextClass, scope)) {
        context.activate();
        return;
      }
    }

    throw new DemoiselleException(getBundle().getString("custom-context-not-found",
        customContextClass.getCanonicalName(), scope.getSimpleName()));
  }

  /**
   * <p>
   * Deactivates a managed context.
   * </p>
   * <p>
   * To be deactivated, a context must fulfill the following requisites:
   * <ul>
   * <li>Must be managed by this class (be of type {@link StaticScoped} or be added with
   * {@link #add(CustomContext context, AfterBeanDiscovery event)})</li>
   * <li>Must have been activated by a previous call to {@link #activate(Class customContextClass, Class scope)}</li>
   * <li>This previous call must have returned <code>true</code>.
   * </ul>
   * </p>
   *
   * @param customContextClass
   *            Type of context to deactivate
   * @param scope
   *            The scope the context controled when it was active
   * @return <code>true</code> if there was an active context of this type and scope and it was activated by a
   *         previous call to {@link #activate(Class customContextClass, Class scope)}
   * @throws DemoiselleException
   *             if there is no managed context of the provided type and scope.
   */
  public static synchronized void deactivate(Class<? extends CustomContext> customContextClass,
      Class<? extends Annotation> scope) {
    if (!initialized) {
      throw new DemoiselleException(getBundle().getString("custom-context-manager-not-initialized"));
    }

    for (CustomContextCounter context : contexts) {
      if (context.isSame(customContextClass, scope)) {
        context.deactivate();
        return;
      }
    }

    throw new DemoiselleException(getBundle().getString("custom-context-not-found",
        customContextClass.getCanonicalName(), scope.getSimpleName()));
  }

  /**
   * <p>
   * This method should be called when the application is shutting down.
   * </p>
   */
  public static synchronized void shutdown() {
    for (CustomContextCounter context : contexts) {
      context.shutdown();
    }

    contexts.clear();
    initialized = false;
  }

  static Logger getLogger() {
    if (logger == null) {
      logger = LoggerProducer.create(ContextManager.class);
    }

    return logger;
  }

  static ResourceBundle getBundle() {
    if (bundle == null) {
      bundle = new ResourceBundle("demoiselle-core-bundle", Locale.getDefault());
    }

    return bundle;
  }
}

/**
* Class that counts how many attemps to activate and deactivate this context received, avoiding cases where one client
* activates given context and another one deactivates it, leaving the first client with no active context before
* completion.
*
* @author serpro
*/
class CustomContextCounter {

  private CustomContext context;

  private int activationCounter = 0;

  public CustomContextCounter(CustomContext customContext) {
    this.context = customContext;
  }

  public boolean isSame(Class<? extends CustomContext> customContextClass, Class<? extends Annotation> scope) {
    if (context.getClass().getCanonicalName().equals(customContextClass.getCanonicalName())
        && context.getScope().equals(scope)) {
      return true;
    }

    return false;
  }

  public CustomContext getInternalContext() {
    return this.context;
  }

  public void setInternalContext(CustomContext context) {
    this.context = context;
  }

  public synchronized void activate() {
    try {
      BeanManager beanManager = Beans.getReference(BeanManager.class);
      Context c = beanManager.getContext(context.getScope());

      if (c == context) {
        activationCounter++;
      } else {
        ContextManager.getLogger().trace(
            ContextManager.getBundle().getString("custom-context-already-activated",
                context.getClass().getCanonicalName(), c.getScope().getCanonicalName(),
                c.getClass().getCanonicalName()));
      }
    } catch (ContextNotActiveException ce) {
      context.setActive(true);
      activationCounter++;
      ContextManager.getLogger().trace(
          ContextManager.getBundle().getString("custom-context-was-activated",
              context.getClass().getCanonicalName(), context.getScope().getCanonicalName()));
    }
  }

  public synchronized void deactivate() {
    try {
      Context c = Beans.getBeanManager().getContext(context.getScope());
      if (c == context) {
        activationCounter--;
        if (activationCounter == 0) {
          context.setActive(false);
          ContextManager.getLogger().trace(
              ContextManager.getBundle().getString("custom-context-was-deactivated",
                  context.getClass().getCanonicalName(), context.getScope().getCanonicalName()));
        }
      }
    } catch (ContextNotActiveException ce) {
    }
  }

  public synchronized void shutdown() {
    context.setActive(false);
    context = null;
    activationCounter = 0;
  }
}
TOP

Related Classes of br.gov.frameworkdemoiselle.internal.context.CustomContextCounter

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.