IGNITE-2765 WebSessionFilter doesn't survive client reconnect
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/0ad4f395 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/0ad4f395 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/0ad4f395 Branch: refs/heads/ignite-1786 Commit: 0ad4f395a4b18a5cb1cba4926261cf5dd56e606d Parents: ed030e5 Author: Anton Vinogradov <a...@apache.org> Authored: Fri Mar 11 19:23:49 2016 +0300 Committer: Anton Vinogradov <a...@apache.org> Committed: Fri Mar 11 19:34:23 2016 +0300 ---------------------------------------------------------------------- .../config/websession/example-cache-base.xml | 148 +++++++++++++++ .../config/websession/example-cache-client.xml | 33 ++++ .../test/config/websession/example-cache.xml | 128 +------------ .../test/config/websession/example-cache2.xml | 31 +++ .../cache/websession/WebSessionFilter.java | 188 ++++++++++++++----- .../cache/websession/WebSessionListener.java | 60 +++--- .../internal/websession/WebSessionSelfTest.java | 72 +++++++ 7 files changed, 452 insertions(+), 208 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/modules/core/src/test/config/websession/example-cache-base.xml ---------------------------------------------------------------------- diff --git a/modules/core/src/test/config/websession/example-cache-base.xml b/modules/core/src/test/config/websession/example-cache-base.xml new file mode 100644 index 0000000..d3d5b46 --- /dev/null +++ b/modules/core/src/test/config/websession/example-cache-base.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ 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. + --> + +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util + http://www.springframework.org/schema/util/spring-util.xsd"> + <bean abstract="true" id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration"> + <!-- Set to true to enable distributed class loading for examples, default is false. --> + <property name="peerClassLoadingEnabled" value="true"/> + + <property name="marshaller"> + <bean class="org.apache.ignite.marshaller.optimized.OptimizedMarshaller"> + <!-- Set to false to allow non-serializable objects in examples, default is true. --> + <property name="requireSerializable" value="false"/> + </bean> + </property> + + <!-- Enable cache events for examples. --> + <property name="includeEventTypes"> + <util:constant static-field="org.apache.ignite.events.EventType.EVTS_CACHE"/> + </property> + + <!-- Cache configurations (all properties are optional). --> + <property name="cacheConfiguration"> + <list> + <!-- Partitioned cache example configuration (Atomic mode). --> + <bean parent="cache-template"> + <property name="name" value="partitioned"/> + <property name="cacheMode" value="PARTITIONED"/> + <property name="atomicityMode" value="ATOMIC"/> + <property name="backups" value="1"/> + + <property name="indexedTypes"> + <list> + <!-- Key and value type for SQL table Long. --> + <value>java.lang.Integer</value> + <value>java.lang.Long</value> + </list> + </property> + </bean> + + <!-- Partitioned cache example configuration (Atomic mode, PRIMARY write order mode). --> + <bean parent="cache-template"> + <property name="name" value="partitioned_primary"/> + <property name="cacheMode" value="PARTITIONED"/> + <property name="atomicityMode" value="ATOMIC"/> + <property name="atomicWriteOrderMode" value="PRIMARY"/> + <property name="backups" value="1"/> + </bean> + + <!-- Partitioned cache example configuration (Transactional mode). --> + <bean parent="cache-template"> + <property name="name" value="partitioned_tx"/> + <property name="cacheMode" value="PARTITIONED"/> + <property name="atomicityMode" value="TRANSACTIONAL"/> + <property name="nearConfiguration"> + <bean class="org.apache.ignite.configuration.NearCacheConfiguration"/> + </property> + <property name="backups" value="1"/> + </bean> + + <!-- Replicated cache example configuration (Atomic mode). --> + <bean parent="cache-template"> + <property name="name" value="replicated"/> + <property name="cacheMode" value="REPLICATED"/> + <property name="atomicityMode" value="ATOMIC"/> + <property name="indexedTypes"> + <list> + <!-- Key and value type for SQL table DimStore. --> + <value>java.lang.Integer</value> + <value>java.lang.Integer</value> + </list> + </property> + </bean> + + <!-- Replicated cache example configuration (Transactional mode). --> + <bean parent="cache-template"> + <property name="name" value="replicated_tx"/> + <property name="cacheMode" value="REPLICATED"/> + <property name="atomicityMode" value="TRANSACTIONAL"/> + </bean> + + <!-- Local cache example configuration (Atomic mode). --> + <bean parent="cache-template"> + <property name="name" value="local"/> + <property name="cacheMode" value="LOCAL"/> + <property name="atomicityMode" value="ATOMIC"/> + </bean> + + <!-- Local cache example configuration (Transactional mode). --> + <bean parent="cache-template"> + <property name="name" value="local_tx"/> + <property name="cacheMode" value="LOCAL"/> + <property name="atomicityMode" value="TRANSACTIONAL"/> + </bean> + </list> + </property> + + <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. --> + <property name="discoverySpi"> + <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi"> + <property name="ipFinder"> + <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"> + <property name="addresses"> + <list> + <!-- In distributed environment, replace with actual host IP address. --> + <value>127.0.0.1:47500..47509</value> + </list> + </property> + </bean> + </property> + </bean> + </property> + </bean> + + <!-- Template for all example cache configurations. --> + <bean id="cache-template" abstract="true" class="org.apache.ignite.configuration.CacheConfiguration"> + <!-- Initial cache size. --> + <property name="startSize" value="3000000"/> + + <!-- Set synchronous rebalancing (default is asynchronous). --> + <property name="rebalanceMode" value="SYNC"/> + + <!-- Set to FULL_SYNC for examples, default is PRIMARY_SYNC. --> + <property name="writeSynchronizationMode" value="FULL_SYNC"/> + </bean> +</beans> http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/modules/core/src/test/config/websession/example-cache-client.xml ---------------------------------------------------------------------- diff --git a/modules/core/src/test/config/websession/example-cache-client.xml b/modules/core/src/test/config/websession/example-cache-client.xml new file mode 100644 index 0000000..9272e14 --- /dev/null +++ b/modules/core/src/test/config/websession/example-cache-client.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + 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. +--> + +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + <!-- Imports Ignite configuration --> + <import resource="example-cache-base.xml"/> + + <bean parent="ignite.cfg"> + <property name="clientMode" value="true"/> + + <property name="gridName" value="client"/> + </bean> + +</beans> http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/modules/core/src/test/config/websession/example-cache.xml ---------------------------------------------------------------------- diff --git a/modules/core/src/test/config/websession/example-cache.xml b/modules/core/src/test/config/websession/example-cache.xml index 0cc0e1e..1090220 100644 --- a/modules/core/src/test/config/websession/example-cache.xml +++ b/modules/core/src/test/config/websession/example-cache.xml @@ -28,130 +28,12 @@ --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation=" - http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/util - http://www.springframework.org/schema/util/spring-util.xsd"> - <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration"> - <!-- Set to true to enable distributed class loading for examples, default is false. --> - <property name="peerClassLoadingEnabled" value="true"/> + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + <!-- Imports Ignite configuration --> + <import resource="example-cache-base.xml"/> - <property name="marshaller"> - <bean class="org.apache.ignite.marshaller.optimized.OptimizedMarshaller"> - <!-- Set to false to allow non-serializable objects in examples, default is true. --> - <property name="requireSerializable" value="false"/> - </bean> - </property> - - <!-- Enable cache events for examples. --> - <property name="includeEventTypes"> - <util:constant static-field="org.apache.ignite.events.EventType.EVTS_CACHE"/> - </property> - - <!-- Cache configurations (all properties are optional). --> - <property name="cacheConfiguration"> - <list> - <!-- Partitioned cache example configuration (Atomic mode). --> - <bean parent="cache-template"> - <property name="name" value="partitioned"/> - <property name="cacheMode" value="PARTITIONED"/> - <property name="atomicityMode" value="ATOMIC"/> - <property name="backups" value="1"/> - - <property name="indexedTypes"> - <list> - <!-- Key and value type for SQL table Long. --> - <value>java.lang.Integer</value> - <value>java.lang.Long</value> - </list> - </property> - </bean> - - <!-- Partitioned cache example configuration (Atomic mode, PRIMARY write order mode). --> - <bean parent="cache-template"> - <property name="name" value="partitioned_primary"/> - <property name="cacheMode" value="PARTITIONED"/> - <property name="atomicityMode" value="ATOMIC"/> - <property name="atomicWriteOrderMode" value="PRIMARY"/> - <property name="backups" value="1"/> - </bean> - - <!-- Partitioned cache example configuration (Transactional mode). --> - <bean parent="cache-template"> - <property name="name" value="partitioned_tx"/> - <property name="cacheMode" value="PARTITIONED"/> - <property name="atomicityMode" value="TRANSACTIONAL"/> - <property name="nearConfiguration"> - <bean class="org.apache.ignite.configuration.NearCacheConfiguration"/> - </property> - <property name="backups" value="1"/> - </bean> - - <!-- Replicated cache example configuration (Atomic mode). --> - <bean parent="cache-template"> - <property name="name" value="replicated"/> - <property name="cacheMode" value="REPLICATED"/> - <property name="atomicityMode" value="ATOMIC"/> - <property name="indexedTypes"> - <list> - <!-- Key and value type for SQL table DimStore. --> - <value>java.lang.Integer</value> - <value>java.lang.Integer</value> - </list> - </property> - </bean> - - <!-- Replicated cache example configuration (Transactional mode). --> - <bean parent="cache-template"> - <property name="name" value="replicated_tx"/> - <property name="cacheMode" value="REPLICATED"/> - <property name="atomicityMode" value="TRANSACTIONAL"/> - </bean> - - <!-- Local cache example configuration (Atomic mode). --> - <bean parent="cache-template"> - <property name="name" value="local"/> - <property name="cacheMode" value="LOCAL"/> - <property name="atomicityMode" value="ATOMIC"/> - </bean> - - <!-- Local cache example configuration (Transactional mode). --> - <bean parent="cache-template"> - <property name="name" value="local_tx"/> - <property name="cacheMode" value="LOCAL"/> - <property name="atomicityMode" value="TRANSACTIONAL"/> - </bean> - </list> - </property> - - <!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. --> - <property name="discoverySpi"> - <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi"> - <property name="ipFinder"> - <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"> - <property name="addresses"> - <list> - <!-- In distributed environment, replace with actual host IP address. --> - <value>127.0.0.1:47500..47509</value> - </list> - </property> - </bean> - </property> - </bean> - </property> + <bean parent="ignite.cfg"> </bean> - <!-- Template for all example cache configurations. --> - <bean id="cache-template" abstract="true" class="org.apache.ignite.configuration.CacheConfiguration"> - <!-- Initial cache size. --> - <property name="startSize" value="3000000"/> - - <!-- Set synchronous rebalancing (default is asynchronous). --> - <property name="rebalanceMode" value="SYNC"/> - - <!-- Set to FULL_SYNC for examples, default is PRIMARY_SYNC. --> - <property name="writeSynchronizationMode" value="FULL_SYNC"/> - </bean> </beans> http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/modules/core/src/test/config/websession/example-cache2.xml ---------------------------------------------------------------------- diff --git a/modules/core/src/test/config/websession/example-cache2.xml b/modules/core/src/test/config/websession/example-cache2.xml new file mode 100644 index 0000000..59a244f --- /dev/null +++ b/modules/core/src/test/config/websession/example-cache2.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + 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. +--> + +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + <!-- Imports Ignite configuration --> + <import resource="example-cache-base.xml"/> + + <bean parent="ignite.cfg"> + <property name="gridName" value="grid2"/> + </bean> + +</beans> http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/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 42de43b..f718035 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 @@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpSession; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteClientDisconnectedException; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.IgniteTransactions; @@ -123,6 +124,15 @@ import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_REA * </td> * <td>{@code 3}</td> * </tr> + * <tr> + * <td>IgniteWebSessionsRetriesTimeout</td> + * <td> + * Retry timeout. Related to IgniteWebSessionsMaximumRetriesOnFail param. + * <p> + * Further attempts will be cancelled in case timeout was exceeded. + * </td> + * <td>{@code 10000} (10 seconds)</td> + * </tr> * </table> * These parameters are taken from either filter init parameter list or * servlet context parameters. You can specify filter init parameters as follows: @@ -169,9 +179,15 @@ public class WebSessionFilter implements Filter { /** Web sessions caching retry on fail parameter name (valid for ATOMIC cache only). */ public static final String WEB_SES_MAX_RETRIES_ON_FAIL_NAME_PARAM = "IgniteWebSessionsMaximumRetriesOnFail"; + /** Web sessions caching retry on fail timeout parameter name. */ + public static final String WEB_SES_RETRIES_TIMEOUT_NAME_PARAM = "IgniteWebSessionsRetriesTimeout"; + /** 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; + /** Cache. */ private IgniteCache<String, WebSession> cache; @@ -193,9 +209,18 @@ public class WebSessionFilter implements Filter { /** Transactions enabled flag. */ private boolean txEnabled; + /** Node. */ + private Ignite webSesIgnite; + + /** Cache name. */ + private String cacheName; + /** */ private int retries; + /** */ + private int retriesTimeout; + /** {@inheritDoc} */ @Override public void init(FilterConfig cfg) throws ServletException { ctx = cfg.getServletContext(); @@ -204,7 +229,7 @@ public class WebSessionFilter implements Filter { cfg.getInitParameter(WEB_SES_NAME_PARAM), ctx.getInitParameter(WEB_SES_NAME_PARAM)); - String cacheName = U.firstNotNull( + cacheName = U.firstNotNull( cfg.getInitParameter(WEB_SES_CACHE_NAME_PARAM), ctx.getInitParameter(WEB_SES_CACHE_NAME_PARAM)); @@ -219,7 +244,19 @@ public class WebSessionFilter implements Filter { throw new IgniteException("Maximum number of retries parameter is invalid: " + retriesStr, e); } - Ignite webSesIgnite = G.ignite(gridName); + String retriesTimeoutStr = U.firstNotNull( + cfg.getInitParameter(WEB_SES_RETRIES_TIMEOUT_NAME_PARAM), + ctx.getInitParameter(WEB_SES_RETRIES_TIMEOUT_NAME_PARAM)); + + try { + retriesTimeout = retriesTimeoutStr != null ? + Integer.parseInt(retriesTimeoutStr) : DFLT_RETRIES_ON_FAIL_TIMEOUT; + } + catch (NumberFormatException e) { + throw new IgniteException("Retries timeout parameter is invalid: " + retriesTimeoutStr, e); + } + + webSesIgnite = G.ignite(gridName); if (webSesIgnite == null) throw new IgniteException("Grid for web sessions caching is not started (is it configured?): " + @@ -229,35 +266,9 @@ public class WebSessionFilter implements Filter { log = webSesIgnite.log(); - cache = webSesIgnite.cache(cacheName); - - if (cache == null) - throw new IgniteException("Cache for web sessions is not started (is it configured?): " + cacheName); - - CacheConfiguration cacheCfg = cache.getConfiguration(CacheConfiguration.class); - - if (cacheCfg.getWriteSynchronizationMode() == FULL_ASYNC) - throw new IgniteException("Cache for web sessions cannot be in FULL_ASYNC mode: " + cacheName); - - if (!cacheCfg.isEagerTtl()) - throw new IgniteException("Cache for web sessions cannot operate with lazy TTL. " + - "Consider setting eagerTtl to true for cache: " + cacheName); - - if (cacheCfg.getCacheMode() == LOCAL) - U.quietAndWarn(webSesIgnite.log(), "Using LOCAL cache for web sessions caching " + - "(this is only OK in test mode): " + cacheName); + initCache(); - if (cacheCfg.getCacheMode() == PARTITIONED && cacheCfg.getAtomicityMode() != ATOMIC) - U.quietAndWarn(webSesIgnite.log(), "Using " + cacheCfg.getAtomicityMode() + " atomicity for web sessions " + - "caching (switch to ATOMIC mode for better performance)"); - - if (log.isInfoEnabled()) - log.info("Started web sessions caching [gridName=" + gridName + ", cacheName=" + cacheName + - ", maxRetriesOnFail=" + retries + ']'); - - txEnabled = cacheCfg.getAtomicityMode() == TRANSACTIONAL; - - lsnr = new WebSessionListener(webSesIgnite, cache, retries); + lsnr = new WebSessionListener(webSesIgnite, this, retries); String srvInfo = ctx.getServerInfo(); @@ -286,6 +297,46 @@ public class WebSessionFilter implements Filter { } }; } + + if (log.isInfoEnabled()) + log.info("Started web sessions caching [gridName=" + gridName + ", cacheName=" + cacheName + + ", maxRetriesOnFail=" + retries + ']'); + } + + /** + * @return Cache. + */ + IgniteCache<String, WebSession> getCache(){ + return cache; + } + + /** + * Init cache. + */ + void initCache() { + cache = webSesIgnite.cache(cacheName); + + if (cache == null) + throw new IgniteException("Cache for web sessions is not started (is it configured?): " + cacheName); + + CacheConfiguration cacheCfg = cache.getConfiguration(CacheConfiguration.class); + + if (cacheCfg.getWriteSynchronizationMode() == FULL_ASYNC) + throw new IgniteException("Cache for web sessions cannot be in FULL_ASYNC mode: " + cacheName); + + if (!cacheCfg.isEagerTtl()) + throw new IgniteException("Cache for web sessions cannot operate with lazy TTL. " + + "Consider setting eagerTtl to true for cache: " + cacheName); + + if (cacheCfg.getCacheMode() == LOCAL) + U.quietAndWarn(webSesIgnite.log(), "Using LOCAL cache for web sessions caching " + + "(this is only OK in test mode): " + cacheName); + + if (cacheCfg.getCacheMode() == PARTITIONED && cacheCfg.getAtomicityMode() != ATOMIC) + U.quietAndWarn(webSesIgnite.log(), "Using " + cacheCfg.getAtomicityMode() + " atomicity for web sessions " + + "caching (switch to ATOMIC mode for better performance)"); + + txEnabled = cacheCfg.getAtomicityMode() == TRANSACTIONAL; } /** {@inheritDoc} */ @@ -333,7 +384,7 @@ public class WebSessionFilter implements Filter { */ private String doFilter0(HttpServletRequest httpReq, ServletResponse res, FilterChain chain) throws IOException, ServletException, CacheException { - WebSession cached; + WebSession cached = null; String sesId = httpReq.getRequestedSessionId(); @@ -341,7 +392,24 @@ public class WebSessionFilter implements Filter { if (sesIdTransformer != null) sesId = sesIdTransformer.apply(sesId); - cached = cache.get(sesId); + for (int i = 0; i < retries; i++) { + try { + cached = cache.get(sesId); + } + 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); + } + } + } if (cached != null) { if (log.isDebugEnabled()) @@ -455,7 +523,7 @@ public class WebSessionFilter implements Filter { break; } - catch (CacheException | IgniteException e) { + catch (CacheException | IgniteException | IllegalStateException e) { if (log.isDebugEnabled()) log.debug(e.getMessage()); @@ -465,29 +533,49 @@ public class WebSessionFilter implements Filter { if (log.isDebugEnabled()) log.debug("Failed to save session (will retry): " + sesId); - IgniteFuture<?> retryFut = null; + handleCacheOperationException(e); + } + } + } - if (X.hasCause(e, ClusterTopologyException.class)) { - ClusterTopologyException cause = X.cause(e, ClusterTopologyException.class); + return cached; + } - assert cause != null : e; + /** + * Handles cache operation exception. + * @param e Exception + */ + void handleCacheOperationException(Exception e){ + IgniteFuture<?> retryFut = null; - retryFut = cause.retryReadyFuture(); - } + if (e instanceof IllegalStateException) { + initCache(); - if (retryFut != null) { - try { - retryFut.get(); - } - catch (IgniteException retryErr) { - throw new IgniteException("Failed to save session: " + sesId, retryErr); - } - } - } - } + return; } + else if (X.hasCause(e, IgniteClientDisconnectedException.class)) { + IgniteClientDisconnectedException cause = X.cause(e, IgniteClientDisconnectedException.class); - return cached; + assert cause != null : e; + + retryFut = cause.reconnectFuture(); + } + else if (X.hasCause(e, ClusterTopologyException.class)) { + ClusterTopologyException cause = X.cause(e, ClusterTopologyException.class); + + assert cause != null : e; + + retryFut = cause.retryReadyFuture(); + } + + if (retryFut != null) { + try { + retryFut.get(retriesTimeout); + } + catch (IgniteException retryErr) { + throw new IgniteException("Failed to wait for retry: " + retryErr); + } + } } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/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 0d7c44e..0d8ffec 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 @@ -32,12 +32,9 @@ import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; -import org.apache.ignite.cluster.ClusterTopologyException; 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.lang.IgniteFuture; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -45,11 +42,8 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; * Session listener for web sessions caching. */ class WebSessionListener { - /** */ - private static final long RETRY_DELAY = 1000; - - /** Cache. */ - private final IgniteCache<String, WebSession> cache; + /** Filter. */ + private final WebSessionFilter filter; /** Maximum retries. */ private final int retries; @@ -59,14 +53,14 @@ class WebSessionListener { /** * @param ignite Grid. - * @param cache Cache. + * @param filter Filter. * @param retries Maximum retries. */ - WebSessionListener(Ignite ignite, IgniteCache<String, WebSession> cache, int retries) { + WebSessionListener(Ignite ignite, WebSessionFilter filter, int retries) { assert ignite != null; - assert cache != null; + assert filter != null; - this.cache = cache; + this.filter = filter; this.retries = retries > 0 ? retries : 1; log = ignite.log(); @@ -77,13 +71,22 @@ class WebSessionListener { */ 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); - try { - if (cache.remove(sesId) && log.isDebugEnabled()) - log.debug("Session destroyed: " + sesId); - } - catch (CacheException e) { - U.error(log, "Failed to remove session: " + sesId, e); + filter.handleCacheOperationException(e); + } + } } } @@ -110,16 +113,16 @@ class WebSessionListener { ExpiryPolicy plc = new ModifiedExpiryPolicy(new Duration(MILLISECONDS, ttl)); - cache0 = cache.withExpiryPolicy(plc); + cache0 = filter.getCache().withExpiryPolicy(plc); } else - cache0 = cache; + cache0 = filter.getCache(); cache0.invoke(sesId, new AttributesProcessor(updates)); break; } - catch (CacheException | IgniteException e) { + 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 + ']'); @@ -127,20 +130,7 @@ class WebSessionListener { else { U.warn(log, "Failed to apply updates for session (will retry): " + sesId); - IgniteFuture<?> retryFut = null; - - if (X.hasCause(e, ClusterTopologyException.class)) { - ClusterTopologyException cause = X.cause(e, ClusterTopologyException.class); - - assert cause != null : e; - - retryFut = cause.retryReadyFuture(); - } - - if (retryFut != null) - retryFut.get(); - else - U.sleep(RETRY_DELAY); + filter.handleCacheOperationException(e); } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/0ad4f395/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 298baf1..8706e1f 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 @@ -37,6 +37,7 @@ import javax.servlet.http.HttpSession; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; 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; @@ -83,6 +84,77 @@ public class WebSessionSelfTest extends GridCommonAbstractTest { } /** + * @throws Exception If failed. + */ + public void testClientReconnectRequest() throws Exception { + testClientReconnectRequest("/modules/core/src/test/config/websession/example-cache.xml", + "/modules/core/src/test/config/websession/example-cache2.xml", + "/modules/core/src/test/config/websession/example-cache-client.xml"); + } + + /** + * Tests single request to a server. Checks the presence of session in cache. + * + * @param srvCfg Server configuration. + * @param clientCfg Client configuration. + * @throws Exception If failed. + */ + private void testClientReconnectRequest(String srvCfg, String srvCfg2, String clientCfg) throws Exception { + Server srv = null; + + Ignite ignite = Ignition.start(srvCfg); + + try { + srv = startServer(TEST_JETTY_PORT, clientCfg, "client", new SessionCreateServlet()); + + URL url = new URL("http://localhost:" + TEST_JETTY_PORT + "/ignitetest/test"); + + URLConnection conn = url.openConnection(); + + conn.connect(); + + try (BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String sesId = rdr.readLine(); + + assertNotNull(sesId); + } + + stopGrid(ignite.name()); + + ignite = Ignition.start(srvCfg); + + conn = url.openConnection(); + + conn.connect(); + + try (BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String sesId = rdr.readLine(); + + assertNotNull(sesId); + } + + Ignite ignite2 = Ignition.start(srvCfg2); + + stopGrid(ignite.name()); + + conn = url.openConnection(); + + conn.connect(); + + try (BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String sesId = rdr.readLine(); + + assertNotNull(sesId); + } + } + finally { + stopServer(srv); + + stopAllGrids(); + } + } + + /** * Tests single request to a server. Checks the presence of session in cache. * * @param cfg Configuration.