Fix deserialization issue caused by unregistered Instantiator (#595)

Fixes gh-594 & gh-595
This commit is contained in:
John Blum
2016-08-16 19:09:01 -07:00
committed by Rob Winch
parent 240c979679
commit f6101b7e30

View File

@@ -55,7 +55,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
@@ -65,18 +65,20 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* AbstractGemFireOperationsSessionRepository is an abstract base class encapsulating
* functionality common to all implementations that support SessionRepository operations
* backed by GemFire.
* AbstractGemFireOperationsSessionRepository is an abstract base class encapsulating functionality
* common to all implementations that support {@link SessionRepository} operations backed by GemFire.
*
* @author John Blum
* @since 1.1.0
* @see EnableGemFireHttpSession
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.context.ApplicationEventPublisherAware
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.FindByIndexNameSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see com.gemstone.gemfire.cache.util.CacheListenerAdapter
*/
public abstract class AbstractGemFireOperationsSessionRepository
extends CacheListenerAdapter<Object, ExpiringSession>
implements InitializingBean, FindByIndexNameSessionRepository<ExpiringSession>,
ApplicationEventPublisherAware {
public abstract class AbstractGemFireOperationsSessionRepository extends CacheListenerAdapter<Object, ExpiringSession>
implements InitializingBean, FindByIndexNameSessionRepository<ExpiringSession>, ApplicationEventPublisherAware {
private int maxInactiveIntervalInSeconds = GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
@@ -125,10 +127,8 @@ public abstract class AbstractGemFireOperationsSessionRepository
* publish Session-based events.
* @see org.springframework.context.ApplicationEventPublisher
*/
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher,
"ApplicationEventPublisher must not be null");
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher, "ApplicationEventPublisher must not be null");
this.applicationEventPublisher = applicationEventPublisher;
}
@@ -190,10 +190,13 @@ public abstract class AbstractGemFireOperationsSessionRepository
}
/**
* Callback method during Spring bean initialization that will capture the
* fully-qualified name of the GemFire cache {@link Region} used to manage Session
* state and register this SessionRepository as a GemFire
* {@link com.gemstone.gemfire.cache.CacheListener}.
* Callback method during Spring bean initialization that will capture the fully-qualified name
* of the GemFire cache {@link Region} used to manage Session state and register this SessionRepository
* as a GemFire {@link com.gemstone.gemfire.cache.CacheListener}.
*
* Additionally, this method registers GemFire {@link Instantiator}s for the {@link GemFireSession}
* and {@link GemFireSessionAttributes} types to optimize GemFire's instantiation logic on deserialization
* using the data serialization framework when accessing the {@link Session}'s state stored in GemFire.
*
* @throws Exception if an error occurs during the initialization process.
*/
@@ -206,6 +209,20 @@ public abstract class AbstractGemFireOperationsSessionRepository
this.fullyQualifiedRegionName = region.getFullPath();
region.getAttributesMutator().addCacheListener(this);
Instantiator.register(new Instantiator(GemFireSession.class, 800813552) {
@Override
public DataSerializable newInstance() {
return new GemFireSession();
}
});
Instantiator.register(new Instantiator(GemFireSessionAttributes.class, 800828008) {
@Override
public DataSerializable newInstance() {
return new GemFireSessionAttributes();
}
});
}
/* (non-Javadoc) */
@@ -229,8 +246,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
@Override
public void afterCreate(EntryEvent<Object, ExpiringSession> event) {
if (isExpiringSessionOrNull(event.getNewValue())) {
handleCreated(event.getKey().toString(),
toExpiringSession(event.getNewValue()));
handleCreated(event.getKey().toString(), toExpiringSession(event.getNewValue()));
}
}
@@ -244,8 +260,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
*/
@Override
public void afterDestroy(EntryEvent<Object, ExpiringSession> event) {
handleDestroyed(event.getKey().toString(),
toExpiringSession(event.getOldValue()));
handleDestroyed(event.getKey().toString(), toExpiringSession(event.getOldValue()));
}
/**
@@ -329,17 +344,15 @@ public abstract class AbstractGemFireOperationsSessionRepository
getApplicationEventPublisher().publishEvent(event);
}
catch (Throwable t) {
this.logger.error(
String.format("error occurred publishing event (%1$s)", event), t);
this.logger.error(String.format("error occurred publishing event (%1$s)", event), t);
}
}
/**
* GemFireSession is a GemFire representation model of a Spring
* {@link ExpiringSession} for storing and accessing Session state information in
* GemFire. This class implements GemFire's {@link DataSerializable} interface to
* better handle replication of Session information across the GemFire cluster.
*
* GemFireSession is a GemFire representation model of a Spring {@link ExpiringSession}
* that stores and manages Session state information in GemFire. This class implements
* GemFire's {@link DataSerializable} interface to better handle replication of Session
* state information across the GemFire cluster.
*/
@SuppressWarnings("serial")
public static class GemFireSession implements Comparable<ExpiringSession>,
@@ -352,15 +365,6 @@ public abstract class AbstractGemFireOperationsSessionRepository
protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
static {
Instantiator.register(new Instantiator(GemFireSession.class, 800813552) {
@Override
public DataSerializable newInstance() {
return new GemFireSession();
}
});
}
private transient boolean delta = false;
private int maxInactiveIntervalInSeconds;
@@ -469,8 +473,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
/* (non-Javadoc) */
private long idleTimeout(long maxInactiveIntervalInSeconds) {
return (System.currentTimeMillis()
- TimeUnit.SECONDS.toMillis(maxInactiveIntervalInSeconds));
return (System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(maxInactiveIntervalInSeconds));
}
/* (non-Javadoc) */
@@ -485,8 +488,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
}
/* (non-Javadoc) */
public synchronized void setMaxInactiveIntervalInSeconds(
final int maxInactiveIntervalInSeconds) {
public synchronized void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.delta |= (this.maxInactiveIntervalInSeconds != maxInactiveIntervalInSeconds);
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
@@ -509,8 +511,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
Object authentication = getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = this.parser
.parseExpression("authentication?.name");
Expression expression = this.parser.parseExpression("authentication?.name");
principalName = expression.getValue(authentication, String.class);
}
}
@@ -526,8 +527,8 @@ public abstract class AbstractGemFireOperationsSessionRepository
out.writeInt(getMaxInactiveIntervalInSeconds());
String principalName = getPrincipalName();
int length = (StringUtils.hasText(principalName) ? principalName.length()
: 0);
int length = (StringUtils.hasText(principalName) ? principalName.length() : 0);
out.writeInt(length);
@@ -546,8 +547,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
}
/* (non-Javadoc) */
public synchronized void fromData(DataInput in)
throws ClassNotFoundException, IOException {
public synchronized void fromData(DataInput in) throws ClassNotFoundException, IOException {
this.id = in.readUTF();
this.creationTime = in.readLong();
setLastAccessedTime(in.readLong());
@@ -623,12 +623,10 @@ public abstract class AbstractGemFireOperationsSessionRepository
/* (non-Javadoc) */
@Override
public synchronized String toString() {
return String.format(
"{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s"
+ ", maxInactiveIntervalInSeconds = %5$s, principalName = %6$s }",
getClass().getName(), getId(), toString(getCreationTime()),
toString(getLastAccessedTime()), getMaxInactiveIntervalInSeconds(),
getPrincipalName());
return String.format("{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s"
+ ", maxInactiveIntervalInSeconds = %5$s, principalName = %6$s }",
getClass().getName(), getId(), toString(getCreationTime()), toString(getLastAccessedTime()),
getMaxInactiveIntervalInSeconds(), getPrincipalName());
}
/* (non-Javadoc) */
@@ -638,17 +636,16 @@ public abstract class AbstractGemFireOperationsSessionRepository
}
/**
* The GemFireSessionAttributes class is a container for Session attributes that
* implements both the {@link DataSerializable} and {@link Delta} GemFire interfaces
* for efficient storage and distribution (replication) in GemFire. Additionally,
* GemFireSessionAttributes extends {@link AbstractMap} providing {@link Map}-like
* behavior since attributes of a Session are effectively a name to value mapping.
* The GemFireSessionAttributes class is a container for Session attributes implementing
* both the {@link DataSerializable} and {@link Delta} GemFire interfaces for efficient
* storage and distribution (replication) in GemFire. Additionally, GemFireSessionAttributes
* extends {@link AbstractMap} providing {@link Map}-like behavior since attributes of a Session
* are effectively a name to value mapping.
*
* @see java.util.AbstractMap
* @see com.gemstone.gemfire.DataSerializable
* @see com.gemstone.gemfire.DataSerializer
* @see com.gemstone.gemfire.Delta
* @see com.gemstone.gemfire.Instantiator
*/
@SuppressWarnings("serial")
public static class GemFireSessionAttributes extends AbstractMap<String, Object>
@@ -656,16 +653,6 @@ public abstract class AbstractGemFireOperationsSessionRepository
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
static {
Instantiator.register(
new Instantiator(GemFireSessionAttributes.class, 800828008) {
@Override
public DataSerializable newInstance() {
return new GemFireSessionAttributes();
}
});
}
private transient final Map<String, Object> sessionAttributes = new HashMap<String, Object>();
private transient final Map<String, Object> sessionAttributeDeltas = new HashMap<String, Object>();
@@ -685,8 +672,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
public void setAttribute(String attributeName, Object attributeValue) {
synchronized (this.lock) {
if (attributeValue != null) {
if (!attributeValue.equals(
this.sessionAttributes.put(attributeName, attributeValue))) {
if (!attributeValue.equals(this.sessionAttributes.put(attributeName, attributeValue))) {
this.sessionAttributeDeltas.put(attributeName, attributeValue);
}
}
@@ -716,8 +702,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
/* (non-Javadoc) */
public Set<String> getAttributeNames() {
synchronized (this.lock) {
return Collections.unmodifiableSet(
new HashSet<String>(this.sessionAttributes.keySet()));
return Collections.unmodifiableSet(new HashSet<String>(this.sessionAttributes.keySet()));
}
}
@@ -733,10 +718,8 @@ public abstract class AbstractGemFireOperationsSessionRepository
return new AbstractSet<Entry<String, Object>>() {
@Override
public Iterator<Entry<String, Object>> iterator() {
return Collections
.unmodifiableMap(
GemFireSessionAttributes.this.sessionAttributes)
.entrySet().iterator();
return Collections.unmodifiableMap(GemFireSessionAttributes.this.sessionAttributes)
.entrySet().iterator();
}
@Override
@@ -759,8 +742,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
public void from(GemFireSessionAttributes sessionAttributes) {
synchronized (this.lock) {
for (String attributeName : sessionAttributes.getAttributeNames()) {
setAttribute(attributeName,
sessionAttributes.getAttribute(attributeName));
setAttribute(attributeName, sessionAttributes.getAttribute(attributeName));
}
}
}
@@ -812,8 +794,7 @@ public abstract class AbstractGemFireOperationsSessionRepository
synchronized (this.lock) {
out.writeInt(this.sessionAttributeDeltas.size());
for (Map.Entry<String, Object> entry : this.sessionAttributeDeltas
.entrySet()) {
for (Map.Entry<String, Object> entry : this.sessionAttributeDeltas.entrySet()) {
out.writeUTF(entry.getKey());
writeObject(entry.getValue(), out);
}
@@ -850,5 +831,4 @@ public abstract class AbstractGemFireOperationsSessionRepository
return this.sessionAttributes.toString();
}
}
}