Fixed - multiple Tomcat requests share different instances stored in the same session in readMode=REDIS #2476

pull/3048/head
Nikita Koksharov 4 years ago
parent be4f10218f
commit b50bfcc574

@ -29,6 +29,7 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Redisson Session object for Apache Tomcat
@ -64,8 +65,9 @@ public class RedissonSession extends StandardSession {
private final ReadMode readMode;
private final UpdateMode updateMode;
private final AtomicInteger usages = new AtomicInteger();
private Map<String, Object> loadedAttributes = Collections.emptyMap();
private Set<String> removedAttributes = Collections.emptySet();
private Set<String> updatedAttributes = Collections.emptySet();
private final boolean broadcastSessionEvents;
@ -79,7 +81,9 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
updatedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
if (readMode == ReadMode.REDIS) {
loadedAttributes = new ConcurrentHashMap<>();
}
try {
@ -103,12 +107,19 @@ public class RedissonSession extends StandardSession {
return null;
}
if (updatedAttributes.contains(name)
|| removedAttributes.contains(name)) {
if (removedAttributes.contains(name)) {
return super.getAttribute(name);
}
return map.get(name);
Object value = loadedAttributes.get(name);
if (value == null) {
value = map.get(name);
if (value != null) {
loadedAttributes.put(name, value);
}
}
return value;
} else {
if (!loaded) {
synchronized (this) {
@ -170,6 +181,7 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributesClearMessage(redissonManager.getNodeId(), getId()));
}
map = null;
loadedAttributes.clear();
}
@Override
@ -310,9 +322,11 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.DEFAULT && map != null) {
fastPut(name, value);
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.put(name, value);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.remove(name);
updatedAttributes.add(name);
}
}
@ -330,9 +344,11 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributeRemoveMessage(redissonManager.getNodeId(), getId(), new HashSet<String>(Arrays.asList(name))));
}
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.remove(name);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.add(name);
updatedAttributes.remove(name);
}
}
@ -377,8 +393,8 @@ public class RedissonSession extends StandardSession {
}
}
updatedAttributes.clear();
removedAttributes.clear();
loadedAttributes.clear();
expireSession();
}
@ -430,6 +446,16 @@ public class RedissonSession extends StandardSession {
public void recycle() {
super.recycle();
map = null;
loadedAttributes.clear();
}
public void startUsage() {
usages.incrementAndGet();
}
public void endUsage() {
if (usages.decrementAndGet() == 0) {
loadedAttributes.clear();
}
}
}

@ -17,28 +17,19 @@ package org.redisson.tomcat;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.*;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RMap;
import org.redisson.api.RSet;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
@ -71,7 +62,7 @@ public class RedissonSessionManager extends ManagerBase {
private final String nodeId = UUID.randomUUID().toString();
private static UpdateValve updateValve;
private static ValveBase updateValve;
private static Set<String> contextInUse = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
@ -151,7 +142,7 @@ public class RedissonSessionManager extends ManagerBase {
}
return session;
}
public RSet<String> getNotifiedNodes(String sessionId) {
String separator = keyPrefix == null || keyPrefix.isEmpty() ? "" : ":";
String name = keyPrefix + separator + "redisson:tomcat_notified_nodes:" + sessionId;
@ -186,7 +177,7 @@ public class RedissonSessionManager extends ManagerBase {
log.error("Can't read session object by id: " + id, e);
}
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
if (attrs.isEmpty() || (broadcastSessionEvents && getNotifiedNodes(id).contains(nodeId))) {
log.info("Session " + id + " can't be found");
return null;
}
@ -207,7 +198,6 @@ public class RedissonSessionManager extends ManagerBase {
return result;
}
@Override
public Session createEmptySession() {
@ -219,7 +209,7 @@ public class RedissonSessionManager extends ManagerBase {
super.add(session);
((RedissonSession)session).save();
}
@Override
public void remove(Session session, boolean update) {
super.remove(session, update);
@ -228,7 +218,7 @@ public class RedissonSessionManager extends ManagerBase {
((RedissonSession)session).delete();
}
}
public RedissonClient getRedisson() {
return redisson;
}
@ -264,6 +254,11 @@ public class RedissonSessionManager extends ManagerBase {
updateValve = new UpdateValve();
pipeline.addValve(updateValve);
}
} else if (readMode == ReadMode.REDIS) {
if (updateValve == null) {
updateValve = new UsageValve();
pipeline.addValve(updateValve);
}
}
}

