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.pipeline.valve.impl;
18  
19  import java.io.IOException;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  
24  import javax.portlet.PortletException;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.jetspeed.PortalReservedParameters;
31  import org.apache.jetspeed.cache.ContentCacheKey;
32  import org.apache.jetspeed.cache.JetspeedContentCache;
33  import org.apache.jetspeed.container.window.PortletWindowAccessor;
34  import org.apache.jetspeed.exception.JetspeedException;
35  import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
36  import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
37  import org.apache.jetspeed.om.page.ContentFragment;
38  import org.apache.jetspeed.om.page.ContentFragmentImpl;
39  import org.apache.jetspeed.om.page.ContentPage;
40  import org.apache.jetspeed.om.page.Fragment;
41  import org.apache.jetspeed.om.page.Page;
42  import org.apache.jetspeed.pipeline.PipelineException;
43  import org.apache.jetspeed.pipeline.valve.AbstractValve;
44  import org.apache.jetspeed.pipeline.valve.ActionValve;
45  import org.apache.jetspeed.pipeline.valve.ValveContext;
46  import org.apache.jetspeed.request.RequestContext;
47  import org.apache.pluto.PortletContainer;
48  import org.apache.pluto.PortletContainerException;
49  import org.apache.pluto.om.entity.PortletEntity;
50  import org.apache.pluto.om.window.PortletWindow;
51  
52  /***
53   * <p>
54   * ActionValveImpl
55   * </p>
56   * 
57   * Default implementation of the ActionValve interface.  Expects to be
58   * called after the ContainerValve has set up the appropriate action window
59   * within the request context.  This should come before ANY rendering takes
60   * place.
61   * 
62   * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
63   * @version $Id: ActionValveImpl.java 550655 2007-06-26 01:41:35Z taylor $
64   *
65   */
66  public class ActionValveImpl extends AbstractValve implements ActionValve
67  {
68  
69      private static final Log log = LogFactory.getLog(ActionValveImpl.class);
70      private PortletContainer container;
71      private PortletWindowAccessor windowAccessor;
72      private boolean patchResponseCommitted = false;
73      private JetspeedContentCache portletContentCache;
74  
75      public ActionValveImpl(PortletContainer container, PortletWindowAccessor windowAccessor, JetspeedContentCache portletContentCache)
76      {
77          this.container = container;
78          this.windowAccessor = windowAccessor;
79          this.portletContentCache = portletContentCache;
80      }
81      
82      public ActionValveImpl(PortletContainer container, PortletWindowAccessor windowAccessor, JetspeedContentCache portletContentCache, boolean patchResponseCommitted)
83      {
84          this.container = container;
85          this.windowAccessor = windowAccessor;
86          this.portletContentCache = portletContentCache;        
87          this.patchResponseCommitted = patchResponseCommitted;
88      }
89  
90      /***
91       * @see org.apache.jetspeed.pipeline.valve.Valve#invoke(org.apache.jetspeed.request.RequestContext, org.apache.jetspeed.pipeline.valve.ValveContext)
92       */
93      public void invoke(RequestContext request, ValveContext context) throws PipelineException
94      {     
95          boolean responseCommitted = false;
96          try
97          {            
98              PortletWindow actionWindow = request.getActionWindow();
99              if (actionWindow != null)
100             {
101                 // If portlet entity is null, try to refresh the actionWindow.
102                 // Under some clustered environments, a cached portlet window could have null entity.
103                 if (null == actionWindow.getPortletEntity())
104                 {
105                     try 
106                     {
107                         Fragment fragment = request.getPage().getFragmentById(actionWindow.getId().toString());
108                         ContentFragment contentFragment = new ContentFragmentImpl(fragment, new HashMap());
109                         actionWindow = this.windowAccessor.getPortletWindow(contentFragment);
110                     } 
111                     catch (Exception e)
112                     {
113                         log.error("Failed to refresh action window.", e);
114                     }
115                 }
116 
117                 initWindow(actionWindow, request);
118                 HttpServletResponse response = request.getResponseForWindow(actionWindow);
119                 HttpServletRequest requestForWindow = request.getRequestForWindow(actionWindow);
120                 requestForWindow.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE, request);
121                 
122                 //PortletMessagingImpl msg = new PortletMessagingImpl(windowAccessor);
123                 
124                 requestForWindow.setAttribute("JETSPEED_ACTION", request);
125                 container.processPortletAction(
126                     actionWindow,
127                     requestForWindow,
128                     response);
129                 // The container redirects the client after PortletAction processing
130                 // so there is no need to continue the pipeline
131                 
132                 //msg.processActionMessage("todo", request);
133                 
134                 // clear the cache for all portlets on the current page
135                 clearPortletCacheForPage(request, actionWindow);
136                 
137                 if (patchResponseCommitted)
138                 {
139                     responseCommitted = true;
140                 }
141                 else
142                 {
143                     responseCommitted = response.isCommitted();
144                 }
145                 request.setAttribute(PortalReservedParameters.PIPELINE, null); // clear the pipeline
146             }
147         }
148         catch (PortletContainerException e)
149         {
150             log.fatal("Unable to retrieve portlet container!", e);
151             throw new PipelineException("Unable to retrieve portlet container!", e);
152         }
153         catch (PortletException e)
154         {
155             log.warn("Unexpected PortletException in ActionValveImpl", e);
156             //  throw new PipelineException("Unexpected PortletException in ActionValveImpl", e);
157 
158         }
159         catch (IOException e)
160         {
161             log.error("Unexpected IOException in ActionValveImpl", e);
162             // throw new PipelineException("Unexpected IOException in ActionValveImpl", e);
163         }
164         catch (IllegalStateException e)
165         {
166             log.error("Illegal State Exception. Response was written to in Action Phase", e);
167             responseCommitted = true;
168         }
169         catch (Throwable t)
170         {
171             log.error("Unknown exception processing Action", t);
172         }
173         finally
174         {
175             // Check if an action was processed and if its response has been committed
176             // (Pluto will redirect the client after PorletAction processing)
177             if ( responseCommitted )
178             {
179                 log.info("Action processed and response committed (pipeline processing stopped)");
180             }
181             else
182             {
183                 // Pass control to the next Valve in the Pipeline
184                 context.invokeNext(request);
185             }
186         }
187 
188     }
189 
190     protected void clearPortletCacheForPage(RequestContext request, PortletWindow actionWindow)
191     throws JetspeedException
192     {
193         ContentPage page = request.getPage();
194         if (null == page)
195         {
196             throw new JetspeedException("Failed to find PSML Pin ContentPageAggregator.build");
197         }
198         ContentFragment root = page.getRootContentFragment();
199         if (root == null)
200         {
201             throw new JetspeedException("No root ContentFragment found in ContentPage");
202         }
203         if (!isNonStandardAction(actionWindow))
204         {
205             notifyFragments(root, request, page);
206         }
207         else
208         {
209             ContentFragment fragment = page.getContentFragmentById(actionWindow.getId().toString());
210             clearTargetCache(fragment, request);
211         }
212     }
213     
214     /***
215      * Actions can be marked as non-standard if they don't participate in
216      * JSR-168 standard action behavior. By default, actions are supposed
217      * to clear the cache of all other portlets on the page.
218      * By setting this parameter, we can ignore the standard behavior
219      * and not clear the cache on actions. This is useful for portlets
220      * which never participate with other portlets.
221      * 
222      */    
223     protected boolean isNonStandardAction(PortletWindow actionWindow)
224     {
225         PortletEntity entity = actionWindow.getPortletEntity();
226         if (entity != null)
227         {
228             PortletDefinitionComposite portletDefinition = (PortletDefinitionComposite)entity.getPortletDefinition();
229             if (portletDefinition != null)
230             {
231                 Collection actionList = null;
232         
233                 if (portletDefinition != null)
234                 {
235                     actionList = portletDefinition.getMetadata().getFields(PortalReservedParameters.PORTLET_EXTENDED_DESCRIPTOR_NON_STANDARD_ACTION);
236                 }
237                 if (actionList != null) 
238                 {
239                     if (!actionList.isEmpty())
240                         return true;
241                 }
242             }
243         }
244         return false;
245     }
246    
247     protected void notifyFragments(ContentFragment f, RequestContext context, ContentPage page)
248     {
249         if (f.getContentFragments() != null && f.getContentFragments().size() > 0)
250         {
251             Iterator children = f.getContentFragments().iterator();
252             while (children.hasNext())
253             {
254                 ContentFragment child = (ContentFragment) children.next();
255                 if (!"hidden".equals(f.getState()))
256                 {
257                     notifyFragments(child, context, page);
258                 }
259             } 
260         }    
261         ContentCacheKey cacheKey = portletContentCache.createCacheKey(context, f.getId());
262         if (portletContentCache.isKeyInCache(cacheKey))
263         {
264             portletContentCache.remove(cacheKey);
265             portletContentCache.invalidate(context);
266         }
267     }
268 
269     protected void clearTargetCache(ContentFragment f, RequestContext context)
270     {
271         ContentCacheKey cacheKey = portletContentCache.createCacheKey(context, f.getId());        
272         if (portletContentCache.isKeyInCache(cacheKey))
273         {
274             portletContentCache.remove(cacheKey);
275             portletContentCache.invalidate(context);            
276         }
277     }
278     
279     /***
280      * @see java.lang.Object#toString()
281      */
282     public String toString()
283     {
284         // TODO Auto-generated method stub
285         return "ActionValveImpl";
286     }
287     
288     /***
289      * Makes sure that this PortletWindow's PortletEntity is set to have the
290      * current requests fragment.
291      * @param window
292      * @param request
293      */
294     protected void initWindow(PortletWindow window, RequestContext request)
295     {
296         Page page = request.getPage();
297         Fragment fragment = page.getFragmentById(window.getId().toString());
298         ((MutablePortletEntity)window.getPortletEntity()).setFragment(fragment);
299     }
300 
301 }