blob: a3fc705d4462324bcf641e745c9106e9e6e5603b [file] [log] [blame]
// Copyright 2004 The Apache Software Foundation
//
// 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.apache.tapestry.record;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.request.RequestContext;
import org.apache.tapestry.util.StringSplitter;
/**
* Simple implementation of {@link org.apache.tapestry.engine.IPageRecorder}that stores page
* changes as {@link javax.servlet.http.HttpSession}attributes.
*
* @author Howard Ship
* @version $Id$
*/
public class SessionPageRecorder extends PageRecorder
{
private static final Log LOG = LogFactory.getLog(SessionPageRecorder.class);
/**
* Dictionary of changes, keyed on an instance of {@link ChangeKey}(which enapsulates component
* path and property name). The value is the new value for the object. The same information is
* stored into the {@link HttpSession}, which is used as a kind of write-behind cache.
*/
private Map _changes;
/**
* The session into which changes are recorded.
*
* @since 3.0
*/
private HttpSession _session;
/**
* The fully qualified name of the page being recorded.
*
* @since 3.0
*/
private String _pageName;
/**
* The prefix (for {@link HttpSession}attributes) used by this page recorder.
*/
private String _attributePrefix;
public void initialize(String pageName, IRequestCycle cycle)
{
if (LOG.isDebugEnabled())
LOG.debug("Initializing for " + pageName);
RequestContext context = cycle.getRequestContext();
_pageName = pageName;
_session = context.getSession();
_attributePrefix = context.getServlet().getServletName() + "/" + _pageName + "/";
restorePageChanges();
}
public void discard()
{
if (Tapestry.isEmpty(_changes))
return;
Iterator i = _changes.keySet().iterator();
while (i.hasNext())
{
ChangeKey key = (ChangeKey) i.next();
String attributeKey = constructAttributeKey(key.getComponentPath(), key
.getPropertyName());
if (LOG.isDebugEnabled())
LOG.debug("Removing session attribute " + attributeKey);
_session.removeAttribute(attributeKey);
}
}
/**
* Simply clears the dirty flag, because there is no external place to store changed page
* properties. Sets the locked flag to prevent subsequent changes from occuring now.
*/
public void commit()
{
setDirty(false);
setLocked(true);
}
/**
* Returns true if the recorder has any changes recorded.
*/
public boolean getHasChanges()
{
if (_changes == null)
return false;
return (_changes.size() > 0);
}
public Collection getChanges()
{
if (_changes == null)
return Collections.EMPTY_LIST;
int count = _changes.size();
Collection result = new ArrayList(count);
Iterator i = _changes.entrySet().iterator();
while (i.hasNext())
{
Map.Entry entry = (Map.Entry) i.next();
ChangeKey key = (ChangeKey) entry.getKey();
Object value = entry.getValue();
PageChange change = new PageChange(key.getComponentPath(), key.getPropertyName(), value);
result.add(change);
}
return result;
}
protected void recordChange(String componentPath, String propertyName, Object newValue)
{
ChangeKey key = new ChangeKey(componentPath, propertyName);
if (_changes == null)
_changes = new HashMap();
setDirty(true);
_changes.put(key, newValue);
// Now, build a key used to store the new value
// in the HttpSession
String attributeKey = constructAttributeKey(componentPath, propertyName);
if (newValue == null)
_session.removeAttribute(attributeKey);
else
_session.setAttribute(attributeKey, newValue);
if (LOG.isDebugEnabled())
LOG.debug("Stored session attribute " + attributeKey + " = " + newValue);
}
private String constructAttributeKey(String componentPath, String propertyName)
{
StringBuffer buffer = new StringBuffer(_attributePrefix);
if (componentPath != null)
{
buffer.append(componentPath);
buffer.append('/');
}
buffer.append(propertyName);
return buffer.toString();
}
private void restorePageChanges()
{
int count = 0;
Enumeration e = _session.getAttributeNames();
StringSplitter splitter = null;
while (e.hasMoreElements())
{
String key = (String) e.nextElement();
if (!key.startsWith(_attributePrefix))
continue;
if (LOG.isDebugEnabled())
LOG.debug("Restoring page change from session attribute " + key);
if (_changes == null)
{
_changes = new HashMap();
splitter = new StringSplitter('/');
}
String[] names = splitter.splitToArray(key);
// The first name is the servlet name, which allows
// multiple Tapestry apps to share a HttpSession, even
// when they use the same page names. The second name
// is the page name, which we already know.
int i = 2;
String componentPath = (names.length == 4) ? names[i++] : null;
String propertyName = names[i++];
Object value = _session.getAttribute(key);
ChangeKey changeKey = new ChangeKey(componentPath, propertyName);
_changes.put(changeKey, value);
count++;
}
if (LOG.isDebugEnabled())
LOG.debug(count == 0 ? "No recorded changes." : "Restored " + count
+ " recorded changes.");
}
}