001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 003 * agreements. See the NOTICE file distributed with this work for additional information regarding 004 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 005 * "License"); you may not use this file except in compliance with the License. You may obtain a 006 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 007 * law or agreed to in writing, software distributed under the License is distributed on an "AS IS" 008 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 009 * for the specific language governing permissions and limitations under the License. 010 */ 011 package javax.portlet.faces.component; 012 013 import java.io.Serializable; 014 015 import javax.faces.context.FacesContext; 016 import javax.faces.component.NamingContainer; 017 import javax.faces.component.UIViewRoot; 018 import javax.faces.context.ExternalContext; 019 020 import javax.portlet.faces.Bridge; 021 import javax.portlet.faces.BridgeUtil; 022 import javax.portlet.faces.annotation.PortletNamingContainer; 023 024 /** 025 * <code>UIViewRoot</code> that implements portlet specific <code>NamingContainer</code> 026 * that ensures the consumer's unique portlet Id is encoded in all tree components. 027 * The class is annotated by <code>javax.portlet.faces.annotation.PortletNamingContainer</code> 028 * allowing the bridge to recognize that this specific <code>UIViewRoot</code> 029 * implements the behavior. 030 */ 031 @PortletNamingContainer 032 public class PortletNamingContainerUIViewRoot extends UIViewRoot implements Serializable, NamingContainer 033 { 034 035 //TODO: This should be regenerated each time this is modified. Can this be added to maven? 036 private static final long serialVersionUID = -4524288011655837711L; 037 private static final String PORTLET_NAMESPACE_ID_PREFIX = "_jpfcpncuivr_"; 038 039 // Assumes that first use of the UIViewRoot always occurs in a render as portlet namespace 040 // is only available during a render. If this isn't the case we disable use of the 041 // NamingContainer 042 public PortletNamingContainerUIViewRoot() 043 { 044 super(); 045 } 046 047 /** 048 * NamingContainer semantics worked generically (serviced by subclasses) as long as the class 049 * is marked as implementing NamingContainer and we use the portletNamespace Id as 050 * (part of) the component's id. 051 */ 052 053 @Override 054 public String getContainerClientId(FacesContext context) 055 { 056 if (BridgeUtil.isPortletRequest()) 057 { 058 // Some impls (Facelets don't set an id on the UIViewRoot) -- Also handles the action case 059 if ((this.getId() == null || !this.getId().startsWith(PORTLET_NAMESPACE_ID_PREFIX))) 060 { 061 setId(this.getId()); // setId can handle the null 062 } 063 return super.getContainerClientId(context); 064 } 065 else 066 { 067 return null; 068 } 069 } 070 071 @Override 072 public void setId(String id) 073 { 074 if (BridgeUtil.isPortletRequest()) 075 { 076 077 // Turns out that in Facelets the UIViewRoot doesn't seem to have its id set -- i.e. its null 078 // So recognize null or the temp one we set and change to "" 079 if (id == null) 080 { 081 id = createUniqueId(); 082 } 083 084 /* Can only calculate the namespace during a render (in portlet 1.0) */ 085 if (!id.startsWith(PORTLET_NAMESPACE_ID_PREFIX) && BridgeUtil.getPortletRequestPhase() == Bridge.PortletPhase.RENDER_PHASE) 086 { 087 // Turns out some Faces impls (the RI) , on restoreView, manually sets the id from 088 // the extracted state prior to telling the component to restore itself from this state. 089 // (At which point the self restore overwrites any id set prior.). As this manual 090 // set is using the already encoded (saved) value we could end up with a doubly 091 // encoded id until the restore overwrites. To avoid this -- first test if 092 // its already encoded and don't re-encode. 093 ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext(); 094 id = PORTLET_NAMESPACE_ID_PREFIX + ec.encodeNamespace("") + "_" + id; 095 } 096 097 } 098 super.setId(id); 099 } 100 101 }