All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.portlet.faces.GenericFacesPortlet Maven / Gradle / Ivy

/******************************************************************************
 * JBoss, a division of Red Hat                                               *
 * Copyright 2006, Red Hat Middleware, LLC, and individual                    *
 * contributors as indicated by the @authors tag. See the                     *
 * copyright.txt in the distribution for a full listing of                    *
 * individual contributors.                                                   *
 *                                                                            *
 * This is free software; you can redistribute it and/or modify it            *
 * under the terms of the GNU Lesser General Public License as                *
 * published by the Free Software Foundation; either version 2.1 of           *
 * the License, or (at your option) any later version.                        *
 *                                                                            *
 * This software is distributed in the hope that it will be useful,           *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU           *
 * Lesser General Public License for more details.                            *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public           *
 * License along with this software; if not, write to the Free                *
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA         *
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.                   *
 ******************************************************************************/
package javax.portlet.faces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.WindowState;

/**
 * JSR 301 generic faces pottlet implementation.
 * 
 * @author asmirnov
 * 
 */
public class GenericFacesPortlet extends GenericPortlet {

	private static final int EXTENDED_ATTR_PREFIX_LENGTH = Bridge.EXTENDED_PORTLET_ATTR_PREFIX.length();
	private static final String BRIDGE_SERVICE_CLASSPATH = "META-INF/services/javax.portlet.faces.Bridge";
	public static final String BRIDGE_CLASS = "javax.portlet.faces.BridgeImplClass";
	public static final String DEFAULT_CONTENT_TYPE = Bridge.BRIDGE_PACKAGE_PREFIX
			+ "defaultContentType";
	public static final String DEFAULT_CHARACTERSET_ENCODING = Bridge.BRIDGE_PACKAGE_PREFIX
			+ "defaultCharacterSetEncoding";

	private volatile String bridgeClassName = null;
	private volatile Class facesBridgeClass;
	private volatile Bridge facesPortletBridge = null;
	private Map viewIdMap;
	// Following are names of request attributes a portletbridge must set before
	// calling the Bridge to process a request
	private static final String DEFAULT_VIEWID = Bridge.BRIDGE_PACKAGE_PREFIX + "defaultViewId";
	
	private static final int DEFAULT_VIEW_ID_LENGTH = DEFAULT_VIEWID.length()+1;