@ -0,0 +1,71 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed 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.redisson.tomcat;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Redisson Valve object for Apache Tomcat
*
* @author Nikita Koksharov
*
*/
public class UsageValve extends ValveBase {
private static final String ALREADY_FILTERED_NOTE = UsageValve.class.getName() + ".ALREADY_FILTERED_NOTE";
public UsageValve() {
super(true);
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
if (getNext() == null) {
return;
}
//check if we already filtered/processed this request
if (request.getNote(ALREADY_FILTERED_NOTE) == null) {
request.setNote(ALREADY_FILTERED_NOTE, Boolean.TRUE);
RedissonSession s = null;
try {
HttpSession session = request.getSession(false);
if (session != null) {
s = (RedissonSession) request.getContext().getManager().findSession(session.getId());
if (s != null) {
s.startUsage();
}
}
getNext().invoke(request, response);
} finally {
request.removeNote(ALREADY_FILTERED_NOTE);
if (s != null) {
s.endUsage();
}
}
} else {
getNext().invoke(request, response);
}
}
}

@ -29,6 +29,7 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Redisson Session object for Apache Tomcat
@ -64,8 +65,9 @@ public class RedissonSession extends StandardSession {
private final ReadMode readMode;
private final UpdateMode updateMode;
private final AtomicInteger usages = new AtomicInteger();
private Map<String, Object> loadedAttributes = Collections.emptyMap();
private Set<String> removedAttributes = Collections.emptySet();
private Set<String> updatedAttributes = Collections.emptySet();
private final boolean broadcastSessionEvents;
@ -79,7 +81,9 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
updatedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
if (readMode == ReadMode.REDIS) {
loadedAttributes = new ConcurrentHashMap<>();
}
try {
@ -103,12 +107,19 @@ public class RedissonSession extends StandardSession {
return null;
}
if (updatedAttributes.contains(name)
|| removedAttributes.contains(name)) {
if (removedAttributes.contains(name)) {
return super.getAttribute(name);
}
return map.get(name);
Object value = loadedAttributes.get(name);
if (value == null) {
value = map.get(name);
if (value != null) {
loadedAttributes.put(name, value);
}
}
return value;
} else {
if (!loaded) {
synchronized (this) {
@ -170,6 +181,7 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributesClearMessage(redissonManager.getNodeId(), getId()));
}
map = null;
loadedAttributes.clear();
}
@Override
@ -310,9 +322,11 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.DEFAULT && map != null) {
fastPut(name, value);
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.put(name, value);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.remove(name);
updatedAttributes.add(name);
}
}
@ -330,9 +344,11 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributeRemoveMessage(redissonManager.getNodeId(), getId(), new HashSet<String>(Arrays.asList(name))));
}
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.remove(name);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.add(name);
updatedAttributes.remove(name);
}
}
@ -377,8 +393,8 @@ public class RedissonSession extends StandardSession {
}
}
updatedAttributes.clear();
removedAttributes.clear();
loadedAttributes.clear();
expireSession();
}
@ -430,6 +446,16 @@ public class RedissonSession extends StandardSession {
public void recycle() {
super.recycle();
map = null;
loadedAttributes.clear();
}
public void startUsage() {
usages.incrementAndGet();
}
public void endUsage() {
if (usages.decrementAndGet() == 0) {
loadedAttributes.clear();
}
}
}

@ -17,11 +17,7 @@ package org.redisson.tomcat;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpSession;
@ -33,6 +29,7 @@ import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
@ -70,7 +67,7 @@ public class RedissonSessionManager extends ManagerBase {
private final String nodeId = UUID.randomUUID().toString();
private static UpdateValve updateValve;
private static ValveBase updateValve;
private static Set<String> contextInUse = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
@ -262,6 +259,11 @@ public class RedissonSessionManager extends ManagerBase {
updateValve = new UpdateValve();
pipeline.addValve(updateValve);
}
} else if (readMode == ReadMode.REDIS) {
if (updateValve == null) {
updateValve = new UsageValve();
pipeline.addValve(updateValve);
}
}
}

@ -0,0 +1,71 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed 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.redisson.tomcat;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Redisson Valve object for Apache Tomcat
*
* @author Nikita Koksharov
*
*/
public class UsageValve extends ValveBase {
private static final String ALREADY_FILTERED_NOTE = UsageValve.class.getName() + ".ALREADY_FILTERED_NOTE";
public UsageValve() {
super(true);
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
if (getNext() == null) {
return;
}
//check if we already filtered/processed this request
if (request.getNote(ALREADY_FILTERED_NOTE) == null) {
request.setNote(ALREADY_FILTERED_NOTE, Boolean.TRUE);
RedissonSession s = null;
try {
HttpSession session = request.getSession(false);
if (session != null) {
s = (RedissonSession) request.getContext().getManager().findSession(session.getId());
if (s != null) {
s.startUsage();
}
}
getNext().invoke(request, response);
} finally {
request.removeNote(ALREADY_FILTERED_NOTE);
if (s != null) {
s.endUsage();
}
}
} else {
getNext().invoke(request, response);
}
}
}

