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.application.viewstate;
20  
21  import javax.faces.context.ExternalContext;
22  import javax.faces.context.FacesContext;
23  
24  import org.apache.myfaces.application.StateCache;
25  import org.apache.myfaces.application.viewstate.token.ClientSideStateTokenProcessor;
26  import org.apache.myfaces.application.viewstate.token.StateTokenProcessor;
27  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
28  import org.apache.myfaces.shared.util.WebConfigParamUtils;
29  
30  class ClientSideStateCacheImpl extends StateCache<Object, Object>
31  {
32      
33      /**
34       * Define the time in minutes where the view state is valid when
35       * client side state saving is used. By default it is set to 0
36       * (infinite).
37       */
38      @JSFWebConfigParam(since="2.1.9, 2.0.15", defaultValue="0", group="state")
39      public static final String INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT = 
40              "org.apache.myfaces.CLIENT_VIEW_STATE_TIMEOUT";
41      public static final Long INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT_DEFAULT = 0L;
42      
43      private static final int STATE_PARAM = 0;
44      private static final int VIEWID_PARAM = 1;
45      private static final int TIMESTAMP_PARAM = 2;
46      
47      private static final Object[] EMPTY_STATES = new Object[]{null, null};
48      
49      private Long _clientViewStateTimeout;
50      
51      private CsrfSessionTokenFactory csrfSessionTokenFactory;
52      private StateTokenProcessor stateTokenProcessor;
53      
54      public ClientSideStateCacheImpl()
55      {
56          FacesContext facesContext = FacesContext.getCurrentInstance();
57          
58          String csrfRandomMode = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
59                  RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM, 
60                  RANDOM_KEY_IN_CSRF_SESSION_TOKEN_PARAM_DEFAULT);
61          if (RANDOM_KEY_IN_CSRF_SESSION_TOKEN_SECURE_RANDOM.equals(csrfRandomMode))
62          {
63              csrfSessionTokenFactory = new SecureRandomCsrfSessionTokenFactory(facesContext);
64          }
65          else
66          {
67              csrfSessionTokenFactory = new RandomCsrfSessionTokenFactory(facesContext);
68          }
69          
70          stateTokenProcessor = new ClientSideStateTokenProcessor();
71      }
72  
73      @Override
74      public Object saveSerializedView(FacesContext facesContext,
75              Object serializedView)
76      {
77          // The state in this case the state is saved on the token sent to
78          // the client (isWriteStateAfterRenderViewRequired() is set to true).
79          // No additional operation is required here.
80          return encodeSerializedState(facesContext, serializedView);
81      }
82  
83      @Override
84      public Object restoreSerializedView(FacesContext facesContext,
85              String viewId, Object viewState)
86      {
87          Object[] state = (Object[]) viewState;
88          long clientViewStateTimeout = getClientViewStateTimeout(facesContext.getExternalContext());
89          
90          if (clientViewStateTimeout > 0L)
91          {
92              Long timeStamp = (Long) state[TIMESTAMP_PARAM];
93              if (timeStamp == null)
94              {
95                  //If no timestamp, state is invalid.
96                  return null;
97              }
98              long passedTime = (System.currentTimeMillis() - timeStamp.longValue()) / 60000;
99              
100             if (passedTime > clientViewStateTimeout)
101             {
102                 //expire
103                 return null;
104             }
105         }
106         
107         String restoredViewId = (String) state[VIEWID_PARAM];
108         
109         if (viewId != null && !viewId.equals(restoredViewId))
110         {
111             //invalid viewId, expire
112             return null;
113         }
114         
115         //return the state
116         if (state[STATE_PARAM] == null)
117         {
118             return EMPTY_STATES;
119         }
120         else
121         {
122             Object serializedView = state[STATE_PARAM];
123             if (serializedView instanceof Object[] &&
124             ((Object[])serializedView).length == 2 &&
125             ((Object[])serializedView)[0] == null &&
126             ((Object[])serializedView)[1] == null)
127             {
128                 // Remember inside the state null is stored as an empty array.
129                 return null;
130             }
131             
132             return state[STATE_PARAM];
133         }
134     }
135 
136     @Override
137     public Object encodeSerializedState(FacesContext facesContext,
138             Object serializedView)
139     {
140         Object[] state = null;
141         
142         if (getClientViewStateTimeout(facesContext.getExternalContext()).longValue() > 0L)
143         {
144             state = new Object[3];
145             state[TIMESTAMP_PARAM] = System.currentTimeMillis();
146         }
147         else
148         {
149             state = new Object[2];
150         }
151         
152         if (serializedView == null)
153         {
154             state[STATE_PARAM] = EMPTY_STATES;
155         }
156         else if (serializedView instanceof Object[] &&
157             ((Object[])serializedView).length == 2 &&
158             ((Object[])serializedView)[0] == null &&
159             ((Object[])serializedView)[1] == null)
160         {
161             // The generated state can be considered zero, set it as null
162             // into the map.
163             state[STATE_PARAM] = null;
164         }
165         else
166         {
167             state[STATE_PARAM] = serializedView;
168         }
169 
170         state[VIEWID_PARAM] = facesContext.getViewRoot().getViewId();
171         
172         return state;
173     }
174 
175     @Override
176     public boolean isWriteStateAfterRenderViewRequired(FacesContext facesContext)
177     {
178         return true;
179     }
180 
181     /**
182      * @return the _clientViewStateTimeout
183      */
184     protected Long getClientViewStateTimeout(ExternalContext context)
185     {
186         if (_clientViewStateTimeout == null)
187         {
188             _clientViewStateTimeout = WebConfigParamUtils.getLongInitParameter(
189                     context, INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT,
190                     INIT_PARAM_CLIENT_VIEW_STATE_TIMEOUT_DEFAULT);
191             if (_clientViewStateTimeout.longValue() < 0L)
192             {
193                 _clientViewStateTimeout = 0L;
194             }
195         }
196         return _clientViewStateTimeout;
197     }
198 
199     @Override
200     public String createCryptographicallyStrongTokenFromSession(FacesContext context)
201     {
202         return csrfSessionTokenFactory.createCryptographicallyStrongTokenFromSession(context);
203     }
204     
205     @Override
206     public StateTokenProcessor getStateTokenProcessor(FacesContext context)
207     {
208         return stateTokenProcessor;
209     }
210 }