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 /** 026 * <code>UIViewRoot</code> that implements portlet specific <code>NamingContainer</code> 027 * that ensures the consumer's unique portlet Id is encoded in all tree components. 028 * The class is annotated by <code>javax.portlet.faces.annotation.PortletNamingContainer</code> 029 * allowing the bridge to recognize that this specific <code>UIViewRoot</code> 030 * implements the behavior. 031 */ 032 @PortletNamingContainer 033 public class PortletNamingContainerUIViewRoot extends UIViewRoot implements Serializable, NamingContainer 034 { 035 036 //TODO: This should be regenerated each time this is modified. Can this be added to maven? 037 private static final long serialVersionUID = -4524288011655837711L; 038 private static final String PORTLET_NAMESPACE_ID_PREFIX = "_jpfcpncuivr_"; 039 040 041 public PortletNamingContainerUIViewRoot() 042 { 043 super(); 044 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 072 @Override 073 public void setId(String id) 074 { 075 if (BridgeUtil.isPortletRequest()) 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 and change to a uniqueId 079 if (id == null) 080 { 081 id = createUniqueId(); 082 } 083 084 // Turns out some Faces impls (the RI) , on restoreView, manually sets the id from 085 // the extracted state prior to telling the component to restore itself from this state. 086 // (At which point the self restore overwrites any id set prior.). As this manual 087 // set is using the already encoded (saved) value we could end up with a doubly 088 // encoded id until the restore overwrites. To avoid this -- first test if 089 // its already encoded and don't re-encode. 090 if (!id.startsWith(PORTLET_NAMESPACE_ID_PREFIX)) 091 { 092 ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext(); 093 id = PORTLET_NAMESPACE_ID_PREFIX + ec.encodeNamespace("") + "_" + id; 094 } 095 } 096 super.setId(id); 097 } 098 099 }