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 }