0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.jetspeed.portalsite.impl;
0018:
0019: import java.io.Serializable;
0020: import java.security.AccessController;
0021: import java.security.Principal;
0022: import java.util.HashMap;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.Map;
0026: import java.util.Set;
0027:
0028: import javax.security.auth.Subject;
0029: import javax.servlet.http.HttpSessionActivationListener;
0030: import javax.servlet.http.HttpSessionBindingEvent;
0031: import javax.servlet.http.HttpSessionBindingListener;
0032: import javax.servlet.http.HttpSessionEvent;
0033:
0034: import org.apache.commons.logging.Log;
0035: import org.apache.commons.logging.LogFactory;
0036: import org.apache.jetspeed.om.folder.Folder;
0037: import org.apache.jetspeed.om.page.Page;
0038: import org.apache.jetspeed.page.PageManager;
0039: import org.apache.jetspeed.page.PageManagerEventListener;
0040: import org.apache.jetspeed.page.document.Node;
0041: import org.apache.jetspeed.page.document.NodeException;
0042: import org.apache.jetspeed.page.document.NodeNotFoundException;
0043: import org.apache.jetspeed.page.document.NodeSet;
0044: import org.apache.jetspeed.portalsite.PortalSiteRequestContext;
0045: import org.apache.jetspeed.portalsite.PortalSiteSessionContext;
0046: import org.apache.jetspeed.portalsite.view.SiteView;
0047: import org.apache.jetspeed.portalsite.view.SiteViewMenuDefinitionLocator;
0048: import org.apache.jetspeed.profiler.ProfileLocator;
0049: import org.apache.jetspeed.profiler.ProfileLocatorProperty;
0050: import org.apache.jetspeed.security.JSSubject;
0051: import org.apache.jetspeed.security.UserPrincipal;
0052:
0053: /**
0054: * This class encapsulates managed session state for and
0055: * interface to the portal-site component and subscribes
0056: * to page manager and session events to flush stale state.
0057: *
0058: * Note that is object is Serializable since it is designed
0059: * to be cached in the session. However, because this object
0060: * is cached only for these two reasons:
0061: *
0062: * 1. a performance optimization to reuse SiteViews, and
0063: * 2. to hold optional folder page history,
0064: *
0065: * this object need not be relocatable between J2 instances.
0066: * Consequently, all data members are marked transient and
0067: * the isValid() method is used to test whether this object
0068: * is a valid context for the session or if it was
0069: * transferred from another server or the persistent session
0070: * store and needs to be discarded.
0071: *
0072: * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
0073: * @version $Id: PortalSiteSessionContextImpl.java 553375 2007-07-05 05:37:00Z taylor $
0074: */
0075: public class PortalSiteSessionContextImpl implements
0076: PortalSiteSessionContext, PageManagerEventListener,
0077: HttpSessionActivationListener, HttpSessionBindingListener,
0078: Serializable {
0079: /**
0080: * log - logging instance
0081: */
0082: private final static Log log = LogFactory
0083: .getLog(PortalSiteSessionContextImpl.class);
0084:
0085: /**
0086: * pageManager - PageManager component
0087: */
0088: private transient PageManager pageManager;
0089:
0090: /**
0091: * profileLocators - map of session profile locators by locator names
0092: */
0093: private transient Map profileLocators;
0094:
0095: /**
0096: * userPrincipal - session user principal
0097: */
0098: private transient String userPrincipal;
0099:
0100: /**
0101: * siteView - session site view
0102: */
0103: private transient SiteView siteView;
0104:
0105: /**
0106: * folderPageHistory - map of last page visited by folder
0107: */
0108: private transient Map folderPageHistory;
0109:
0110: /**
0111: * menuDefinitionLocatorCache - cached menu definition locators for
0112: * absolute menus valid for session
0113: */
0114: private transient Map menuDefinitionLocatorCache;
0115:
0116: /**
0117: * subscribed - flag that indicates whether this context
0118: * is subscribed as event listeners
0119: */
0120: private transient boolean subscribed;
0121:
0122: /**
0123: * stale - flag that indicates whether the state
0124: * managed by this context is stale
0125: */
0126: private transient boolean stale;
0127:
0128: /**
0129: * store which pipeline we are serving
0130: *
0131: */
0132: private transient String pipeline = "";
0133:
0134: /**
0135: * PortalSiteSessionContextImpl - constructor
0136: *
0137: * @param pageManager PageManager component instance
0138: */
0139: public PortalSiteSessionContextImpl(PageManager pageManager) {
0140: this .pageManager = pageManager;
0141: this .pipeline = "";
0142: }
0143:
0144: /**
0145: * newRequestContext - create a new request context instance with fallback and history
0146: *
0147: * @param requestProfileLocators request profile locators
0148: * @return new request context instance
0149: */
0150: public PortalSiteRequestContext newRequestContext(
0151: Map requestProfileLocators) {
0152: return new PortalSiteRequestContextImpl(this ,
0153: requestProfileLocators, true, true);
0154: }
0155:
0156: /**
0157: * newRequestContext - create a new request context instance with history
0158: *
0159: * @param requestProfileLocators request profile locators
0160: * @param requestFallback flag specifying whether to fallback to root folder
0161: * if locators do not select a page or access is forbidden
0162: * @return new request context instance
0163: */
0164: public PortalSiteRequestContext newRequestContext(
0165: Map requestProfileLocators, boolean requestFallback) {
0166: return new PortalSiteRequestContextImpl(this ,
0167: requestProfileLocators, requestFallback, true);
0168: }
0169:
0170: /**
0171: * newRequestContext - create a new request context instance
0172: *
0173: * @param requestProfileLocators request profile locators
0174: * @param requestFallback flag specifying whether to fallback to root folder
0175: * if locators do not select a page or access is forbidden
0176: * @param useHistory flag indicating whether to use visited page
0177: * history to select default page per site folder
0178: * @return new request context instance
0179: */
0180: public PortalSiteRequestContext newRequestContext(
0181: Map requestProfileLocators, boolean requestFallback,
0182: boolean useHistory) {
0183: return new PortalSiteRequestContextImpl(this ,
0184: requestProfileLocators, requestFallback, useHistory);
0185: }
0186:
0187: /**
0188: * selectRequestPage - select page proxy for request given profile locators
0189: *
0190: * @param requestProfileLocators map of profile locators for request
0191: * @param requestFallback flag specifying whether to fallback to root folder
0192: * if locators do not select a page or access is forbidden
0193: * @param useHistory flag indicating whether to use visited page
0194: * history to select default page per site folder
0195: * @return selected page proxy for request
0196: * @throws NodeNotFoundException if not found
0197: * @throws SecurityException if view access not granted
0198: */
0199: public Page selectRequestPage(Map requestProfileLocators,
0200: boolean requestFallback, boolean useHistory)
0201: throws NodeNotFoundException {
0202: // validate and update session profile locators if modified
0203: if (updateSessionProfileLocators(requestProfileLocators)) {
0204: // extract page request path from the locators
0205: String requestPath = Folder.PATH_SEPARATOR;
0206: ProfileLocator locator = (ProfileLocator) requestProfileLocators
0207: .get(ProfileLocator.PAGE_LOCATOR);
0208: if (locator != null) {
0209: // use 'page' locator to determine request page by executing
0210: // profile locator to determine path
0211: requestPath = getRequestPathFromLocator(locator);
0212: } else {
0213: // 'page' locator unavailable, use first locator since
0214: // all locators should have identical request paths, (do
0215: // not execute profile locator though to determine path:
0216: // simply use the request path)
0217: locator = (ProfileLocator) requestProfileLocators
0218: .values().iterator().next();
0219: requestPath = locator.getRequestPath();
0220: }
0221:
0222: // attempt to select request page or folder using
0223: // profile locators and site view; if fallback
0224: // enabled, fallback on missing node or access
0225: // exceptions to the parent folders until the root
0226: // folder access has been attempted
0227: do {
0228: // attempt to access requested path
0229: Exception fallbackException = null;
0230: try {
0231: return selectRequestPage(requestPath, useHistory);
0232: } catch (NodeNotFoundException nnfe) {
0233: if (!requestFallback
0234: || requestPath
0235: .equals(Folder.PATH_SEPARATOR)) {
0236: throw nnfe;
0237: }
0238: fallbackException = nnfe;
0239: } catch (SecurityException se) {
0240: if (!requestFallback
0241: || requestPath
0242: .equals(Folder.PATH_SEPARATOR)) {
0243: throw se;
0244: }
0245: fallbackException = se;
0246: }
0247:
0248: // compute fallback request path
0249: if (requestFallback
0250: && !requestPath.equals(Folder.PATH_SEPARATOR)) {
0251: // compute parent folder fallback request path
0252: String fallbackRequestPath = requestPath;
0253: while (fallbackRequestPath
0254: .endsWith(Folder.PATH_SEPARATOR)) {
0255: fallbackRequestPath = fallbackRequestPath
0256: .substring(0, fallbackRequestPath
0257: .length() - 1);
0258: }
0259: int folderIndex = fallbackRequestPath
0260: .lastIndexOf(Folder.PATH_SEPARATOR);
0261: if (folderIndex >= 2) {
0262: // fallback to parent folder
0263: fallbackRequestPath = fallbackRequestPath
0264: .substring(0, folderIndex);
0265: } else {
0266: // fallback to root folder
0267: fallbackRequestPath = Folder.PATH_SEPARATOR;
0268: }
0269:
0270: // check fallback path and log fallback operation
0271: if (!fallbackRequestPath.equals(requestPath)) {
0272: // log fallback
0273: if (log.isDebugEnabled()) {
0274: log
0275: .debug(
0276: "Missing/forbidden page selection fallback: request path="
0277: + requestPath
0278: + ", attempting fallback request path="
0279: + fallbackRequestPath,
0280: fallbackException);
0281: }
0282:
0283: // clear all history entries for fallback
0284: // request path in advance to make fallback
0285: // page selection more predictable
0286: Iterator folderIter = getFolderPageHistory()
0287: .keySet().iterator();
0288: while (folderIter.hasNext()) {
0289: Folder folder = (Folder) folderIter.next();
0290: if (folder.getUrl().equals(
0291: fallbackRequestPath)) {
0292: folderIter.remove();
0293: break;
0294: }
0295: }
0296:
0297: // retry requested page access
0298: requestPath = fallbackRequestPath;
0299: }
0300: } else {
0301: // fallback attempts complete: no page found for user
0302: break;
0303: }
0304: } while (true);
0305: }
0306:
0307: // no request page available
0308: throw new NodeNotFoundException(
0309: "No request page available in site view.");
0310: }
0311:
0312: /**
0313: * getRequestPathFromLocator - execute profile locator to extract
0314: * request path using locator rules; this
0315: * is request specific and is not part of
0316: * the site view
0317: *
0318: * @param locator profile locator to execute
0319: * @return request path from profile locator
0320: */
0321: private String getRequestPathFromLocator(ProfileLocator locator) {
0322: // use profile iterator to process the initial full
0323: // set of profile locator properties searching for
0324: // the first non control/navigation, (i.e. page/path),
0325: // property that will force the request path if
0326: // non-null; otherwise default to locator request path
0327: String requestPath = locator.getRequestPath();
0328: Iterator locatorIter = locator.iterator();
0329: if (locatorIter.hasNext()) {
0330: ProfileLocatorProperty[] properties = (ProfileLocatorProperty[]) locatorIter
0331: .next();
0332: for (int i = 0; (i < properties.length); i++) {
0333: if (!properties[i].isControl()
0334: && !properties[i].isNavigation()) {
0335: // request page/path property; append to or replace
0336: // using locator specified path
0337: String path = properties[i].getValue();
0338: if (path != null) {
0339: // specified page/path to be appended to request path if
0340: // relative; otherwise specified page/path to replace
0341: // request path
0342: if (!path.startsWith(Folder.PATH_SEPARATOR)) {
0343: // strip page from request path if required
0344: // and append page/path to base request path
0345: String basePath = requestPath;
0346: if (basePath == null) {
0347: basePath = Folder.PATH_SEPARATOR;
0348: } else if (basePath
0349: .endsWith(Page.DOCUMENT_TYPE)) {
0350: basePath = basePath
0351: .substring(
0352: 0,
0353: basePath
0354: .lastIndexOf(Folder.PATH_SEPARATOR) + 1);
0355: } else if (!basePath
0356: .endsWith(Folder.PATH_SEPARATOR)) {
0357: basePath += Folder.PATH_SEPARATOR;
0358: }
0359: path = basePath + path;
0360:
0361: // make sure path ends in page extension
0362: // if folder not explicitly specified
0363: if (!path.endsWith(Folder.PATH_SEPARATOR)
0364: && !path
0365: .endsWith(Page.DOCUMENT_TYPE)) {
0366: path += Page.DOCUMENT_TYPE;
0367: }
0368: }
0369:
0370: // detect profile locator request path modification
0371: if (!path.equals(requestPath)) {
0372: // if modified request path ends with default page,
0373: // strip default page from path to allow folder level
0374: // defaulting to take place: locator should not force
0375: // selection of default page when selection of the
0376: // folder is implied by use in locator page/path
0377: if (path.endsWith(Folder.PATH_SEPARATOR
0378: + Folder.FALLBACK_DEFAULT_PAGE)) {
0379: path = path.substring(0, path.length()
0380: - Folder.FALLBACK_DEFAULT_PAGE
0381: .length());
0382: }
0383:
0384: // log modified page request
0385: if (log.isDebugEnabled()
0386: && !path.equals(requestPath)) {
0387: log
0388: .debug("Request page modified by profile locator: request path="
0389: + path);
0390: }
0391: }
0392: return path;
0393: }
0394: }
0395: }
0396: }
0397:
0398: // return locator request path
0399: return requestPath;
0400: }
0401:
0402: /**
0403: * selectRequestPage - select page proxy for request for specified
0404: * path given profile locators and site view
0405: * associated with this context
0406: *
0407: * @param requestPath request path
0408: * @param useHistory flag indicating whether to use visited page
0409: * history to select default page per site folder
0410: * @return selected page proxy for request
0411: * @throws NodeNotFoundException if not found
0412: * @throws SecurityException if view access not granted
0413: */
0414: private Page selectRequestPage(String requestPath,
0415: boolean useHistory) throws NodeNotFoundException {
0416: // save access exceptions
0417: SecurityException accessException = null;
0418:
0419: // valid SiteView required from session profile locators
0420: SiteView view = getSiteView();
0421: if (view != null) {
0422: // default request to root folder if not specified
0423: if (requestPath == null) {
0424: requestPath = Folder.PATH_SEPARATOR;
0425: }
0426:
0427: // log page request
0428: if (log.isDebugEnabled()) {
0429: log.debug("Request page: request path=" + requestPath);
0430: }
0431:
0432: // lookup request path in view for viewable page or folder
0433: // nodes; note: directly requested pages/folders may be hidden
0434: // or not viewable
0435: Node requestNode = null;
0436: try {
0437: // try page or folder request url
0438: requestNode = view.getNodeProxy(requestPath, null,
0439: false, false);
0440: } catch (NodeNotFoundException nnfe) {
0441: // if request path ends with default page, strip from
0442: // request url to retry for folder default
0443: if (requestPath.endsWith(Folder.PATH_SEPARATOR
0444: + Folder.FALLBACK_DEFAULT_PAGE)) {
0445: // retry folder request url
0446: requestPath = requestPath.substring(0, requestPath
0447: .length()
0448: - Folder.FALLBACK_DEFAULT_PAGE.length());
0449: requestNode = view.getNodeProxy(requestPath, null,
0450: true, false);
0451: } else {
0452: // rethrow original exception
0453: throw nnfe;
0454: }
0455: }
0456:
0457: // invoke default page logic to determine folder page
0458: if (requestNode instanceof Folder) {
0459: Folder requestFolder = (Folder) requestNode;
0460:
0461: // support subfolders specified as default pages;
0462: // find highest subfolder with a default page that
0463: // specifies a default folder, (not a default page).
0464: try {
0465: String defaultFolderName = requestFolder
0466: .getDefaultPage();
0467: if (defaultFolderName != null) {
0468: // do not follow broken default folders
0469: Folder defaultRequestFolder = requestFolder;
0470: // follow default folders to parent folders
0471: while ((defaultRequestFolder != null)
0472: && (defaultFolderName != null)
0473: && defaultFolderName.equals("..")) {
0474: defaultRequestFolder = (Folder) defaultRequestFolder
0475: .getParent();
0476: if (defaultRequestFolder != null) {
0477: defaultFolderName = defaultRequestFolder
0478: .getDefaultPage();
0479: } else {
0480: defaultFolderName = null;
0481: }
0482: }
0483: // follow default folders to subfolders
0484: while ((defaultRequestFolder != null)
0485: && (defaultFolderName != null)
0486: && !defaultFolderName
0487: .endsWith(Page.DOCUMENT_TYPE)
0488: && !defaultFolderName.equals("..")) {
0489: defaultRequestFolder = defaultRequestFolder
0490: .getFolder(defaultFolderName);
0491: defaultFolderName = defaultRequestFolder
0492: .getDefaultPage();
0493: }
0494: // use default request folder
0495: if (defaultRequestFolder != null) {
0496: requestFolder = defaultRequestFolder;
0497: }
0498: }
0499: } catch (NodeException ne) {
0500: } catch (NodeNotFoundException nnfe) {
0501: } catch (SecurityException se) {
0502: requestFolder = null;
0503: accessException = se;
0504: }
0505:
0506: // only request folders with pages can be
0507: // selected by request; otherwise, fall back to
0508: // parent folders assuming that immediate parents
0509: // will have the most appropriate default page
0510: NodeSet requestFolderPages = null;
0511: if (requestFolder != null) {
0512: try {
0513: requestFolderPages = requestFolder.getPages();
0514: while (((requestFolderPages == null) || requestFolderPages
0515: .isEmpty())
0516: && (requestFolder.getParent() != null)) {
0517: requestFolder = (Folder) requestFolder
0518: .getParent();
0519: requestFolderPages = requestFolder
0520: .getPages();
0521: }
0522: } catch (NodeException ne) {
0523: requestFolderPages = null;
0524: } catch (SecurityException se) {
0525: requestFolderPages = null;
0526: accessException = se;
0527: }
0528: }
0529: if ((requestFolder != null)
0530: && (requestFolderPages != null)
0531: && !requestFolderPages.isEmpty()) {
0532: Page requestPage = null;
0533:
0534: // attempt to lookup last visited page by folder proxy
0535: // path, (proxies are hashed by their path), contains
0536: // test must be performed since identical paths may
0537: // occur in multiple site views
0538: if (useHistory) {
0539: requestPage = (Page) getFolderPageHistory()
0540: .get(requestFolder);
0541: if ((requestPage != null)
0542: && requestFolderPages
0543: .contains(requestPage)) {
0544: // log selected request page
0545: if (log.isDebugEnabled()) {
0546: log
0547: .debug("Selected folder historical page: path="
0548: + view.getManagedPage(
0549: requestPage)
0550: .getPath());
0551: }
0552: return requestPage;
0553: }
0554: }
0555:
0556: // get default page for folder proxy if more than one
0557: // page is available to choose from
0558: if (requestFolderPages.size() > 1) {
0559: String defaultPageName = requestFolder
0560: .getDefaultPage();
0561: if (defaultPageName == null) {
0562: // use fallback default if default page
0563: // not explicitly specified
0564: defaultPageName = Folder.FALLBACK_DEFAULT_PAGE;
0565: }
0566: try {
0567: // save last visited non-hidden page for folder proxy
0568: // path, (proxies are hashed by their path), and
0569: // return default page
0570: requestPage = requestFolder
0571: .getPage(defaultPageName);
0572: if (!requestPage.isHidden()) {
0573: getFolderPageHistory().put(
0574: requestFolder, requestPage);
0575: }
0576:
0577: // log selected request page
0578: if (log.isDebugEnabled()) {
0579: log
0580: .debug("Selected folder default page: path="
0581: + view.getManagedPage(
0582: requestPage)
0583: .getPath());
0584: }
0585: return requestPage;
0586: } catch (NodeException ne) {
0587: } catch (NodeNotFoundException nnfe) {
0588: } catch (SecurityException se) {
0589: accessException = se;
0590: }
0591: }
0592:
0593: // default page not available, select first page
0594: // proxy in request folder; save last visited
0595: // non-hidden page for folder proxy path, (proxies
0596: // are hashed by their path), and return default page
0597: requestPage = (Page) requestFolderPages.iterator()
0598: .next();
0599: if (!requestPage.isHidden()) {
0600: getFolderPageHistory().put(requestFolder,
0601: requestPage);
0602: }
0603:
0604: // log selected request page
0605: if (log.isDebugEnabled()) {
0606: log.debug("Selected first folder page, path="
0607: + view.getManagedPage(requestPage)
0608: .getPath());
0609: }
0610: return requestPage;
0611: }
0612: } else if (requestNode instanceof Page) {
0613: Page requestPage = (Page) requestNode;
0614:
0615: // save last visited non-hidden page for folder proxy
0616: // path, (proxies are hashed by their path), and
0617: // return matched page
0618: Folder requestFolder = (Folder) requestPage.getParent();
0619: if (!requestPage.isHidden()) {
0620: getFolderPageHistory().put(requestFolder,
0621: requestPage);
0622: }
0623:
0624: // log selected request page
0625: if (log.isDebugEnabled()) {
0626: log.debug("Selected page, path="
0627: + view.getManagedPage(requestPage)
0628: .getPath());
0629: }
0630: return requestPage;
0631: }
0632: }
0633:
0634: // no page matched or accessible
0635: if (accessException != null) {
0636: throw accessException;
0637: }
0638: throw new NodeNotFoundException("No page matched "
0639: + requestPath + " request in site view.");
0640: }
0641:
0642: /**
0643: * getRequestRootFolder - select root folder proxy for given profile locators
0644: *
0645: * @param requestProfileLocators map of profile locators for request
0646: * @return root folder proxy for request
0647: * @throws NodeNotFoundException if not found
0648: * @throws SecurityException if view access not granted
0649: */
0650: public Folder getRequestRootFolder(Map requestProfileLocators)
0651: throws NodeNotFoundException {
0652: // validate and update session profile locators if modified
0653: if (updateSessionProfileLocators(requestProfileLocators)) {
0654: // valid site view required from session profile locators
0655: SiteView view = getSiteView();
0656: if (view != null) {
0657: // return root folder proxy from session site view
0658: return view.getRootFolderProxy();
0659: }
0660: }
0661:
0662: // no root folder available
0663: throw new NodeNotFoundException(
0664: "No root folder available in site view.");
0665: }
0666:
0667: /**
0668: * updateSessionProfileLocators - detect modification of and update cached
0669: * session profile locators
0670: *
0671: * @param requestProfileLocators map of profile locators for request
0672: * @return profile locators validation flag
0673: */
0674: private boolean updateSessionProfileLocators(
0675: Map requestProfileLocators) {
0676: // request profile locators are required
0677: if ((requestProfileLocators != null)
0678: && !requestProfileLocators.isEmpty()) {
0679: // get current user principal; ignore derivative
0680: // changes in role and group principals
0681: String currentUserPrincipal = null;
0682: Subject subject = JSSubject.getSubject(AccessController
0683: .getContext());
0684: if (subject != null) {
0685: Iterator principals = subject.getPrincipals()
0686: .iterator();
0687: while (principals.hasNext()) {
0688: Principal principal = (Principal) principals.next();
0689: if (principal instanceof UserPrincipal) {
0690: if (currentUserPrincipal == null) {
0691: currentUserPrincipal = principal.getName();
0692: } else {
0693: currentUserPrincipal += "|"
0694: + principal.getName();
0695: }
0696: }
0697: }
0698: }
0699:
0700: // detect stale session, modification of user
0701: // principal, or changed profile locators for
0702: // this session context
0703: boolean userUpdate = false;
0704: boolean locatorsUpdate = false;
0705: boolean updated = false;
0706: synchronized (this ) {
0707: userUpdate = (((userPrincipal == null) && (currentUserPrincipal != null)) || ((userPrincipal != null) && !userPrincipal
0708: .equals(currentUserPrincipal)));
0709: locatorsUpdate = ((profileLocators == null) || !locatorsEquals(
0710: profileLocators, requestProfileLocators));
0711: if (stale || userUpdate || locatorsUpdate) {
0712: // reset cached session profile locators, view,
0713: // folder page history, menu definition locators,
0714: // and stale flag
0715: clearSessionProfileLocators();
0716: profileLocators = requestProfileLocators;
0717: userPrincipal = currentUserPrincipal;
0718: updated = true;
0719: }
0720: }
0721:
0722: // log session context setup and update
0723: if (updated && log.isDebugEnabled()) {
0724: StringBuffer debug = new StringBuffer();
0725: if (userUpdate) {
0726: debug.append("Updated user");
0727: if (locatorsUpdate) {
0728: debug.append("/locators");
0729: }
0730: if (stale) {
0731: debug.append("/stale");
0732: }
0733: } else if (locatorsUpdate) {
0734: debug.append("Updated locators");
0735: if (stale) {
0736: debug.append("/stale");
0737: }
0738: } else {
0739: debug.append("Updated stale");
0740: }
0741: debug.append(" context: user=" + userPrincipal
0742: + ", profileLocators=(");
0743: if (profileLocators != null) {
0744: boolean firstEntry = true;
0745: Iterator entriesIter = profileLocators.entrySet()
0746: .iterator();
0747: while (entriesIter.hasNext()) {
0748: Map.Entry entry = (Map.Entry) entriesIter
0749: .next();
0750: String locatorName = (String) entry.getKey();
0751: ProfileLocator locator = (ProfileLocator) entry
0752: .getValue();
0753: if (!firstEntry) {
0754: debug.append(",");
0755: } else {
0756: firstEntry = false;
0757: }
0758: debug.append(locatorName);
0759: debug.append("=");
0760: debug.append(locator.toString());
0761: }
0762: } else {
0763: debug.append("null");
0764: }
0765: debug.append(")");
0766: log.debug(debug);
0767: }
0768:
0769: // return valid
0770: return true;
0771: }
0772:
0773: // return invalid
0774: return false;
0775: }
0776:
0777: /**
0778: * clearSessionProfileLocators - clear cache session profile locators
0779: */
0780: private void clearSessionProfileLocators() {
0781: // clear cached session profile locators, view,
0782: // folder page history, menu definition locators,
0783: // and stale flag
0784: synchronized (this ) {
0785: profileLocators = null;
0786: userPrincipal = null;
0787: siteView = null;
0788: folderPageHistory = null;
0789: if (menuDefinitionLocatorCache != null) {
0790: menuDefinitionLocatorCache.clear();
0791: }
0792: stale = false;
0793: }
0794: }
0795:
0796: /**
0797: * getSiteView - lookup and/or create site view for
0798: * profile locators of this context
0799: *
0800: * @return site view instance
0801: */
0802: public SiteView getSiteView() {
0803: if ((siteView == null) && (pageManager != null)
0804: && (profileLocators != null)) {
0805: // create new site view
0806: siteView = new SiteView(pageManager, profileLocators);
0807:
0808: // log site view creation
0809: if (log.isDebugEnabled()) {
0810: log.debug("Created site view: search paths="
0811: + siteView.getSearchPathsString());
0812: }
0813: }
0814: return siteView;
0815: }
0816:
0817: /**
0818: * getPageManager - return PageManager component instance
0819: *
0820: * @return PageManager instance
0821: */
0822: public PageManager getPageManager() {
0823: return pageManager;
0824: }
0825:
0826: /**
0827: * isValid - return flag indicating whether this context instance
0828: * is valid or if it is stale after being persisted and
0829: * reloaded as session state
0830: *
0831: * @return valid context status
0832: */
0833: public boolean isValid() {
0834: // existant transient page manager implies valid context
0835: return (pageManager != null);
0836: }
0837:
0838: /**
0839: * getProfileLocators - get session profile locators
0840: */
0841: public Map getProfileLocators() {
0842: return profileLocators;
0843: }
0844:
0845: /**
0846: * getStandardMenuNames - get set of available standard menu names
0847: *
0848: * @return menu names set
0849: */
0850: public Set getStandardMenuNames() {
0851: // return standard menu names defined for site view
0852: SiteView view = getSiteView();
0853: if (view != null) {
0854: return view.getStandardMenuNames();
0855: }
0856: return null;
0857: }
0858:
0859: /**
0860: * getMenuDefinitionLocators - get list of node proxy menu definition
0861: * locators from site view
0862: *
0863: * @param node site view node proxy
0864: * @return definition locator list
0865: */
0866: public List getMenuDefinitionLocators(Node node) {
0867: // return menu definition locators for node in site view
0868: SiteView view = getSiteView();
0869: if (view != null) {
0870: return view.getMenuDefinitionLocators(node);
0871: }
0872: return null;
0873: }
0874:
0875: /**
0876: * getMenuDefinitionLocator - get named node proxy menu definition
0877: * locator from site view
0878: *
0879: * @param node site view node proxy
0880: * @param name menu definition name
0881: * @return menu definition locator
0882: */
0883: public SiteViewMenuDefinitionLocator getMenuDefinitionLocator(
0884: Node node, String name) {
0885: // return named menu definition locator for node in site view
0886: SiteView view = getSiteView();
0887: if (view != null) {
0888: return view.getMenuDefinitionLocator(node, name);
0889: }
0890: return null;
0891: }
0892:
0893: /**
0894: * getManagedPage - get concrete page instance from page proxy
0895: *
0896: * @param page page proxy
0897: * @return managed page
0898: */
0899: public Page getManagedPage(Page page) {
0900: // return managed page in site view
0901: SiteView view = getSiteView();
0902: if (view != null) {
0903: return view.getManagedPage(page);
0904: }
0905: return null;
0906: }
0907:
0908: /**
0909: * getMenuDefinitionLocatorCache - get menu definition locators cache
0910: * for absolute menus
0911: *
0912: * @return menu definition locators cache
0913: */
0914: public Map getMenuDefinitionLocatorCache() {
0915: return menuDefinitionLocatorCache;
0916: }
0917:
0918: /**
0919: * setMenuDefinitionLocatorCache - set menu definition locators cache
0920: * for absolute menus
0921: *
0922: * @return menu definition locators cache
0923: */
0924: public void setMenuDefinitionLocatorCache(Map cache) {
0925: menuDefinitionLocatorCache = cache;
0926: }
0927:
0928: /**
0929: * locatorsEquals - test profile locator maps for equivalence
0930: * ignoring request specifics
0931: *
0932: * @param locators0 request profile locator map
0933: * @param locators1 request profile locator map
0934: * @return boolean flag indicating equivalence
0935: */
0936: private static boolean locatorsEquals(Map locators0, Map locators1) {
0937: // trivial comparison
0938: if (locators0 == locators1) {
0939: return true;
0940: }
0941:
0942: // compare locator map sizes
0943: if (locators0.size() != locators1.size()) {
0944: return false;
0945: }
0946:
0947: // compare locator map entries
0948: Iterator entriesIter = locators0.entrySet().iterator();
0949: if (entriesIter.hasNext()) {
0950: Map.Entry entry = (Map.Entry) entriesIter.next();
0951: ProfileLocator locator0 = (ProfileLocator) entry.getValue();
0952: ProfileLocator locator1 = (ProfileLocator) locators1
0953: .get(entry.getKey());
0954: if (locator1 == null) {
0955: return false;
0956: }
0957:
0958: // compare locators using the most specific,
0959: // (i.e. first), locator properties array
0960: // returned by the locator iterator
0961: ProfileLocatorProperty[] properties0 = (ProfileLocatorProperty[]) locator0
0962: .iterator().next();
0963: ProfileLocatorProperty[] properties1 = (ProfileLocatorProperty[]) locator1
0964: .iterator().next();
0965: if ((properties0 != null) || (properties1 != null)) {
0966: if ((properties0 == null) || (properties1 == null)
0967: || (properties0.length != properties1.length)) {
0968: return false;
0969: }
0970:
0971: // compare ordered locator properties
0972: for (int i = 0, limit = properties0.length; (i < limit); i++) {
0973: // compare property names, control flags, navigation flags,
0974: // and values. note: properties values are compared only for
0975: // control or navigation properties; otherwise they are
0976: // assumed to contain variable request paths that should
0977: // be treated as equivalent
0978: if (!properties0[i].getName().equals(
0979: properties1[i].getName())
0980: || (properties0[i].isControl() && !properties1[i]
0981: .isControl())
0982: || (properties0[i].isNavigation() && !properties1[i]
0983: .isNavigation())
0984: || ((properties0[i].isControl() || properties0[i]
0985: .isNavigation()) && (((properties0[i]
0986: .getValue() == null) && (properties1[i]
0987: .getValue() != null)) || ((properties0[i]
0988: .getValue() != null) && !properties0[i]
0989: .getValue().equals(
0990: properties1[i].getValue()))))) {
0991: return false;
0992: }
0993: }
0994: }
0995: }
0996: return true;
0997: }
0998:
0999: /**
1000: * locatorRequestPath - extract request specific path from profile locator
1001: * using request path from locator
1002: *
1003: * @param locator request profile locator
1004: * @return request path
1005:
1006: private static String locatorRequestPath(ProfileLocator locator)
1007: {
1008: // use request path in locator as default
1009: return locatorRequestPath(locator, locator.getRequestPath());
1010: }
1011: */
1012:
1013: /**
1014: * locatorRequestPath - extract request specific path from profile locator
1015: *
1016: * @param locator request profile locator
1017: * @param requestPath request path
1018: * @return request path
1019:
1020: private static String locatorRequestPath(ProfileLocator locator, String requestPath)
1021: {
1022: // search locator using the most specific,
1023: // (i.e. first), locator properties array
1024: // returned by the locator iterator and return
1025: // first valued property that is not a control
1026: // or navigation type
1027: ProfileLocatorProperty [] properties = (ProfileLocatorProperty [])locator.iterator().next();
1028: for (int i = 0, limit = properties.length; (i < limit); i++)
1029: {
1030: if (!properties[i].isControl() && !properties[i].isNavigation() && (properties[i].getValue() != null))
1031: {
1032: // use specified locator path
1033: String locatorPath = properties[i].getValue();
1034:
1035: // return specified locatorPath if absolute
1036: if (locatorPath.startsWith(Folder.PATH_SEPARATOR))
1037: {
1038: return locatorPath;
1039: }
1040:
1041: // page names and relative paths are assumed relative to
1042: // request path and that any locator paths with no url
1043: // separator should have the page extension appended
1044: // get default page if page path null
1045: if ((locatorPath.indexOf(Folder.PATH_SEPARATOR) == -1) && !locatorPath.endsWith(Page.DOCUMENT_TYPE))
1046: {
1047: locatorPath += Page.DOCUMENT_TYPE;
1048: }
1049:
1050: // append locator path to request path, replacing
1051: // requested pages and preserving requested folders
1052: boolean rootFolderRequest = requestPath.equals(Folder.PATH_SEPARATOR);
1053: boolean folderRequest = (!requestPath.endsWith(Page.DOCUMENT_TYPE));
1054: int lastSeparatorIndex = requestPath.lastIndexOf(Folder.PATH_SEPARATOR_CHAR);
1055: if ((lastSeparatorIndex > 0) && (!folderRequest || requestPath.endsWith(Folder.PATH_SEPARATOR)))
1056: {
1057: // append locator to request path base path
1058: return requestPath.substring(0, lastSeparatorIndex) + Folder.PATH_SEPARATOR + locatorPath;
1059: }
1060: else if (!rootFolderRequest && folderRequest)
1061: {
1062: // append locator to request path root folder
1063: return requestPath + Folder.PATH_SEPARATOR + locatorPath;
1064: }
1065: else
1066: {
1067: // use root folder locator
1068: return Folder.PATH_SEPARATOR + locatorPath;
1069: }
1070: }
1071: }
1072: return requestPath;
1073: }
1074: */
1075:
1076: /**
1077: * newNode - invoked when the definition of a node is
1078: * created by the page manager or when the
1079: * node creation is otherwise detected
1080: *
1081: * @param node new managed node if known
1082: */
1083: public void newNode(Node node) {
1084: // equivalent to node updated event
1085: updatedNode(node);
1086: }
1087:
1088: /**
1089: * updatedNode - invoked when the definition of a node is
1090: * updated by the page manager or when the
1091: * node modification is otherwise detected
1092: *
1093: * @param node updated managed node if known
1094: */
1095: public void updatedNode(Node node) {
1096: // set stale flag to force session context state reset
1097: synchronized (this ) {
1098: stale = true;
1099: }
1100:
1101: // log updated node event
1102: if (log.isDebugEnabled()) {
1103: if (node != null) {
1104: log.debug("Page manager update event, (node="
1105: + node.getPath()
1106: + "): set session context state stale");
1107: } else {
1108: log
1109: .debug("Page manager update event: set session context state stale");
1110: }
1111: }
1112: }
1113:
1114: /**
1115: * removedNode - invoked when the definition of a node is
1116: * removed by the page manager or when the
1117: * node removal is otherwise detected
1118: *
1119: * @param node removed managed node if known
1120: */
1121: public void removedNode(Node node) {
1122: // equivalent to node updated event
1123: updatedNode(node);
1124: }
1125:
1126: /**
1127: * sessionDidActivate - notification that the session has just
1128: * been activated
1129: *
1130: * @param event session activation event
1131: */
1132: public void sessionDidActivate(HttpSessionEvent event) {
1133: // set stale flag to force session context state reset
1134: synchronized (this ) {
1135: stale = true;
1136: }
1137:
1138: // log activation event
1139: if (log.isDebugEnabled()) {
1140: log
1141: .debug("Session activation event: set session context state stale");
1142: }
1143: }
1144:
1145: /**
1146: * sessionWillPassivate - notification that the session is about
1147: * to be passivated
1148: *
1149: * @param event session activation event
1150: */
1151: public void sessionWillPassivate(HttpSessionEvent event) {
1152: // clear session context state
1153: clearSessionProfileLocators();
1154:
1155: // log activation event
1156: if (log.isDebugEnabled()) {
1157: log
1158: .debug("Session deactivation event: clear session context state");
1159: }
1160: }
1161:
1162: /**
1163: * valueBound - notifies this context that it is being bound to
1164: * a session and identifies the session
1165: *
1166: * @param event session binding event
1167: */
1168: public void valueBound(HttpSessionBindingEvent event) {
1169: // subscribe this session context to page manager events
1170: synchronized (this ) {
1171: if (!subscribed && (pageManager != null)) {
1172: pageManager.addListener(this );
1173: subscribed = true;
1174: }
1175: }
1176:
1177: // log binding event
1178: if (log.isDebugEnabled()) {
1179: log
1180: .debug("Session bound event: setup page manager listener");
1181: }
1182: }
1183:
1184: /**
1185: * valueUnbound - notifies this context that it is being unbound
1186: * from a session and identifies the session
1187: *
1188: * @param event session binding event
1189: */
1190: public void valueUnbound(HttpSessionBindingEvent event) {
1191: // unsubscribe this session context to page manager events
1192: synchronized (this ) {
1193: if (subscribed && (pageManager != null)) {
1194: pageManager.removeListener(this );
1195: subscribed = false;
1196: }
1197: }
1198:
1199: // clear session context state
1200: clearSessionProfileLocators();
1201:
1202: // log binding event
1203: if (log.isDebugEnabled()) {
1204: log
1205: .debug("Session unbound event: clear page manager listener and session context state");
1206: }
1207: }
1208:
1209: private Map getFolderPageHistory() {
1210: if (folderPageHistory == null) {
1211: folderPageHistory = new HashMap();
1212: }
1213: return folderPageHistory;
1214: }
1215:
1216: public void setPipeline(String pipeline) {
1217: this .pipeline = pipeline;
1218: }
1219:
1220: public String getPipeline() {
1221: return this.pipeline;
1222: }
1223: }
|