001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.wsrp.consumer;
023:
024: import org.jboss.logging.Logger;
025: import org.jboss.portal.api.event.PortalEvent;
026: import org.jboss.portal.api.event.PortalEventContext;
027: import org.jboss.portal.api.event.PortalEventListener;
028: import org.jboss.portal.api.session.event.PortalSessionEvent;
029: import org.jboss.portal.common.util.ParameterValidation;
030: import org.jboss.portal.portlet.InvokerUnavailableException;
031: import org.jboss.portal.portlet.Portlet;
032: import org.jboss.portal.portlet.PortletInvokerException;
033: import org.jboss.portal.portlet.invocation.PortletInvocation;
034: import org.jboss.portal.wsrp.WSRPTypeFactory;
035: import org.jboss.portal.wsrp.consumer.portlet.info.WSRPPortletInfo;
036: import org.jboss.portal.wsrp.core.CookieProtocol;
037: import org.jboss.portal.wsrp.core.InitCookie;
038: import org.jboss.portal.wsrp.core.InvalidRegistrationFault;
039: import org.jboss.portal.wsrp.core.ReleaseSessions;
040: import org.jboss.portal.wsrp.core.RuntimeContext;
041: import org.jboss.portal.wsrp.core.SessionContext;
042: import org.jboss.portal.wsrp.handler.RequestHeaderClientHandler;
043:
044: import javax.servlet.http.HttpSession;
045: import java.util.ArrayList;
046: import java.util.HashMap;
047: import java.util.HashSet;
048: import java.util.List;
049: import java.util.Map;
050: import java.util.Set;
051:
052: /**
053: * Manages session informations on behalf of a consumer.
054: *
055: * @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
056: * @version $Revision: 9360 $
057: * @since 2.4 (May 31, 2006)
058: */
059: public class SessionHandler implements PortalEventListener {
060: protected WSRPConsumerImpl consumer;
061: private static Logger log = Logger.getLogger(SessionHandler.class);
062:
063: /** Cookie protocol required by the producer with the consumer */
064: private CookieProtocol requiresInitCookie;
065:
066: /** The prefix used to isolate WSRP-related session information in the actual session object. */
067: private static final String SESSION_ID_PREFIX = "org.jboss.portal.wsrp.session.";
068:
069: /** session id -> ProducerSessionInformation */
070: private Map<String, ProducerSessionInformation> sessionInfos = new HashMap<String, ProducerSessionInformation>(); // todo: thread-safe?
071:
072: /**
073: * Constructs a new SessionHandler.
074: *
075: * @param consumer the consumer this SessionHandler is associated with.
076: */
077: public SessionHandler(WSRPConsumerImpl consumer) {
078: this .consumer = consumer;
079: }
080:
081: public boolean isPerUserCookieInit() {
082: return CookieProtocol.perUser.equals(requiresInitCookie);
083: }
084:
085: public boolean requiresInitCookie() {
086: return requiresInitCookie != null
087: && !CookieProtocol.none.equals(requiresInitCookie);
088: }
089:
090: public void setRequiresInitCookie(CookieProtocol requiresInitCookie) {
091: this .requiresInitCookie = requiresInitCookie;
092: }
093:
094: void initProducerSessionInformation(PortletInvocation invocation)
095: throws PortletInvokerException {
096: // if we need cookies, set the current group id
097: String groupId;
098: if (requiresGroupInitCookie()) {
099: WSRPPortletInfo info = consumer.getPortletInfo(invocation);
100: groupId = info.getGroupId();
101: } else {
102: groupId = null;
103: }
104:
105: RequestHeaderClientHandler.setCurrentInfo(groupId,
106: getProducerSessionInformation(invocation, true));
107: }
108:
109: private boolean requiresGroupInitCookie() {
110: return requiresInitCookie()
111: && CookieProtocol.perGroup.equals(requiresInitCookie);
112: }
113:
114: /** Resets the information held by RequestHeaderClientHandler for the current interaction. */
115: public void resetCurrentlyHeldInformation() {
116: RequestHeaderClientHandler.resetCurrentInfo();
117: }
118:
119: public void initCookieIfNeeded(PortletInvocation invocation)
120: throws PortletInvokerException {
121: initCookieIfNeeded(invocation, true);
122: }
123:
124: private void initCookieIfNeeded(PortletInvocation invocation,
125: boolean retryIfFails) throws PortletInvokerException {
126: // check if the cookie protocol requires cookie initialization
127: if (!requiresInitCookie()) {
128: return;
129: }
130:
131: // check if we have already initialized cookies for this user
132: ProducerSessionInformation sessionInformation = getProducerSessionInformation(invocation);
133: if (sessionInformation.isInitCookieDone()) {
134: return;
135: }
136: RequestHeaderClientHandler.setCurrentInfo(null,
137: sessionInformation);
138:
139: InitCookie initCookie = WSRPTypeFactory
140: .createInitCookie(consumer.getRegistrationContext());
141:
142: if (isPerUserCookieInit()) {
143: log.debug("Cookie initialization per user requested.");
144: sessionInformation.setPerGroupCookies(false);
145: initCookie(initCookie, invocation, retryIfFails);
146: } else {
147: log.debug("Cookie initialization per group requested.");
148: sessionInformation.setPerGroupCookies(true);
149: Map<String, Set<Portlet>> groups = consumer
150: .getPortletGroupMap();
151:
152: try {
153: for (String groupId : groups.keySet()) {
154: RequestHeaderClientHandler
155: .setCurrentGroupId(groupId);
156: log.debug("Initializing cookie for group '"
157: + groupId + "'.");
158: initCookie(initCookie, invocation, retryIfFails);
159: }
160: } finally {
161: resetCurrentlyHeldInformation();
162: }
163: }
164:
165: sessionInformation.setInitCookieDone(true);
166: }
167:
168: private void initCookie(InitCookie initCookie,
169: PortletInvocation invocation, boolean retryIfFails)
170: throws PortletInvokerException {
171: try {
172: consumer.getMarkupService().initCookie(initCookie);
173: } catch (InvalidRegistrationFault invalidRegistrationFault) {
174: consumer.handleInvalidRegistrationFault();
175: if (retryIfFails) {
176: initCookieIfNeeded(invocation, false);
177: }
178: } catch (Exception e) {
179: throw new InvokerUnavailableException(
180: "Couldn't init cookies!", e);
181: }
182: }
183:
184: void setSessionIdIfNeeded(PortletInvocation invocation,
185: RuntimeContext runtimeContext, String portletHandle) {
186: ProducerSessionInformation producerSessionInfo = getProducerSessionInformation(
187: invocation, false);
188: if (producerSessionInfo != null) {
189: String sessionId = producerSessionInfo
190: .getSessionIdForPortlet(portletHandle);
191: runtimeContext.setSessionID(sessionId);
192: }
193: }
194:
195: void updateSessionIfNeeded(SessionContext sessionContext,
196: PortletInvocation invocation, String portletHandle) {
197: if (sessionContext != null) {
198: log.debug("Portlet '" + portletHandle
199: + "' created session with id '"
200: + sessionContext.getSessionID() + "'");
201: ProducerSessionInformation sessionInfo = getProducerSessionInformation(invocation);
202: sessionInfo.addSessionForPortlet(portletHandle,
203: sessionContext);
204: }
205: }
206:
207: void updateCookiesIfNeeded(PortletInvocation invocation) {
208: ProducerSessionInformation sessionInfo = getProducerSessionInformation(
209: invocation, true);
210: ProducerSessionInformation currentSessionInfo = RequestHeaderClientHandler
211: .getCurrentProducerSessionInformation();
212: sessionInfo.replaceUserCookiesWith(currentSessionInfo);
213: }
214:
215: ProducerSessionInformation getProducerSessionInformation(
216: PortletInvocation invocation) {
217: return getProducerSessionInformation(invocation, true);
218: }
219:
220: private ProducerSessionInformation getProducerSessionInformation(
221: PortletInvocation invocation, boolean create) {
222: HttpSession session = WSRPConsumerImpl
223: .getHttpSession(invocation);
224: return getProducerSessionInformation(session, create);
225: }
226:
227: ProducerSessionInformation getProducerSessionInformation(
228: HttpSession session) {
229: return getProducerSessionInformation(session, false);
230: }
231:
232: private ProducerSessionInformation getProducerSessionInformation(
233: HttpSession session, boolean create) {
234: ParameterValidation.throwIllegalArgExceptionIfNull(session,
235: "Session");
236: String producerSessionKey = getProducerSessionInformationKey();
237: ProducerSessionInformation sessionInformation = (ProducerSessionInformation) session
238: .getAttribute(producerSessionKey);
239:
240: if (sessionInformation != null) {
241: sessionInformation.setParent(this );
242: sessionInformation.setParentSessionId(session.getId());
243: } else {
244: if (create) {
245: sessionInformation = new ProducerSessionInformation();
246: sessionInformation.setParentSessionId(session.getId());
247: session.setAttribute(producerSessionKey,
248: sessionInformation);
249: sessionInformation.setParent(this );
250: }
251: }
252:
253: return sessionInformation;
254: }
255:
256: private String getProducerSessionInformationKey() {
257: return SESSION_ID_PREFIX + consumer.getProducerId();
258: }
259:
260: void handleInvalidSessionFault(PortletInvocation invocation,
261: RuntimeContext runtimeContext) {
262: log.info("Resending information after InvalidSessionFault");
263: // invalidate current session
264: invalidateSession(invocation);
265:
266: // set the session id to null
267: runtimeContext.setSessionID(null);
268: }
269:
270: private void invalidateSession(PortletInvocation invocation) {
271: HttpSession session = WSRPConsumerImpl
272: .getHttpSession(invocation);
273:
274: // remove the associated info from the known producer session informations
275:
276: ProducerSessionInformation info = getProducerSessionInformation(
277: session, false);
278: if (info != null) {
279: try {
280: internalReleaseSessions(info.removeSessions());
281: } catch (PortletInvokerException e) {
282: // ignore since it's logged by internalReleaseSessions already
283: }
284: }
285:
286: session.removeAttribute(getProducerSessionInformationKey());
287: RequestHeaderClientHandler.resetCurrentInfo();
288: }
289:
290: /** @since 2.6 */
291: void releaseSessions() throws PortletInvokerException {
292: List<String> idsToRelease = new ArrayList<String>();
293:
294: Set<ProducerSessionInformation> uniqueInfos = new HashSet<ProducerSessionInformation>(
295: sessionInfos.values());
296:
297: for (ProducerSessionInformation info : uniqueInfos) {
298: idsToRelease.addAll(info.removeSessions());
299: }
300:
301: internalReleaseSessions(idsToRelease);
302: }
303:
304: /**
305: * @param sessionIds
306: * @throws PortletInvokerException
307: * @since 2.6
308: */
309: void releaseSessions(String[] sessionIds)
310: throws PortletInvokerException {
311: if (sessionIds != null) {
312: List<String> idsToRelease = new ArrayList<String>();
313:
314: for (String sessionId : sessionIds) {
315: ProducerSessionInformation info = sessionInfos
316: .get(sessionId);
317: sessionId = info.removeSession(sessionId);
318: if (sessionId != null) {
319: idsToRelease.add(sessionId);
320: }
321: }
322:
323: internalReleaseSessions(idsToRelease);
324: }
325: }
326:
327: private void internalReleaseSessions(List<String> idsToRelease)
328: throws PortletInvokerException {
329: if (idsToRelease != null && !idsToRelease.isEmpty()) {
330: ReleaseSessions releaseSessions = WSRPTypeFactory
331: .createReleaseSessions(consumer
332: .getRegistrationContext(), idsToRelease
333: .toArray(new String[idsToRelease.size()]));
334:
335: try {
336: consumer.getMarkupService().releaseSessions(
337: releaseSessions);
338: } catch (InvalidRegistrationFault invalidRegistrationFault) {
339: log.debug("Invalid Registration");
340: consumer.handleInvalidRegistrationFault();
341: } catch (Exception e) {
342: log.debug(e);
343: throw new PortletInvokerException(
344: "Couldn't release sessions " + idsToRelease, e);
345: }
346: }
347: }
348:
349: // ProducerSessionInformation callbacks
350:
351: /**
352: * Update session mappings when a session has expired
353: *
354: * @param id
355: * @since 2.6
356: */
357: void removeSessionId(String id) {
358: sessionInfos.remove(id);
359: }
360:
361: /**
362: * Update session mappings when a new session id is added to the specified ProducerSessionInformation
363: *
364: * @param sessionID
365: * @param producerSessionInformation
366: * @since 2.6
367: */
368: void addSessionMapping(String sessionID,
369: ProducerSessionInformation producerSessionInformation) {
370: sessionInfos.put(sessionID, producerSessionInformation);
371: }
372:
373: // End ProducerSessionInformation callbacks
374:
375: // PortalEventListener implementation
376:
377: public void onEvent(PortalEventContext eventContext,
378: PortalEvent event) {
379: if (event instanceof PortalSessionEvent) {
380: PortalSessionEvent use = (PortalSessionEvent) event;
381: int type = use.getType();
382: String id = eventContext.getPortalRuntimeContext()
383: .getSession().getId();
384: switch (type) {
385: case PortalSessionEvent.SESSION_CREATED:
386: break; // nothing to do
387:
388: case PortalSessionEvent.SESSION_DESTROYED:
389: // check if the session being destroyed is the one associated with this thread
390: ProducerSessionInformation info = RequestHeaderClientHandler
391: .getCurrentProducerSessionInformation();
392: if (info != null) {
393: if (id != null
394: && id.equals(info.getParentSessionId())) {
395: try {
396: internalReleaseSessions(info
397: .removeSessions());
398: } catch (PortletInvokerException e) {
399: // already logged...
400: }
401: log
402: .debug("Released session '"
403: + id
404: + "' after session was destroyed by Portal.");
405: }
406: }
407: break;
408:
409: default:
410: throw new RuntimeException("Unexpected event: " + event);
411: }
412: }
413: }
414:
415: /**
416: * @param originalHandle
417: * @param newHandle
418: * @param invocation
419: * @since 2.6
420: */
421: public void updateSessionInfoFor(String originalHandle,
422: String newHandle, PortletInvocation invocation) {
423: ProducerSessionInformation info = getProducerSessionInformation(
424: invocation, false);
425: if (info != null) {
426: info.updateHandleAssociatedInfo(originalHandle, newHandle);
427: }
428: }
429: }
|