com.sun.faces.context.StateContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javax.faces Show documentation
Show all versions of javax.faces Show documentation
This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 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.faces.context;
import com.sun.faces.RIConstants;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.application.ApplicationStateInfo;
import com.sun.faces.util.ComponentStruct;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.PreRemoveFromViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import static com.sun.faces.RIConstants.DYNAMIC_CHILD_COUNT;
import static com.sun.faces.RIConstants.DYNAMIC_COMPONENT;
import com.sun.faces.facelets.tag.jsf.ComponentSupport;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.MostlySingletonSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.logging.Logger;
import javax.faces.FacesException;
/**
* Context for dealing with partial state saving mechanics.
*/
public class StateContext {
private static final String KEY = StateContext.class.getName() + "_KEY";
private boolean partial;
private boolean partialLocked;
private boolean trackMods = true;
private AddRemoveListener modListener;
private ApplicationStateInfo stateInfo;
private WeakReference viewRootRef = new WeakReference(null);
private static final Logger LOGGER = FacesLogger.CONTEXT.getLogger();
// ------------------------------------------------------------ Constructors
private StateContext(ApplicationStateInfo stateInfo) {
this.stateInfo = stateInfo;
}
// ---------------------------------------------------------- Public Methods
/**
* Release the state context.
*
* @param facesContext the Faces context.
*/
public static void release(FacesContext facesContext) {
StateContext stateContext = (StateContext) facesContext.getAttributes().get(KEY);
UIViewRoot viewRoot = facesContext.getViewRoot();
if (viewRoot != null && stateContext.modListener != null) {
viewRoot.unsubscribeFromViewEvent(PostAddToViewEvent.class, stateContext.modListener);
viewRoot.unsubscribeFromViewEvent(PreRemoveFromViewEvent.class, stateContext.modListener);
}
facesContext.getAttributes().remove(KEY);
}
/**
* @param ctx the FacesContext
for the current request
* @return StateContext
for this request
*/
public static StateContext getStateContext(FacesContext ctx) {
StateContext stateCtx = (StateContext) ctx.getAttributes().get(KEY);
if (stateCtx == null) {
ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance();
ApplicationStateInfo info = associate.getApplicationStateInfo();
stateCtx = new StateContext(info);
ctx.getAttributes().put(KEY, stateCtx);
}
return stateCtx;
}
/**
* @param ctx FacesContext.
* @param viewId the view ID to check or null if viewId is unknown.
* @return true
if partial state saving should be used for the
* specified view ID, otherwise false
*/
public boolean isPartialStateSaving(FacesContext ctx, String viewId) {
// track UIViewRoot changes
UIViewRoot root = ctx.getViewRoot();
UIViewRoot refRoot = viewRootRef.get();
if (root != refRoot) {
// set weak reference to current viewRoot
this.viewRootRef = new WeakReference(root);
// On first call in restore phase, viewRoot is null, so we treat the first
// change to not null not as a changing viewRoot.
if (refRoot != null) {
// view root changed in request processing - force usage of a
// new AddRemoveListener instance for the new viewId ...
modListener = null;
// ... and also force check for partial state saving for the new viewId
partialLocked = false;
}
}
if (!partialLocked) {
if (viewId == null) {
if (root != null) {
viewId = root.getViewId();
} else {
// View root has not yet been initialized. Check to see whether
// the target view id has been stashed away for us.
viewId = (String)ctx.getAttributes().get(RIConstants.VIEWID_KEY_NAME);
}
}
partial = stateInfo.usePartialStateSaving(viewId);
partialLocked = true;
}
return partial;
}
/**
* @return true
if view modifications outside of the initial
* construction of the view are being tracked.
*/
public boolean trackViewModifications() {
return trackMods;
}
/**
* Installs a SystemEventListener
on the UIViewRoot
* to track components added to or removed from the view.
*/
public void startTrackViewModifications(FacesContext ctx, UIViewRoot root) {
if (modListener == null) {
if (root != null) {
modListener = createAddRemoveListener(ctx, root);
root.subscribeToViewEvent(PostAddToViewEvent.class, modListener);
root.subscribeToViewEvent(PreRemoveFromViewEvent.class, modListener);
} else {
LOGGER.warning("Unable to attach AddRemoveListener to UIViewRoot because it is null");
}
}
setTrackViewModifications(true);
}
/**
* Toggles the current modification tracking status.
*
* @param trackMods if true
and the listener installed by
* {@link #startTrackViewModifications(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot)
* is present, then view modifications will be tracked. If
* false
, then modification events will be ignored.
*/
public void setTrackViewModifications(boolean trackMods) {
this.trackMods = trackMods;
}
/**
* @param c the UIComponent to check
* @return true
if the component was added after the initial
* view construction
*/
public boolean componentAddedDynamically(UIComponent c) {
return c.getAttributes().containsKey(DYNAMIC_COMPONENT);
}
public int getIndexOfDynamicallyAddedChildInParent(UIComponent c) {
int result = -1;
Map attrs = c.getAttributes();
if (attrs.containsKey(DYNAMIC_COMPONENT)) {
result = (Integer) attrs.get(DYNAMIC_COMPONENT);
}
return result;
}
public boolean hasOneOrMoreDynamicChild(UIComponent parent) {
return parent.getAttributes().containsKey(DYNAMIC_CHILD_COUNT);
}
private int incrementDynamicChildCount(FacesContext context, UIComponent parent) {
int result;
Map attrs = parent.getAttributes();
Integer cur = (Integer) attrs.get(DYNAMIC_CHILD_COUNT);
if (null != cur) {
result = cur++;
} else {
result = 1;
}
attrs.put(DYNAMIC_CHILD_COUNT, (Integer) result);
context.getViewRoot().getAttributes().put(RIConstants.TREE_HAS_DYNAMIC_COMPONENTS, Boolean.TRUE);
return result;
}
private int decrementDynamicChildCount(FacesContext context, UIComponent parent) {
int result = 0;
Map attrs = parent.getAttributes();
Integer cur = (Integer) attrs.get(DYNAMIC_CHILD_COUNT);
if (null != cur) {
result = (0 < cur) ? cur-- : 0;
}
if (0 == result && null != cur){
attrs.remove(DYNAMIC_CHILD_COUNT);
}
context.getViewRoot().getAttributes().put(RIConstants.TREE_HAS_DYNAMIC_COMPONENTS, Boolean.TRUE);
return result;
}
/**
* Get the dynamic list (of adds and removes).
*/
public List getDynamicActions() {
return ((modListener != null) ? modListener.getDynamicActions() : null);
}
/**
* Get the hash map of dynamic components.
*
* @return the hash map of dynamic components.
*/
public HashMap getDynamicComponents() {
return ((modListener != null) ? modListener.getDynamicComponents() : null);
}
// ---------------------------------------------------------- Nested Classes
private AddRemoveListener createAddRemoveListener(FacesContext context, UIViewRoot root) {
return isPartialStateSaving(context, root.getViewId()) ?
new DynamicAddRemoveListener(context) :
new StatelessAddRemoveListener(context);
}
abstract private class AddRemoveListener implements SystemEventListener {
/**
* Stores the state context we work for,
*/
private StateContext stateCtx;
/**
* Constructor.
*
* @param context the Faces context.
*/
protected AddRemoveListener(FacesContext context) {
stateCtx = StateContext.getStateContext(context);
}
/**
* Get the list of adds/removes.
*
* @return the list of adds/removes.
*/
abstract public List getDynamicActions();
/**
* Get the hash map of dynamic components.
*
* @return the hash map of dynamic components.
*/
abstract public HashMap getDynamicComponents();
/**
* Process the add/remove event.
*
* @param event the add/remove event.
* @throws AbortProcessingException when processing should be aborted.
*/
public void processEvent(SystemEvent event)
throws AbortProcessingException {
FacesContext ctx = FacesContext.getCurrentInstance();
if (event instanceof PreRemoveFromViewEvent) {
if (stateCtx.trackViewModifications()) {
handleRemove(ctx, ((PreRemoveFromViewEvent) event).getComponent());
ctx.getViewRoot().getAttributes().put(RIConstants.TREE_HAS_DYNAMIC_COMPONENTS, Boolean.TRUE);
}
} else {
if (stateCtx.trackViewModifications()) {
handleAdd(ctx, ((PostAddToViewEvent) event).getComponent());
ctx.getViewRoot().getAttributes().put(RIConstants.TREE_HAS_DYNAMIC_COMPONENTS, Boolean.TRUE);
}
}
}
/**
* Are we listening for these particular changes.
*
*
* Note we are only interested in UIComponent adds/removes that are
* not the UIViewRoot itself.
*
*
* @param source the source object we might be listening for.
* @return true if the source is OK, false otherwise.
*/
public boolean isListenerForSource(Object source) {
return (source instanceof UIComponent && !(source instanceof UIViewRoot));
}
/**
* Handle the remove.
*
* @param context the Faces context.
* @param component the UI component to add to the list as a REMOVE.
*/
abstract protected void handleRemove(FacesContext context, UIComponent component);
/**
* Handle the add.
*
* @param context the Faces context.
* @param component the UI component to add to the list as an ADD.
*/
abstract protected void handleAdd(FacesContext context, UIComponent component);
}
public class NoopAddRemoveListener extends AddRemoveListener {
// This is silly. We should be able to use Colletions.emptyMap(),
// but cannot as StateContext.getDynamicComponents() API returns a
// HashMap instead of a Map.
private HashMap emptyComponentsMap = new HashMap();
public NoopAddRemoveListener(FacesContext context) {
super(context);
}
@Override
public List getDynamicActions() {
return Collections.emptyList();
}
@Override
public HashMap getDynamicComponents() {
return emptyComponentsMap;
}
@Override
protected void handleRemove(FacesContext context, UIComponent component) {
}
@Override
protected void handleAdd(FacesContext context, UIComponent component) {
}
}
/**
* An AddRemoveListener that implements the new dynamic component
* strategy where no state is managed by the listener itself. Instead,
* we use expando attributes on the dynamic components (and their parents)
* to track/preserve the dynamic nature of these components.
*/
public class StatelessAddRemoveListener extends NoopAddRemoveListener {
public StatelessAddRemoveListener(FacesContext context) {
super(context);
}
private boolean thisEventCorrespondsToSubtreeRootRemove(FacesContext context, UIComponent c) {
boolean result = false;
if (null != c) {
c = c.getParent();
if (null != c) {
result = c.isInView();
}
}
return result;
}
private boolean thisEventCorrespondsToSubtreeRootAdd(FacesContext context, UIComponent c) {
boolean result = false;
Map