dev@javaserverfaces.java.net

Review: Fix "id" clashes between multiple portlet instances

From: Jayashri Visvanathan <Jayashri.Visvanathan_at_Sun.COM>
Date: Mon, 28 Mar 2005 18:59:15 -0800

M portlet-lib/build.xml
  copy jsf-portlet.tld to appropriate location.

M portlet-lib/src/conf/META-INF/faces-config.xml
  declare PortletComponent.

M portlet-lib/src/java/com/sun/faces/portlet/ExternalContextImpl.java
  added setResponse and setRequest methods.


A portlet-lib/src/java/com/sun/faces/portlet/PortletComponent.java
  Custom component that guarantees "id" uniqueness when one portlet
  is deployed multiple times in a portal environment.

A portlet-lib/src/java/com/sun/faces/portlet/PortletComponentTag.java
  Tag handler for PortletComponent.


A portlet-lib/src/java/com/sun/faces/portlet/jsf-portlet.tld
  TLD for declare PortletComponentTag.


Index: portlet-lib/build.xml
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-portlet/portlet-lib/build.xml,v
retrieving revision 1.7
diff -u -r1.7 build.xml
--- portlet-lib/build.xml 8 Sep 2004 17:55:59 -0000 1.7
+++ portlet-lib/build.xml 29 Mar 2005 02:50:29 -0000
@@ -69,9 +69,12 @@
   <target name="prepare" depends="init">
     <mkdir dir="${build.home}"/>
     <mkdir dir="${build.home}/classes"/>
+ <mkdir dir="${build.home}/classes/META-INF"/>
     <mkdir dir="${build.home}/lib"/>
     <mkdir dir="${build.home}/javadocs"/>
     
+ <copy file="src/java/com/sun/faces/portlet/jsf-portlet.tld"
+ toDir="${build.home}/classes/META-INF"/>
   </target>
 
 
Index: portlet-lib/src/conf/META-INF/faces-config.xml
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-portlet/portlet-lib/src/conf/META-INF/faces-config.xml,v
retrieving revision 1.5
diff -u -r1.5 faces-config.xml
--- portlet-lib/src/conf/META-INF/faces-config.xml 8 Nov 2004 19:23:05 -0000 1.5
+++ portlet-lib/src/conf/META-INF/faces-config.xml 29 Mar 2005 02:50:30 -0000
@@ -28,4 +28,20 @@
     </view-handler>
   </application>
 
+ <component>
+ <component-type>PortletComponent</component-type>
+ <component-class>com.sun.faces.portlet.PortletComponent</component-class>
+ <property>
+ <description>
+ ValueBinding Expression which evaluates to a PortletId for the Portlet
+ </description>
+ <property-name>portletId</property-name>
+ <property-class>java.lang.String</property-class>
+ </property>
+
+ <component-extension>
+ <component-family>PortletComponent</component-family>
+ </component-extension>
+
+ </component>
 </faces-config>
Index: portlet-lib/src/java/com/sun/faces/portlet/ExternalContextImpl.java
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-portlet/portlet-lib/src/java/com/sun/faces/portlet/ExternalContextImpl.java,v
retrieving revision 1.5
diff -u -r1.5 ExternalContextImpl.java
--- portlet-lib/src/java/com/sun/faces/portlet/ExternalContextImpl.java 2 Dec 2004 23:27:09 -0000 1.5
+++ portlet-lib/src/java/com/sun/faces/portlet/ExternalContextImpl.java 29 Mar 2005 02:50:30 -0000
@@ -347,7 +347,18 @@
         context.log(message, exception);
     }
 
