You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/05/17 11:18:31 UTC
[3/5] ignite git commit: IGNITE-3087 - WebSessions: session objects
are no longer deserialized on the server.
IGNITE-3087 - WebSessions: session objects are no longer deserialized on the server.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/b369d030
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/b369d030
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/b369d030
Branch: refs/heads/ignite-3149
Commit: b369d0306a49bf24a9a6ad09dd8a06929bdc572e
Parents: fb0b0e0
Author: dkarachentsev <dk...@gridgain.com>
Authored: Tue May 17 13:58:39 2016 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Tue May 17 13:58:39 2016 +0300
----------------------------------------------------------------------
.../processors/cache/GridCacheTtlManager.java | 5 +-
.../WebSessionAttributeProcessor.java | 134 +++++
.../internal/websession/WebSessionEntity.java | 193 +++++++
.../ignite/cache/websession/WebSession.java | 14 +-
.../cache/websession/WebSessionFilter.java | 499 ++++++++++++++++---
.../cache/websession/WebSessionListener.java | 117 +----
.../ignite/cache/websession/WebSessionV2.java | 405 +++++++++++++++
.../IgniteWebSessionSelfTestSuite.java | 35 ++
.../internal/websession/WebSessionSelfTest.java | 268 ++++++++--
.../webapp2/META-INF/ignite-webapp-config.xml | 279 +++++++++++
modules/web/src/test/webapp2/WEB-INF/web.xml | 45 ++
11 files changed, 1774 insertions(+), 220 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
index 657cf8d..4e1fc6d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
@@ -293,9 +293,8 @@ public class GridCacheTtlManager extends GridCacheManagerAdapter {
@Override public boolean add(EntryWrapper e) {
boolean res = super.add(e);
- assert res : "Failed to add entry wrapper:" + e;
-
- size.increment();
+ if (res)
+ size.increment();
return res;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionAttributeProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionAttributeProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionAttributeProcessor.java
new file mode 100644
index 0000000..bef42e4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionAttributeProcessor.java
@@ -0,0 +1,134 @@
+/*
+ * 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.internal.websession;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.internal.util.typedef.F;
+
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Updates web session attributes according to {@link #updatesMap} and {@link #accessTime},
+ * {@link #maxInactiveInterval}.
+ */
+public class WebSessionAttributeProcessor implements EntryProcessor<String, WebSessionEntity, Void>,
+ Serializable, Binarylizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Attribute updates. */
+ private Map<String, byte[]> updatesMap;
+
+ /** Access time update. */
+ private long accessTime;
+
+ /** Max inactive interval update. */
+ private int maxInactiveInterval;
+
+ /** Indicates whether apply or not max inactive interval update. */
+ private boolean maxIntervalChanged;
+
+ /**
+ * Empty constructor for serialization.
+ */
+ public WebSessionAttributeProcessor() {
+ // No-op.
+ }
+
+ /**
+ * Constructs attribute processor.
+ *
+ * @param updatesMap Updates that should be performed on entity attributes.
+ * @param accessTime Access time.
+ * @param maxInactiveInterval Max inactive interval.
+ * @param maxIntervalChanged {@code True} if max inactive interval should be updated.
+ */
+ public WebSessionAttributeProcessor(final Map<String, byte[]> updatesMap, final long accessTime,
+ final int maxInactiveInterval, final boolean maxIntervalChanged) {
+ this.updatesMap = updatesMap;
+ this.accessTime = accessTime;
+ this.maxInactiveInterval = maxInactiveInterval;
+ this.maxIntervalChanged = maxIntervalChanged;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(final BinaryWriter writer) throws BinaryObjectException {
+ final BinaryRawWriter rawWriter = writer.rawWriter();
+
+ rawWriter.writeMap(updatesMap);
+ rawWriter.writeLong(accessTime);
+ rawWriter.writeBoolean(maxIntervalChanged);
+
+ if (maxIntervalChanged)
+ rawWriter.writeInt(maxInactiveInterval);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(final BinaryReader reader) throws BinaryObjectException {
+ final BinaryRawReader rawReader = reader.rawReader();
+
+ updatesMap = rawReader.readMap();
+ accessTime = rawReader.readLong();
+ maxIntervalChanged = rawReader.readBoolean();
+
+ if (maxIntervalChanged)
+ maxInactiveInterval = rawReader.readInt();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Void process(final MutableEntry<String, WebSessionEntity> entry,
+ final Object... arguments) throws EntryProcessorException {
+ final WebSessionEntity entity = entry.getValue();
+
+ final WebSessionEntity newEntity = new WebSessionEntity(entity);
+
+ if (newEntity.accessTime() < accessTime)
+ newEntity.accessTime(accessTime);
+
+ if (maxIntervalChanged)
+ newEntity.maxInactiveInterval(maxInactiveInterval);
+
+ if (!F.isEmpty(updatesMap)) {
+ for (final Map.Entry<String, byte[]> update : updatesMap.entrySet()) {
+ final String name = update.getKey();
+
+ assert name != null;
+
+ final byte[] val = update.getValue();
+
+ if (val != null)
+ newEntity.putAttribute(name, val);
+ else
+ newEntity.removeAttribute(name);
+ }
+ }
+
+ entry.setValue(newEntity);
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionEntity.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionEntity.java b/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionEntity.java
new file mode 100644
index 0000000..c504ee0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/websession/WebSessionEntity.java
@@ -0,0 +1,193 @@
+/*
+ * 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.internal.websession;
+
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawReader;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryReader;
+import org.apache.ignite.binary.BinaryWriter;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Entity
+ */
+public class WebSessionEntity implements Serializable, Binarylizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Session ID. */
+ private String id;
+
+ /** Creation time. */
+ private long createTime;
+
+ /** Last access time. */
+ private long accessTime;
+
+ /** Maximum inactive interval. */
+ private int maxInactiveInterval;
+
+ /** Attributes. */
+ @GridToStringExclude
+ private Map<String, byte[]> attrs;
+
+ /**
+ * Constructor.
+ */
+ public WebSessionEntity() {
+ // No-op.
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param id Session ID.
+ * @param createTime Session create time.
+ * @param accessTime Session last access time.
+ * @param maxInactiveInterval Session will be removed if not accessed more then this value.
+ */
+ public WebSessionEntity(final String id, final long createTime, final long accessTime,
+ final int maxInactiveInterval) {
+ this.id = id;
+ this.createTime = createTime;
+ this.accessTime = accessTime;
+ this.maxInactiveInterval = maxInactiveInterval;
+ }
+
+ /**
+ * Constructor.
+ */
+ public WebSessionEntity(final WebSessionEntity other) {
+ this(other.id(), other.createTime(), other.accessTime(), other.maxInactiveInterval());
+
+ if (!other.attributes().isEmpty())
+ attrs = new HashMap<>(other.attributes());
+ }
+
+ /**
+ * @return Session ID.
+ */
+ public String id() {
+ return id;
+ }
+
+ /**
+ * @return Create time.
+ */
+ public long createTime() {
+ return createTime;
+ }
+
+ /**
+ * @return Access time.
+ */
+ public long accessTime() {
+ return accessTime;
+ }
+
+ /**
+ * Set access time.
+ *
+ * @param accessTime Access time.
+ */
+ public void accessTime(final long accessTime) {
+ this.accessTime = accessTime;
+ }
+
+ /**
+ * @return Max inactive interval.
+ */
+ public int maxInactiveInterval() {
+ return maxInactiveInterval;
+ }
+
+ /**
+ * Set max inactive interval;
+ *
+ * @param maxInactiveInterval Max inactive interval.
+ */
+ public void maxInactiveInterval(final int maxInactiveInterval) {
+ this.maxInactiveInterval = maxInactiveInterval;
+ }
+
+ /**
+ * @return Session attributes or {@link Collections#emptyMap()}.
+ */
+ public Map<String, byte[]> attributes() {
+ return attrs == null ? Collections.<String, byte[]>emptyMap() : attrs;
+ }
+
+ /**
+ * Add attribute to attribute map.
+ *
+ * @param name Attribute name.
+ * @param val Attribute value.
+ */
+ public void putAttribute(final String name, final byte[] val) {
+ if (attrs == null)
+ attrs = new HashMap<>();
+
+ attrs.put(name, val);
+ }
+
+ /**
+ * Remove attribute.
+ *
+ * @param name Attribute name.
+ */
+ public void removeAttribute(final String name) {
+ if (attrs != null)
+ attrs.remove(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(final BinaryWriter writer) throws BinaryObjectException {
+ final BinaryRawWriter rawWriter = writer.rawWriter();
+
+ rawWriter.writeString(id);
+ rawWriter.writeLong(createTime);
+ rawWriter.writeLong(accessTime);
+ rawWriter.writeInt(maxInactiveInterval);
+ rawWriter.writeMap(attrs);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(final BinaryReader reader) throws BinaryObjectException {
+ final BinaryRawReader rawReader = reader.rawReader();
+
+ id = rawReader.readString();
+ createTime = rawReader.readLong();
+ accessTime = rawReader.readLong();
+ maxInactiveInterval = rawReader.readInt();
+ attrs = rawReader.readMap();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(WebSessionEntity.class, this,
+ "attributes", attrs != null ? attrs.keySet() : Collections.<String>emptySet());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSession.java
----------------------------------------------------------------------
diff --git a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSession.java b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSession.java
index 675c4ca..2e43f2f 100644
--- a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSession.java
+++ b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSession.java
@@ -79,9 +79,9 @@ class WebSession implements HttpSession, Externalizable {
@GridToStringExclude
private transient ServletContext ctx;
- /** Listener. */
+ /** Web session filter. */
@GridToStringExclude
- private transient WebSessionListener lsnr;
+ private transient WebSessionFilter filter;
/** New session flag. */
private transient boolean isNew;
@@ -155,12 +155,12 @@ class WebSession implements HttpSession, Externalizable {
}
/**
- * @param lsnr Listener.
+ * @param filter Filter.
*/
- public void listener(WebSessionListener lsnr) {
- assert lsnr != null;
+ public void filter(final WebSessionFilter filter) {
+ assert filter != null;
- this.lsnr = lsnr;
+ this.filter = filter;
}
/**
@@ -310,7 +310,7 @@ class WebSession implements HttpSession, Externalizable {
updates = null;
- lsnr.destroySession(id);
+ filter.destroySession(id);
genSes.invalidate();
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionFilter.java
----------------------------------------------------------------------
diff --git a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionFilter.java b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionFilter.java
index f718035..785f472 100644
--- a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionFilter.java
+++ b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionFilter.java
@@ -19,6 +19,7 @@ package org.apache.ignite.cache.websession;
import java.io.IOException;
import java.util.Collection;
+import java.util.Map;
import javax.cache.CacheException;
import javax.cache.expiry.Duration;
import javax.cache.expiry.ExpiryPolicy;
@@ -42,13 +43,17 @@ import org.apache.ignite.IgniteTransactions;
import org.apache.ignite.cluster.ClusterTopologyException;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.internal.util.typedef.C1;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.websession.WebSessionAttributeProcessor;
+import org.apache.ignite.internal.websession.WebSessionEntity;
import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.startup.servlet.ServletContextListenerStartup;
import org.apache.ignite.transactions.Transaction;
@@ -182,21 +187,27 @@ public class WebSessionFilter implements Filter {
/** Web sessions caching retry on fail timeout parameter name. */
public static final String WEB_SES_RETRIES_TIMEOUT_NAME_PARAM = "IgniteWebSessionsRetriesTimeout";
+ /** */
+ public static final String WEB_SES_KEEP_BINARY_PARAM = "IgniteWebSessionsKeepBinary";
+
/** Default retry on fail flag value. */
public static final int DFLT_MAX_RETRIES_ON_FAIL = 3;
/** Default retry on fail timeout flag value. */
public static final int DFLT_RETRIES_ON_FAIL_TIMEOUT = 10000;
+ /** Default keep binary flag. */
+ public static final boolean DFLT_KEEP_BINARY_FLAG = true;
+
/** Cache. */
private IgniteCache<String, WebSession> cache;
+ /** Binary cache */
+ private IgniteCache<String, WebSessionEntity> binaryCache;
+
/** Transactions. */
private IgniteTransactions txs;
- /** Listener. */
- private WebSessionListener lsnr;
-
/** Logger. */
private IgniteLogger log;
@@ -221,6 +232,12 @@ public class WebSessionFilter implements Filter {
/** */
private int retriesTimeout;
+ /** */
+ private boolean keepBinary = DFLT_KEEP_BINARY_FLAG;
+
+ /** */
+ private Marshaller marshaller;
+
/** {@inheritDoc} */
@Override public void init(FilterConfig cfg) throws ServletException {
ctx = cfg.getServletContext();
@@ -256,6 +273,11 @@ public class WebSessionFilter implements Filter {
throw new IgniteException("Retries timeout parameter is invalid: " + retriesTimeoutStr, e);
}
+ final String binParam = cfg.getInitParameter(WEB_SES_KEEP_BINARY_PARAM);
+
+ if (!F.isEmpty(binParam))
+ keepBinary = Boolean.parseBoolean(binParam);
+
webSesIgnite = G.ignite(gridName);
if (webSesIgnite == null)
@@ -266,9 +288,9 @@ public class WebSessionFilter implements Filter {
log = webSesIgnite.log();
- initCache();
+ marshaller = webSesIgnite.configuration().getMarshaller();
- lsnr = new WebSessionListener(webSesIgnite, this, retries);
+ initCache();
String srvInfo = ctx.getServerInfo();
@@ -304,17 +326,12 @@ public class WebSessionFilter implements Filter {
}
/**
- * @return Cache.
- */
- IgniteCache<String, WebSession> getCache(){
- return cache;
- }
-
- /**
* Init cache.
*/
+ @SuppressWarnings("unchecked")
void initCache() {
cache = webSesIgnite.cache(cacheName);
+ binaryCache = webSesIgnite.cache(cacheName);
if (cache == null)
throw new IgniteException("Cache for web sessions is not started (is it configured?): " + cacheName);
@@ -357,13 +374,13 @@ public class WebSessionFilter implements Filter {
try {
if (txEnabled) {
try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
- sesId = doFilter0(httpReq, res, chain);
+ sesId = doFilterDispatch(httpReq, res, chain);
tx.commit();
}
}
else
- sesId = doFilter0(httpReq, res, chain);
+ sesId = doFilterDispatch(httpReq, res, chain);
}
catch (Exception e) {
U.error(log, "Failed to update web session: " + sesId, e);
@@ -374,6 +391,25 @@ public class WebSessionFilter implements Filter {
}
/**
+ * Use {@link WebSession} or {@link WebSessionV2} according to {@link #keepBinary} flag.
+ *
+ * @param httpReq Request.
+ * @param res Response.
+ * @param chain Filter chain.
+ * @return Session ID.
+ * @throws IOException
+ * @throws ServletException
+ * @throws CacheException
+ */
+ private String doFilterDispatch(HttpServletRequest httpReq, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException, CacheException {
+ if (keepBinary)
+ return doFilterV2(httpReq, res, chain);
+
+ return doFilterV1(httpReq, res, chain);
+ }
+
+ /**
* @param httpReq Request.
* @param res Response.
* @param chain Filter chain.
@@ -382,32 +418,23 @@ public class WebSessionFilter implements Filter {
* @throws ServletException In case of servlet error.
* @throws CacheException In case of other error.
*/
- private String doFilter0(HttpServletRequest httpReq, ServletResponse res, FilterChain chain) throws IOException,
+ private String doFilterV1(HttpServletRequest httpReq, ServletResponse res, FilterChain chain) throws IOException,
ServletException, CacheException {
WebSession cached = null;
String sesId = httpReq.getRequestedSessionId();
if (sesId != null) {
- if (sesIdTransformer != null)
- sesId = sesIdTransformer.apply(sesId);
+ sesId = transformSessionId(sesId);
for (int i = 0; i < retries; i++) {
try {
cached = cache.get(sesId);
+
+ break;
}
catch (CacheException | IgniteException | IllegalStateException e) {
- if (log.isDebugEnabled())
- log.debug(e.getMessage());
-
- if (i == retries - 1)
- throw new IgniteException("Failed to handle request [session= " + sesId + "]", e);
- else {
- if (log.isDebugEnabled())
- log.debug("Failed to handle request (will retry): " + sesId);
-
- handleCacheOperationException(e);
- }
+ handleLoadSessionException(sesId, i, e);
}
}
@@ -436,16 +463,15 @@ public class WebSessionFilter implements Filter {
cached = createSession(httpReq);
}
}
- else {
+ else
cached = createSession(httpReq);
- sesId = cached.getId();
- }
-
assert cached != null;
+ sesId = cached.getId();
+
cached.servletContext(ctx);
- cached.listener(lsnr);
+ cached.filter(this);
cached.resetUpdates();
cached.genSes(httpReq.getSession(false));
@@ -456,13 +482,131 @@ public class WebSessionFilter implements Filter {
HttpSession ses = httpReq.getSession(false);
if (ses != null && ses instanceof WebSession) {
- Collection<T2<String, Object>> updates = ((WebSession)ses).updates();
+ Collection<T2<String, Object>> updates = ((WebSession) ses).updates();
+
+ if (updates != null)
+ updateAttributes(transformSessionId(sesId), updates, ses.getMaxInactiveInterval());
+ }
+
+ return sesId;
+ }
+
+ /**
+ * @param httpReq Request.
+ * @param res Response.
+ * @param chain Filter chain.
+ * @return Session ID.
+ * @throws IOException In case of I/O error.
+ * @throws ServletException In case oif servlet error.
+ * @throws CacheException In case of other error.
+ */
+ private String doFilterV2(HttpServletRequest httpReq, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException, CacheException {
+ WebSessionV2 cached = null;
+
+ String sesId = httpReq.getRequestedSessionId();
+
+ if (sesId != null) {
+ sesId = transformSessionId(sesId);
+
+ // Load from cache.
+ for (int i = 0; i < retries; i++) {
+ try {
+ final WebSessionEntity entity = binaryCache.get(sesId);
+
+ if (entity != null)
+ cached = new WebSessionV2(sesId, null, false, ctx, entity, marshaller);
+
+ break;
+ }
+ catch (CacheException | IgniteException | IllegalStateException e) {
+ handleLoadSessionException(sesId, i, e);
+ }
+ }
+
+ if (cached != null) {
+ if (log.isDebugEnabled())
+ log.debug("Using cached session for ID: " + sesId);
+ }
+ // If not found - invalidate session and create new one.
+ // Invalidate, because session might be removed from cache
+ // according to expiry policy.
+ else {
+ if (log.isDebugEnabled())
+ log.debug("Cached session was invalidated and doesn't exist: " + sesId);
- if (updates != null) {
- lsnr.updateAttributes(sesIdTransformer != null ? sesIdTransformer.apply(ses.getId()) : ses.getId(),
- updates, ses.getMaxInactiveInterval());
+ final HttpSession ses = httpReq.getSession(false);
+
+ if (ses != null) {
+ try {
+ ses.invalidate();
+ }
+ catch (IllegalStateException ignore) {
+ // Session was already invalidated.
+ }
+ }
+
+ cached = createSessionV2(httpReq);
}
}
+ // No session was requested by the client, create new one and put in the request.
+ else
+ cached = createSessionV2(httpReq);
+
+ assert cached != null;
+
+ sesId = cached.getId();
+
+ httpReq = new RequestWrapperV2(httpReq, cached);
+
+ chain.doFilter(httpReq, res);
+
+ if (!cached.isValid())
+ binaryCache.remove(cached.id());
+ // Changed session ID.
+ else if (!cached.getId().equals(sesId)) {
+ final String oldId = cached.getId();
+
+ cached.invalidate();
+
+ binaryCache.remove(oldId);
+ }
+ else
+ updateAttributesV2(cached.getId(), cached);
+
+ return sesId;
+ }
+
+ /**
+ * Log and process exception happened on loading session from cache.
+ *
+ * @param sesId Session ID.
+ * @param tryCnt Try count.
+ * @param e Caught exception.
+ */
+ private void handleLoadSessionException(final String sesId, final int tryCnt, final RuntimeException e) {
+ if (log.isDebugEnabled())
+ log.debug(e.getMessage());
+
+ if (tryCnt == retries - 1)
+ throw new IgniteException("Failed to handle request [session= " + sesId + "]", e);
+ else {
+ if (log.isDebugEnabled())
+ log.debug("Failed to handle request (will retry): " + sesId);
+
+ handleCacheOperationException(e);
+ }
+ }
+
+ /**
+ * Transform session ID if ID transformer present.
+ *
+ * @param sesId Session ID to transform.
+ * @return Transformed session ID or the same if no session transformer available.
+ */
+ private String transformSessionId(final String sesId) {
+ if (sesIdTransformer != null)
+ return sesIdTransformer.apply(sesId);
return sesId;
}
@@ -477,7 +621,7 @@ public class WebSessionFilter implements Filter {
private WebSession createSession(HttpServletRequest httpReq) {
HttpSession ses = httpReq.getSession(true);
- String sesId = sesIdTransformer != null ? sesIdTransformer.apply(ses.getId()) : ses.getId();
+ String sesId = transformSessionId(ses.getId());
return createSession(ses, sesId);
}
@@ -500,19 +644,10 @@ public class WebSessionFilter implements Filter {
for (int i = 0; i < retries; i++) {
try {
- IgniteCache<String, WebSession> cache0;
-
- if (cached.getMaxInactiveInterval() > 0) {
- long ttl = cached.getMaxInactiveInterval() * 1000;
-
- ExpiryPolicy plc = new ModifiedExpiryPolicy(new Duration(MILLISECONDS, ttl));
-
- cache0 = cache.withExpiryPolicy(plc);
- }
- else
- cache0 = cache;
+ final IgniteCache<String, WebSession> cache0 =
+ cacheWithExpiryPolicy(cached.getMaxInactiveInterval(), cache);
- WebSession old = cache0.getAndPutIfAbsent(sesId, cached);
+ final WebSession old = cache0.getAndPutIfAbsent(sesId, cached);
if (old != null) {
cached = old;
@@ -524,27 +659,210 @@ public class WebSessionFilter implements Filter {
break;
}
catch (CacheException | IgniteException | IllegalStateException e) {
- if (log.isDebugEnabled())
- log.debug(e.getMessage());
+ handleCreateSessionException(sesId, i, e);
+ }
+ }
+
+ return cached;
+ }
+
+ /**
+ * Log error and delegate exception processing to {@link #handleCacheOperationException(Exception)}
+ *
+ * @param sesId Session ID.
+ * @param tryCnt Try count.
+ * @param e Exception to process.
+ */
+ private void handleCreateSessionException(final String sesId, final int tryCnt, final RuntimeException e) {
+ if (log.isDebugEnabled())
+ log.debug(e.getMessage());
+
+ if (tryCnt == retries - 1)
+ throw new IgniteException("Failed to save session: " + sesId, e);
+ else {
+ if (log.isDebugEnabled())
+ log.debug("Failed to save session (will retry): " + sesId);
+
+ handleCacheOperationException(e);
+ }
+ }
+
+ private WebSessionV2 createSessionV2(final HttpSession ses, final String sesId) throws IOException {
+ if (log.isDebugEnabled())
+ log.debug("Session created: " + sesId);
- if (i == retries - 1)
- throw new IgniteException("Failed to save session: " + sesId, e);
+ WebSessionV2 cached = new WebSessionV2(sesId, ses, true, ctx, null, marshaller);
+
+ final WebSessionEntity marshaledEntity = cached.marshalAttributes();
+
+ for (int i = 0; i < retries; i++) {
+ try {
+ final IgniteCache<String, WebSessionEntity> cache0 = cacheWithExpiryPolicy(
+ cached.getMaxInactiveInterval(), binaryCache);
+
+ final WebSessionEntity old = cache0.getAndPutIfAbsent(sesId, marshaledEntity);
+
+ if (old != null)
+ cached = new WebSessionV2(sesId, null, false, ctx, old, marshaller);
+ else
+ cached = new WebSessionV2(sesId, null, false, ctx, marshaledEntity, marshaller);
+
+ break;
+ }
+ catch (CacheException | IgniteException | IllegalStateException e) {
+ handleCreateSessionException(sesId, i, e);
+ }
+ }
+
+ return cached;
+ }
+
+ /**
+ * @param httpReq HTTP request.
+ * @return Cached session.
+ */
+ private WebSessionV2 createSessionV2(HttpServletRequest httpReq) throws IOException {
+ final HttpSession ses = httpReq.getSession(true);
+
+ final String sesId = transformSessionId(ses.getId());
+
+ return createSessionV2(ses, sesId);
+ }
+
+ /**
+ * @param maxInactiveInteval Interval to use in expiry policy.
+ * @param cache Cache.
+ * @param <T> Cached object type.
+ * @return Cache with expiry policy if {@code maxInactiveInteval} greater than zero.
+ */
+ private <T> IgniteCache<String, T> cacheWithExpiryPolicy(final int maxInactiveInteval,
+ final IgniteCache<String, T> cache) {
+ if (maxInactiveInteval > 0) {
+ long ttl = maxInactiveInteval * 1000;
+
+ ExpiryPolicy plc = new ModifiedExpiryPolicy(new Duration(MILLISECONDS, ttl));
+
+ return cache.withExpiryPolicy(plc);
+ }
+
+ return cache;
+ }
+
+ /**
+ * @param sesId Session ID.
+ */
+ public void destroySession(String sesId) {
+ assert sesId != null;
+ for (int i = 0; i < retries; i++) {
+ try {
+ if (cache.remove(sesId) && log.isDebugEnabled())
+ log.debug("Session destroyed: " + sesId);
+ }
+ catch (CacheException | IgniteException | IllegalStateException e) {
+ if (i == retries - 1) {
+ U.warn(log, "Failed to remove session [sesId=" +
+ sesId + ", retries=" + retries + ']');
+ }
else {
- if (log.isDebugEnabled())
- log.debug("Failed to save session (will retry): " + sesId);
+ U.warn(log, "Failed to remove session (will retry): " + sesId);
handleCacheOperationException(e);
}
}
}
+ }
- return cached;
+ /**
+ * @param sesId Session ID.
+ * @param updates Updates list.
+ * @param maxInactiveInterval Max session inactive interval.
+ */
+ @SuppressWarnings("unchecked")
+ public void updateAttributes(String sesId, Collection<T2<String, Object>> updates, int maxInactiveInterval) {
+ assert sesId != null;
+ assert updates != null;
+
+ if (log.isDebugEnabled())
+ log.debug("Session attributes updated [id=" + sesId + ", updates=" + updates + ']');
+
+ try {
+ for (int i = 0; i < retries; i++) {
+ try {
+ final IgniteCache<String, WebSession> cache0 = cacheWithExpiryPolicy(maxInactiveInterval, cache);
+
+ cache0.invoke(sesId, WebSessionListener.newAttributeProcessor(updates));
+
+ break;
+ }
+ catch (CacheException | IgniteException | IllegalStateException e) {
+ handleAttributeUpdateException(sesId, i, e);
+ }
+ }
+ }
+ catch (Exception e) {
+ U.error(log, "Failed to update session attributes [id=" + sesId + ']', e);
+ }
+ }
+
+ /**
+ * @param sesId Session ID.
+ * @param ses Web session.
+ */
+ public void updateAttributesV2(final String sesId, final WebSessionV2 ses) throws IOException {
+ assert sesId != null;
+ assert ses != null;
+
+ final Map<String, byte[]> updatesMap = ses.binaryUpdatesMap();
+
+ if (log.isDebugEnabled())
+ log.debug("Session binary attributes updated [id=" + sesId + ", updates=" + updatesMap.keySet() + ']');
+
+ try {
+ for (int i = 0; i < retries; i++) {
+ try {
+ final IgniteCache<String, WebSessionEntity> cache0 =
+ cacheWithExpiryPolicy(ses.getMaxInactiveInterval(), binaryCache);
+
+ cache0.invoke(sesId, new WebSessionAttributeProcessor(updatesMap.isEmpty() ? null : updatesMap,
+ ses.getLastAccessedTime(), ses.getMaxInactiveInterval(), ses.isMaxInactiveIntervalChanged()));
+
+ break;
+ }
+ catch (CacheException | IgniteException | IllegalStateException e) {
+ handleAttributeUpdateException(sesId, i, e);
+ }
+ }
+ }
+ catch (Exception e) {
+ U.error(log, "Failed to update session V2 attributes [id=" + sesId + ']', e);
+ }
+ }
+
+ /**
+ * Log error and delegate processing to {@link #handleCacheOperationException(Exception)}.
+ *
+ * @param sesId Session ID.
+ * @param tryCnt Try count.
+ * @param e Exception to process.
+ */
+ private void handleAttributeUpdateException(final String sesId, final int tryCnt, final RuntimeException e) {
+ if (tryCnt == retries - 1) {
+ U.warn(log, "Failed to apply updates for session (maximum number of retries exceeded) [sesId=" +
+ sesId + ", retries=" + retries + ']');
+ }
+ else {
+ U.warn(log, "Failed to apply updates for session (will retry): " + sesId);
+
+ handleCacheOperationException(e);
+ }
}
+
/**
* Handles cache operation exception.
* @param e Exception
*/
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
void handleCacheOperationException(Exception e){
IgniteFuture<?> retryFut = null;
@@ -608,7 +926,7 @@ public class WebSessionFilter implements Filter {
if (create) {
this.ses = createSession((HttpServletRequest)getRequest());
this.ses.servletContext(ctx);
- this.ses.listener(lsnr);
+ this.ses.filter(WebSessionFilter.this);
this.ses.resetUpdates();
}
else
@@ -633,10 +951,73 @@ public class WebSessionFilter implements Filter {
this.ses = createSession(ses, newId);
this.ses.servletContext(ctx);
- this.ses.listener(lsnr);
+ this.ses.filter(WebSessionFilter.this);
this.ses.resetUpdates();
return newId;
}
}
+
+ /**
+ * Request wrapper V2.
+ */
+ private class RequestWrapperV2 extends HttpServletRequestWrapper {
+ /** Session. */
+ private WebSessionV2 ses;
+
+ /**
+ * @param req Request.
+ * @param ses Session.
+ */
+ private RequestWrapperV2(HttpServletRequest req, WebSessionV2 ses) {
+ super(req);
+
+ assert ses != null;
+
+ this.ses = ses;
+ }
+
+ /** {@inheritDoc} */
+ @Override public HttpSession getSession(boolean create) {
+ if (!ses.isValid()) {
+ binaryCache.remove(ses.id());
+
+ if (create) {
+ try {
+ ses = createSessionV2((HttpServletRequest) getRequest());
+ }
+ catch (IOException e) {
+ throw new IgniteException(e);
+ }
+ }
+ else
+ return null;
+ }
+
+ return ses;
+ }
+
+ /** {@inheritDoc} */
+ @Override public HttpSession getSession() {
+ return getSession(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String changeSessionId() {
+ final HttpServletRequest req = (HttpServletRequest) getRequest();
+
+ final String newId = req.changeSessionId();
+
+ if (!F.eq(newId, ses.getId())) {
+ try {
+ ses = createSessionV2(ses, newId);
+ }
+ catch (IOException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ return newId;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionListener.java
----------------------------------------------------------------------
diff --git a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionListener.java b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionListener.java
index 0d8ffec..ab41879 100644
--- a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionListener.java
+++ b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionListener.java
@@ -22,127 +22,24 @@ import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
-import javax.cache.CacheException;
-import javax.cache.expiry.Duration;
-import javax.cache.expiry.ExpiryPolicy;
-import javax.cache.expiry.ModifiedExpiryPolicy;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.MutableEntry;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.IgniteException;
-import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.util.typedef.T2;
-import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
/**
* Session listener for web sessions caching.
*/
class WebSessionListener {
- /** Filter. */
- private final WebSessionFilter filter;
-
- /** Maximum retries. */
- private final int retries;
-
- /** Logger. */
- private final IgniteLogger log;
-
- /**
- * @param ignite Grid.
- * @param filter Filter.
- * @param retries Maximum retries.
- */
- WebSessionListener(Ignite ignite, WebSessionFilter filter, int retries) {
- assert ignite != null;
- assert filter != null;
-
- this.filter = filter;
- this.retries = retries > 0 ? retries : 1;
-
- log = ignite.log();
- }
-
- /**
- * @param sesId Session ID.
- */
- public void destroySession(String sesId) {
- assert sesId != null;
- for (int i = 0; i < retries; i++) {
- try {
- if (filter.getCache().remove(sesId) && log.isDebugEnabled())
- log.debug("Session destroyed: " + sesId);
- }
- catch (CacheException | IgniteException | IllegalStateException e) {
- if (i == retries - 1) {
- U.warn(log, "Failed to remove session [sesId=" +
- sesId + ", retries=" + retries + ']');
- }
- else {
- U.warn(log, "Failed to remove session (will retry): " + sesId);
-
- filter.handleCacheOperationException(e);
- }
- }
- }
- }
-
/**
- * @param sesId Session ID.
- * @param updates Updates list.
- * @param maxInactiveInterval Max session inactive interval.
+ * Creates new instance of attribute processor. Used for compatibility.
+ *
+ * @param updates Updates.
+ * @return New instance of attribute processor.
*/
- @SuppressWarnings("unchecked")
- public void updateAttributes(String sesId, Collection<T2<String, Object>> updates, int maxInactiveInterval) {
- assert sesId != null;
- assert updates != null;
-
- if (log.isDebugEnabled())
- log.debug("Session attributes updated [id=" + sesId + ", updates=" + updates + ']');
-
- try {
- for (int i = 0; i < retries; i++) {
- try {
- IgniteCache<String, WebSession> cache0;
-
- if (maxInactiveInterval > 0) {
- long ttl = maxInactiveInterval * 1000;
-
- ExpiryPolicy plc = new ModifiedExpiryPolicy(new Duration(MILLISECONDS, ttl));
-
- cache0 = filter.getCache().withExpiryPolicy(plc);
- }
- else
- cache0 = filter.getCache();
-
- cache0.invoke(sesId, new AttributesProcessor(updates));
-
- break;
- }
- catch (CacheException | IgniteException | IllegalStateException e) {
- if (i == retries - 1) {
- U.warn(log, "Failed to apply updates for session (maximum number of retries exceeded) [sesId=" +
- sesId + ", retries=" + retries + ']');
- }
- else {
- U.warn(log, "Failed to apply updates for session (will retry): " + sesId);
-
- filter.handleCacheOperationException(e);
- }
- }
- }
- }
- catch (Exception e) {
- U.error(log, "Failed to update session attributes [id=" + sesId + ']', e);
- }
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return S.toString(WebSessionListener.class, this);
+ public static EntryProcessor<String, WebSession, Void> newAttributeProcessor(
+ final Collection<T2<String, Object>> updates) {
+ return new AttributesProcessor(updates);
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionV2.java
----------------------------------------------------------------------
diff --git a/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionV2.java b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionV2.java
new file mode 100644
index 0000000..07a85db
--- /dev/null
+++ b/modules/web/src/main/java/org/apache/ignite/cache/websession/WebSessionV2.java
@@ -0,0 +1,405 @@
+/*
+ * 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.cache.websession;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.websession.WebSessionEntity;
+import org.apache.ignite.marshaller.Marshaller;
+import org.jetbrains.annotations.Nullable;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Session implementation that uses internal entity, which stores binary attributes,
+ * for safe caching and processing on remote nodes.
+ */
+class WebSessionV2 implements HttpSession {
+ /** Empty session context. */
+ @SuppressWarnings("deprecation")
+ private static final HttpSessionContext EMPTY_SES_CTX = new HttpSessionContext() {
+ @Nullable @Override public HttpSession getSession(String id) {
+ return null;
+ }
+
+ @Override public Enumeration<String> getIds() {
+ return Collections.enumeration(Collections.<String>emptyList());
+ }
+ };
+
+ /** Placeholder for removed attribute. */
+ private static final Object REMOVED_ATTR = new Object();
+
+ /** Servlet context. */
+ @GridToStringExclude
+ private final ServletContext ctx;
+
+ /** Entity that holds binary attributes. */
+ private WebSessionEntity entity;
+
+ /** Attributes. */
+ protected Map<String, Object> attrs;
+
+ /** Timestamp that shows when this object was created. (Last access time from user request) */
+ private final long accessTime;
+
+ /** Cached session TTL since last query. */
+ private int maxInactiveInterval;
+
+ /** Flag indicates if {@link #maxInactiveInterval} waiting for update in cache. */
+ private boolean maxInactiveIntervalChanged;
+
+ /** New session flag. */
+ private boolean isNew;
+
+ /** Session invalidation flag. */
+ private boolean invalidated;
+
+ /** Grid marshaller. */
+ private final Marshaller marshaller;
+
+ /** Original session to delegate invalidation. */
+ private final HttpSession genuineSes;
+
+ /**
+ * @param id Session ID.
+ * @param ses Session.
+ * @param isNew Is new flag.
+ */
+ WebSessionV2(final String id, final @Nullable HttpSession ses, final boolean isNew, final ServletContext ctx,
+ @Nullable WebSessionEntity entity, final Marshaller marshaller) {
+ assert id != null;
+ assert marshaller != null;
+ assert ctx != null;
+ assert ses != null || entity != null;
+
+ this.marshaller = marshaller;
+ this.ctx = ctx;
+ this.isNew = isNew;
+ this.genuineSes = ses;
+
+ accessTime = System.currentTimeMillis();
+
+ if (entity == null) {
+ entity = new WebSessionEntity(id, ses.getCreationTime(), accessTime,
+ ses.getMaxInactiveInterval());
+ }
+
+ this.entity = entity;
+
+ maxInactiveInterval = entity.maxInactiveInterval();
+
+ if (ses != null) {
+ final Enumeration<String> names = ses.getAttributeNames();
+
+ while (names.hasMoreElements()) {
+ final String name = names.nextElement();
+
+ attributes().put(name, ses.getAttribute(name));
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getCreationTime() {
+ assertValid();
+
+ return entity.createTime();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getId() {
+ assertValid();
+
+ return entity.id();
+ }
+
+ /**
+ * @return Session ID without throwing exception.
+ */
+ public String id() {
+ return entity.id();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getLastAccessedTime() {
+ assertValid();
+
+ return accessTime;
+ }
+
+ /** {@inheritDoc} */
+ @Override public ServletContext getServletContext() {
+ return ctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMaxInactiveInterval(final int interval) {
+ maxInactiveInterval = interval;
+
+ maxInactiveIntervalChanged = true;
+ }
+
+ /**
+ * @return {@code True} if {@link #setMaxInactiveInterval(int)} was invoked.
+ */
+ public boolean isMaxInactiveIntervalChanged() {
+ return maxInactiveIntervalChanged;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMaxInactiveInterval() {
+ return maxInactiveInterval;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("deprecation")
+ @Override public HttpSessionContext getSessionContext() {
+ return EMPTY_SES_CTX;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object getAttribute(final String name) {
+ assertValid();
+
+ Object attr = attributes().get(name);
+
+ if (attr == REMOVED_ATTR)
+ return null;
+
+ if (attr == null) {
+ final byte[] bytes = entity.attributes().get(name);
+
+ if (bytes != null) {
+ // deserialize
+ try {
+ attr = unmarshal(bytes);
+ }
+ catch (IOException e) {
+ throw new IgniteException(e);
+ }
+
+ attributes().put(name, attr);
+ }
+ }
+
+ return attr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object getValue(final String name) {
+ return getAttribute(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setAttribute(final String name, final Object val) {
+ assertValid();
+
+ if (val == null)
+ removeAttribute(name);
+ else
+ attributes().put(name, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void putValue(final String name, final Object val) {
+ setAttribute(name, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Enumeration<String> getAttributeNames() {
+ assertValid();
+
+ return Collections.enumeration(attributeNames());
+ }
+
+ /**
+ * @return Set of attribute names.
+ */
+ private Set<String> attributeNames() {
+ if (!F.isEmpty(attrs)) {
+ final Set<String> names = new HashSet<>(entity.attributes().size() + attrs.size());
+
+ names.addAll(entity.attributes().keySet());
+
+ for (final Map.Entry<String, Object> entry : attrs.entrySet()) {
+ if (entry != REMOVED_ATTR)
+ names.add(entry.getKey());
+ }
+
+ return names;
+ }
+
+ return entity.attributes().keySet();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String[] getValueNames() {
+ assertValid();
+
+ final Set<String> names = attributeNames();
+
+ return names.toArray(new String[names.size()]);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeAttribute(final String name) {
+ assertValid();
+
+ attributes().put(name, REMOVED_ATTR);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeValue(final String name) {
+ removeAttribute(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void invalidate() {
+ assertValid();
+
+ if (genuineSes != null) {
+ try {
+ genuineSes.invalidate();
+ }
+ catch (IllegalStateException e) {
+ // Already invalidated, keep going.
+ }
+ }
+
+ invalidated = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isNew() {
+ assertValid();
+
+ return isNew;
+ }
+
+ /**
+ * @return Marshaled updates or empty map if no params.
+ * @throws IOException
+ */
+ public Map<String, byte[]> binaryUpdatesMap() throws IOException {
+ final Map<String, Object> map = attributes();
+
+ if (F.isEmpty(map))
+ return Collections.emptyMap();
+
+ final Map<String, byte[]> res = new HashMap<>(map.size());
+
+ for (final Map.Entry<String, Object> entry : map.entrySet()) {
+ Object val = entry.getValue() == REMOVED_ATTR ? null : entry.getValue();
+
+ res.put(entry.getKey(), marshal(val));
+ }
+
+ return res;
+ }
+
+ /**
+ * Unmarshal object.
+ *
+ * @param bytes Data.
+ * @param <T> Expected type.
+ * @return Unmarshaled object.
+ * @throws IOException If unarshaling failed.
+ */
+ @Nullable private <T> T unmarshal(final byte[] bytes) throws IOException {
+ if (marshaller != null) {
+ try {
+ return marshaller.unmarshal(bytes, getClass().getClassLoader());
+ }
+ catch (IgniteCheckedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Marshal object.
+ *
+ * @param obj Object to marshal.
+ * @return Binary data.
+ * @throws IOException If marshaling failed.
+ */
+ @Nullable private byte[] marshal(final Object obj) throws IOException {
+ if (marshaller != null) {
+ try {
+ return marshaller.marshal(obj);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Marshal all attributes and save to serializable entity.
+ */
+ public WebSessionEntity marshalAttributes() throws IOException {
+ final WebSessionEntity marshaled = new WebSessionEntity(getId(), entity.createTime(), accessTime,
+ maxInactiveInterval);
+
+ for (final Map.Entry<String, Object> entry : attributes().entrySet())
+ marshaled.putAttribute(entry.getKey(), marshal(entry.getValue()));
+
+ return marshaled;
+ }
+
+ /**
+ * @return Session attributes.
+ */
+ private Map<String, Object> attributes() {
+ if (attrs == null)
+ attrs = new HashMap<>();
+
+ return attrs;
+ }
+
+ /**
+ * @return {@code True} if session wasn't invalidated.
+ */
+ public boolean isValid() {
+ return !invalidated;
+ }
+
+ /**
+ * Throw {@link IllegalStateException} if session was invalidated.
+ */
+ private void assertValid() {
+ if (invalidated)
+ throw new IllegalStateException("Session was invalidated.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/test/java/org/apache/ignite/internal/websession/IgniteWebSessionSelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/web/src/test/java/org/apache/ignite/internal/websession/IgniteWebSessionSelfTestSuite.java b/modules/web/src/test/java/org/apache/ignite/internal/websession/IgniteWebSessionSelfTestSuite.java
index 2e47262..1d15127 100644
--- a/modules/web/src/test/java/org/apache/ignite/internal/websession/IgniteWebSessionSelfTestSuite.java
+++ b/modules/web/src/test/java/org/apache/ignite/internal/websession/IgniteWebSessionSelfTestSuite.java
@@ -38,6 +38,11 @@ public class IgniteWebSessionSelfTestSuite extends TestSuite {
suite.addTestSuite(WebSessionTransactionalSelfTest.class);
suite.addTestSuite(WebSessionReplicatedSelfTest.class);
+ // Old implementation tests.
+ suite.addTestSuite(WebSessionV1SelfTest.class);
+ suite.addTestSuite(WebSessionTransactionalV1SelfTest.class);
+ suite.addTestSuite(WebSessionReplicatedV1SelfTest.class);
+
System.setProperty(IGNITE_OVERRIDE_MCAST_GRP,
GridTestUtils.getNextMulticastGroup(IgniteWebSessionSelfTestSuite.class));
@@ -78,4 +83,34 @@ public class IgniteWebSessionSelfTestSuite extends TestSuite {
return "replicated";
}
}
+
+ /**
+ * Old version test.
+ */
+ public static class WebSessionV1SelfTest extends WebSessionSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean keepBinary() {
+ return false;
+ }
+ }
+
+ /**
+ * Tests web sessions with TRANSACTIONAL cache in compatibility mode.
+ */
+ public static class WebSessionTransactionalV1SelfTest extends WebSessionTransactionalSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean keepBinary() {
+ return false;
+ }
+ }
+
+ /**
+ * Tests web sessions with REPLICATED cache in compatibility mode.
+ */
+ public static class WebSessionReplicatedV1SelfTest extends WebSessionReplicatedSelfTest {
+ /** {@inheritDoc} */
+ @Override protected boolean keepBinary() {
+ return false;
+ }
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/b369d030/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java b/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
index ccf0852..1e01d3c 100644
--- a/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
+++ b/modules/web/src/test/java/org/apache/ignite/internal/websession/WebSessionSelfTest.java
@@ -18,9 +18,12 @@
package org.apache.ignite.internal.websession;
import java.io.BufferedReader;
+import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
import java.net.URL;
import java.net.URLConnection;
import java.util.Random;
@@ -28,7 +31,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import javax.servlet.ServletException;
@@ -38,12 +40,14 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.events.Event;
import org.apache.ignite.Ignition;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
@@ -72,6 +76,13 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
}
/**
+ * @return Keep binary flag.
+ */
+ protected boolean keepBinary() {
+ return true;
+ }
+
+ /**
* @throws Exception If failed.
*/
public void testSingleRequest() throws Exception {
@@ -114,7 +125,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
Ignite ignite = Ignition.start(srvCfg);
try {
- srv = startServer(TEST_JETTY_PORT, clientCfg, "client", new SessionCreateServlet());
+ srv = startServer(TEST_JETTY_PORT, clientCfg, "client", new SessionCreateServlet(keepBinary()));
URL url = new URL("http://localhost:" + TEST_JETTY_PORT + "/ignitetest/test");
@@ -173,7 +184,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
Server srv = null;
try {
- srv = startServer(TEST_JETTY_PORT, cfg, null, new SessionCreateServlet());
+ srv = startServer(TEST_JETTY_PORT, cfg, null, new SessionCreateServlet(keepBinary()));
String sesId = sendRequestAndCheckMarker("marker1", null);
sendRequestAndCheckMarker("test_string", sesId);
@@ -184,7 +195,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
}
}
- private String sendRequestAndCheckMarker(String reqMarker, String sesId) throws IOException {
+ private String sendRequestAndCheckMarker(String reqMarker, String sesId) throws IOException, IgniteCheckedException {
URLConnection conn = new URL("http://localhost:" + TEST_JETTY_PORT +
"/ignitetest/test?marker=" + reqMarker).openConnection();
conn.addRequestProperty("Cookie", "JSESSIONID=" + sesId);
@@ -194,14 +205,33 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
try (BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
sesId = rdr.readLine();
- IgniteCache<String, HttpSession> cache = G.ignite().cache(getCacheName());
+ if (!keepBinary()) {
+ IgniteCache<String, HttpSession> cache = G.ignite().cache(getCacheName());
- assertNotNull(cache);
+ assertNotNull(cache);
- HttpSession ses = cache.get(sesId);
+ HttpSession ses = cache.get(sesId);
- assertNotNull(ses);
- assertEquals(reqMarker, ((Profile)ses.getAttribute("profile")).getMarker());
+ assertNotNull(ses);
+ assertEquals(reqMarker, ((Profile) ses.getAttribute("profile")).getMarker());
+ }
+ else {
+ IgniteCache<String, WebSessionEntity> cache = G.ignite().cache(getCacheName());
+
+ assertNotNull(cache);
+
+ WebSessionEntity ses = cache.get(sesId);
+
+ assertNotNull(ses);
+
+ final byte[] data = ses.attributes().get("profile");
+
+ assertNotNull(data);
+
+ final Marshaller marshaller = G.ignite().configuration().getMarshaller();
+
+ assertEquals(reqMarker, marshaller.<Profile>unmarshal(data, getClass().getClassLoader()).getMarker());
+ }
}
return sesId;
}
@@ -216,7 +246,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
Server srv = null;
try {
- srv = startServer(TEST_JETTY_PORT, cfg, null, new SessionCreateServlet());
+ srv = startServer(TEST_JETTY_PORT, cfg, null, new SessionCreateServlet(keepBinary()));
URLConnection conn = new URL("http://localhost:" + TEST_JETTY_PORT + "/ignitetest/test").openConnection();
@@ -225,14 +255,36 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
try (BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String sesId = rdr.readLine();
- IgniteCache<String, HttpSession> cache = G.ignite().cache(getCacheName());
+ if (!keepBinary()) {
+ IgniteCache<String, HttpSession> cache = G.ignite().cache(getCacheName());
- assertNotNull(cache);
+ assertNotNull(cache);
- HttpSession ses = cache.get(sesId);
+ HttpSession ses = cache.get(sesId);
- assertNotNull(ses);
- assertEquals("val1", ses.getAttribute("key1"));
+ assertNotNull(ses);
+
+ assertEquals("val1", ses.getAttribute("key1"));
+ }
+ else {
+ final IgniteCache<String, WebSessionEntity> cache = G.ignite().cache(getCacheName());
+
+ assertNotNull(cache);
+
+ final WebSessionEntity entity = cache.get(sesId);
+
+ assertNotNull(entity);
+
+ final byte[] data = entity.attributes().get("key1");
+
+ assertNotNull(data);
+
+ final Marshaller marshaller = G.ignite().configuration().getMarshaller();
+
+ final String val = marshaller.unmarshal(data, getClass().getClassLoader());
+
+ assertEquals("val1", val);
+ }
}
}
finally {
@@ -266,18 +318,34 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
assertNotNull(invalidatedSesId);
- IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
+ if (!keepBinary()) {
+ IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
- assertNotNull(cache);
+ assertNotNull(cache);
+
+ HttpSession invalidatedSes = cache.get(invalidatedSesId);
+
+ assertNull(invalidatedSes);
+
+ // requests to subsequent getSession() returns null.
+ String ses = rdr.readLine();
- HttpSession invalidatedSes = cache.get(invalidatedSesId);
+ assertEquals("null", ses);
+ }
+ else {
+ IgniteCache<String, WebSessionEntity> cache = ignite.cache(getCacheName());
+
+ assertNotNull(cache);
- assertNull(invalidatedSes);
+ WebSessionEntity invalidatedSes = cache.get(invalidatedSesId);
- // requests to subsequent getSession() returns null.
- String ses = rdr.readLine();
+ assertNull(invalidatedSes);
- assertEquals("null", ses);
+ // requests to subsequent getSession() returns null.
+ String ses = rdr.readLine();
+
+ assertEquals("null", ses);
+ }
}
// put and update.
@@ -309,15 +377,31 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
assertTrue(latch.await(10, TimeUnit.SECONDS));
- IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
+ if (!keepBinary()) {
+ IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
- assertNotNull(cache);
+ assertNotNull(cache);
- HttpSession ses = cache.get(sesId);
+ HttpSession ses = cache.get(sesId);
- assertNotNull(ses);
+ assertNotNull(ses);
+
+ assertEquals("val10", ses.getAttribute("key10"));
+ }
+ else {
+ IgniteCache<String, WebSessionEntity> cache = ignite.cache(getCacheName());
+
+ assertNotNull(cache);
+
+ WebSessionEntity entity = cache.get(sesId);
+
+ assertNotNull(entity);
+
+ final Marshaller marshaller = ignite.configuration().getMarshaller();
- assertEquals("val10", ses.getAttribute("key10"));
+ assertEquals("val10",
+ marshaller.unmarshal(entity.attributes().get("key10"), getClass().getClassLoader()));
+ }
}
}
finally {
@@ -365,17 +449,35 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
assertTrue(newGenSesId.equals(newWebSesId));
- IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
+ if (!keepBinary()) {
+ IgniteCache<String, HttpSession> cache = ignite.cache(getCacheName());
- assertNotNull(cache);
+ assertNotNull(cache);
- Thread.sleep(1000);
+ Thread.sleep(1000);
- HttpSession ses = cache.get(newWebSesId);
+ HttpSession ses = cache.get(newWebSesId);
- assertNotNull(ses);
+ assertNotNull(ses);
+
+ assertEquals("val1", ses.getAttribute("key1"));
+ }
+ else {
+ IgniteCache<String, WebSessionEntity> cache = ignite.cache(getCacheName());
+
+ assertNotNull(cache);
+
+ Thread.sleep(1000);
- assertEquals("val1", ses.getAttribute("key1"));
+ WebSessionEntity ses = cache.get(newWebSesId);
+
+ assertNotNull(ses);
+
+ final Marshaller marshaller = ignite.configuration().getMarshaller();
+
+ assertEquals("val1",
+ marshaller.<String>unmarshal(ses.attributes().get("key1"), getClass().getClassLoader()));
+ }
}
conn = new URL("http://localhost:" + TEST_JETTY_PORT + "/ignitetest/simple").openConnection();
@@ -524,14 +626,18 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
* @param servlet Servlet.
* @return Servlet container web context for this test.
*/
- protected WebAppContext getWebContext(@Nullable String cfg, @Nullable String gridName, HttpServlet servlet) {
- WebAppContext ctx = new WebAppContext(U.resolveIgnitePath("modules/core/src/test/webapp").getAbsolutePath(),
+ protected WebAppContext getWebContext(@Nullable String cfg, @Nullable String gridName,
+ boolean keepBinaryFlag, HttpServlet servlet) {
+ final String path = keepBinaryFlag ? "modules/core/src/test/webapp" : "modules/web/src/test/webapp2";
+
+ WebAppContext ctx = new WebAppContext(U.resolveIgnitePath(path).getAbsolutePath(),
"/ignitetest");
ctx.setInitParameter("IgniteConfigurationFilePath", cfg);
ctx.setInitParameter("IgniteWebSessionsGridName", gridName);
ctx.setInitParameter("IgniteWebSessionsCacheName", getCacheName());
ctx.setInitParameter("IgniteWebSessionsMaximumRetriesOnFail", "100");
+ ctx.setInitParameter("IgniteWebSessionsKeepBinary", Boolean.toString(keepBinaryFlag));
ctx.addServlet(new ServletHolder(servlet), "/*");
@@ -552,7 +658,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
throws Exception {
Server srv = new Server(port);
- WebAppContext ctx = getWebContext(cfg, gridName, servlet);
+ WebAppContext ctx = getWebContext(cfg, gridName, keepBinary(), servlet);
srv.setHandler(ctx);
@@ -576,6 +682,16 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
* Test servlet.
*/
private static class SessionCreateServlet extends HttpServlet {
+ /** Keep binary flag. */
+ private final boolean keepBinaryFlag;
+
+ /**
+ * @param keepBinaryFlag Keep binary flag.
+ */
+ private SessionCreateServlet(final boolean keepBinaryFlag) {
+ this.keepBinaryFlag = keepBinaryFlag;
+ }
+
/** {@inheritDoc} */
@Override protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
@@ -584,6 +700,7 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
ses.setAttribute("checkCnt", 0);
ses.setAttribute("key1", "val1");
ses.setAttribute("key2", "val2");
+ ses.setAttribute("mkey", new TestObj("mval", keepBinaryFlag));
Profile p = (Profile)ses.getAttribute("profile");
@@ -638,23 +755,25 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
assert ses != null;
+ final String sesId = ses.getId();
+
if (req.getPathInfo().equals("/invalidated")) {
- X.println(">>>", "Session to invalidate with id: " + ses.getId(), ">>>");
+ X.println(">>>", "Session to invalidate with id: " + sesId, ">>>");
ses.invalidate();
- res.getWriter().println(ses.getId());
+ res.getWriter().println(sesId);
// invalidates again.
req.getSession().invalidate();
}
else if (req.getPathInfo().equals("/valid")) {
- X.println(">>>", "Created session: " + ses.getId(), ">>>");
+ X.println(">>>", "Created session: " + sesId, ">>>");
ses.setAttribute("key10", "val10");
}
- res.getWriter().println((req.getSession(false) == null) ? "null" : ses.getId());
+ res.getWriter().println((req.getSession(false) == null) ? "null" : sesId);
res.getWriter().flush();
}
@@ -746,4 +865,71 @@ public class WebSessionSelfTest extends GridCommonAbstractTest {
res.getWriter().flush();
}
}
-}
+
+ /**
+ *
+ */
+ private static class TestObj implements Externalizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private String val;
+
+ /** */
+ private boolean keepBinaryFlag;
+
+ /**
+ *
+ */
+ public TestObj() {
+ }
+
+ /**
+ * @param val Value.
+ * @param keepBinaryFlag Keep binary flag.
+ */
+ public TestObj(final String val, final boolean keepBinaryFlag) {
+ this.val = val;
+ this.keepBinaryFlag = keepBinaryFlag;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeExternal(final ObjectOutput out) throws IOException {
+ U.writeString(out, val);
+ out.writeBoolean(keepBinaryFlag);
+ System.out.println("TestObj marshalled");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+ val = U.readString(in);
+ keepBinaryFlag = in.readBoolean();
+
+ // It must be unmarshalled only on client side.
+ if (keepBinaryFlag)
+ fail("Should not be unmarshalled");
+
+ System.out.println("TestObj unmarshalled");
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final TestObj testObj = (TestObj) o;
+
+ if (keepBinaryFlag != testObj.keepBinaryFlag) return false;
+ return val != null ? val.equals(testObj.val) : testObj.val == null;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int result = val != null ? val.hashCode() : 0;
+ result = 31 * result + (keepBinaryFlag ? 1 : 0);
+ return result;
+ }
+ }
+}
\ No newline at end of file