	public void init(PortletConfig config) throws PortletException {
		super.init(config);
		PortletContext portletContext = this.getPortletContext();
		String bridgeClassName = getBridgeClassName();
		try {
			facesBridgeClass = getClassLoader().loadClass(bridgeClassName).asSubclass(Bridge.class);
		} catch (ClassNotFoundException e) {
			throw new PortletException("Faces portlet Bridge implementation class not found",e);
		}

		String portletName = getPortletName();
		Boolean isPreserveActionParams = getPreserveActionParameters();
		portletContext.setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX
				+ portletName + "." + Bridge.PRESERVE_ACTION_PARAMS,
				isPreserveActionParams);
		List attrsList = getExcludedRequestAttributes();
		if (null != attrsList) {
			portletContext.setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX
					+ portletName + "."
					+ Bridge.EXCLUDED_REQUEST_ATTRIBUTES, attrsList);

		}
		// Get extension config attributes.
		Enumeration configNames = config.getInitParameterNames();
		while (configNames.hasMoreElements()) {
			String name = (String) configNames.nextElement();
			if(name.startsWith(Bridge.EXTENDED_PORTLET_ATTR_PREFIX)){
				int i = name.lastIndexOf('.');
				if(i>EXTENDED_ATTR_PREFIX_LENGTH+2){
					String attribute = name.substring(i);
					String preffix = name.substring(EXTENDED_ATTR_PREFIX_LENGTH,i+1);
					String extensionAttributeName = Bridge.EXTENDED_PORTLET_ATTR_PREFIX+preffix+portletName+attribute;
					portletContext.setAttribute(extensionAttributeName, config.getInitParameter(name));
				}
			}
		}
		
		Map viewIdMap = getDefaultViewIdMap();
	    getPortletContext().setAttribute(
	                                      Bridge.BRIDGE_PACKAGE_PREFIX + portletName + "."
	                                      + Bridge.DEFAULT_VIEWID_MAP,
	                                      viewIdMap);
	}

	private String calculateBridgeClassName(PortletContext portletContext)
			throws PortletException {
		String bridgeClassName = portletContext.getInitParameter(BRIDGE_CLASS);
		if (bridgeClassName == null) {
			ClassLoader loader = getClassLoader();
			InputStream stream = loader
					.getResourceAsStream(BRIDGE_SERVICE_CLASSPATH);
			if (null != stream) {
				try {
					BufferedReader reader = null;
					try {
						reader = new BufferedReader(new InputStreamReader(
								stream, "UTF-8"));
					} catch (UnsupportedEncodingException e) {
						reader = new BufferedReader(new InputStreamReader(
								stream));
					}
					bridgeClassName = reader.readLine();
					if (null != bridgeClassName) {
						bridgeClassName = bridgeClassName.trim();
					}
				} catch (IOException e) {
					// Ignore
				} catch (SecurityException e) {
					// Ignore
				} finally {
					try {
						stream.close();
					} catch (IOException e) {
						throw new PortletException(
						"Error to close input stream for a resource "+BRIDGE_SERVICE_CLASSPATH);
					}
				}
			}
		}
		if (null == bridgeClassName) {
			throw new PortletException(
					"Can't detect bridge implementation class name");
		}
		return bridgeClassName;
	}

	/**
	 * 4.2.7 getExcludedRequestAttributes() As a portlet lifecycle allows
	 * multiple (re)renders to occur following an action, the bridge manages an
	 * extended notion of a request scope to ensure that such rerenders produces
	 * identical results. Specifically, portlet scoped request attributes are
	 * saved/restored by the bridge across such rerenders [5.1.2]. However,
	 * sometimes a portlet request scoped attribute truly must be removed when
	 * the request scope ends. The bridge uses multiple mechanisms for
	 * determining which attributes are marked for exclusion from its managed
	 * scope. The portlet can directly instruct the bridge to exclude attributes
	 * on a per portlet basis by setting a PorletContext attribute [3.2]. This
	 * attribute's value is a List containing the excluded attribute names.
	 * 
	 * The GenericFacesPortlet sets this attribute based on the result of
	 * calling getExcludedRequestAttributes() in its init() method. The default
	 * (GenericFacesPortlet) implementation for getExcludedRequestAttributes()
	 * returns a List constructed by parsing the comma delimited String value
	 * from the corresponding portlet initialization parameter,
	 * javax.portlet.faces.excludedRequestAttributes. If this initialization
	 * parameter isn't present null is returned which causes the
	 * GenericFacesPortlet to not set the corresponding PortletContext
	 * attribute.
	 * 
	 * @return
	 */
	public List getExcludedRequestAttributes() {
		List attrsList = null;
		String excludedAttrs = getPortletConfig().getInitParameter(
				Bridge.BRIDGE_PACKAGE_PREFIX
						+ Bridge.EXCLUDED_REQUEST_ATTRIBUTES);
		if (null != excludedAttrs) {
			String[] atrs = excludedAttrs.split(",");
			attrsList = new ArrayList(atrs.length);
			for (String string : atrs) {
				attrsList.add(string);
			}
		}
		return attrsList;
	}

	/**
	 * 4.2.9 getResponseContentType() In Portlet 1.0 a portlet must set the
	 * response content type prior to dispatching to a servlet or jsp as setting
	 * a content type in a dispatch (include) is ignored. When the Faces view is
	 * represented in a jsp, the bridge uses dispatch to render the view. In
	 * this situation any content type set by the jsp/render is ignored. To
	 * ensure a content type is set, the bridge sets the content type to the one
	 * which the portlet container indicates is preferred for this request if
	 * one hasn't been set by the portlet. To provide more flexibility the
	 * GenericFacesPortlet always sets the response content type by calling
	 * getResponseContentType() on itself. If not overridden, the
	 * GenericFacesPortlet returns the value of the portlet initialization
	 * parameter javax.portlet.faces.defaultContentType, or if this parameter
	 * doesn't exists, the portlet container's indication of the preferred
	 * content type for this request.
	 * 
	 * @param request
	 * @return
	 */
	public String getResponseContentType(PortletRequest request) {
		String contentType = getPortletConfig().getPortletContext()
				.getInitParameter(DEFAULT_CONTENT_TYPE);

		if (contentType == null) {
			contentType = request.getResponseContentType();
		}
		return contentType;
	}

	/**
	 * 4.2.10 getResponseCharacterSetEncoding() Though a portlet is prevented
	 * from setting the character set encoding of the actual response as this is
	 * under the control of the portlet container, its common for portlet
	 * containers to interpret such settings as an indication of the encoding
	 * that is being written and hence should be translated by the container to
	 * its response encoding. In such circumstances character set encoding
	 * information is passed as extra information in the string used to set the
	 * response content type. As part of the content type it too must be
	 * expressed prior to any dispatching. In the situation that the bridge sets
	 * a response content type because none has been set yet, it doesn't set a
	 * corresponding character set encoding. I.e. it that the Faces view renders
	 * in the encoding chosen by the container. To provide more flexibility the
	 * GenericFacesPortlet extends its behavior which sets a response content
	 * type. As part of the process of determining the content type to set, the
	 * GenericFacesPortlet calls getResponseCharacterSetEncoding on itself. If a
	 * non-null value is returned, the value is appended (; delimited) to the
	 * content type prior to setting the response content type such that its
	 * form is the same as one returned in the http response CONTENT-TYPE
	 * header. The GenericFacesPortlet implements
	 * getResponseCharacterSetEncoding by returning the value of the portlet
	 * initialization parameter javax.portlet.faces.defaultCharacterSetEncoding
	 * or null if this parameter doesn't exist.
	 * 
	 * @param request
	 * @return
	 */
	public String getResponseCharacterSetEncoding(PortletRequest request) {
		return getPortletConfig().getPortletContext().getInitParameter(
				DEFAULT_CHARACTERSET_ENCODING);
	}

	/**
	 * 4.2.8 getPreserveActionParameters() By default the bridge doesn't
	 * preserve action parameters into subsequent renders. This can be
	 * overridden on a per portlet basis by passing a value of true in the
	 * appropriate PortletContext attribute [3.2]. To determine the setting for
	 * this attributes for this particular portlet, the GenericFacesPortlet
	 * calls getPreserveActionParameters() in its init() method. The default
	 * (GenericFacesPortlet) implementation returns the Boolean value
	 * corresponding to the String value represented in the portlet
	 * initialization parameter, javax.portlet.faces.preserveActionParams. If
	 * this initialization parameter doesn't exist, Boolean.FALSE is returned.
	 * 
	 * @return preserve or not action attributes.
	 */
	public Boolean getPreserveActionParameters() {
		String preserveActionParams = getPortletConfig().getInitParameter(
				Bridge.BRIDGE_PACKAGE_PREFIX + Bridge.PRESERVE_ACTION_PARAMS);
		Boolean isPreserveActionParams = Boolean.valueOf(preserveActionParams);
		return isPreserveActionParams;
	}

	protected void doDispatch(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		PortletMode mode = request.getPortletMode();
		if (mode == PortletMode.VIEW || mode == PortletMode.EDIT
				|| mode == PortletMode.HELP) {
			super.doDispatch(request, response);
		} else {
			doFacesDispatch(request, response);
		}

	}

	protected void doEdit(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		doFacesDispatch(request, response);
	}

	protected void doHelp(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		doFacesDispatch(request, response);
	}

	protected void doView(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		doFacesDispatch(request, response);
	}

	public void processAction(ActionRequest request, ActionResponse response)
			throws PortletException, IOException {
//		String defaultViewId = getDefaultViewIdMap().get(request
//				.getPortletMode());
		Bridge bridge = getFacesPortletBridge();
//		request.setAttribute(GenericFacesPortlet.DEFAULT_VIEWID, defaultViewId);
		try {
			bridge.doFacesRequest(request, response);

		} catch (BridgeException e) {
			throw new PortletException("Error process faces request", e);
		}
	}

	void doFacesDispatch(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		String defaultViewId = getDefaultViewIdMap().get(request
				.getPortletMode().toString());
		if (null != defaultViewId
				&& !request.getWindowState().equals(WindowState.MINIMIZED)) {
			Bridge bridge = getFacesPortletBridge();
//			request.setAttribute(GenericFacesPortlet.DEFAULT_VIEWID, defaultViewId);
			String responseContentType = getResponseContentType(request);
			if (null != responseContentType) {
				StringBuilder contentType = new StringBuilder(
						responseContentType);
				String characterSetEncoding = getResponseCharacterSetEncoding(request);
				if (null != characterSetEncoding) {
					contentType.append(';').append(characterSetEncoding);
				}
				response.setContentType(contentType.toString());

			}
			try {
				bridge.doFacesRequest(request, response);

			} catch (BridgeException e) {
				throw new PortletException("Error process faces request", e);
			}
		}
	}

	/**
	 * JSR 301 API method
	 * 
	 * @return
	 */
	public String getBridgeClassName() throws PortletException {
		if(null == bridgeClassName){
			bridgeClassName = calculateBridgeClassName(getPortletContext());
		}
		return bridgeClassName;
	}

	private ClassLoader getClassLoader() {
		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();
		if (null == classLoader) {
			classLoader = this.getClass().getClassLoader();
		}
		return classLoader;
	}

	public Map getDefaultViewIdMap() {
		if(null == viewIdMap){
			viewIdMap = calculateDefaultViewIdMap();
		}
		return viewIdMap;

	}

	private Map calculateDefaultViewIdMap() {
			Map viewIdMap = new HashMap();
			PortletConfig portletConfig = getPortletConfig();
			Enumeration configNames = portletConfig.getInitParameterNames();
			while (configNames.hasMoreElements()) {
				String name = (String) configNames.nextElement();
				if(name.startsWith(DEFAULT_VIEWID)){
					// Put viewId with mode name as key.
					viewIdMap.put(name.substring(DEFAULT_VIEW_ID_LENGTH), portletConfig.getInitParameter(name));
				}
			}
			return viewIdMap;
	}

	public void destroy() {
		bridgeClassName = null;
		// If bridge was initialized, destroy it.
		if (null != facesPortletBridge) {
			facesPortletBridge.destroy();
			facesPortletBridge = null;
		}
		super.destroy();
	}

	/**
	 * @return the facesPortletBridge
	 * @throws PortletException
	 */
	@SuppressWarnings("unchecked")
	synchronized Bridge getFacesPortletBridge() throws PortletException {
		if (null == facesPortletBridge) {
			try {
				facesPortletBridge = (Bridge) facesBridgeClass.newInstance();
				facesPortletBridge.init(getPortletConfig());
			} catch (InstantiationException e) {
				throw new PortletException(
						"Error on create instance of a JSF Portlet Bridge", e);
			} catch (IllegalAccessException e) {
				throw new PortletException(
						"IllegalAccess on create instance of a JSF Portlet Bridge",
						e);
			} catch (BridgeException e) {
				throw new PortletException(
						"Bridge initialization error",
						e);
			}
		}
		return facesPortletBridge;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy