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.view.facelets;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.io.OutputStream;
28  import java.io.Serializable;
29  import java.security.AccessController;
30  import java.security.PrivilegedActionException;
31  import java.security.PrivilegedExceptionAction;
32  import java.util.ArrayList;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.logging.Level;
37  import java.util.logging.Logger;
38  import java.util.zip.GZIPInputStream;
39  import java.util.zip.GZIPOutputStream;
40  
41  import javax.faces.context.ExternalContext;
42  import javax.faces.context.FacesContext;
43  
44  import org.apache.commons.collections.map.AbstractReferenceMap;
45  import org.apache.commons.collections.map.ReferenceMap;
46  import org.apache.myfaces.shared.renderkit.RendererUtils;
47  import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
48  
49  /**
50   * This helper class contains methods used by DefaultFaceletsStateManagementStrategy that comes
51   * from JspStateManagerImpl, but are used by our default StateManagementStrategy
52   *  
53   * @author Leonardo Uribe (latest modification by $Author: bommel $)
54   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
55   * @since 2.0
56   * @deprecated replaced by org.apache.myfaces.application.StateCache
57   *
58   */
59  @Deprecated
60  class DefaultFaceletsStateManagementHelper
61  {
62      //private static final Log log = LogFactory.getLog(DefaultFaceletsStateManagementHelper.class);
63      private static final Logger log = Logger.getLogger(DefaultFaceletsStateManagementHelper.class.getName());    
64      
65      private static final String SERIALIZED_VIEW_SESSION_ATTR= 
66          DefaultFaceletsStateManagementHelper.class.getName() + ".SERIALIZED_VIEW";
67      
68      public static final String SERIALIZED_VIEW_REQUEST_ATTR = 
69          DefaultFaceletsStateManagementHelper.class.getName() + ".SERIALIZED_VIEW";
70      
71      private static final String RESTORED_SERIALIZED_VIEW_REQUEST_ATTR = 
72          DefaultFaceletsStateManagementHelper.class.getName() + ".RESTORED_SERIALIZED_VIEW";
73  
74      /**
75       * Only applicable if state saving method is "server" (= default).
76       * Defines the amount (default = 20) of the latest views are stored in session.
77       */
78      private static final String NUMBER_OF_VIEWS_IN_SESSION_PARAM = "org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION";
79  
80      /**
81       * Default value for <code>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</code> context parameter.
82       */
83      private static final int DEFAULT_NUMBER_OF_VIEWS_IN_SESSION = 20;
84  
85      /**
86       * Only applicable if state saving method is "server" (= default).
87       * If <code>true</code> (default) the state will be serialized to a byte stream before it is written to the session.
88       * If <code>false</code> the state will not be serialized to a byte stream.
89       */
90      private static final String SERIALIZE_STATE_IN_SESSION_PARAM = "org.apache.myfaces.SERIALIZE_STATE_IN_SESSION";
91  
92      /**
93       * Only applicable if state saving method is "server" (= default) and if <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> is <code>true</code> (= default).
94       * If <code>true</code> (default) the serialized state will be compressed before it is written to the session.
95       * If <code>false</code> the state will not be compressed.
96       */
97      private static final String COMPRESS_SERVER_STATE_PARAM = "org.apache.myfaces.COMPRESS_STATE_IN_SESSION";
98  
99      /**
100      * Default value for <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
101      */
102     private static final boolean DEFAULT_COMPRESS_SERVER_STATE_PARAM = true;
103 
104     /**
105      * Default value for <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
106      */
107     private static final boolean DEFAULT_SERIALIZE_STATE_IN_SESSION = true;
108 
109     /**
110      * Define the way of handle old view references(views removed from session), making possible to
111      * store it in a cache, so the state manager first try to get the view from the session. If is it
112      * not found and soft or weak ReferenceMap is used, it try to get from it.
113      * <p>
114      * Only applicable if state saving method is "server" (= default).
115      * </p>
116      * <p>
117      * The gc is responsible for remove the views, according to the rules used for soft, weak or phantom
118      * references. If a key in soft and weak mode is garbage collected, its values are purged.
119      * </p>
120      * <p>
121      * By default no cache is used, so views removed from session became phantom references.
122      * </p>
123      * <ul> 
124      * <li> off, no: default, no cache is used</li> 
125      * <li> hard-soft: use an ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT)</li>
126      * <li> soft: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true) </li>
127      * <li> soft-weak: use an ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true) </li>
128      * <li> weak: use an ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true) </li>
129      * </ul>
130      * 
131      */
132     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE = "org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE";
133     
134     /**
135      * This option uses an hard-soft ReferenceMap, but it could cause a 
136      * memory leak, because the keys are not removed by any method
137      * (MYFACES-1660). So use with caution.
138      */
139     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT = "hard-soft";
140     
141     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT = "soft";
142     
143     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK = "soft-weak";
144     
145     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK = "weak";
146     
147     private static final String CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF = "off";
148 
149     private static final int UNCOMPRESSED_FLAG = 0;
150     private static final int COMPRESSED_FLAG = 1;
151 
152     private static final int JSF_SEQUENCE_INDEX = 0;
153     
154     protected Integer getServerStateId(Object[] state)
155     {
156         if (state != null)
157         {
158             Object serverStateId = state[JSF_SEQUENCE_INDEX];
159             if (serverStateId != null)
160             {
161                 return Integer.valueOf((String) serverStateId, Character.MAX_RADIX);
162             }
163         }
164         return null;
165     }
166 
167     protected void saveSerializedViewInServletSession(FacesContext context,
168                                                       Object serializedView)
169     {
170         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
171         SerializedViewCollection viewCollection = (SerializedViewCollection) sessionMap
172                 .get(SERIALIZED_VIEW_SESSION_ATTR);
173         if (viewCollection == null)
174         {
175             viewCollection = new SerializedViewCollection();
176             sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
177         }
178         viewCollection.add(context, serializeView(context, serializedView));
179         // replace the value to notify the container about the change
180         sessionMap.put(SERIALIZED_VIEW_SESSION_ATTR, viewCollection);
181     }
182 
183     protected Object getSerializedViewFromServletSession(FacesContext context, String viewId, Integer sequence)
184     {
185         ExternalContext externalContext = context.getExternalContext();
186         Map<String, Object> requestMap = externalContext.getRequestMap();
187         Object serializedView = null;
188         if (requestMap.containsKey(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR))
189         {
190             serializedView = requestMap.get(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR);
191         }
192         else
193         {
194             SerializedViewCollection viewCollection = (SerializedViewCollection) externalContext
195                     .getSessionMap().get(SERIALIZED_VIEW_SESSION_ATTR);
196             if (viewCollection != null)
197             {
198                 /*
199                 String sequenceStr = externalContext.getRequestParameterMap().get(
200                        RendererUtils.SEQUENCE_PARAM);
201                 Integer sequence = null;
202                 if (sequenceStr == null)
203                 {
204                     // use latest sequence
205                     Map map = externalContext.getSessionMap();
206                     sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
207                 }
208                 else
209                 {
210                     sequence = new Integer(sequenceStr);
211                 }
212                 */
213                 if (sequence != null)
214                 {
215                     Object state = viewCollection.get(sequence, viewId);
216                     if (state != null)
217                     {
218                         serializedView = deserializeView(state);
219                     }
220                 }
221             }
222             requestMap.put(RESTORED_SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
223             nextViewSequence(context);
224         }
225         return serializedView;
226     }
227 
228     protected int getNextViewSequence(FacesContext context)
229     {
230         ExternalContext externalContext = context.getExternalContext();
231 
232         if (!externalContext.getRequestMap().containsKey(RendererUtils.SEQUENCE_PARAM))
233         {
234             nextViewSequence(context);
235         }
236 
237         Integer sequence = (Integer) externalContext.getRequestMap().get(RendererUtils.SEQUENCE_PARAM);
238         return sequence.intValue();
239     }
240 
241     protected void nextViewSequence(FacesContext facescontext)
242     {
243         ExternalContext externalContext = facescontext.getExternalContext();
244         Object sessionObj = externalContext.getSession(true);
245         synchronized(sessionObj) // synchronized to increase sequence if multiple requests
246                                  // are handled at the same time for the session
247         {
248             Map<String, Object> map = externalContext.getSessionMap();
249             Integer sequence = (Integer) map.get(RendererUtils.SEQUENCE_PARAM);
250             if(sequence == null || sequence.intValue() == Integer.MAX_VALUE)
251             {
252                 sequence = Integer.valueOf(1);
253             }
254             else
255             {
256                 sequence = Integer.valueOf(sequence.intValue() + 1);
257             }
258             map.put(RendererUtils.SEQUENCE_PARAM, sequence);
259             externalContext.getRequestMap().put(RendererUtils.SEQUENCE_PARAM, sequence);
260         }
261     }
262 
263     protected Object serializeView(FacesContext context, Object serializedView)
264     {
265         if (log.isLoggable(Level.FINEST)) log.finest("Entering serializeView");
266 
267         if(isSerializeStateInSession(context))
268         {
269             if (log.isLoggable(Level.FINEST)) log.finest("Processing serializeView - serialize state in session");
270 
271             ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
272             try
273             {
274                 OutputStream os = baos;
275                 if(isCompressStateInSession(context))
276                 {
277                     if (log.isLoggable(Level.FINEST)) log.finest("Processing serializeView - serialize compressed");
278 
279                     os.write(COMPRESSED_FLAG);
280                     os = new GZIPOutputStream(os, 1024);
281                 }
282                 else
283                 {
284                     if (log.isLoggable(Level.FINEST)) log.finest("Processing serializeView - serialize uncompressed");
285 
286                     os.write(UNCOMPRESSED_FLAG);
287                 }
288 
289                 Object[] stateArray = (Object[]) serializedView;
290 
291                 ObjectOutputStream out = new ObjectOutputStream(os);
292                 out.writeObject(stateArray[0]);
293                 out.writeObject(stateArray[1]);
294                 out.close();
295                 baos.close();
296 
297                 if (log.isLoggable(Level.FINEST)) log.finest("Exiting serializeView - serialized. Bytes : "+baos.size());
298                 return baos.toByteArray();
299             }
300             catch (IOException e)
301             {
302                 log.log(Level.SEVERE, "Exiting serializeView - Could not serialize state: " + e.getMessage(), e);
303                 return null;
304             }
305         }
306 
307 
308         if (log.isLoggable(Level.FINEST))
309             log.finest("Exiting serializeView - do not serialize state in session.");
310 
311         return serializedView;
312 
313     }
314 
315     /**
316      * Reads the value of the <code>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</code> context parameter.
317      * @see SERIALIZE_STATE_IN_SESSION_PARAM
318      * @param context <code>FacesContext</code> for the request we are processing.
319      * @return boolean true, if the server state should be serialized in the session
320      */
321     protected boolean isSerializeStateInSession(FacesContext context)
322     {
323         String value = context.getExternalContext().getInitParameter(
324                 SERIALIZE_STATE_IN_SESSION_PARAM);
325         boolean serialize = DEFAULT_SERIALIZE_STATE_IN_SESSION;
326         if (value != null)
327         {
328            serialize = Boolean.valueOf(value);
329         }
330         return serialize;
331     }
332 
333     /**
334      * Reads the value of the <code>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</code> context parameter.
335      * @see COMPRESS_SERVER_STATE_PARAM
336      * @param context <code>FacesContext</code> for the request we are processing.
337      * @return boolean true, if the server state steam should be compressed
338      */
339     protected boolean isCompressStateInSession(FacesContext context)
340     {
341         String value = context.getExternalContext().getInitParameter(
342                 COMPRESS_SERVER_STATE_PARAM);
343         boolean compress = DEFAULT_COMPRESS_SERVER_STATE_PARAM;
344         if (value != null)
345         {
346            compress = Boolean.valueOf(value);
347         }
348         return compress;
349     }
350 
351     protected Object deserializeView(Object state)
352     {
353         if (log.isLoggable(Level.FINEST)) log.finest("Entering deserializeView");
354 
355         if(state instanceof byte[])
356         {
357             if (log.isLoggable(Level.FINEST)) log.finest("Processing deserializeView - deserializing serialized state. Bytes : "+((byte[]) state).length);
358 
359             try
360             {
361                 ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
362                 InputStream is = bais;
363                 if(is.read() == COMPRESSED_FLAG)
364                 {
365                     is = new GZIPInputStream(is);
366                 }
367                 ObjectInputStream ois = null;
368                 try
369                 {
370                     final ObjectInputStream in = new MyFacesObjectInputStream(is);
371                     ois = in;
372                     Object object = null;
373                     if (System.getSecurityManager() != null) 
374                     {
375                         object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object []>() 
376                         {
377                             public Object[] run() throws PrivilegedActionException, IOException, ClassNotFoundException
378                             {
379                                 return new Object[] {in.readObject(), in.readObject()};                                    
380                             }
381                         });
382                     }
383                     else
384                     {
385                         object = new Object[] {in.readObject(), in.readObject()};
386                     }
387                     return object;
388                 }
389                 finally
390                 {
391                     if (ois != null)
392                     {
393                         ois.close();
394                         ois = null;
395                     }
396                 }
397             }
398             catch (PrivilegedActionException e) 
399             {
400                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
401                 return null;
402             }
403             catch (IOException e)
404             {
405                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
406                 return null;
407             }
408             catch (ClassNotFoundException e)
409             {
410                 log.log(Level.SEVERE, "Exiting deserializeView - Could not deserialize state: " + e.getMessage(), e);
411                 return null;
412             }
413         }
414         else if (state instanceof Object[])
415         {
416             if (log.isLoggable(Level.FINEST)) log.finest("Exiting deserializeView - state not serialized.");
417 
418             return state;
419         }
420         else if(state == null)
421         {
422             log.severe("Exiting deserializeView - this method should not be called with a null-state.");
423             return null;
424         }
425         else
426         {
427             log.severe("Exiting deserializeView - this method should not be called with a state of type : "+state.getClass());
428             return null;
429         }
430     }
431     
432     protected static class SerializedViewCollection implements Serializable
433     {
434         private static final long serialVersionUID = -3734849062185115847L;
435 
436         private final List<Object> _keys = new ArrayList<Object>(DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
437         private final Map<Object, Object> _serializedViews = new HashMap<Object, Object>();
438 
439         // old views will be hold as soft references which will be removed by
440         // the garbage collector if free memory is low
441         private transient Map<Object, Object> _oldSerializedViews = null;
442 
443         public synchronized void add(FacesContext context, Object state)
444         {
445             Object key = new SerializedViewKey(context);
446             _serializedViews.put(key, state);
447 
448             while (_keys.remove(key));
449             _keys.add(key);
450 
451             int views = getNumberOfViewsInSession(context);
452             while (_keys.size() > views)
453             {
454                 key = _keys.remove(0);
455                 Object oldView = _serializedViews.remove(key);
456                 if (oldView != null && 
457                     !CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF.equals(getCacheOldViewsInSessionMode(context))) 
458                 {
459                     getOldSerializedViewsMap().put(key, oldView);
460                 }
461             }
462         }
463 
464         /**
465          * Reads the amount (default = 20) of views to be stored in session.
466          * @see NUMBER_OF_VIEWS_IN_SESSION_PARAM
467          * @param context FacesContext for the current request, we are processing
468          * @return Number vf views stored in the session
469          */
470         protected int getNumberOfViewsInSession(FacesContext context)
471         {
472             String value = context.getExternalContext().getInitParameter(
473                     NUMBER_OF_VIEWS_IN_SESSION_PARAM);
474             int views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
475             if (value != null)
476             {
477                 try
478                 {
479                     views = Integer.parseInt(value);
480                     if (views <= 0)
481                     {
482                         log.severe("Configured value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
483                                   + " is not valid, must be an value > 0, using default value ("
484                                   + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION);
485                         views = DEFAULT_NUMBER_OF_VIEWS_IN_SESSION;
486                     }
487                 }
488                 catch (Throwable e)
489                 {
490                     log.log(Level.SEVERE, "Error determining the value for " + NUMBER_OF_VIEWS_IN_SESSION_PARAM
491                               + ", expected an integer value > 0, using default value ("
492                               + DEFAULT_NUMBER_OF_VIEWS_IN_SESSION + "): " + e.getMessage(), e);
493                 }
494             }
495             return views;
496         }
497 
498         /**
499          * @return old serialized views map
500          */
501         @SuppressWarnings("unchecked")
502         protected Map<Object, Object> getOldSerializedViewsMap()
503         {
504             FacesContext context = FacesContext.getCurrentInstance();
505             if (_oldSerializedViews == null && context != null)
506             {
507                 String cacheMode = getCacheOldViewsInSessionMode(context); 
508                 if (CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK.equals(cacheMode))
509                 {
510                     _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK, true);
511                 }
512                 else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK.equals(cacheMode))
513                 {
514                     _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.WEAK, true);
515                 }
516                 else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT.equals(cacheMode))
517                 {
518                     _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true);
519                 }
520                 else if (CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT.equals(cacheMode))
521                 {
522                     _oldSerializedViews = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT);
523                 }
524             }
525             
526             return _oldSerializedViews;
527         }
528         
529         /**
530          * Reads the value of the <code>org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE</code> context parameter.
531          * 
532          * @since 1.2.5
533          * @param context
534          * @return constant indicating caching mode
535          * @see CACHE_OLD_VIEWS_IN_SESSION_MODE
536          */
537         protected String getCacheOldViewsInSessionMode(FacesContext context)
538         {
539             String value = context.getExternalContext().getInitParameter(
540                     CACHE_OLD_VIEWS_IN_SESSION_MODE);
541             if (value == null)
542             {
543                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
544             }
545             else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT))
546             {
547                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT;
548             }
549             else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK))
550             {
551                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_SOFT_WEAK;
552             }            
553             else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK))
554             {
555                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_WEAK;
556             }
557             else if (value.equalsIgnoreCase(CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT))
558             {
559                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_HARD_SOFT;
560             }
561             else
562             {
563                 return CACHE_OLD_VIEWS_IN_SESSION_MODE_OFF;
564             }
565         }
566         
567         public Object get(Integer sequence, String viewId)
568         {
569             Object key = new SerializedViewKey(viewId, sequence);
570             Object value = _serializedViews.get(key);
571             if (value == null)
572             {
573                 Map<Object,Object> oldSerializedViewMap = getOldSerializedViewsMap();
574                 if (oldSerializedViewMap != null)
575                 {
576                     value = oldSerializedViewMap.get(key);
577                 }
578             }
579             return value;
580         }
581     }
582 
583     protected static class SerializedViewKey implements Serializable
584     {
585         private static final long serialVersionUID = -1170697124386063642L;
586 
587         private final String _viewId;
588         private final Integer _sequenceId;
589 
590         public SerializedViewKey(String viewId, Integer sequence)
591         {
592             _sequenceId = sequence;
593             _viewId = viewId;
594         }
595 
596         public SerializedViewKey(FacesContext context)
597         {
598             _sequenceId = RendererUtils.getViewSequence(context);
599             _viewId = context.getViewRoot().getViewId();
600         }
601 
602         @Override
603         public int hashCode()
604         {
605             final int PRIME = 31;
606             int result = 1;
607             result = PRIME * result + ((_sequenceId == null) ? 0 : _sequenceId.hashCode());
608             result = PRIME * result + ((_viewId == null) ? 0 : _viewId.hashCode());
609             return result;
610         }
611 
612         @Override
613         public boolean equals(Object obj)
614         {
615             if (this == obj)
616                 return true;
617             if (obj == null)
618                 return false;
619             if (getClass() != obj.getClass())
620                 return false;
621             final SerializedViewKey other = (SerializedViewKey) obj;
622             if (_sequenceId == null)
623             {
624                 if (other._sequenceId != null)
625                     return false;
626             }
627             else if (!_sequenceId.equals(other._sequenceId))
628                 return false;
629             if (_viewId == null)
630             {
631                 if (other._viewId != null)
632                     return false;
633             }
634             else if (!_viewId.equals(other._viewId))
635                 return false;
636             return true;
637         }
638 
639     }
640 }