/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.cocoon.acting; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Redirector; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.Session; import org.apache.cocoon.environment.SourceResolver; import java.util.HashMap; import java.util.Map; /** * Store the session's current state in a session attribute. * * <p> To keep track of the state of a user's session, a string is * stored in a session attribute in order to allow to chose between * different pipelines in the sitemap accordingly.</p> * * <p> For added flexibility it is possible to use sub states as * well. For this declare your own name for the session state * attribute and give the number of sublevels plus the level to * modify. (This is <b>one</b> based!) Sub states below the current * one are removed from the session so that the default sub state will * be reentered when the user returns. If you don't like this * behaviour and prefer independent sub states, use this action * several times with different attribute names rather than sub * levels. </p> * * <p><b>Global and local parameters:</b></p> * * <table border="1"> * <tr> * <td><code>state-key-prefix</code></td> * <td>String that identifies the attribute that stores the session state in the * session object. When sublevels are used, this is a prefix ie. the * number of the level is appended to the prefix. Example prefix is * "<code>__sessionState</code>", sub-levels is 2, attributes * "<code>__sessionState1</code>", "<code>__sessionState2</code>", and * "<code>__sessionState3</code>" will be used to store the * information. * </td> * </tr> * <tr> * <td><code>new-state</code></td> * <td>String that identifies the current state</td> * </tr> * <tr> * <td><code>sub-levels</code></td> * <td>Number of sub levels to use</td> * </tr> * <tr> * <td><code>state-level</code></td> * <td>Sub level to modify, this is <b>one</b> based</td> * </tr> * </table> * * @see org.apache.cocoon.matching.WildcardSessionAttributeMatcher * @see org.apache.cocoon.selection.SessionAttributeSelector * * @author <a href="mailto:haul@apache.org">Christian Haul</a> * @version CVS $Id$ */ public class SessionStateAction extends AbstractConfigurableAction implements ThreadSafe { protected String statekey = "org.apache.cocoon.SessionState"; protected String newstate = null; protected int sublevels = 0; protected int mylevel = 0; /** * Configures the Action. */ public void configure(Configuration conf) throws ConfigurationException { super.configure(conf); if (settings.containsKey("state-key-prefix")) { statekey = (String) settings.get("state-key-prefix"); } if (settings.containsKey("new-state")) { newstate = (String) settings.get("new-state"); } if (settings.containsKey("sub-levels")) { sublevels = Integer.parseInt((String) settings.get("sub-levels")); } if (settings.containsKey("state-level")) { mylevel = Integer.parseInt((String) settings.get("state-level")); } } public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, Parameters par) throws Exception { Request request = ObjectModelHelper.getRequest(objectModel); // read local settings String newstate = par.getParameter("new-state", this.newstate); String statekey = par.getParameter("state-key", this.statekey); int sublevels = par.getParameterAsInteger("sublevels", this.sublevels); int mylevel = par.getParameterAsInteger("state-level", this.mylevel); if (newstate == null) { if (this.getLogger().isDebugEnabled()) { getLogger().error("new-state is null"); } return null; } if (request != null) { Session session = request.getSession(false); if (session != null && request.isRequestedSessionIdValid()) { String oldstate = null; if (sublevels == 0) { oldstate = (String) session.getAttribute(statekey); session.setAttribute(statekey, newstate); if (this.getLogger().isDebugEnabled()) { getLogger().debug(statekey + "=" + newstate); } } else { // sublevels != 0 oldstate = (String)session.getAttribute(statekey + mylevel); for (int i = mylevel + 1; i <= sublevels; i++) { session.removeAttribute(statekey + i); if (this.getLogger().isDebugEnabled()) { getLogger().debug("Remove " + statekey + i); } } session.setAttribute(statekey + mylevel, newstate); if (this.getLogger().isDebugEnabled()) { getLogger().debug(statekey + mylevel + "=" + newstate); } } if (this.getLogger().isDebugEnabled()) { getLogger().debug("Transition " + oldstate + " -> " + newstate); } HashMap map = new HashMap(1); map.put("newstate", newstate); return map; } else { getLogger().warn( "A session object was not present or no longer valid"); return null; } } else { getLogger().warn("No request object"); return null; } } }