001 // Copyright 2007, 2008, 2010 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.internal.services;
016
017 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
018
019 import java.util.Collection;
020 import java.util.Collections;
021 import java.util.List;
022
023 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024 import org.apache.tapestry5.services.PersistentFieldChange;
025 import org.apache.tapestry5.services.PersistentFieldStrategy;
026 import org.apache.tapestry5.services.Request;
027 import org.apache.tapestry5.services.Session;
028
029 /**
030 * Base class for strategies that store their values as keys in the session. Implements a uniform format for the keys,
031 * based on a prefix to identify the particular strategy.
032 */
033 public abstract class AbstractSessionPersistentFieldStrategy implements PersistentFieldStrategy
034 {
035 private final String prefix;
036
037 private final Request request;
038
039 protected AbstractSessionPersistentFieldStrategy(String prefix, Request request)
040 {
041 this.prefix = prefix;
042 this.request = request;
043 }
044
045 public final Collection<PersistentFieldChange> gatherFieldChanges(String pageName)
046 {
047 Session session = request.getSession(false);
048
049 if (session == null) return Collections.emptyList();
050
051 List<PersistentFieldChange> result = newList();
052
053 String fullPrefix = prefix + pageName + ":";
054
055 for (String name : session.getAttributeNames(fullPrefix))
056 {
057 Object persistedValue = session.getAttribute(name);
058
059 Object applicationValue = persistedValue == null ? null : convertPersistedToApplicationValue(
060 persistedValue);
061
062 PersistentFieldChange change = buildChange(name, applicationValue);
063
064 result.add(change);
065
066 didReadChange(session, name);
067 }
068
069 return result;
070 }
071
072 public void discardChanges(String pageName)
073 {
074 Session session = request.getSession(false);
075
076 if (session == null) return;
077
078 String fullPrefix = prefix + pageName + ":";
079
080 for (String name : session.getAttributeNames(fullPrefix))
081 {
082 session.setAttribute(name, null);
083 }
084 }
085
086 /**
087 * Called after each key is read by {@link #gatherFieldChanges(String)}. This implementation does nothing,
088 * subclasses may override.
089 *
090 * @param session the session from which a value was just read
091 * @param attributeName the name of the attribute used to read a value
092 */
093 protected void didReadChange(Session session, String attributeName)
094 {
095 }
096
097 private PersistentFieldChange buildChange(String name, Object newValue)
098 {
099 String[] chunks = name.split(":");
100
101 // Will be empty string for the root component
102 String componentId = chunks[2];
103 String fieldName = chunks[3];
104
105 return new PersistentFieldChangeImpl(componentId, fieldName, newValue);
106 }
107
108 public final void postChange(String pageName, String componentId, String fieldName,
109 Object newValue)
110 {
111 assert InternalUtils.isNonBlank(pageName);
112 assert InternalUtils.isNonBlank(fieldName);
113 Object persistedValue = newValue == null ? null : convertApplicationValueToPersisted(newValue);
114
115 StringBuilder builder = new StringBuilder(prefix);
116 builder.append(pageName);
117 builder.append(':');
118
119 if (componentId != null) builder.append(componentId);
120
121 builder.append(':');
122 builder.append(fieldName);
123
124 Session session = request.getSession(persistedValue != null);
125
126 // TAPESTRY-2308: The session will be false when newValue is null and the session
127 // does not already exist.
128
129 if (session != null)
130 {
131 session.setAttribute(builder.toString(), persistedValue);
132 }
133 }
134
135 /**
136 * Hook that allows a value to be converted as it is written to the session. Passed the new value provided by the
137 * application, returns the object to be stored in the session. This implementation simply returns the provided
138 * value.
139 *
140 * @param newValue non-null value
141 * @return persisted value
142 * @see #convertPersistedToApplicationValue(Object)
143 */
144 protected Object convertApplicationValueToPersisted(Object newValue)
145 {
146 return newValue;
147 }
148
149 /**
150 * Converts a persisted value stored in the session back into an application value. This implementation returns
151 * the persisted value as is.
152 *
153 * @param persistedValue non-null persisted value
154 * @return application value
155 * @see #convertPersistedToApplicationValue(Object)
156 */
157 protected Object convertPersistedToApplicationValue(Object persistedValue)
158 {
159 return persistedValue;
160 }
161 }