@@ -67,6 +67,7 @@ static SessionPersistPolicy fromName(String name) {
6767
6868 protected RedisSessionHandlerValve handlerValve ;
6969 protected ThreadLocal <RedisSession > currentSession = new ThreadLocal <>();
70+ protected ThreadLocal <SessionSerializationMetadata > currentSessionSerializationMetadata = new ThreadLocal <>();
7071 protected ThreadLocal <String > currentSessionId = new ThreadLocal <>();
7172 protected ThreadLocal <Boolean > currentSessionIsPersisted = new ThreadLocal <>();
7273 protected Serializer serializer ;
@@ -380,6 +381,7 @@ This ensures that the save(session) at the end of the request
380381 currentSession .set (session );
381382 currentSessionId .set (sessionId );
382383 currentSessionIsPersisted .set (false );
384+ currentSessionSerializationMetadata .set (new SessionSerializationMetadata ());
383385
384386 if (null != session ) {
385387 try {
@@ -417,27 +419,32 @@ public void add(Session session) {
417419
418420 @ Override
419421 public Session findSession (String id ) throws IOException {
420- RedisSession session ;
422+ RedisSession session = null ;
421423
422- if (id == null ) {
423- session = null ;
424+ if (null == id ) {
424425 currentSessionIsPersisted .set (false );
426+ currentSession .set (null );
427+ currentSessionSerializationMetadata .set (null );
428+ currentSessionId .set (null );
425429 } else if (id .equals (currentSessionId .get ())) {
426430 session = currentSession .get ();
427431 } else {
428- session = loadSessionFromRedis (id );
429-
430- if (session != null ) {
432+ byte [] data = loadSessionDataFromRedis (id );
433+ if (data != null ) {
434+ DeserializedSessionContainer container = sessionFromSerializedData (id , data );
435+ session = container .session ;
436+ currentSession .set (session );
437+ currentSessionSerializationMetadata .set (container .metadata );
431438 currentSessionIsPersisted .set (true );
439+ currentSessionId .set (id );
432440 } else {
433441 currentSessionIsPersisted .set (false );
434- id = null ;
442+ currentSession .set (null );
443+ currentSessionSerializationMetadata .set (null );
444+ currentSessionId .set (null );
435445 }
436446 }
437447
438- currentSession .set (session );
439- currentSessionId .set (id );
440-
441448 return session ;
442449 }
443450
@@ -485,9 +492,7 @@ public String[] keys() throws IOException {
485492 }
486493 }
487494
488- public RedisSession loadSessionFromRedis (String id ) throws IOException {
489- RedisSession session ;
490-
495+ public byte [] loadSessionDataFromRedis (String id ) throws IOException {
491496 Jedis jedis = null ;
492497 Boolean error = true ;
493498
@@ -500,41 +505,54 @@ public RedisSession loadSessionFromRedis(String id) throws IOException {
500505
501506 if (data == null ) {
502507 log .trace ("Session " + id + " not found in Redis" );
503- session = null ;
504- } else {
505- log .trace ("Deserializing session " + id + " from Redis" );
506- session = (RedisSession )createEmptySession ();
507- serializer .deserializeInto (data , session );
508- session .setId (id );
509- session .setNew (false );
510- session .setMaxInactiveInterval (getMaxInactiveInterval () * 1000 );
511- session .access ();
512- session .setValid (true );
513- session .resetDirtyTracking ();
514-
515- if (log .isTraceEnabled ()) {
516- log .trace ("Session Contents [" + id + "]:" );
517- Enumeration en = session .getAttributeNames ();
518- while (en .hasMoreElements ()) {
519- log .trace (" " + en .nextElement ());
520- }
521- }
522508 }
523509
524- return session ;
525- } catch (IOException e ) {
526- log .fatal (e .getMessage ());
527- throw e ;
528- } catch (ClassNotFoundException ex ) {
529- log .fatal ("Unable to deserialize into session" , ex );
530- throw new IOException ("Unable to deserialize into session" , ex );
510+ return data ;
531511 } finally {
532512 if (jedis != null ) {
533513 returnConnection (jedis , error );
534514 }
535515 }
536516 }
537517
518+ public DeserializedSessionContainer sessionFromSerializedData (String id , byte [] data ) throws IOException {
519+ log .trace ("Deserializing session " + id + " from Redis" );
520+
521+ if (Arrays .equals (NULL_SESSION , data )) {
522+ log .error ("Encountered serialized session " + id + " with data equal to NULL_SESSION. This is a bug." );
523+ throw new IOException ("Serialized session data was equal to NULL_SESSION" );
524+ }
525+
526+ RedisSession session = null ;
527+ SessionSerializationMetadata metadata = new SessionSerializationMetadata ();
528+
529+ try {
530+ session = (RedisSession )createEmptySession ();
531+
532+ serializer .deserializeInto (data , session , metadata );
533+
534+ session .setId (id );
535+ session .setNew (false );
536+ session .setMaxInactiveInterval (getMaxInactiveInterval () * 1000 );
537+ session .access ();
538+ session .setValid (true );
539+ session .resetDirtyTracking ();
540+
541+ if (log .isTraceEnabled ()) {
542+ log .trace ("Session Contents [" + id + "]:" );
543+ Enumeration en = session .getAttributeNames ();
544+ while (en .hasMoreElements ()) {
545+ log .trace (" " + en .nextElement ());
546+ }
547+ }
548+ } catch (ClassNotFoundException ex ) {
549+ log .fatal ("Unable to deserialize into session" , ex );
550+ throw new IOException ("Unable to deserialize into session" , ex );
551+ }
552+
553+ return new DeserializedSessionContainer (session , metadata );
554+ }
555+
538556 public void save (Session session ) throws IOException {
539557 save (session , false );
540558 }
@@ -571,17 +589,49 @@ protected boolean saveInternal(Jedis jedis, Session session, boolean forceSave)
571589 }
572590 }
573591
574- Boolean sessionIsDirty = redisSession .isDirty ();
575-
576- redisSession .resetDirtyTracking ();
577592 byte [] binaryId = redisSession .getId ().getBytes ();
578593
579- Boolean isCurrentSessionPersisted = this .currentSessionIsPersisted .get ();
580- if (forceSave || sessionIsDirty || (isCurrentSessionPersisted == null || !isCurrentSessionPersisted )) {
581- jedis .set (binaryId , serializer .serializeFrom (redisSession ));
582- }
594+ Boolean isCurrentSessionPersisted ;
595+ SessionSerializationMetadata sessionSerializationMetadata = currentSessionSerializationMetadata .get ();
596+ byte [] originalSessionAttributesHash = sessionSerializationMetadata .getSessionAttributesHash ();
597+ byte [] sessionAttributesHash = null ;
598+ if (
599+ forceSave
600+ || redisSession .isDirty ()
601+ || null == (isCurrentSessionPersisted = this .currentSessionIsPersisted .get ())
602+ || !isCurrentSessionPersisted
603+ || !Arrays .equals (originalSessionAttributesHash , (sessionAttributesHash = serializer .attributesHashFrom (redisSession )))
604+ ) {
605+
606+ log .trace ("Save was determined to be necessary" );
607+
608+ /*if (forceSave) {
609+ log.info("save was necessary: forceSave=true");
610+ } else if (redisSession.isDirty()) {
611+ log.info("save was necessary: isDirty()=true");
612+ } else if (null == (isCurrentSessionPersisted = this.currentSessionIsPersisted.get())) {
613+ log.info("save was necessary: isCurrentSessionPersisted=null");
614+ } else if (!isCurrentSessionPersisted) {
615+ log.info("save was necessary: isCurrentSessionPersisted=false");
616+ } else if (!Arrays.equals(originalSessionAttributesHash, (sessionAttributesHash = serializer.attributesHashFrom(redisSession)))) {
617+ log.info("save was necessary: sessionsAttributeHashChanged: orig=" + new String(Base64.encodeBase64(originalSessionAttributesHash)) + " new=" + new String(Base64.encodeBase64(sessionAttributesHash)));
618+ }*/
619+
620+ if (null == sessionAttributesHash ) {
621+ sessionAttributesHash = serializer .attributesHashFrom (redisSession );
622+ }
583623
584- currentSessionIsPersisted .set (true );
624+ SessionSerializationMetadata updatedSerializationMetadata = new SessionSerializationMetadata ();
625+ updatedSerializationMetadata .setSessionAttributesHash (sessionAttributesHash );
626+
627+ jedis .set (binaryId , serializer .serializeFrom (redisSession , updatedSerializationMetadata ));
628+
629+ redisSession .resetDirtyTracking ();
630+ currentSessionSerializationMetadata .set (updatedSerializationMetadata );
631+ currentSessionIsPersisted .set (true );
632+ } else {
633+ log .trace ("Save was determined to be unnecessary" );
634+ }
585635
586636 log .trace ("Setting expire timeout on session [" + redisSession .getId () + "] to " + getMaxInactiveInterval ());
587637 jedis .expire (binaryId , getMaxInactiveInterval ());
@@ -834,3 +884,12 @@ public void setJmxNamePrefix(String jmxNamePrefix) {
834884 this .connectionPoolConfig .setJmxNamePrefix (jmxNamePrefix );
835885 }
836886}
887+
888+ class DeserializedSessionContainer {
889+ public final RedisSession session ;
890+ public final SessionSerializationMetadata metadata ;
891+ public DeserializedSessionContainer (RedisSession session , SessionSerializationMetadata metadata ) {
892+ this .session = session ;
893+ this .metadata = metadata ;
894+ }
895+ }
0 commit comments