Package org.omnifaces.cdi.viewscope

Source Code of org.omnifaces.cdi.viewscope.ViewScopeManager$BeanStorageEvictionListener

/*
* Copyright 2013 OmniFaces.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.omnifaces.cdi.viewscope;

import static org.omnifaces.util.Faces.getInitParameter;
import static org.omnifaces.util.Faces.getViewAttribute;
import static org.omnifaces.util.Faces.setViewAttribute;

import java.io.Serializable;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.context.spi.Contextual;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;

import org.omnifaces.application.ViewScopeEventListener;
import org.omnifaces.cdi.BeanStorage;
import org.omnifaces.cdi.ViewScoped;
import org.omnifaces.util.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import org.omnifaces.util.concurrentlinkedhashmap.EvictionListener;

/**
* Manage the view scoped beans by listening on view scope and session scope creation and destroy.
* The view scope destroy is done externally with aid of {@link ViewScopeEventListener} which is registered in
* <code>faces-config.xml</code>.
*
* @author Radu Creanga {@literal <rdcrng@gmail.com>}
* @author Bauke Scholtz
* @see ViewScoped
* @see ViewScopeContext
* @since 1.6
*/
@SessionScoped
public class ViewScopeManager implements Serializable {

  // Public constants -----------------------------------------------------------------------------------------------

  /** OmniFaces specific context parameter name of maximum active view scopes in session. */
  public static final String PARAM_NAME_MAX_ACTIVE_VIEW_SCOPES =
    "org.omnifaces.VIEW_SCOPE_MANAGER_MAX_ACTIVE_VIEW_SCOPES";

  /** Mojarra specific context parameter name of maximum number of logical views in session. */
  public static final String PARAM_NAME_MOJARRA_NUMBER_OF_VIEWS =
    "com.sun.faces.numberOfLogicalViews";

  /** MyFaces specific context parameter name of maximum number of views in session. */
  public static final String PARAM_NAME_MYFACES_NUMBER_OF_VIEWS =
    "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";

  /** Default value of maximum active view scopes in session. */
  public static final int DEFAULT_MAX_ACTIVE_VIEW_SCOPES = 20; // Mojarra's default is 15 and MyFaces' default is 20.

  // Private constants ----------------------------------------------------------------------------------------------

  private static final long serialVersionUID = 42L;
  private static final String[] PARAM_NAMES_MAX_ACTIVE_VIEW_SCOPES = {
    PARAM_NAME_MAX_ACTIVE_VIEW_SCOPES, PARAM_NAME_MOJARRA_NUMBER_OF_VIEWS, PARAM_NAME_MYFACES_NUMBER_OF_VIEWS
  };
  private static final int DEFAULT_BEANS_PER_VIEW_SCOPE = 3;
  private static final String ERROR_MAX_ACTIVE_VIEW_SCOPES = "The '%s' init param must be a number."
    + " Encountered an invalid value of '%s'.";

  // Static variables -----------------------------------------------------------------------------------------------

  private static Integer maxActiveViewScopes;

  // Variables ------------------------------------------------------------------------------------------------------

  private ConcurrentMap<UUID, BeanStorage> activeViewScopes;

  @Inject
  private BeanManager manager;

  // Actions --------------------------------------------------------------------------------------------------------

  /**
   * Create a new LRU map of active view scopes with maximum weighted capacity depending on several context params.
   * See javadoc of {@link ViewScoped} for details.
   */
  @PostConstruct
  public void postConstructSession() {
    activeViewScopes = new ConcurrentLinkedHashMap.Builder<UUID, BeanStorage>()
      .maximumWeightedCapacity(getMaxActiveViewScopes())
      .listener(new BeanStorageEvictionListener())
      .build();
  }

  /**
   * Create and returns the CDI view scoped managed bean from the current JSF view scope.
   * @param <T> The expected return type.
   * @param type The contextual type of the CDI managed bean.
   * @param context The CDI context to create the CDI managed bean in.
   * @return The created CDI view scoped managed bean from the current JSF view scope.
   */
  public <T> T createBean(Contextual<T> type, CreationalContext<T> context) {
    return activeViewScopes.get(getBeanStorageId(true)).createBean(type, context);
  }

  /**
   * Returns the CDI view scoped managed bean from the current JSF view scope.
   * @param <T> The expected return type.
   * @param type The contextual type of the CDI managed bean.
   * @return The CDI view scoped managed bean from the current JSF view scope.
   */
  public <T> T getBean(Contextual<T> type) {
    return activeViewScopes.get(getBeanStorageId(true)).getBean(type, manager);
  }

  /**
   * This method is invoked during view destroy by {@link ViewScopeEventListener}, in that case destroy all beans in
   * current active view scope.
   */
  public void preDestroyView() {
    BeanStorage storage = activeViewScopes.remove(getBeanStorageId(false));

    if (storage != null) {
      storage.destroyBeans();
    }
  }

  /**
   * This method is invoked during session destroy, in that case destroy all beans in all active view scopes.
   */
  @PreDestroy
  public void preDestroySession() {
    for (BeanStorage storage : activeViewScopes.values()) {
      storage.destroyBeans();
    }
  }

  // Helpers --------------------------------------------------------------------------------------------------------

  /**
   * Returns the max active view scopes depending on available context params. This will be calculated lazily once
   * and re-returned everytime; the faces context is namely not available during class' initialization/construction,
   * but only during a post construct.
   */
  private int getMaxActiveViewScopes() {
    if (maxActiveViewScopes != null) {
      return maxActiveViewScopes;
    }

    for (String name : PARAM_NAMES_MAX_ACTIVE_VIEW_SCOPES) {
      String value = getInitParameter(name);

      if (value != null) {
        try {
          return (maxActiveViewScopes = Integer.valueOf(value));
        }
        catch (NumberFormatException e) {
          throw new IllegalArgumentException(String.format(ERROR_MAX_ACTIVE_VIEW_SCOPES, name, value), e);
        }
      }
    }

    return (maxActiveViewScopes = DEFAULT_MAX_ACTIVE_VIEW_SCOPES);
  }

  /**
   * Returns the unique ID from the current JSF view scope which is to be associated with the CDI bean storage.
   * If none is found, then a new ID will be auto-created. If <code>create</code> is <code>true</code>, then a new
   * CDI bean storage will also be auto-created.
   */
  private UUID getBeanStorageId(boolean create) {
    UUID id = (UUID) getViewAttribute(ViewScopeManager.class.getName());

    if (id == null || activeViewScopes.get(id) == null) {
      id = UUID.randomUUID();

      if (create) {
        activeViewScopes.put(id, new BeanStorage(DEFAULT_BEANS_PER_VIEW_SCOPE));
      }

      setViewAttribute(ViewScopeManager.class.getName(), id);
    }

    return id;
  }

  // Nested classes -------------------------------------------------------------------------------------------------

  /**
   * Listener for {@link ConcurrentLinkedHashMap} which will be invoked when an entry is evicted. It will in turn
   * invoke {@link BeanStorage#destroyBeans()}.
   */
  private static final class BeanStorageEvictionListener implements EvictionListener<UUID, BeanStorage>, Serializable {

    private static final long serialVersionUID = 42L;

    @Override
    public void onEviction(UUID id, BeanStorage storage) {
      storage.destroyBeans();
    }

  }

}
TOP

Related Classes of org.omnifaces.cdi.viewscope.ViewScopeManager$BeanStorageEvictionListener

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.