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