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.pipeline.valve.impl;
018:
019: import java.io.IOException;
020: import java.util.Collection;
021: import java.util.HashMap;
022: import java.util.Iterator;
023:
024: import javax.portlet.PortletException;
025: import javax.servlet.http.HttpServletRequest;
026: import javax.servlet.http.HttpServletResponse;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.apache.jetspeed.PortalReservedParameters;
031: import org.apache.jetspeed.cache.ContentCacheKey;
032: import org.apache.jetspeed.cache.JetspeedContentCache;
033: import org.apache.jetspeed.container.window.PortletWindowAccessor;
034: import org.apache.jetspeed.container.state.MutableNavigationalState;
035: import org.apache.jetspeed.exception.JetspeedException;
036: import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
037: import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
038: import org.apache.jetspeed.om.page.ContentFragment;
039: import org.apache.jetspeed.om.page.ContentFragmentImpl;
040: import org.apache.jetspeed.om.page.ContentPage;
041: import org.apache.jetspeed.om.page.Fragment;
042: import org.apache.jetspeed.om.page.Page;
043: import org.apache.jetspeed.pipeline.PipelineException;
044: import org.apache.jetspeed.pipeline.valve.AbstractValve;
045: import org.apache.jetspeed.pipeline.valve.ActionValve;
046: import org.apache.jetspeed.pipeline.valve.ValveContext;
047: import org.apache.jetspeed.request.RequestContext;
048: import org.apache.pluto.PortletContainer;
049: import org.apache.pluto.PortletContainerException;
050: import org.apache.pluto.om.entity.PortletEntity;
051: import org.apache.pluto.om.window.PortletWindow;
052:
053: /**
054: * <p>
055: * ActionValveImpl
056: * </p>
057: *
058: * Default implementation of the ActionValve interface. Expects to be
059: * called after the ContainerValve has set up the appropriate action window
060: * within the request context. This should come before ANY rendering takes
061: * place.
062: *
063: * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
064: * @version $Id: ActionValveImpl.java 589933 2007-10-30 01:51:50Z woonsan $
065: *
066: */
067: public class ActionValveImpl extends AbstractValve implements
068: ActionValve {
069:
070: private static final Log log = LogFactory
071: .getLog(ActionValveImpl.class);
072: private PortletContainer container;
073: private PortletWindowAccessor windowAccessor;
074: private boolean patchResponseCommitted = false;
075: private JetspeedContentCache portletContentCache;
076:
077: public ActionValveImpl(PortletContainer container,
078: PortletWindowAccessor windowAccessor,
079: JetspeedContentCache portletContentCache) {
080: this .container = container;
081: this .windowAccessor = windowAccessor;
082: this .portletContentCache = portletContentCache;
083: }
084:
085: public ActionValveImpl(PortletContainer container,
086: PortletWindowAccessor windowAccessor,
087: JetspeedContentCache portletContentCache,
088: boolean patchResponseCommitted) {
089: this .container = container;
090: this .windowAccessor = windowAccessor;
091: this .portletContentCache = portletContentCache;
092: this .patchResponseCommitted = patchResponseCommitted;
093: }
094:
095: /**
096: * @see org.apache.jetspeed.pipeline.valve.Valve#invoke(org.apache.jetspeed.request.RequestContext, org.apache.jetspeed.pipeline.valve.ValveContext)
097: */
098: public void invoke(RequestContext request, ValveContext context)
099: throws PipelineException {
100: boolean responseCommitted = false;
101: try {
102: PortletWindow actionWindow = request.getActionWindow();
103: if (actionWindow != null) {
104: // If portlet entity is null, try to refresh the actionWindow.
105: // Under some clustered environments, a cached portlet window could have null entity.
106: if (null == actionWindow.getPortletEntity()) {
107: try {
108: Fragment fragment = request
109: .getPage()
110: .getFragmentById(
111: actionWindow.getId().toString());
112:
113: if (fragment != null) {
114: ContentFragment contentFragment = new ContentFragmentImpl(
115: fragment, new HashMap());
116: actionWindow = this .windowAccessor
117: .getPortletWindow(contentFragment);
118: }
119: } catch (Exception e) {
120: log
121: .error(
122: "Failed to refresh action window.",
123: e);
124: }
125: }
126:
127: if (actionWindow.getPortletEntity() == null) {
128: // a session is expired and the target actionWindow doesn't have portlet entity.
129: // Redirect the user back to the target page (with possibly retaining the other windows navigational state).
130: log
131: .warn("Portlet action was canceled because the session was expired. The actionWindow's id is "
132: + actionWindow.getId());
133:
134: request.setActionWindow(null);
135: MutableNavigationalState state = (MutableNavigationalState) request
136: .getPortalURL().getNavigationalState();
137:
138: if (state != null) {
139: state.removeState(actionWindow);
140: state.sync(request);
141: request.getResponse().sendRedirect(
142: request.getPortalURL().getPortalURL());
143: return;
144: }
145: }
146:
147: initWindow(actionWindow, request);
148: HttpServletResponse response = request
149: .getResponseForWindow(actionWindow);
150: HttpServletRequest requestForWindow = request
151: .getRequestForWindow(actionWindow);
152: requestForWindow
153: .setAttribute(
154: PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE,
155: request);
156:
157: //PortletMessagingImpl msg = new PortletMessagingImpl(windowAccessor);
158:
159: requestForWindow.setAttribute("JETSPEED_ACTION",
160: request);
161: container.processPortletAction(actionWindow,
162: requestForWindow, response);
163: // The container redirects the client after PortletAction processing
164: // so there is no need to continue the pipeline
165:
166: //msg.processActionMessage("todo", request);
167:
168: // clear the cache for all portlets on the current page
169: clearPortletCacheForPage(request, actionWindow);
170:
171: if (patchResponseCommitted) {
172: responseCommitted = true;
173: } else {
174: responseCommitted = response.isCommitted();
175: }
176: request.setAttribute(PortalReservedParameters.PIPELINE,
177: null); // clear the pipeline
178: }
179: } catch (PortletContainerException e) {
180: log.fatal("Unable to retrieve portlet container!", e);
181: throw new PipelineException(
182: "Unable to retrieve portlet container!", e);
183: } catch (PortletException e) {
184: log.warn("Unexpected PortletException in ActionValveImpl",
185: e);
186: // throw new PipelineException("Unexpected PortletException in ActionValveImpl", e);
187:
188: } catch (IOException e) {
189: log.error("Unexpected IOException in ActionValveImpl", e);
190: // throw new PipelineException("Unexpected IOException in ActionValveImpl", e);
191: } catch (IllegalStateException e) {
192: log
193: .error(
194: "Illegal State Exception. Response was written to in Action Phase",
195: e);
196: responseCommitted = true;
197: } catch (Throwable t) {
198: log.error("Unknown exception processing Action", t);
199: } finally {
200: // Check if an action was processed and if its response has been committed
201: // (Pluto will redirect the client after PorletAction processing)
202: if (responseCommitted) {
203: log
204: .info("Action processed and response committed (pipeline processing stopped)");
205: } else {
206: // Pass control to the next Valve in the Pipeline
207: context.invokeNext(request);
208: }
209: }
210:
211: }
212:
213: protected void clearPortletCacheForPage(RequestContext request,
214: PortletWindow actionWindow) throws JetspeedException {
215: ContentPage page = request.getPage();
216: if (null == page) {
217: throw new JetspeedException(
218: "Failed to find PSML Pin ContentPageAggregator.build");
219: }
220: ContentFragment root = page.getRootContentFragment();
221: if (root == null) {
222: throw new JetspeedException(
223: "No root ContentFragment found in ContentPage");
224: }
225: if (!isNonStandardAction(actionWindow)) {
226: notifyFragments(root, request, page);
227:
228: // if the fragment is rendered from a decorator template, the target cache would not be cleared by the above notification.
229: // so, let's clear target cache of action window directly again.
230: String fragmentId = actionWindow.getId().toString();
231: if (page.getFragmentById(fragmentId) == null) {
232: clearTargetCache(fragmentId, request);
233: }
234: } else {
235: ContentFragment fragment = page
236: .getContentFragmentById(actionWindow.getId()
237: .toString());
238:
239: if (fragment != null) {
240: clearTargetCache(fragment, request);
241: } else {
242: clearTargetCache(actionWindow.getId().toString(),
243: request);
244: }
245: }
246: }
247:
248: /**
249: * Actions can be marked as non-standard if they don't participate in
250: * JSR-168 standard action behavior. By default, actions are supposed
251: * to clear the cache of all other portlets on the page.
252: * By setting this parameter, we can ignore the standard behavior
253: * and not clear the cache on actions. This is useful for portlets
254: * which never participate with other portlets.
255: *
256: */
257: protected boolean isNonStandardAction(PortletWindow actionWindow) {
258: PortletEntity entity = actionWindow.getPortletEntity();
259: if (entity != null) {
260: PortletDefinitionComposite portletDefinition = (PortletDefinitionComposite) entity
261: .getPortletDefinition();
262: if (portletDefinition != null) {
263: Collection actionList = null;
264:
265: if (portletDefinition != null) {
266: actionList = portletDefinition
267: .getMetadata()
268: .getFields(
269: PortalReservedParameters.PORTLET_EXTENDED_DESCRIPTOR_NON_STANDARD_ACTION);
270: }
271: if (actionList != null) {
272: if (!actionList.isEmpty())
273: return true;
274: }
275: }
276: }
277: return false;
278: }
279:
280: protected void notifyFragments(ContentFragment f,
281: RequestContext context, ContentPage page) {
282: if (f.getContentFragments() != null
283: && f.getContentFragments().size() > 0) {
284: Iterator children = f.getContentFragments().iterator();
285: while (children.hasNext()) {
286: ContentFragment child = (ContentFragment) children
287: .next();
288: if (!"hidden".equals(f.getState())) {
289: notifyFragments(child, context, page);
290: }
291: }
292: }
293: ContentCacheKey cacheKey = portletContentCache.createCacheKey(
294: context, f.getId());
295: if (portletContentCache.isKeyInCache(cacheKey)) {
296: portletContentCache.remove(cacheKey);
297: portletContentCache.invalidate(context);
298: }
299: }
300:
301: protected void clearTargetCache(ContentFragment f,
302: RequestContext context) {
303: clearTargetCache(f.getId(), context);
304: }
305:
306: protected void clearTargetCache(String fragmentId,
307: RequestContext context) {
308: ContentCacheKey cacheKey = portletContentCache.createCacheKey(
309: context, fragmentId);
310:
311: if (portletContentCache.isKeyInCache(cacheKey)) {
312: portletContentCache.remove(cacheKey);
313: portletContentCache.invalidate(context);
314: }
315: }
316:
317: /**
318: * @see java.lang.Object#toString()
319: */
320: public String toString() {
321: // TODO Auto-generated method stub
322: return "ActionValveImpl";
323: }
324:
325: /**
326: * Makes sure that this PortletWindow's PortletEntity is set to have the
327: * current requests fragment.
328: * @param window
329: * @param request
330: */
331: protected void initWindow(PortletWindow window,
332: RequestContext request) {
333: Page page = request.getPage();
334: Fragment fragment = page.getFragmentById(window.getId()
335: .toString());
336:
337: if (fragment != null) {
338: ((MutablePortletEntity) window.getPortletEntity())
339: .setFragment(fragment);
340: }
341: }
342:
343: }
|