/* * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jetspeed.portal.portlets; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest; import org.apache.ecs.ConcreteElement; import org.apache.jetspeed.om.profile.Profile; import org.apache.jetspeed.portal.PortletException; import org.apache.jetspeed.portal.portlets.viewprocessor.ViewProcessor; import org.apache.jetspeed.portal.portlets.viewprocessor.ViewProcessorFactory; import org.apache.jetspeed.services.logging.JetspeedLogFactoryService; import org.apache.jetspeed.services.logging.JetspeedLogger; import org.apache.jetspeed.services.rundata.JetspeedRunData; import org.apache.jetspeed.util.PortletSessionState; import org.apache.jetspeed.util.template.JetspeedLink; import org.apache.jetspeed.util.template.JetspeedTemplateLink; import org.apache.jetspeed.util.template.JspTemplate; import org.apache.turbine.modules.ActionLoader; import org.apache.turbine.services.pull.TurbinePull; import org.apache.turbine.util.RunData; /** * Provides the basic MVC Portlet functionality independant of any * specific view technology (ie jsp, velocity, xslt, etc). It handles * the views via a ViewProcessor, which is a pluggable, factory * created, run time module for which ever view technology your portlet * uses. * * There is no need to extend this portlet class, just define your porlet * entry in the registy as a child of this class and provide your template * and action class (extened from GenericMVCAction of course) and you * are good to go. * * Example .xreg entry: * * <portlet-entry name="GenericMVCPortlet" hidden="false" * type="abstract" application="false"> * <security-ref parent="default"/> * <classname>com.cisco.it.psf.portal.portlets.GenericMVCPortlet</classname> * <media-type ref="html"/> * <url cachedOnURL="true"/> * </portlet-entry> * <portlet-entry name="VelocityMVCExample" hidden="false" type="ref" * parent="GenericMVCPortlet" application="false"> * <meta-info> * <title>Velocity MVC Portlet</title> * <description>Velocity Generic MVC Portlet</description> * </meta-info> * <classname>com.cisco.it.psf.portal.portlets.GenericMVCPortlet</classname> * <parameter name="template" value="mvc-example" hidden="true" * cachedOnName="true" cachedOnValue="true"/> * <parameter name="viewtype" value="Velocity" hidden="true" * cachedOnName="true" cachedOnValue="true"/> * <parameter name="action" * value="portlets.ExampleGenericMVCAction" hidden="true" * cachedOnName="true" cachedOnValue="true"/> * <url cachedOnURL="true"/> * </portlet-entry> * * See the Velocity and JSP MVC Portlet examples for template and action class clues. * * To add new view processor types, simply implement the ViewProcessor * interface and add your type into the <b>viewprocessor.properties</b> file as * shown in the example below: * * mvc.viewprocessor.Velocity = org.apache.jetspeedportlets.viewprocessors.VelocityViewProcessor * mvc.viewprocessor.JSP = org.apache.jetspeedportlets.viewprocessors.JSPViewProcessor * mvc.viewprocessor.XSL = org.apache.jetspeedportlets.viewprocessors.XSLViewProcessor * * @stereotype role * @author tkuebler * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a> * @version $Id: GenericMVCPortlet.java,v 1.11 2003/02/11 23:09:18 tkuebler Exp $ */ public class GenericMVCPortlet extends AbstractInstancePortlet { /** * Static initialization of the logger for this class */ private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(GenericMVCPortlet.class.getName()); // STW: Context keys public static final String PORTLET = "portlet"; public static final String TEMPLATE = "template"; public static final String RUNDATA = "data"; public static final String PORTLET_CONFIG = "conf"; public static final String SKIN = "skin"; public static final String VIEW_TYPE = "viewType"; public static final String IS_CACHEABLE = "_IsCacheable"; // need cache timer, etc private String viewType = "ERROR: not set in config"; private String actionName = "ERROR: not set in config"; private String template = "ERROR: not set in config"; private String configureTemplate; private String maximizedTemplate; private ViewProcessor processor = null; private boolean providesCustomization; public static final String RENDERING_DELAYED = "renderingDelayed"; public static final String SIMULATE_DELAY = "simulateDelay"; public static final String PORTLET_ID = "__portletId"; public static final String DOC_URL = "__docUrl"; public void init() throws PortletException { //STW: check custimization attribute String provConf = getPortletConfig().getInitParameter("provides.customization", "false"); providesCustomization = new Boolean(provConf).booleanValue(); // pull the important info out of the portlet config actionName = getPortletConfig().getInitParameter("action"); // STW: Allow subclasses to set viewtype for backward compatibillity if (getPortletConfig().getInitParameter("viewtype") != null) { viewType = getPortletConfig().getInitParameter("viewtype"); } template = getPortletConfig().getInitParameter("template"); // get viewprocessor from factory logger.info( "GenericMVCPortlet - creating view processor for viewtype = " + viewType + ", template = " + template); processor = ViewProcessorFactory.getViewProcessor(viewType); // initialize view processor with portlet info processor.init(this); // should make this config file driven // STW removed this so subclasses can decide this for // themselves. // setCacheable(false); } /** * By default MVCPortlets are cacheable. This can be overriden by specifying * "_IsCacheable" parameter. * * @return */ public boolean isCacheable() { return getPortletConfig().getInitParameter(IS_CACHEABLE, "true").equalsIgnoreCase("true"); } /** * Whether or not this portlet provides it's own customizer. * This can be set at the registry level by adding a * boolean paramter "provides.customization" * Defaults to "false" */ public boolean providesCustomization() { return providesCustomization; } public ConcreteElement getContent(RunData rundata) { if (useDelayedRendering(rundata)) { Profile profile = ((JetspeedRunData) rundata).getProfile(); String path = profile.getPath(); String portletId = getID(); // FIXME: please use JetspeedLink to create Portal URLs String docUrl = "portal/" + path + "/js_peid/" + portletId + "?action=controls.Print"; docUrl = URLEncoder.encode(docUrl); //, "UTF-8"); // END FIXME: HttpServletRequest request = rundata.getRequest(); request.setAttribute(PORTLET_ID, portletId); request.setAttribute(DOC_URL, docUrl); // render content that pulls return renderJspTemplate(rundata, "delayedContent.jsp"); } simulateDelay(); //if caching is turned off or no expiration time set, generate and return the content if (!isCacheable() || null == getExpirationMillis()) { return buildContent(rundata); } //is the cached content still valid, if not, generate and return the content if (getExpirationMillis().longValue() <= System.currentTimeMillis()) { return buildContent(rundata); } //else, the cached content is fine to be returned return getContent(rundata, null, true); } protected ConcreteElement buildContent(RunData rundata) { // create a new context // populate it with data GenericMVCContext context = new GenericMVCContext(TurbinePull.getGlobalContext()); context.put(RUNDATA, rundata); context.put(PORTLET, this); context.put(PORTLET_CONFIG, this.getPortletConfig()); context.put(SKIN, this.getPortletConfig().getPortletSkin()); context.put(TEMPLATE, getCurrentTemplate(rundata)); context.put(VIEW_TYPE, viewType); populateRequest(rundata); // put references to the pull tools in the context TurbinePull.populateContext(context, rundata); // Initialize jlink and jslink tools with ourselves // to enable links between portlets Object jlink = context.get("jlink"); if (jlink instanceof JetspeedTemplateLink) { ((JetspeedTemplateLink) jlink).setPortlet(this); } Object jslink = context.get("jslink"); if (jslink instanceof JetspeedLink) { ((JetspeedLink) jslink).setPortlet(this); } // Handle Action if (actionName != null) { try { // store the context so that the action can retrieve it //Log.debug("VelocityPortlet found action "+actionName+" context "+context); // note: the name it is stored under is legacy, leaving it this way // because some of the routines fall back to old default behavior // of the velocity portlet and might depend on the name rundata.getTemplateInfo().setTemplateContext("VelocityPortletContext", context); if (logger.isDebugEnabled()) { logger.debug( "GenericMVCPortlet: Executing action [" + actionName + "] for portlet [" + this.getName() + "]"); } ActionLoader.getInstance().exec(rundata, actionName); } catch (Exception e) { logger.error("GenericMVCPortlet - error executing action", e); } } // Process View // call processView method logger.info("GenericMVCPortlet - calling processView on processor"); ConcreteElement result = (ConcreteElement) processor.processView(context); logger.info("GenericMVCPortlet - setting this portlet's content"); clearContent(); setContent(result); // only needed when caching is true I believe // return result return result; } /** * @see setViewType() * @return String */ protected String getViewType() { return viewType; } /** * STW: Added for backward compatibility when using this * class to subclass the existing Jsp and Velocity portlets * so they can set their view prior to super.init(); * @param viewType The viewType to set */ protected void setViewType(String viewType) { this.viewType = viewType; } /** * This is called before any action execution happens. * This provides backward compatibility to JspPortletActions * who retreive information, like Portlet, from the request * BEFORE the ViewProcessor.processView() is called * which normally populates the request with Context objects. * @author <a href="mailto:sweaver@rippe.com">Scott Weaver</a> */ protected void populateRequest(RunData rundata) { HttpServletRequest request = rundata.getRequest(); request.setAttribute("data", rundata); request.setAttribute("portlet", this); request.setAttribute("conf", this.getPortletConfig()); request.setAttribute("skin", this.getPortletConfig().getPortletSkin()); request.setAttribute("template", getCurrentTemplate(rundata)); request.setAttribute("viewType", viewType); } /** * */ protected String getCurrentTemplate( RunData data) { String useTemplate = (String) PortletSessionState.getAttribute(this, data, TEMPLATE); if(useTemplate == null) { useTemplate = this.template; } return useTemplate; } protected boolean useDelayedRendering(RunData rundata) { String renderingDelayedString = getPortletConfig().getInitParameter(RENDERING_DELAYED); boolean renderingDelayed = false; if (renderingDelayedString != null) { renderingDelayed = (Boolean.valueOf(renderingDelayedString) == Boolean.TRUE); } HttpServletRequest request = rundata.getRequest(); String action = rundata.getAction(); return renderingDelayed && (action == null || action.length() == 0 || "controls.Restore".equals(action)); } protected ConcreteElement renderJspTemplate(RunData rundata, String templateName) { JspTemplate t = new JspTemplate(rundata, "/portlets/html/" + templateName); PrintWriter out = null; try { out = rundata.getOut(); out.println(t.getContent()); } catch (IOException ioe) { logger.error(ioe); } return null; } private void simulateDelay() { String simulateDelayString = getPortletConfig().getInitParameter(SIMULATE_DELAY); int simulateDelay = 0; // seconds if (simulateDelayString != null) { simulateDelay = Integer.parseInt(simulateDelayString); } if (simulateDelay > 0) { long delayInMilliseconds = simulateDelay * 1000; try { Thread.sleep(delayInMilliseconds); } catch (InterruptedException e) { } } } }