1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.myfaces.trinidad.util; 20 21 import java.lang.reflect.Method; 22 23 import java.util.HashMap; 24 import java.util.Map; 25 import java.util.UUID; 26 27 import javax.faces.context.ExternalContext; 28 29 import javax.portlet.faces.annotation.ExcludeFromManagedRequestScope; 30 31 import javax.servlet.ServletRequest; 32 33 /** 34 * This object provides a map that is primarily used to enforce a consistent contract for "state" information in both 35 * a portal environment and a servlet environment. In a portal environment, request-scope attributes are not necessarily 36 * "saved" between processAction and render. According to the JSF Portlet Bridge Spec, a request-scope attribute may 37 * hang around from a single processAction to multiple render requests unless it is excluded. Of course, excluding the 38 * object from the managed request scope will suffer from the problem of the request scope going away BETWEEN action and 39 * render. 40 * <p/> 41 * This class will save an attribute in a processAction portion of the request, will preserve it on the session, and will 42 * read it on the first render following the action. This allows attributes to be reliably exchanged from action to render 43 * without having to worry about subsequent renders inheriting the same state. This is useful for storing things like 44 * "ppr" data and such when a subsequent render may be expected to render the entire page. 45 * <p/> 46 * In a servlet environment, this class is the same as the request scope. 47 */ 48 @ExcludeFromManagedRequestScope 49 public class RequestStateMap extends HashMap<String, Object> 50 { 51 52 /** 53 * Returns an instance of the RequestStateMap. If one is not already present on the request, it will create a new one. 54 * This version of the getInstance DOES NOT attempt to restore the map from the session because such an action is only 55 * valid in a PortletEnvironment which should not have a ServletRequest. This method is provided for the convenience 56 * of getting access to the class from the filter. 57 * 58 * @param req a servletRequest 59 * @return the RequestStateMap for this servletRequest 60 */ 61 static public RequestStateMap getInstance(ServletRequest req) 62 { 63 RequestStateMap map = (RequestStateMap)req.getAttribute(_STATE_MAP); 64 65 if(map == null) 66 { 67 map = new RequestStateMap(); 68 req.setAttribute(_STATE_MAP, map); 69 } 70 71 return map; 72 } 73 74 /** 75 * Returns an instance of the RequestStateMap. If the RequestStateMap is not present, this class will attempt to find 76 * a version that was saved from {@link #saveState(ExternalContext)}. If no state was saved, then it will create a new 77 * map and return it. 78 * <p/> 79 * Please keep in mind that regardless of whether a map was already present on the request or not, executing this method 80 * will ALWAYS clear any state which might have been saved to the session. This is what enforces the "single restore" 81 * type functionality. 82 * 83 * @param ec an ExternalContext 84 * @return the RequestStateMap for this ExternalContext 85 */ 86 static public RequestStateMap getInstance(ExternalContext ec) 87 { 88 Map<String, Object> reqMap = ec.getRequestMap(); 89 RequestStateMap map = (RequestStateMap)reqMap.get(_STATE_MAP); 90 91 //For now, always check this on a render so it can be removed from the session. 92 //This can be optimized to only save the state when request attributes are NOT preserved 93 if(!ExternalContextUtils.isRequestFromClient(ec)) 94 { 95 String uuid = ec.getRequestParameterMap().get(_STATE_MAP); 96 if(uuid!= null) 97 { 98 RequestStateMap myMap= (RequestStateMap)ec.getSessionMap().remove(_STATE_MAP+"."+uuid); 99 if(map == null) 100 { 101 map = myMap; 102 reqMap.put(_STATE_MAP, map); 103 } 104 else 105 { 106 //TODO: put optimization code here 107 } 108 } 109 } 110 111 if(map == null) 112 { 113 map = new RequestStateMap(); 114 reqMap.put(_STATE_MAP, map); 115 } 116 117 return map; 118 } 119 120 private RequestStateMap(){}; 121 122 /** 123 * Saves the current state to the session. Running this is only manditory if you need to preserve a request across 124 * the natural request boundry in a portlet environment. Executing this function outside of a portlet environment will 125 * not alter operation of his class. 126 * 127 * @param ec the ExternalContext to save the stateMap to. 128 */ 129 public void saveState(ExternalContext ec) 130 { 131 RequestType type = ExternalContextUtils.getRequestType(ec); 132 if(type.isPortlet() && !type.isResponseWritable()) 133 { 134 try 135 { 136 //TODO: use reflection here but it can be replaced.. 137 Object actionResp = ec.getResponse(); 138 Method m = actionResp.getClass().getMethod("setRenderParameter", String.class, String.class); 139 String uuid = UUID.randomUUID().toString(); 140 141 ec.getSessionMap().put(_STATE_MAP+"."+uuid, this); 142 m.invoke(actionResp, _STATE_MAP, uuid); 143 } 144 catch(Throwable t) 145 { 146 //TODO: Log exception 147 t.printStackTrace(); 148 } 149 } 150 } 151 152 private static final String _STATE_MAP = RequestStateMap.class.getName(); 153 @SuppressWarnings("compatibility:-6292931291923989771") 154 private static final long serialVersionUID = 1L; 155 }