001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.profiler.impl;
018:
019: import java.io.IOException;
020: import java.security.Principal;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import javax.security.auth.Subject;
025: import javax.servlet.http.HttpServletResponse;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029: import org.apache.jetspeed.PortalReservedParameters;
030: import org.apache.jetspeed.decoration.PageActionAccess;
031: import org.apache.jetspeed.om.page.ContentPageImpl;
032: import org.apache.jetspeed.om.page.Page;
033: import org.apache.jetspeed.page.document.NodeNotFoundException;
034: import org.apache.jetspeed.pipeline.PipelineException;
035: import org.apache.jetspeed.pipeline.valve.AbstractValve;
036: import org.apache.jetspeed.pipeline.valve.PageProfilerValve;
037: import org.apache.jetspeed.pipeline.valve.ValveContext;
038: import org.apache.jetspeed.portalsite.PortalSite;
039: import org.apache.jetspeed.portalsite.PortalSiteRequestContext;
040: import org.apache.jetspeed.portalsite.PortalSiteSessionContext;
041: import org.apache.jetspeed.profiler.ProfileLocator;
042: import org.apache.jetspeed.profiler.Profiler;
043: import org.apache.jetspeed.profiler.ProfilerException;
044: import org.apache.jetspeed.request.RequestContext;
045: import org.apache.jetspeed.security.SecurityHelper;
046: import org.apache.jetspeed.security.UserPrincipal;
047:
048: /**
049: * ProfilerValveImpl
050: *
051: * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
052: * @version $Id: ProfilerValveImpl.java 569464 2007-08-24 17:43:28Z taylor $
053: */
054: public class ProfilerValveImpl extends AbstractValve implements
055: PageProfilerValve {
056: protected Log log = LogFactory.getLog(ProfilerValveImpl.class);
057:
058: /**
059: * PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY - session portal site context attribute key
060: */
061: public static final String PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY = "org.apache.jetspeed.portalsite.PortalSiteSessionContext";
062:
063: /**
064: * PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY - request portal site context attribute key
065: */
066: public static final String PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY = "org.apache.jetspeed.portalsite.PortalSiteRequestContext";
067:
068: /**
069: * PROFILED_PAGE_CONTEXT_ATTR_KEY - legacy request portal site context attribute key
070: */
071: public static final String PROFILED_PAGE_CONTEXT_ATTR_KEY = "org.apache.jetspeed.profiledPageContext";
072:
073: /**
074: * session key for storing map of PageActionAccess instances
075: */
076: private static final String PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY = "org.apache.jetspeed.profiler.pageActionAccessMap";
077:
078: /**
079: * profiler - profiler component
080: */
081: private Profiler profiler;
082:
083: /**
084: * portalSite - portal site component
085: */
086: private PortalSite portalSite;
087:
088: /**
089: * requestFallback - flag indicating whether request should fallback to root folder
090: * if locators do not select a page or access is forbidden
091: */
092: private boolean requestFallback;
093:
094: /**
095: * useHistory - flag indicating whether to use visited page
096: * history to select default page per site folder
097: */
098: private boolean useHistory;
099:
100: /**
101: * ProfilerValveImpl - constructor
102: *
103: * @param profiler profiler component reference
104: * @param portalSite portal site component reference
105: * @param requestFallback flag to enable root folder fallback
106: * @param useHistory flag to enable selection of last visited folder page
107: */
108: public ProfilerValveImpl(Profiler profiler, PortalSite portalSite,
109: boolean requestFallback, boolean useHistory) {
110: this .profiler = profiler;
111: this .portalSite = portalSite;
112: this .requestFallback = requestFallback;
113: this .useHistory = useHistory;
114: }
115:
116: /**
117: * ProfilerValveImpl - constructor
118: *
119: * @param profiler profiler component reference
120: * @param portalSite portal site component reference
121: * @param requestFallback flag to enable root folder fallback
122: */
123: public ProfilerValveImpl(Profiler profiler, PortalSite portalSite,
124: boolean requestFallback) {
125: this (profiler, portalSite, requestFallback, true);
126: }
127:
128: /**
129: * ProfilerValveImpl - constructor
130: *
131: * @param profiler profiler component reference
132: * @param portalSite portal site component reference
133: */
134: public ProfilerValveImpl(Profiler profiler, PortalSite portalSite) {
135: this (profiler, portalSite, true, true);
136: }
137:
138: /*
139: * (non-Javadoc)
140: *
141: * @see org.apache.jetspeed.pipeline.valve.Valve#invoke(org.apache.jetspeed.request.RequestContext,
142: * org.apache.jetspeed.pipeline.valve.ValveContext)
143: */
144: public void invoke(RequestContext request, ValveContext context)
145: throws PipelineException {
146: try {
147: // get profiler locators for request subject/principal using the profiler
148: Subject subject = request.getSubject();
149: if (subject == null) {
150: throw new ProfilerException(
151: "Missing subject for request: "
152: + request.getPath());
153: }
154: Principal principal = SecurityHelper.getBestPrincipal(
155: subject, UserPrincipal.class);
156: if (principal == null) {
157: throw new ProfilerException(
158: "Missing principal for request: "
159: + request.getPath());
160: }
161:
162: // get request specific profile locators if required
163: Map locators = null;
164: String locatorName = (String) request
165: .getAttribute(PROFILE_LOCATOR_REQUEST_ATTR_KEY);
166: if (locatorName != null) {
167: ProfileLocator locator = profiler.getProfile(request,
168: locatorName);
169: if (locator != null) {
170: locators = new HashMap();
171: locators.put(ProfileLocator.PAGE_LOCATOR, locator);
172: }
173: }
174:
175: // get specified or default locators for the current user,
176: // falling back to global defaults and, if necessary, explicity
177: // fallback to 'page' profile locators
178: if (locators == null) {
179: locators = profiler.getProfileLocators(request,
180: principal);
181: }
182: if (locators.size() == 0) {
183: locators = profiler.getDefaultProfileLocators(request);
184: }
185: if (locators.size() == 0) {
186: locators.put(ProfileLocator.PAGE_LOCATOR, profiler
187: .getProfile(request,
188: ProfileLocator.PAGE_LOCATOR));
189: }
190:
191: // get profiled page using the profiler, page manager,
192: // and portal site components
193: if (locators != null) {
194: // get or create portalsite session context; the session
195: // context maintains the user view of the site and is
196: // searched against to locate the requested page and
197: // used to build site menus from its extent; this is
198: // cached in the session because locators seldom change
199: // during the session so the session view of the site can
200: // be cached unless locators do change; if the context
201: // is invalid, (perhaps because the session was persisted
202: // and is now being reloaded in a new server), it must be
203: // replaced with a newly created session context
204: PortalSiteSessionContext sessionContext = (PortalSiteSessionContext) request
205: .getSessionAttribute(PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY);
206: String pipeline = request.getPipeline().getName();
207: if ((sessionContext == null)
208: || !sessionContext.isValid()
209: || hasPipelineChanged(pipeline, sessionContext
210: .getPipeline())) {
211: sessionContext = portalSite.newSessionContext();
212: sessionContext.setPipeline(pipeline);
213: request.setSessionAttribute(
214: PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY,
215: sessionContext);
216: }
217:
218: // construct and save a new portalsite request context
219: // using session context, locators map, fallback, and
220: // folder page histories; the request context uses the
221: // locators to initialize or resets the session context if
222: // locators have changed for this request; the request
223: // context also acts as a short term request cache for the
224: // selected page and built menus; however, creating the
225: // request context here does not select the page or build
226: // menus: that is done when the request context is
227: // accessed subsequently
228: PortalSiteRequestContext requestContext = sessionContext
229: .newRequestContext(locators, requestFallback,
230: useHistory);
231: request.setAttribute(
232: PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY,
233: requestContext);
234:
235: // additionally save request context under legacy key
236: // to support existing decorator access
237: request.setAttribute(PROFILED_PAGE_CONTEXT_ATTR_KEY,
238: requestContext);
239:
240: // get profiled page from portalsite request context
241: // and save profile locators map; accessing the request
242: // context here and in subsequent valves/decorators
243: // latently selects the page and builds menus from the
244: // user site view using the request context locators;
245: // the managed page accesed here is the raw selected page
246: // as returned by the PageManager component; accessing
247: // the managed page here selects the current page for the
248: // request
249: request.setPage(new ContentPageImpl(requestContext
250: .getManagedPage()));
251: request
252: .setProfileLocators(requestContext
253: .getLocators());
254:
255: request
256: .setAttribute(
257: PortalReservedParameters.PAGE_EDIT_ACCESS_ATTRIBUTE,
258: getPageActionAccess(request));
259: }
260:
261: // continue
262: context.invokeNext(request);
263: } catch (SecurityException se) {
264: // fallback to portal root folder/default page if
265: // no user is available and request path is not
266: // already attempting to access the root folder;
267: // this is rarely the case since the anonymous
268: // user is normally defined unless the default
269: // security system has been replaced/overridden
270: if (request.getRequest().getUserPrincipal() == null
271: && request.getPath() != null
272: && !request.getPath().equals("/")) {
273: try {
274: request.getResponse().sendRedirect(
275: request.getRequest().getContextPath());
276: } catch (IOException ioe) {
277: }
278: return;
279: }
280:
281: // return standard HTTP 403 - FORBIDDEN status
282: log.error(se.getMessage(), se);
283: try {
284: request.getResponse().sendError(
285: HttpServletResponse.SC_FORBIDDEN,
286: se.getMessage());
287: } catch (IOException ioe) {
288: log.error(
289: "Failed to invoke HttpServletReponse.sendError: "
290: + ioe.getMessage(), ioe);
291: }
292: } catch (NodeNotFoundException nnfe) {
293: // return standard HTTP 404 - NOT FOUND status
294: log.error(nnfe.getMessage(), nnfe);
295: try {
296: request.getResponse().sendError(
297: HttpServletResponse.SC_NOT_FOUND,
298: nnfe.getMessage());
299: } catch (IOException ioe) {
300: log.error(
301: "Failed to invoke HttpServletReponse.sendError: "
302: + ioe.getMessage(), ioe);
303: }
304: } catch (Exception e) {
305: log.error("Exception in request pipeline: "
306: + e.getMessage(), e);
307: throw new PipelineException(e.toString(), e);
308: }
309: }
310:
311: protected boolean hasPipelineChanged(String requestPipeline,
312: String sessionPipeline) {
313: if (!requestPipeline.equals(sessionPipeline)) {
314: if (requestPipeline
315: .equals(PortalReservedParameters.JETSPEED_CONFIG_PIPELINE_NAME)
316: || sessionPipeline
317: .equals(PortalReservedParameters.JETSPEED_CONFIG_PIPELINE_NAME))
318: return true;
319: }
320: return false;
321: }
322:
323: /**
324: * Returns the <code>PageActionAccess</code> for the current user request.
325: * @see PageActionAccess
326: * @param requestContext RequestContext of the current portal request.
327: * @return PageActionAccess for the current user request.
328: */
329: protected PageActionAccess getPageActionAccess(
330: RequestContext requestContext) {
331: Page page = requestContext.getPage();
332: String key = page.getId();
333: boolean loggedOn = requestContext.getRequest()
334: .getUserPrincipal() != null;
335: boolean anonymous = !loggedOn;
336: PageActionAccess pageActionAccess = null;
337:
338: Map sessionActions = null;
339: synchronized (this ) {
340: sessionActions = (Map) requestContext
341: .getSessionAttribute(PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY);
342: if (sessionActions == null) {
343: sessionActions = new HashMap();
344: requestContext.setSessionAttribute(
345: PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY,
346: sessionActions);
347: } else {
348: pageActionAccess = (PageActionAccess) sessionActions
349: .get(key);
350: }
351: }
352: synchronized (sessionActions) {
353: if (pageActionAccess == null) {
354: pageActionAccess = new PageActionAccess(anonymous, page);
355: sessionActions.put(key, pageActionAccess);
356: } else {
357: pageActionAccess.checkReset(anonymous, page);
358: }
359: }
360:
361: return pageActionAccess;
362: }
363:
364: public String toString() {
365: return "ProfilerValve";
366: }
367:
368: }
|