View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.profiler.impl;
18  
19  import java.io.IOException;
20  import java.security.Principal;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import javax.security.auth.Subject;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.jetspeed.PortalReservedParameters;
30  import org.apache.jetspeed.decoration.PageActionAccess;
31  import org.apache.jetspeed.om.page.ContentPageImpl;
32  import org.apache.jetspeed.om.page.Page;
33  import org.apache.jetspeed.page.document.NodeNotFoundException;
34  import org.apache.jetspeed.pipeline.PipelineException;
35  import org.apache.jetspeed.pipeline.valve.AbstractValve;
36  import org.apache.jetspeed.pipeline.valve.PageProfilerValve;
37  import org.apache.jetspeed.pipeline.valve.ValveContext;
38  import org.apache.jetspeed.portalsite.PortalSite;
39  import org.apache.jetspeed.portalsite.PortalSiteRequestContext;
40  import org.apache.jetspeed.portalsite.PortalSiteSessionContext;
41  import org.apache.jetspeed.profiler.ProfileLocator;
42  import org.apache.jetspeed.profiler.Profiler;
43  import org.apache.jetspeed.profiler.ProfilerException;
44  import org.apache.jetspeed.request.RequestContext;
45  import org.apache.jetspeed.security.SecurityHelper;
46  import org.apache.jetspeed.security.UserPrincipal;
47  
48  /***
49   * ProfilerValveImpl
50   * 
51   * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
52   * @version $Id: ProfilerValveImpl.java 569464 2007-08-24 17:43:28Z taylor $
53   */
54  public class ProfilerValveImpl extends AbstractValve implements PageProfilerValve
55  {
56      protected Log log = LogFactory.getLog(ProfilerValveImpl.class);   
57  
58      /***
59       * PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY - session portal site context attribute key
60       */
61      public static final String PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY = "org.apache.jetspeed.portalsite.PortalSiteSessionContext";
62  
63      /***
64       * PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY - request portal site context attribute key
65       */
66      public static final String PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY = "org.apache.jetspeed.portalsite.PortalSiteRequestContext";
67  
68      /***
69       * PROFILED_PAGE_CONTEXT_ATTR_KEY - legacy request portal site context attribute key
70       */
71      public static final String PROFILED_PAGE_CONTEXT_ATTR_KEY = "org.apache.jetspeed.profiledPageContext";
72  
73      /***
74       * session key for storing map of PageActionAccess instances
75       */
76      private static final String PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY = "org.apache.jetspeed.profiler.pageActionAccessMap";
77      
78      /***
79       * profiler - profiler component
80       */
81      private Profiler profiler;
82  
83      /***
84       * portalSite - portal site component
85       */
86      private PortalSite portalSite;
87  
88      /***
89       * requestFallback - flag indicating whether request should fallback to root folder
90       *                   if locators do not select a page or access is forbidden
91       */
92      private boolean requestFallback;
93  
94      /***
95       * useHistory - flag indicating whether to use visited page
96       *              history to select default page per site folder
97       */
98      private boolean useHistory;
99  
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     {
111         this.profiler = profiler;
112         this.portalSite = portalSite;
113         this.requestFallback = requestFallback;
114         this.useHistory = useHistory;
115     }
116 
117     /***
118      * ProfilerValveImpl - constructor
119      *
120      * @param profiler profiler component reference
121      * @param portalSite portal site component reference
122      * @param requestFallback flag to enable root folder fallback
123      */
124     public ProfilerValveImpl(Profiler profiler, PortalSite portalSite, 
125                              boolean requestFallback)
126     {
127         this(profiler, portalSite, requestFallback, true);
128     }
129 
130     /***
131      * ProfilerValveImpl - constructor
132      *
133      * @param profiler profiler component reference
134      * @param portalSite portal site component reference
135      */
136     public ProfilerValveImpl(Profiler profiler, PortalSite portalSite)
137     {
138         this(profiler, portalSite, true, true);
139     }
140     
141     /*
142      * (non-Javadoc)
143      * 
144      * @see org.apache.jetspeed.pipeline.valve.Valve#invoke(org.apache.jetspeed.request.RequestContext,
145      *      org.apache.jetspeed.pipeline.valve.ValveContext)
146      */
147     public void invoke( RequestContext request, ValveContext context ) throws PipelineException
148     {
149         try
150         {
151             // get profiler locators for request subject/principal using the profiler
152             Subject subject = request.getSubject();
153             if (subject == null)
154             {
155                 throw new ProfilerException("Missing subject for request: " + request.getPath());
156             }            
157             Principal principal = SecurityHelper.getBestPrincipal(subject, UserPrincipal.class);
158             if (principal == null)
159             {
160                 throw new ProfilerException("Missing principal for request: " + request.getPath());
161             }
162             
163             // get request specific profile locators if required
164             Map locators = null;
165             String locatorName = (String)request.getAttribute(PROFILE_LOCATOR_REQUEST_ATTR_KEY);
166             if ( locatorName != null )
167             {
168                 ProfileLocator locator = profiler.getProfile(request,locatorName);
169                 if ( locator != null )
170                 {
171                     locators = new HashMap();
172                     locators.put(ProfileLocator.PAGE_LOCATOR, locator);
173                 }
174             }
175 
176             // get specified or default locators for the current user,
177             // falling back to global defaults and, if necessary, explicity
178             // fallback to 'page' profile locators
179             if ( locators == null )
180             {
181                 locators = profiler.getProfileLocators(request, principal);
182             }
183             if (locators.size() == 0)
184             {
185                 locators = profiler.getDefaultProfileLocators(request);                
186             }
187             if (locators.size() == 0)
188             {
189                 locators.put(ProfileLocator.PAGE_LOCATOR, profiler.getProfile(request, ProfileLocator.PAGE_LOCATOR));
190             }
191             
192             // get profiled page using the profiler, page manager,
193             // and portal site components
194             if (locators != null)
195             {
196                 // get or create portalsite session context; the session
197                 // context maintains the user view of the site and is
198                 // searched against to locate the requested page and
199                 // used to build site menus from its extent; this is
200                 // cached in the session because locators seldom change
201                 // during the session so the session view of the site can
202                 // be cached unless locators do change; if the context
203                 // is invalid, (perhaps because the session was persisted
204                 // and is now being reloaded in a new server), it must be
205                 // replaced with a newly created session context
206                 PortalSiteSessionContext sessionContext = (PortalSiteSessionContext)request.getSessionAttribute(PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY);
207                 String pipeline = request.getPipeline().getName();
208                 if ((sessionContext == null) || !sessionContext.isValid() || hasPipelineChanged(pipeline, sessionContext.getPipeline()))                     
209                 {                    
210                     sessionContext = portalSite.newSessionContext();
211                     sessionContext.setPipeline(pipeline);
212                     request.setSessionAttribute(PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY, sessionContext);
213                 }
214 
215                 // construct and save a new portalsite request context
216                 // using session context, locators map, fallback, and
217                 // folder page histories; the request context uses the
218                 // locators to initialize or resets the session context if
219                 // locators have changed for this request; the request
220                 // context also acts as a short term request cache for the
221                 // selected page and built menus; however, creating the
222                 // request context here does not select the page or build
223                 // menus: that is done when the request context is
224                 // accessed subsequently
225                 PortalSiteRequestContext requestContext = sessionContext.newRequestContext(locators, requestFallback, useHistory);
226                 request.setAttribute(PORTAL_SITE_REQUEST_CONTEXT_ATTR_KEY, requestContext);
227 
228                 // additionally save request context under legacy key
229                 // to support existing decorator access
230                 request.setAttribute(PROFILED_PAGE_CONTEXT_ATTR_KEY, requestContext);
231 
232                 // get profiled page from portalsite request context
233                 // and save profile locators map; accessing the request
234                 // context here and in subsequent valves/decorators
235                 // latently selects the page and builds menus from the
236                 // user site view using the request context locators;
237                 // the managed page accesed here is the raw selected page
238                 // as returned by the PageManager component; accessing
239                 // the managed page here selects the current page for the
240                 // request
241                 request.setPage(new ContentPageImpl(requestContext.getManagedPage()));
242                 request.setProfileLocators(requestContext.getLocators());
243                 
244                 request.setAttribute(PortalReservedParameters.PAGE_EDIT_ACCESS_ATTRIBUTE,getPageActionAccess(request));                
245             }
246 
247             // continue
248             context.invokeNext(request);
249         }
250         catch (SecurityException se)
251         {
252             // fallback to portal root folder/default page if
253             // no user is available and request path is not
254             // already attempting to access the root folder;
255             // this is rarely the case since the anonymous
256             // user is normally defined unless the default
257             // security system has been replaced/overridden
258             if (request.getRequest().getUserPrincipal() == null &&
259                 request.getPath() != null &&
260                 !request.getPath().equals("/"))
261             {
262                 try 
263                 {
264                     request.getResponse().sendRedirect(request.getRequest().getContextPath());
265                 }
266                 catch (IOException ioe){}
267                 return;
268             }
269 
270             // return standard HTTP 403 - FORBIDDEN status
271             log.error(se.getMessage(), se);
272             try
273             {                
274                 request.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN, se.getMessage());
275             }
276             catch (IOException ioe)
277             {
278                 log.error("Failed to invoke HttpServletReponse.sendError: " + ioe.getMessage(), ioe);
279             }
280         }
281         catch (NodeNotFoundException nnfe)
282         {
283             // return standard HTTP 404 - NOT FOUND status
284             log.error(nnfe.getMessage(), nnfe);
285             try
286             {
287                 request.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND, nnfe.getMessage());
288             }
289             catch (IOException ioe)
290             {
291                 log.error("Failed to invoke HttpServletReponse.sendError: " + ioe.getMessage(), ioe);
292             }
293         }
294         catch (Exception e)
295         {
296             log.error("Exception in request pipeline: " + e.getMessage(), e);
297             throw new PipelineException(e.toString(), e);
298         }
299     }
300     
301     protected boolean hasPipelineChanged(String requestPipeline, String sessionPipeline)
302     {
303         if (!requestPipeline.equals(sessionPipeline))
304         {
305             if (requestPipeline.equals(PortalReservedParameters.JETSPEED_CONFIG_PIPELINE_NAME)
306              || sessionPipeline.equals(PortalReservedParameters.JETSPEED_CONFIG_PIPELINE_NAME))
307                 return true;
308         }
309         return false;
310     }
311 
312     /***
313      * Returns the <code>PageActionAccess</code> for the current user request.
314      * @see PageActionAccess
315      * @param requestContext RequestContext of the current portal request.
316      * @return PageActionAccess for the current user request.
317      */
318     protected PageActionAccess getPageActionAccess(RequestContext requestContext)
319     { 
320         Page page = requestContext.getPage();
321         String key = page.getId();
322         boolean loggedOn = requestContext.getRequest().getUserPrincipal() != null;
323         boolean anonymous = !loggedOn;
324         PageActionAccess pageActionAccess = null;
325 
326         Map sessionActions = null;
327         synchronized (this)
328         {
329             sessionActions = (Map) requestContext.getSessionAttribute(PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY);
330             if (sessionActions == null)
331             {
332                 sessionActions = new HashMap();
333                 requestContext.setSessionAttribute(PAGE_ACTION_ACCESS_MAP_SESSION_ATTR_KEY, sessionActions);
334             }
335             else
336             {
337                 pageActionAccess = (PageActionAccess) sessionActions.get(key);
338             }
339         }
340         synchronized (sessionActions)
341         {
342             if (pageActionAccess == null)
343             {
344                 pageActionAccess = new PageActionAccess(anonymous, page);
345                 sessionActions.put(key, pageActionAccess);
346             }
347             else
348             {
349                 pageActionAccess.checkReset(anonymous, page);
350             }        
351         }
352         
353         return pageActionAccess;
354     }
355 
356     public String toString()
357     {
358         return "ProfilerValve";
359     }
360 
361 }