View Javadoc

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  }