-
+ public void setResponse(Object response) {
+ if (response instanceof RenderResponse) {
+ this.response = (RenderResponse) response;
+ }
+ }
+
+ public void setRequest(Object request) {
+ if (request instanceof RenderRequest) {
+ this.request = (RenderRequest) request;
+ }
+ }
+
     public void redirect(String path) throws IOException {
         if (log.isTraceEnabled()) {
             log.trace("redirectMessage(" + path + ")");


/*
 * $Id: UIForm.java,v 1.45 2004/04/06 18:12:53 eburns Exp $
 */

/*
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.faces.portlet;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponentBase;
import javax.faces.el.ValueBinding;


/**
 * <p><strong>PortletComponent</strong> is a {_at_link UIComponent} that
 * acts as a container for all JSF components in a portlet page. This component
 * works around the problem of "id" clashes in a portal environment where same
 * portlet can be deployed multiple times.This component overrides the
 * <code>getClientId()</code> method to prepend a unique <code>id</code>
 * everytime it is invoked, so that no two JSF components with in two
 * different portlets have the same <code>id</code>. If a <code>portletId</code>
 * is specified, it has to an EL expression and the application is responsible
 * for making sure that it is unique. If no <portletId> is specifed,
 * <code>PortletComponent</code> guarantees <code>id</code> uniqueness.
 */

public class PortletComponent extends UIComponentBase implements NamingContainer {


    // ------------------------------------------------------ Manifest Constants


    /**
     * <p>The standard component type for this component.</p>
     */
    public static final String COMPONENT_TYPE = "PortletComponent";


    /**
     * <p>The standard component family for this component.</p>
     */
    public static final String COMPONENT_FAMILY = "PortletComponent";

    private static final String PORTLET_ID_SERIAL = "PORTLET_ID_SERIAL";
    
     private static final String PORTLET_PAGE = "portletPage";
    
    // ------------------------------------------------------------ Constructors


    /**
     * <p>Create a new {_at_link PortletComponent} instance with default property
     * values.</p>
     */
    public PortletComponent() {

        super();

    }


    // ------------------------------------------------------ Instance Variables


    // -------------------------------------------------------------- Properties


    public String getFamily() {

        return (COMPONENT_FAMILY);

    }

    private String portletId = null;
    /**
     * <p>Returns the <code>value</code> property of the
     * <code>UICommand</code>. This is most often rendered as a label.</p>
     */
    public String getPortletId() {

        if (this.portletId != null) {
            return (this.portletId);
        }
        ValueBinding vb = getValueBinding("portletId");
        if (vb != null) {
            return ((String)vb.getValue(getFacesContext()));
        } else {
            return (null);
        }

    }


    // ----------------------------------------------------- UIComponent Methods
     public String getClientId(FacesContext context) {
         if (portletId == null) {
             // generate a unique "id" and prepend it to "result"
             portletId = createUniquePortletId(context);
         }
         return portletId;
     }
     
    /**
     * Returns a unique PortletId for the portlet. Since the serial is
     * saved in Application scope, it is guaranteed to be unique for the
     * life of the application.
     */
    private String createUniquePortletId(FacesContext context) {
        int portletIdSerial = 1;
        Map applicationMap = context.getExternalContext().getApplicationMap();
        String porletIdStr = (String) applicationMap.get(PORTLET_ID_SERIAL);
        if (porletIdStr != null) {
            portletIdSerial = Integer.parseInt(porletIdStr);
            portletIdSerial++;
            if (portletIdSerial == Integer.MAX_VALUE) {
                portletIdSerial = 1;
            }
        }
        applicationMap.put(PORTLET_ID_SERIAL, String.valueOf(portletIdSerial));
        return (PORTLET_PAGE + String.valueOf(portletIdSerial));
    }
}


/*
 * $Id: ExternalContextImpl.java,v 1.5 2004/12/02 23:27:09 jayashri Exp $
 */

/*
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.faces.portlet;

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;



/**
 * This class is the tag handler that evaluates the <code>portletPage</code>
 * custom tag. All JSF tags in a portlet should be embedded with this this tag
 * if multiple instances of the same portlet can exist within a portal page.
 * Otherwise it may result in potential "id" clashes especially if the portlet
 * uses JavaScript.
 */

public class PortletComponentTag extends UIComponentTag {
    
    
    private String portletId = null;
    
    
    public void setPortletId(String portletId) {
        this.portletId = portletId;
    }
    
    
    public String getComponentType() {
        return ("PortletComponent");
    }
    
    
    public String getRendererType() {
        return null;
    }
    
    
    protected void setProperties(UIComponent component) {
        
        super.setProperties(component);
        ValueBinding vb = null;
        if (portletId != null) {
            if (isValueReference(portletId)) {
                vb = getFacesContext().getApplication().createValueBinding(portletId);
                component.setValueBinding("portletId", vb);
            } else {
                throw new
                    FacesException("portletId attribute must be an ELExpression");
            }
        }
    }
}