@ -29,6 +29,7 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Redisson Session object for Apache Tomcat
@ -64,8 +65,9 @@ public class RedissonSession extends StandardSession {
private final ReadMode readMode;
private final UpdateMode updateMode;
private final AtomicInteger usages = new AtomicInteger();
private Map<String, Object> loadedAttributes = Collections.emptyMap();
private Set<String> removedAttributes = Collections.emptySet();
private Set<String> updatedAttributes = Collections.emptySet();
private final boolean broadcastSessionEvents;
@ -79,7 +81,9 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
updatedAttributes = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
if (readMode == ReadMode.REDIS) {
loadedAttributes = new ConcurrentHashMap<>();
}
try {
@ -103,12 +107,19 @@ public class RedissonSession extends StandardSession {
return null;
}
if (updatedAttributes.contains(name)
|| removedAttributes.contains(name)) {
if (removedAttributes.contains(name)) {
return super.getAttribute(name);
}
return map.get(name);
Object value = loadedAttributes.get(name);
if (value == null) {
value = map.get(name);
if (value != null) {
loadedAttributes.put(name, value);
}
}
return value;
} else {
if (!loaded) {
synchronized (this) {
@ -170,6 +181,7 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributesClearMessage(redissonManager.getNodeId(), getId()));
}
map = null;
loadedAttributes.clear();
}
@Override
@ -310,9 +322,11 @@ public class RedissonSession extends StandardSession {
if (updateMode == UpdateMode.DEFAULT && map != null) {
fastPut(name, value);
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.put(name, value);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.remove(name);
updatedAttributes.add(name);
}
}
@ -330,9 +344,11 @@ public class RedissonSession extends StandardSession {
topic.publish(new AttributeRemoveMessage(redissonManager.getNodeId(), getId(), new HashSet<String>(Arrays.asList(name))));
}
}
if (readMode == ReadMode.REDIS) {
loadedAttributes.remove(name);
}
if (updateMode == UpdateMode.AFTER_REQUEST) {
removedAttributes.add(name);
updatedAttributes.remove(name);
}
}
@ -377,8 +393,8 @@ public class RedissonSession extends StandardSession {
}
}
updatedAttributes.clear();
removedAttributes.clear();
loadedAttributes.clear();
expireSession();
}
@ -430,6 +446,16 @@ public class RedissonSession extends StandardSession {
public void recycle() {
super.recycle();
map = null;
loadedAttributes.clear();
}
public void startUsage() {
usages.incrementAndGet();
}
public void endUsage() {
if (usages.decrementAndGet() == 0) {
loadedAttributes.clear();
}
}
}

@ -17,11 +17,7 @@ package org.redisson.tomcat;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpSession;
@ -33,6 +29,7 @@ import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.redisson.Redisson;
@ -70,7 +67,7 @@ public class RedissonSessionManager extends ManagerBase {
private final String nodeId = UUID.randomUUID().toString();
private static UpdateValve updateValve;
private static ValveBase updateValve;
private static Set<String> contextInUse = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
@ -262,6 +259,11 @@ public class RedissonSessionManager extends ManagerBase {
updateValve = new UpdateValve();
pipeline.addValve(updateValve);
}
} else if (readMode == ReadMode.REDIS) {
if (updateValve == null) {
updateValve = new UsageValve();
pipeline.addValve(updateValve);
}
}
}

@ -0,0 +1,71 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed 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.redisson.tomcat;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Redisson Valve object for Apache Tomcat
*
* @author Nikita Koksharov
*
*/
public class UsageValve extends ValveBase {
private static final String ALREADY_FILTERED_NOTE = UsageValve.class.getName() + ".ALREADY_FILTERED_NOTE";
public UsageValve() {
super(true);
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
if (getNext() == null) {
return;
}
//check if we already filtered/processed this request
if (request.getNote(ALREADY_FILTERED_NOTE) == null) {
request.setNote(ALREADY_FILTERED_NOTE, Boolean.TRUE);
RedissonSession s = null;
try {
HttpSession session = request.getSession(false);
if (session != null) {
s = (RedissonSession) request.getContext().getManager().findSession(session.getId());
if (s != null) {
s.startUsage();
}
}
getNext().invoke(request, response);
} finally {
request.removeNote(ALREADY_FILTERED_NOTE);
if (s != null) {
s.endUsage();
}
}
} else {
getNext().invoke(request, response);
}
}
}
Loading…
Cancel
Save