001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.portlet.context;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import javax.portlet.PortletRequest;
024: import javax.portlet.PortletSession;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.util.Assert;
030: import org.springframework.web.context.request.AbstractRequestAttributes;
031: import org.springframework.web.context.request.RequestAttributes;
032: import org.springframework.web.portlet.util.PortletUtils;
033:
034: /**
035: * Portlet-based implementation of the
036: * {@link org.springframework.web.context.request.RequestAttributes} interface.
037: *
038: * <p>Accesses objects from portlet request and portlet session scope,
039: * with a distinction between "session" (the PortletSession's "portlet scope")
040: * and "global session" (the PortletSession's "application scope").
041: *
042: * @author Juergen Hoeller
043: * @since 2.0
044: * @see javax.portlet.PortletRequest#getAttribute
045: * @see javax.portlet.PortletSession#getAttribute
046: * @see javax.portlet.PortletSession#PORTLET_SCOPE
047: * @see javax.portlet.PortletSession#APPLICATION_SCOPE
048: * @see RequestAttributes#SCOPE_SESSION
049: * @see RequestAttributes#SCOPE_GLOBAL_SESSION
050: */
051: public class PortletRequestAttributes extends AbstractRequestAttributes {
052:
053: /**
054: * We'll create a lot of these objects, so we don't want a new logger every time.
055: */
056: private static final Log logger = LogFactory
057: .getLog(PortletRequestAttributes.class);
058:
059: private final PortletRequest request;
060:
061: private PortletSession session;
062:
063: private final Map sessionAttributesToUpdate = new HashMap();
064:
065: private final Map globalSessionAttributesToUpdate = new HashMap();
066:
067: /**
068: * Create a new PortletRequestAttributes instance for the given request.
069: * @param request current portlet request
070: */
071: public PortletRequestAttributes(PortletRequest request) {
072: Assert.notNull(request, "Request must not be null");
073: this .request = request;
074: // Fetch existing session reference early, to have it available even
075: // after request completion (for example, in a custom child thread).
076: this .session = request.getPortletSession(false);
077: }
078:
079: /**
080: * Exposes the native {@link PortletRequest} that we're wrapping.
081: */
082: public final PortletRequest getRequest() {
083: return this .request;
084: }
085:
086: /**
087: * Exposes the {@link PortletSession} that we're wrapping.
088: * @param allowCreate whether to allow creation of a new session if none exists yet
089: */
090: protected final PortletSession getSession(boolean allowCreate) {
091: try {
092: this .session = this .request.getPortletSession(allowCreate);
093: return this .session;
094: } catch (IllegalStateException ex) {
095: // Couldn't access session... let's check why.
096: if (this .session == null) {
097: // No matter what happened - we cannot offer a session.
098: throw ex;
099: }
100: // We have a fallback session reference...
101: // Let's see whether it is appropriate to return it.
102: if (allowCreate) {
103: boolean canAskForExistingSession = false;
104: try {
105: this .session = this .request
106: .getPortletSession(false);
107: canAskForExistingSession = true;
108: } catch (IllegalStateException ex2) {
109: }
110: if (canAskForExistingSession) {
111: // Could ask for existing session, hence the IllegalStateException
112: // came from trying to create a new session too late -> rethrow.
113: throw ex;
114: }
115: }
116: // Else: Could not even ask for existing session, hence we assume that
117: // the request has been completed and the session is accessed later on
118: // (for example, in a custom child thread).
119: return this .session;
120: }
121: }
122:
123: public Object getAttribute(String name, int scope) {
124: if (scope == SCOPE_REQUEST) {
125: return this .request.getAttribute(name);
126: } else {
127: PortletSession session = getSession(false);
128: if (session != null) {
129: if (scope == SCOPE_GLOBAL_SESSION) {
130: Object value = session.getAttribute(name,
131: PortletSession.APPLICATION_SCOPE);
132: if (value != null) {
133: this .globalSessionAttributesToUpdate.put(name,
134: value);
135: }
136: return value;
137: } else {
138: Object value = session.getAttribute(name);
139: if (value != null) {
140: this .sessionAttributesToUpdate.put(name, value);
141: }
142: return value;
143: }
144: } else {
145: return null;
146: }
147: }
148: }
149:
150: public void setAttribute(String name, Object value, int scope) {
151: if (scope == SCOPE_REQUEST) {
152: this .request.setAttribute(name, value);
153: } else {
154: PortletSession session = getSession(true);
155: if (scope == SCOPE_GLOBAL_SESSION) {
156: session.setAttribute(name, value,
157: PortletSession.APPLICATION_SCOPE);
158: this .globalSessionAttributesToUpdate.remove(name);
159: } else {
160: session.setAttribute(name, value);
161: this .sessionAttributesToUpdate.remove(name);
162: }
163: }
164: }
165:
166: public void removeAttribute(String name, int scope) {
167: if (scope == SCOPE_REQUEST) {
168: this .request.removeAttribute(name);
169: removeRequestDestructionCallback(name);
170: } else {
171: PortletSession session = getSession(false);
172: if (session != null) {
173: if (scope == SCOPE_GLOBAL_SESSION) {
174: session.removeAttribute(name,
175: PortletSession.APPLICATION_SCOPE);
176: this .globalSessionAttributesToUpdate.remove(name);
177: } else {
178: session.removeAttribute(name);
179: this .sessionAttributesToUpdate.remove(name);
180: }
181: }
182: }
183: }
184:
185: public void registerDestructionCallback(String name,
186: Runnable callback, int scope) {
187: if (scope == SCOPE_REQUEST) {
188: registerRequestDestructionCallback(name, callback);
189: } else {
190: registerSessionDestructionCallback(name, callback);
191: }
192: }
193:
194: public String getSessionId() {
195: return getSession(true).getId();
196: }
197:
198: public Object getSessionMutex() {
199: return PortletUtils.getSessionMutex(getSession(true));
200: }
201:
202: /**
203: * Update all accessed session attributes through <code>session.setAttribute</code>
204: * calls, explicitly indicating to the container that they might have been modified.
205: */
206: protected void updateAccessedSessionAttributes() {
207: PortletSession session = this .request.getPortletSession(false);
208: if (session != null) {
209: for (Iterator it = this .sessionAttributesToUpdate
210: .entrySet().iterator(); it.hasNext();) {
211: Map.Entry entry = (Map.Entry) it.next();
212: String name = (String) entry.getKey();
213: Object newValue = entry.getValue();
214: Object oldValue = session.getAttribute(name);
215: if (oldValue == newValue) {
216: session.setAttribute(name, newValue);
217: }
218: }
219: for (Iterator it = this .globalSessionAttributesToUpdate
220: .entrySet().iterator(); it.hasNext();) {
221: Map.Entry entry = (Map.Entry) it.next();
222: String name = (String) entry.getKey();
223: Object newValue = entry.getValue();
224: Object oldValue = session.getAttribute(name,
225: PortletSession.APPLICATION_SCOPE);
226: if (oldValue == newValue) {
227: session.setAttribute(name, newValue,
228: PortletSession.APPLICATION_SCOPE);
229: }
230: }
231: }
232: this .sessionAttributesToUpdate.clear();
233: this .globalSessionAttributesToUpdate.clear();
234: }
235:
236: /**
237: * Register the given callback as to be executed after session termination.
238: * @param name the name of the attribute to register the callback for
239: * @param callback the callback to be executed for destruction
240: */
241: private void registerSessionDestructionCallback(String name,
242: Runnable callback) {
243: if (logger.isWarnEnabled()) {
244: logger
245: .warn("Could not register destruction callback ["
246: + callback
247: + "] for attribute '"
248: + name
249: + "' for session scope because Portlet API 1.0 does not support session attribute callbacks");
250: }
251: }
252:
253: }
|