This is an automated email from the ASF dual-hosted git repository.

anovikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git


The following commit(s) were added to refs/heads/master by this push:
     new d6762c15 IGNITE-18302 ignite-spring-sessions: IgniteSession 
serialization drags its parent class. (#229)
d6762c15 is described below

commit d6762c1548e3bb14e15d8772731bc96837836c3d
Author: Andrey Novikov <[email protected]>
AuthorDate: Mon Sep 11 09:17:34 2023 +0700

    IGNITE-18302 ignite-spring-sessions: IgniteSession serialization drags its 
parent class. (#229)
---
 .../sessions/IgniteHttpSessionConfiguration.java   |   2 +-
 .../sessions/IgniteIndexedSessionRepository.java   | 364 ++++++++-------------
 .../ignite/spring/sessions/IgniteSession.java      | 270 +++++++++++++++
 .../spring/sessions/proxy/ClientSessionProxy.java  |   7 +-
 .../spring/sessions/proxy/IgniteSessionProxy.java  |   7 +-
 .../ignite/spring/sessions/proxy/SessionProxy.java |  40 ++-
 ...AbstractIgniteIndexedSessionRepositoryTest.java |   5 +-
 ...EmbeddedIgniteIndexedSessionRepositoryTest.java |   1 -
 .../IgniteClientIndexedSessionRepositoryTest.java  |   1 -
 .../IgniteHttpSessionConfigurationTest.java        |  13 +-
 .../IgniteIndexedSessionRepositoryTest.java        |  19 +-
 11 files changed, 454 insertions(+), 275 deletions(-)

diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfiguration.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfiguration.java
index f77a211e..61f1fa37 100644
--- 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfiguration.java
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfiguration.java
@@ -185,7 +185,7 @@ public class IgniteHttpSessionConfiguration extends 
SpringHttpSessionConfigurati
                 " delegate OTHER," +
                 " principal VARCHAR" +
                 ") WITH \"template=replicated,atomicity=atomic," +
-                
"value_type=org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository$IgniteSession,"
 +
+                "value_type=org.apache.ignite.spring.sessions.IgniteSession," +
                 "cache_name=" + sesMapName + "\""),
             new SqlFieldsQuery("CREATE INDEX IF NOT EXISTS 
ignitesession_principal_idx ON IgniteSession (principal);")
         );
diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepository.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepository.java
index 4ca6868d..25a3e315 100644
--- 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepository.java
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepository.java
@@ -18,14 +18,10 @@
 package org.apache.ignite.spring.sessions;
 
 import java.time.Duration;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 import javax.annotation.PreDestroy;
 import javax.cache.configuration.CacheEntryListenerConfiguration;
 import javax.cache.configuration.Factory;
@@ -57,6 +53,8 @@ import org.springframework.session.events.SessionDeletedEvent;
 import org.springframework.session.events.SessionExpiredEvent;
 import org.springframework.util.Assert;
 
+import static java.util.Collections.emptyMap;
+
 /**
  * A {@link org.springframework.session.SessionRepository} implementation that 
stores
  * sessions in Apache Ignite distributed {@link SessionProxy}.
@@ -86,45 +84,48 @@ import org.springframework.util.Assert;
  *
  */
 public class IgniteIndexedSessionRepository
-        implements 
FindByIndexNameSessionRepository<IgniteIndexedSessionRepository.IgniteSession>,
-        CacheEntryCreatedListener<String, 
IgniteIndexedSessionRepository.IgniteSession>,
-        CacheEntryRemovedListener<String, 
IgniteIndexedSessionRepository.IgniteSession>,
-        CacheEntryExpiredListener<String, 
IgniteIndexedSessionRepository.IgniteSession> {
+        implements FindByIndexNameSessionRepository<IgniteSession>,
+        CacheEntryCreatedListener<String, IgniteSession>,
+        CacheEntryRemovedListener<String, IgniteSession>,
+        CacheEntryExpiredListener<String, IgniteSession> {
     /**
      * The default name of map used by Spring Session to store sessions.
      */
     public static final String DEFAULT_SESSION_MAP_NAME = 
"spring:session:sessions";
 
-    /** */
-    private static final String SPRING_SECURITY_CONTEXT = 
"SPRING_SECURITY_CONTEXT";
+    /**
+     * Maximum of attempts for atomicity replace. If something wrong with 
IgniteSession, old value can never be equal to
+     * value from repository. In this case replace will never end the loop. If 
this value is exceeded, then plain
+     * {@link SessionProxy#replace(String, IgniteSession)} will be used.
+     */
+    private static final int MAX_UPDATE_ATTEMPT = 100;
 
     /** */
     private static final Log logger = 
LogFactory.getLog(IgniteIndexedSessionRepository.class);
 
     /** */
-    private ApplicationEventPublisher eventPublisher = (event) -> {
-    };
+    private ApplicationEventPublisher evtPublisher = (event) -> {};
 
     /**
      * If non-null, this value is used to override
      * {@link MapSession#setMaxInactiveInterval(Duration)}.
      */
-    private Integer defaultMaxInactiveInterval;
-
-    /** */
-    private IndexResolver<Session> indexResolver = new 
DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
+    private Integer dfltMaxInactiveInterval;
 
     /** */
     private FlushMode flushMode = FlushMode.ON_SAVE;
 
-    /** */
-    private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
+    /** The save mode. */
+    private SaveMode saveMode = SaveMode.ALWAYS;
 
-    /** */
+    /** The index resolver. */
+    private IndexResolver<Session> idxResolver = new 
DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
+
+    /** Sessions cache proxy. */
     private final SessionProxy sessions;
 
     /** */
-    private CacheEntryListenerConfiguration<String, IgniteSession> 
listenerConfiguration;
+    private final CacheEntryListenerConfiguration<String, IgniteSession> 
listenerConfiguration;
 
     /**
      * Create a new {@link IgniteIndexedSessionRepository} instance.
@@ -169,31 +170,32 @@ public class IgniteIndexedSessionRepository
      * Sets the {@link ApplicationEventPublisher} that is used to publish
      * {@link AbstractSessionEvent session events}. The default is to not 
publish session
      * events.
-     * @param applicationEventPublisher the {@link ApplicationEventPublisher} 
that is used
+     * @param applicationEvtPublisher the {@link ApplicationEventPublisher} 
that is used
      * to publish session events. Cannot be null.
      */
-    public void setApplicationEventPublisher(ApplicationEventPublisher 
applicationEventPublisher) {
-        Assert.notNull(applicationEventPublisher, "ApplicationEventPublisher 
cannot be null");
-        this.eventPublisher = applicationEventPublisher;
+    public void setApplicationEventPublisher(ApplicationEventPublisher 
applicationEvtPublisher) {
+        Assert.notNull(applicationEvtPublisher, "ApplicationEventPublisher 
cannot be null");
+        evtPublisher = applicationEvtPublisher;
     }
 
     /**
      * Set the maximum inactive interval in seconds between requests before 
newly created
-     * sessions will be invalidated. A negative time indicates that the 
session will never
-     * timeout. The default is 1800 (30 minutes).
-     * @param defaultMaxInactiveInterval the maximum inactive interval in 
seconds
+     * sessions will be invalidated. A negative time indicates that the 
session will never timeout.
+     * The default is 1800 (30 minutes).
+     * @param dfltMaxInactiveInterval the maximum inactive interval in seconds
      */
-    public void setDefaultMaxInactiveInterval(Integer 
defaultMaxInactiveInterval) {
-        this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
+    public void setDefaultMaxInactiveInterval(Integer dfltMaxInactiveInterval) 
{
+        this.dfltMaxInactiveInterval = dfltMaxInactiveInterval;
     }
 
     /**
      * Set the {@link IndexResolver} to use.
-     * @param indexResolver the index resolver
+     * @param idxResolver the index resolver
      */
-    public void setIndexResolver(IndexResolver<Session> indexResolver) {
-        Assert.notNull(indexResolver, "indexResolver cannot be null");
-        this.indexResolver = indexResolver;
+    public void setIndexResolver(IndexResolver<Session> idxResolver) {
+        Assert.notNull(idxResolver, "indexResolver cannot be null");
+        
+        this.idxResolver = idxResolver;
     }
 
     /**
@@ -217,45 +219,51 @@ public class IgniteIndexedSessionRepository
     /** {@inheritDoc} */
     @Override public IgniteSession createSession() {
         MapSession cached = new MapSession();
-        if (this.defaultMaxInactiveInterval != null)
-            
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
+        
+        if (this.dfltMaxInactiveInterval != null)
+            
cached.setMaxInactiveInterval(Duration.ofSeconds(this.dfltMaxInactiveInterval));
 
-        IgniteSession session = new IgniteSession(cached, true);
-        session.flushImmediateIfNecessary();
-        return session;
+        return new IgniteSession(cached, idxResolver, true, saveMode, 
this::flushImmediateIfNecessary);
     }
 
     /** {@inheritDoc} */
-    @Override public void save(IgniteSession session) {
-        if (session.isNew)
-            ttlSessions(session.getMaxInactiveInterval()).put(session.getId(), 
session);
-
-        else if (session.sessionIdChanged) {
-            this.sessions.remove(session.originalId);
-            session.originalId = session.getId();
-            ttlSessions(session.getMaxInactiveInterval()).put(session.getId(), 
session);
-        }
-        else if (session.hasChanges()) {
-            if (session.maxInactiveIntervalChanged)
-                
ttlSessions(session.getMaxInactiveInterval()).replace(session.getId(), session);
-            else
-                this.sessions.replace(session.getId(), session);
+    @Override public void save(IgniteSession ses) {
+        if (ses.isNew())
+            ttlSessions(ses.getMaxInactiveInterval()).put(ses.getId(), ses);
+        else {
+            String originalId = ses.getOriginalId();
+
+            if (!ses.getId().equals(originalId)) {
+                sessions.remove(originalId);
+
+                ses.resetOriginalId();
+                ttlSessions(ses.getMaxInactiveInterval()).put(ses.getId(), 
ses);
+            }
+            else if (ses.hasChanges()) {
+                if (saveMode == SaveMode.ALWAYS)
+                    
ttlSessions(ses.getMaxInactiveInterval()).replace(ses.getId(), ses);
+                else
+                    updatePartial(ses);
+            }
         }
-        session.clearChangeFlags();
+        
+        ses.clearChangeFlags();
     }
 
     /** {@inheritDoc} */
     @Override public IgniteSession findById(String id) {
-        IgniteSession saved = this.sessions.get(id);
+        IgniteSession saved = sessions.get(id);
+        
         if (saved == null)
             return null;
 
         if (saved.isExpired()) {
             deleteById(saved.getId());
+            
             return null;
         }
-        saved.isNew = false;
-        return saved;
+
+        return new IgniteSession(saved.getDelegate(), idxResolver, false, 
saveMode, this::flushImmediateIfNecessary);
     }
 
     /** {@inheritDoc} */
@@ -264,40 +272,36 @@ public class IgniteIndexedSessionRepository
     }
 
     /** {@inheritDoc} */
-    @Override public Map<String, IgniteSession> 
findByIndexNameAndIndexValue(String indexName, String indexValue) {
-        if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName))
-            return Collections.emptyMap();
+    @Override public Map<String, IgniteSession> 
findByIndexNameAndIndexValue(String idxName, String idxVal) {
+        if (!PRINCIPAL_NAME_INDEX_NAME.equals(idxName))
+            return emptyMap();
 
-        final QueryCursor<List<?>> cursor = this.sessions
-                .query(new SqlFieldsQuery("SELECT * FROM IgniteSession WHERE 
principal='" + indexValue + "'"));
+        QueryCursor<List<?>> cursor = sessions.query(
+            new SqlFieldsQuery("SELECT * FROM IgniteSession WHERE principal = 
?").setArgs(idxVal)
+        );
 
         if (cursor == null)
-            return Collections.emptyMap();
-
-        final List<List<?>> sessions = cursor.getAll();
-
-        Map<String, IgniteSession> sessionMap = new HashMap<>(sessions.size());
-
-        sessions.forEach((List<?> res) -> {
-            final MapSession session = (MapSession)res.get(1);
-            final IgniteSession value = new IgniteSession(session, false);
-            value.principal = (String)res.get(2);
-            sessionMap.put(session.getId(), value);
-        });
-
-        return sessionMap;
+            return emptyMap();
+
+        return cursor.getAll().stream()
+            .map(res -> (MapSession)res.get(1))
+            .collect(Collectors.toMap(
+                MapSession::getId,
+                ses -> new IgniteSession(ses, idxResolver, false, saveMode, 
this::flushImmediateIfNecessary)
+            ));
     }
 
     /** {@inheritDoc} */
     @Override public void onCreated(Iterable<CacheEntryEvent<? extends String, 
? extends IgniteSession>> events)
             throws CacheEntryListenerException {
         events.forEach((event) -> {
-            IgniteSession session = event.getValue();
-            if (session.getId().equals(session.getDelegate().getOriginalId())) 
{
+            IgniteSession ses = event.getValue();
+            
+            if (ses.getId().equals(ses.getDelegate().getOriginalId())) {
                 if (logger.isDebugEnabled())
-                    logger.debug("Session created with id: " + 
session.getId());
+                    logger.debug("Session created with id: " + ses.getId());
 
-                this.eventPublisher.publishEvent(new SessionCreatedEvent(this, 
session));
+                evtPublisher.publishEvent(new SessionCreatedEvent(this, ses));
             }
         });
     }
@@ -309,7 +313,7 @@ public class IgniteIndexedSessionRepository
             if (logger.isDebugEnabled())
                 logger.debug("Session expired with id: " + 
event.getOldValue().getId());
 
-            this.eventPublisher.publishEvent(new SessionExpiredEvent(this, 
event.getOldValue()));
+            evtPublisher.publishEvent(new SessionExpiredEvent(this, 
event.getOldValue()));
         });
     }
 
@@ -317,12 +321,13 @@ public class IgniteIndexedSessionRepository
     @Override public void onRemoved(Iterable<CacheEntryEvent<? extends String, 
? extends IgniteSession>> events)
             throws CacheEntryListenerException {
         events.forEach((event) -> {
-            IgniteSession session = event.getOldValue();
-            if (session != null) {
+            IgniteSession ses = event.getOldValue();
+            
+            if (ses != null) {
                 if (logger.isDebugEnabled())
-                    logger.debug("Session deleted with id: " + 
session.getId());
+                    logger.debug("Session deleted with id: " + ses.getId());
 
-                this.eventPublisher.publishEvent(new SessionDeletedEvent(this, 
session));
+                evtPublisher.publishEvent(new SessionDeletedEvent(this, ses));
             }
         });
     }
@@ -333,7 +338,7 @@ public class IgniteIndexedSessionRepository
      * @return cache with custom duration expiry policy.
      */
     private SessionProxy ttlSessions(Duration duration) {
-        return this.sessions.withExpiryPolicy(createPolicy(duration));
+        return sessions.withExpiryPolicy(createPolicy(duration));
     }
 
     /**
@@ -346,167 +351,54 @@ public class IgniteIndexedSessionRepository
     }
 
     /**
-     * A custom implementation of {@link Session} that uses a {@link 
MapSession} as the
-     * basis for its mapping. It keeps track if changes have been made since 
last save.
+     * Creates a new Session that is capable of being persisted by this 
SessionRepository.
+     * 
+     * @param ses Session.
      */
-    public class IgniteSession implements Session {
-        /** */
-        private final MapSession delegate;
-
-        /** */
-        private boolean isNew;
-
-        /** */
-        private boolean sessionIdChanged;
-
-        /** */
-        private boolean lastAccessedTimeChanged;
-
-        /** */
-        private boolean maxInactiveIntervalChanged;
-
-        /** */
-        private String originalId;
-
-        /** */
-        private Map<String, Object> delta = new HashMap<>();
-
-        /** */
-        private String principal;
-
-        /**
-         * @param cached Map session.
-         * @param isNew Is new flag.
-         */
-        IgniteSession(MapSession cached, boolean isNew) {
-            this.delegate = cached;
-            this.isNew = isNew;
-            this.originalId = cached.getId();
-            if (this.isNew || (IgniteIndexedSessionRepository.this.saveMode == 
SaveMode.ALWAYS))
-                getAttributeNames()
-                    .forEach((attributeName) -> this.delta.put(attributeName, 
cached.getAttribute(attributeName)));
-
-        }
-
-        /** {@inheritDoc} */
-        @Override public void setLastAccessedTime(Instant lastAccessedTime) {
-            this.delegate.setLastAccessedTime(lastAccessedTime);
-            this.lastAccessedTimeChanged = true;
-            flushImmediateIfNecessary();
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean isExpired() {
-            return this.delegate.isExpired();
-        }
+    private void flushImmediateIfNecessary(IgniteSession ses) {
+        if (flushMode == FlushMode.IMMEDIATE)
+            save(ses);
+    }
 
-        /** {@inheritDoc} */
-        @Override public Instant getCreationTime() {
-            return this.delegate.getCreationTime();
-        }
+    /**
+     * @param targetSes Target session.
+     * @param activeSes Active session.
+     */
+    private void copyChanges(IgniteSession targetSes, IgniteSession activeSes) 
{
+        if (activeSes.isLastAccessedTimeChanged())
+            targetSes.setLastAccessedTime(activeSes.getLastAccessedTime());
 
-        /** {@inheritDoc} */
-        @Override public String getId() {
-            return this.delegate.getId();
-        }
+        Map<String, Object> changes = activeSes.getAttributesChanges();
 
-        /** {@inheritDoc} */
-        @Override public String changeSessionId() {
-            String newSessionId = this.delegate.changeSessionId();
-            this.sessionIdChanged = true;
-            return newSessionId;
-        }
+        if (!changes.isEmpty())
+            changes.forEach(targetSes::setAttribute);
+    }
 
-        /** {@inheritDoc} */
-        @Override public Instant getLastAccessedTime() {
-            return this.delegate.getLastAccessedTime();
-        }
+    /**
+     * @param ses Session.
+     */
+    private void updatePartial(IgniteSession ses) {
+        IgniteSession oldSes, updatedSes;
+        int attempt = 0;
 
-        /** {@inheritDoc} */
-        @Override public void setMaxInactiveInterval(Duration interval) {
-            this.delegate.setMaxInactiveInterval(interval);
-            this.maxInactiveIntervalChanged = true;
-            flushImmediateIfNecessary();
-        }
+        do {
+            attempt++;
 
-        /** {@inheritDoc} */
-        @Override public Duration getMaxInactiveInterval() {
-            return this.delegate.getMaxInactiveInterval();
-        }
+            oldSes = sessions.get(ses.getId());
 
-        /** {@inheritDoc} */
-        @Override public <T> T getAttribute(String attributeName) {
-            T attributeValue = this.delegate.getAttribute(attributeName);
-            if (attributeValue != null
-                    && 
IgniteIndexedSessionRepository.this.saveMode.equals(SaveMode.ON_GET_ATTRIBUTE))
-                this.delta.put(attributeName, attributeValue);
+            if (oldSes == null)
+                break;
 
-            return attributeValue;
-        }
+            updatedSes = new IgniteSession(oldSes.getDelegate(), idxResolver, 
false, saveMode, this::flushImmediateIfNecessary);
+            copyChanges(updatedSes, ses);
 
-        /** {@inheritDoc} */
-        @Override public Set<String> getAttributeNames() {
-            return this.delegate.getAttributeNames();
-        }
+            if (attempt > MAX_UPDATE_ATTEMPT) {
+                logger.warn("Session maximum update attempts has been 
reached," +
+                    " 'replace' will be used instead [id=" + 
updatedSes.getId() + "]");
 
-        /** {@inheritDoc} */
-        @Override public void setAttribute(String attributeName, Object 
attributeValue) {
-            this.delegate.setAttribute(attributeName, attributeValue);
-            this.delta.put(attributeName, attributeValue);
-            if (SPRING_SECURITY_CONTEXT.equals(attributeName)) {
-                Map<String, String> indexes = 
IgniteIndexedSessionRepository.this.indexResolver.resolveIndexesFor(this);
-                String principal = (attributeValue != null) ? 
indexes.get(PRINCIPAL_NAME_INDEX_NAME) : null;
-                this.delegate.setAttribute(PRINCIPAL_NAME_INDEX_NAME, 
principal);
-                this.principal = principal;
+                ttlSessions(ses.getMaxInactiveInterval()).replace(ses.getId(), 
updatedSes);
+                break;
             }
-            flushImmediateIfNecessary();
-        }
-
-        /** {@inheritDoc} */
-        @Override public void removeAttribute(String attributeName) {
-            setAttribute(attributeName, null);
-        }
-
-        /** */
-        MapSession getDelegate() {
-            return this.delegate;
-        }
-
-        /** */
-        boolean hasChanges() {
-            return (this.lastAccessedTimeChanged || 
this.maxInactiveIntervalChanged || !this.delta.isEmpty());
-        }
-
-        /** */
-        void clearChangeFlags() {
-            this.isNew = false;
-            this.lastAccessedTimeChanged = false;
-            this.sessionIdChanged = false;
-            this.maxInactiveIntervalChanged = false;
-            this.delta.clear();
-        }
-
-        /** */
-        private void flushImmediateIfNecessary() {
-            if (IgniteIndexedSessionRepository.this.flushMode == 
FlushMode.IMMEDIATE)
-                IgniteIndexedSessionRepository.this.save(this);
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean equals(Object o) {
-            if (this == o)
-                return true;
-
-            if (o == null || getClass() != o.getClass())
-                return false;
-
-            IgniteSession session = (IgniteSession)o;
-            return this.delegate.equals(session.delegate);
-        }
-
-        /** {@inheritDoc} */
-        @Override public int hashCode() {
-            return Objects.hash(this.delegate);
-        }
+        } while 
(ttlSessions(ses.getMaxInactiveInterval()).replace(ses.getId(), oldSes, 
updatedSes));
     }
 }
diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteSession.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteSession.java
new file mode 100644
index 00000000..fbc3d791
--- /dev/null
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/IgniteSession.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.spring.sessions;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.springframework.session.IndexResolver;
+import org.springframework.session.MapSession;
+import org.springframework.session.SaveMode;
+import org.springframework.session.Session;
+
+import static 
org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
+import static org.springframework.session.SaveMode.ON_GET_ATTRIBUTE;
+
+/**
+ * A custom implementation of {@link Session} that uses a {@link MapSession} 
as the basis for its mapping. It keeps
+ * track if changes have been made since last save.
+ */
+public class IgniteSession implements Session {
+    /** */
+    public static final String SPRING_SECURITY_CONTEXT = 
"SPRING_SECURITY_CONTEXT";
+
+    /** The map session. */
+    private MapSession delegate;
+
+    /** Cached principal name for query. */
+    @SuppressWarnings("unused")
+    private String principal;
+
+    /** */
+    @GridDirectTransient
+    private transient boolean isNew;
+
+    /** */
+    @GridDirectTransient
+    private transient boolean lastAccessedTimeChanged;
+
+    /** */
+    @GridDirectTransient
+    private transient boolean maxInactiveIntervalChanged;
+
+    /** */
+    @GridDirectTransient
+    private final transient Map<String, Object> delta = new HashMap<>();
+
+    /** The index resolver. */
+    @GridDirectTransient
+    private final transient IndexResolver<Session> idxResolver;
+
+    /** Session save mode. */
+    @GridDirectTransient
+    private final transient SaveMode saveMode;
+
+    /** */
+    @GridDirectTransient
+    private final transient Consumer<IgniteSession> flusher;
+
+    /**
+     * @param delegate The map session.
+     * @param idxResolver The index resolver.
+     * @param isNew Is new flag.
+     * @param saveMode Mode of tracking and saving session changes to session 
store.
+     * @param flusher Flusher for session store.
+     */
+    IgniteSession(
+        MapSession delegate,
+        IndexResolver<Session> idxResolver,
+        boolean isNew,
+        SaveMode saveMode,
+        Consumer<IgniteSession> flusher
+    ) {
+        this.delegate = delegate;
+        this.isNew = isNew;
+
+        this.idxResolver = idxResolver;
+        this.saveMode = saveMode;
+        this.flusher = flusher;
+
+        principal = this.delegate.getAttribute(PRINCIPAL_NAME_INDEX_NAME);
+
+        if (this.isNew || this.saveMode == SaveMode.ALWAYS)
+            getAttributeNames().forEach(attrName -> delta.put(attrName, 
this.delegate.getAttribute(attrName)));
+
+        if (isNew)
+            this.flusher.accept(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setLastAccessedTime(Instant lastAccessedTime) {
+        delegate.setLastAccessedTime(lastAccessedTime);
+        lastAccessedTimeChanged = true;
+
+        flusher.accept(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isExpired() {
+        return delegate.isExpired();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Instant getCreationTime() {
+        return delegate.getCreationTime();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getId() {
+        return delegate.getId();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String changeSessionId() {
+        return delegate.changeSessionId();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Instant getLastAccessedTime() {
+        return delegate.getLastAccessedTime();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setMaxInactiveInterval(Duration interval) {
+        delegate.setMaxInactiveInterval(interval);
+        maxInactiveIntervalChanged = true;
+
+        flusher.accept(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Duration getMaxInactiveInterval() {
+        return delegate.getMaxInactiveInterval();
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T getAttribute(String attrName) {
+        T attrVal = this.delegate.getAttribute(attrName);
+
+        if (attrVal != null && saveMode.equals(ON_GET_ATTRIBUTE))
+            delta.put(attrName, attrVal);
+
+        return attrVal;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set<String> getAttributeNames() {
+        return this.delegate.getAttributeNames();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void setAttribute(String attrName, Object attrVal) {
+        delegate.setAttribute(attrName, attrVal);
+        delta.put(attrName, attrVal);
+
+        if (SPRING_SECURITY_CONTEXT.equals(attrName)) {
+            Map<String, String> indexes = idxResolver.resolveIndexesFor(this);
+            String principal = (attrVal != null) ? 
indexes.get(PRINCIPAL_NAME_INDEX_NAME) : null;
+
+            this.principal = principal;
+            
+            delegate.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal);
+            delta.put(PRINCIPAL_NAME_INDEX_NAME, principal);
+        }
+
+        flusher.accept(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void removeAttribute(String attrName) {
+        setAttribute(attrName, null);
+    }
+
+    /**
+     * @return Is new session.
+     */
+    public boolean isNew() {
+        return isNew;
+    }
+
+    /**
+     * @return Internal session object.
+     */
+    public MapSession getDelegate() {
+        return delegate;
+    }
+
+    /**
+     * @return {@code True} if session is changed.
+     */
+    public Map<String, Object> getAttributesChanges() {
+        return new HashMap<>(delta);
+    }
+
+    /**
+     * Get the original session id.
+     * @return the original session id.
+     * @see #changeSessionId()
+     */
+    public String getOriginalId() {
+        return delegate.getOriginalId();
+    }
+
+    /**
+     * Reset the original session id.
+     * @see #changeSessionId()
+     */
+    public void resetOriginalId() {
+        delegate = new MapSession(delegate);
+    }
+
+    /** Reset the change flags. */
+    public void clearChangeFlags() {
+        isNew = false;
+        lastAccessedTimeChanged = false;
+        maxInactiveIntervalChanged = false;
+        delta.clear();
+    }
+
+    /**
+     * @return Last accessed time changed.
+     */
+    public boolean isLastAccessedTimeChanged() {
+        return lastAccessedTimeChanged;
+    }
+
+    /**
+     * @return Session changed.
+     */
+    public boolean hasChanges() {
+        return lastAccessedTimeChanged || maxInactiveIntervalChanged || 
!delta.isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        IgniteSession ses = (IgniteSession)o;
+        
+        return delegate.equals(ses.delegate);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(delegate);
+    }
+}
diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/ClientSessionProxy.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/ClientSessionProxy.java
index 1cb88c13..bcde5858 100644
--- 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/ClientSessionProxy.java
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/ClientSessionProxy.java
@@ -22,7 +22,7 @@ import javax.cache.expiry.ExpiryPolicy;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.client.ClientCache;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
+import org.apache.ignite.spring.sessions.IgniteSession;
 
 /**
  * Represents {@link SessionProxy} implementation that uses {@link 
ClientCache} to perform session operations.
@@ -77,6 +77,11 @@ public class ClientSessionProxy implements SessionProxy {
         return cache.replace(key, val);
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean replace(String key, IgniteSession oldVal, 
IgniteSession newVal) {
+        return cache.replace(key, oldVal, newVal);
+    }
+
     /** {@inheritDoc} */
     @Override public <R> QueryCursor<R> query(Query<R> qry) {
         return cache.query(qry);
diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/IgniteSessionProxy.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/IgniteSessionProxy.java
index f19baf1b..6f41918e 100644
--- 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/IgniteSessionProxy.java
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/IgniteSessionProxy.java
@@ -22,7 +22,7 @@ import javax.cache.expiry.ExpiryPolicy;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
+import org.apache.ignite.spring.sessions.IgniteSession;
 
 /**
  * Represents {@link SessionProxy} implementation that uses {@link 
IgniteCache} to perform session operations.
@@ -77,6 +77,11 @@ public class IgniteSessionProxy implements SessionProxy {
         return cache.replace(key, val);
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean replace(String key, IgniteSession oldVal, 
IgniteSession newVal) {
+        return cache.replace(key, oldVal, newVal);
+    }
+
     /** {@inheritDoc} */
     @Override public <R> QueryCursor<R> query(Query<R> qry) {
         return cache.query(qry);
diff --git 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/SessionProxy.java
 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/SessionProxy.java
index a2779b14..aaf36a9f 100644
--- 
a/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/SessionProxy.java
+++ 
b/modules/spring-session-ext/src/main/java/org/apache/ignite/spring/sessions/proxy/SessionProxy.java
@@ -25,7 +25,7 @@ import javax.cache.expiry.ExpiryPolicy;
 import javax.cache.integration.CacheWriter;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
-import org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository;
+import org.apache.ignite.spring.sessions.IgniteSession;
 
 /** Represents Ignite client-independent session operations. */
 public interface SessionProxy {
@@ -33,24 +33,20 @@ public interface SessionProxy {
      * Registers a {@link CacheEntryListener}. The supplied {@link 
CacheEntryListenerConfiguration} is used to
      * instantiate a listener and apply it to those events specified in the 
configuration.
      *
-     * @param lsnrCfg a factory and related configuration for creating the 
listener.
+     * @param cacheEntryListenerConfiguration a factory and related 
configuration for creating the listener.
      * @throws IllegalArgumentException is the same 
CacheEntryListenerConfiguration is used more than once or
      *          if some unsupported by thin client properties are set.
      * @see CacheEntryListener
      */
-    public void registerCacheEntryListener(
-        CacheEntryListenerConfiguration<String, 
IgniteIndexedSessionRepository.IgniteSession> lsnrCfg
-    );
+    public void 
registerCacheEntryListener(CacheEntryListenerConfiguration<String, 
IgniteSession> cacheEntryListenerConfiguration);
 
     /**
      * Deregisters a listener, using the {@link 
CacheEntryListenerConfiguration} that was used to register it.
      *
-     * @param lsnrCfg the factory and related configuration that was used to 
create the
+     * @param cacheEntryListenerConfiguration the factory and related 
configuration that was used to create the
      *         listener.
      */
-    public void deregisterCacheEntryListener(
-        CacheEntryListenerConfiguration<String, 
IgniteIndexedSessionRepository.IgniteSession> lsnrCfg
-    );
+    public void 
deregisterCacheEntryListener(CacheEntryListenerConfiguration<String, 
IgniteSession> cacheEntryListenerConfiguration);
 
     /**
      * Returns cache with the specified expired policy set. This policy will 
be used for each operation invoked on
@@ -67,7 +63,7 @@ public interface SessionProxy {
      * @param key the key whose associated value is to be returned
      * @return the element, or null, if it does not exist.
      */
-    public IgniteIndexedSessionRepository.IgniteSession get(String key);
+    public IgniteSession get(String key);
 
     /**
      * Associates the specified value with the specified key in the cache.
@@ -75,7 +71,7 @@ public interface SessionProxy {
      * @param key key with which the specified value is to be associated
      * @param val value to be associated with the specified key.
      */
-    public void put(String key, IgniteIndexedSessionRepository.IgniteSession 
val);
+    public void put(String key, IgniteSession val);
 
     /**
      * Removes the mapping for a key from this cache if it is present.
@@ -110,7 +106,27 @@ public interface SessionProxy {
      *                               configured for the {@link Cache}
      * @see CacheWriter#write
      */
-    public boolean replace(String key, 
IgniteIndexedSessionRepository.IgniteSession val);
+    public boolean replace(String key, IgniteSession val);
+
+    /**
+     * Atomically replaces the entry for a key only if currently mapped to a 
given value.
+     * <p>
+     * This is equivalent to performing the following operations as a single 
atomic action:
+     * <pre><code>
+     * if (cache.containsKey(key) &amp;&amp; equals(cache.get(key), oldValue)) 
{
+     *  cache.put(key, newValue);
+     * return true;
+     * } else {
+     *  return false;
+     * }
+     * </code></pre>
+     *
+     * @param key Key with which the specified value is associated.
+     * @param oldVal Value expected to be associated with the specified key.
+     * @param newVal Value to be associated with the specified key.
+     * @return <tt>true</tt> if the value was replaced
+     */
+    public boolean replace(String key, IgniteSession oldVal, IgniteSession 
newVal);
 
     /**
      * Execute SQL query and get cursor to iterate over results.
diff --git 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/AbstractIgniteIndexedSessionRepositoryTest.java
 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/AbstractIgniteIndexedSessionRepositoryTest.java
index eb180d6d..62f83a3f 100644
--- 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/AbstractIgniteIndexedSessionRepositoryTest.java
+++ 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/AbstractIgniteIndexedSessionRepositoryTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.spring.sessions;
 
 import java.time.Duration;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import 
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -28,15 +27,13 @@ import 
org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.session.FindByIndexNameSessionRepository;
 
+import static 
org.apache.ignite.spring.sessions.IgniteSession.SPRING_SECURITY_CONTEXT;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
  * Base class for {@link IgniteIndexedSessionRepository} integration tests.
  */
 abstract class AbstractIgniteIndexedSessionRepositoryTest {
-    /** */
-    private static final String SPRING_SECURITY_CONTEXT = 
"SPRING_SECURITY_CONTEXT";
-
     /** */
     @Autowired
     protected IgniteIndexedSessionRepository repo;
diff --git 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/EmbeddedIgniteIndexedSessionRepositoryTest.java
 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/EmbeddedIgniteIndexedSessionRepositoryTest.java
index 530f7a9b..db62332a 100644
--- 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/EmbeddedIgniteIndexedSessionRepositoryTest.java
+++ 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/EmbeddedIgniteIndexedSessionRepositoryTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.spring.sessions;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.Ignition;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteClientIndexedSessionRepositoryTest.java
 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteClientIndexedSessionRepositoryTest.java
index 34231f9c..219969d0 100644
--- 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteClientIndexedSessionRepositoryTest.java
+++ 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteClientIndexedSessionRepositoryTest.java
@@ -21,7 +21,6 @@ import org.apache.ignite.Ignition;
 import org.apache.ignite.client.ClientCache;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.configuration.ClientConfiguration;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
diff --git 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfigurationTest.java
 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfigurationTest.java
index 9b3969a0..a6b2f185 100644
--- 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfigurationTest.java
+++ 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteHttpSessionConfigurationTest.java
@@ -57,13 +57,12 @@ public class IgniteHttpSessionConfigurationTest {
     private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600;
 
     /** */
-    private AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext();
+    private final AnnotationConfigApplicationContext ctx = new 
AnnotationConfigApplicationContext();
 
     /** */
     @AfterEach
     void closeContext() {
-        if (this.ctx != null)
-            this.ctx.close();
+        this.ctx.close();
     }
 
     /** */
@@ -110,7 +109,7 @@ public class IgniteHttpSessionConfigurationTest {
 
         IgniteIndexedSessionRepository repo = 
this.ctx.getBean(IgniteIndexedSessionRepository.class);
         assertThat(repo).isNotNull();
-        assertThat(getField(repo, "defaultMaxInactiveInterval"))
+        assertThat(getField(repo, "dfltMaxInactiveInterval"))
                 .isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
     }
 
@@ -121,7 +120,7 @@ public class IgniteHttpSessionConfigurationTest {
 
         IgniteIndexedSessionRepository repo = 
this.ctx.getBean(IgniteIndexedSessionRepository.class);
         assertThat(repo).isNotNull();
-        assertThat(getField(repo, "defaultMaxInactiveInterval"))
+        assertThat(getField(repo, "dfltMaxInactiveInterval"))
                 .isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
     }
 
@@ -230,7 +229,7 @@ public class IgniteHttpSessionConfigurationTest {
         IndexResolver<Session> idxResolver = 
this.ctx.getBean(IndexResolver.class);
         assertThat(repo).isNotNull();
         assertThat(idxResolver).isNotNull();
-        assertThat(repo).hasFieldOrPropertyWithValue("indexResolver", 
idxResolver);
+        assertThat(repo).hasFieldOrPropertyWithValue("idxResolver", 
idxResolver);
     }
 
     /** */
@@ -238,7 +237,7 @@ public class IgniteHttpSessionConfigurationTest {
     void sessionRepositoryCustomizer() {
         registerAndRefresh(SessionRepositoryCustomizerConfiguration.class);
         IgniteIndexedSessionRepository sesRepo = 
this.ctx.getBean(IgniteIndexedSessionRepository.class);
-        
assertThat(sesRepo).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
+        
assertThat(sesRepo).hasFieldOrPropertyWithValue("dfltMaxInactiveInterval",
                 MAX_INACTIVE_INTERVAL_IN_SECONDS);
     }
 
diff --git 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepositoryTest.java
 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepositoryTest.java
index 05c7585d..342d901e 100644
--- 
a/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepositoryTest.java
+++ 
b/modules/spring-session-ext/src/test/java/org/apache/ignite/spring/sessions/IgniteIndexedSessionRepositoryTest.java
@@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
 import javax.cache.expiry.TouchedExpiryPolicy;
 import org.apache.ignite.cache.query.Query;
 import org.apache.ignite.cache.query.QueryCursor;
-import 
org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
 import org.apache.ignite.spring.sessions.proxy.SessionProxy;
 import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.BeforeEach;
@@ -36,7 +35,6 @@ import org.junit.jupiter.api.Test;
 import 
org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.authority.AuthorityUtils;
-import org.springframework.session.FindByIndexNameSessionRepository;
 import org.springframework.session.FlushMode;
 import org.springframework.session.MapSession;
 
@@ -50,6 +48,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static 
org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
 
 /**
  * Tests for {@link IgniteIndexedSessionRepository}.
@@ -162,7 +161,7 @@ public class IgniteIndexedSessionRepositoryTest {
 
         IgniteSession ses = repo.createSession();
         ses.setAttribute("testName", "testValue");
-        verify(sessions, 
times(1)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
+        verify(sessions, 
times(2)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
         verify(sessions, times(1)).put(eq(ses.getId()), eq(ses));
         verify(sessions, times(1)).replace(eq(ses.getId()), eq(ses));
 
@@ -196,7 +195,7 @@ public class IgniteIndexedSessionRepositoryTest {
         ses.removeAttribute("testName");
         verify(sessions, times(1)).put(eq(ses.getId()), eq(ses));
         verify(sessions, times(1)).replace(eq(ses.getId()), eq(ses));
-        verify(sessions, 
times(1)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
+        verify(sessions, 
times(2)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
 
         repo.save(ses);
         verifyNoMoreInteractions(sessions);
@@ -228,7 +227,7 @@ public class IgniteIndexedSessionRepositoryTest {
         ses.setLastAccessedTime(Instant.now());
         verify(sessions, times(1)).put(eq(ses.getId()), eq(ses));
         verify(sessions, times(1)).replace(eq(ses.getId()), eq(ses));
-        verify(sessions, 
times(1)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
+        verify(sessions, 
times(2)).withExpiryPolicy(eq(createExpiryPolicy(ses)));
 
         repo.save(ses);
         verifyNoMoreInteractions(sessions);
@@ -316,7 +315,7 @@ public class IgniteIndexedSessionRepositoryTest {
     void getSessionExpired() {
         verify(sessions, times(1)).registerCacheEntryListener(any());
 
-        IgniteSession expired = repo.new IgniteSession(new MapSession(), true);
+        IgniteSession expired = repo.createSession();
 
         
expired.setLastAccessedTime(Instant.now().minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS
 + 1));
         given(sessions.get(eq(expired.getId()))).willReturn(expired);
@@ -334,7 +333,7 @@ public class IgniteIndexedSessionRepositoryTest {
     void getSessionFound() {
         verify(sessions, times(1)).registerCacheEntryListener(any());
 
-        IgniteSession saved = repo.new IgniteSession(new MapSession(), true);
+        IgniteSession saved = repo.createSession();
         saved.setAttribute("savedName", "savedValue");
         given(sessions.get(eq(saved.getId()))).willReturn(saved);
 
@@ -379,8 +378,7 @@ public class IgniteIndexedSessionRepositoryTest {
 
         String principal = "username";
 
-        Map<String, IgniteSession> sesMap = repo
-                
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
 principal);
+        Map<String, IgniteSession> sesMap = 
repo.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principal);
 
         assertThat(sesMap).isEmpty();
 
@@ -424,8 +422,7 @@ public class IgniteIndexedSessionRepositoryTest {
             }
         });
 
-        Map<String, IgniteSession> sesMap = repo
-                
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
 principal);
+        Map<String, IgniteSession> sesMap = 
repo.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principal);
 
         assertThat(sesMap).hasSize(2);
         verify(sessions, times(1)).query(any());

Reply via email to