From b6809f555538344b4cccb2043048046269754102 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Thu, 18 Apr 2019 17:24:25 +1000 Subject: [PATCH] Jetty 9.4.x 2140 infinispan expired sessions (#3457) Infinispan and hazelcast changes to scavenge zombie expired sessions. Signed-off-by: Lachlan Roberts Signed-off-by: Jan Bartel Signed-off-by: olivier lamy --- .../session-configuration-hazelcast.adoc | 14 +- .../session-configuration-infinispan.adoc | 42 ++++- jetty-hazelcast/pom.xml | 2 +- .../config/etc/sessions/hazelcast/default.xml | 1 + .../config/etc/sessions/hazelcast/remote.xml | 1 + .../session-store-hazelcast-embedded.mod | 3 +- .../session-store-hazelcast-remote.mod | 5 +- .../session/HazelcastSessionDataStore.java | 89 ++++++++- .../HazelcastSessionDataStoreFactory.java | 19 +- .../etc}/infinispan-embedded.xml | 2 +- jetty-home/pom.xml | 48 ++++- jetty-home/start.ini | 9 + jetty-infinispan/infinispan-common/pom.xml | 74 ++++++++ .../sessions/infinispan/infinispan-common.xml | 37 ++++ .../main/config/modules/infinispan-common.mod | 20 +++ .../infinispan/InfinispanSessionData.java | 0 .../InfinispanSessionDataStore.java | 131 +++++++++----- .../InfinispanSessionDataStoreFactory.java | 20 ++- .../InfinispanSessionLegacyConverter.java | 0 .../infinispan/NullQueryManagerFactory.java | 36 ++++ .../session/infinispan/QueryManager.java | 27 +++ .../infinispan/QueryManagerFactory.java | 27 +++ .../infinispan/SessionDataMarshaller.java | 0 .../session/infinispan/WebAppMarshaller.java | 0 .../src/main/resources/session.proto | 0 .../infinispan-embedded-query/pom.xml | 125 +++++++++++++ .../src/main/assembly/config.xml | 27 +++ .../infinispan/infinispan-embedded-query.xml | 86 +++++++++ .../modules/infinispan-embedded-query.mod | 20 +++ .../modules/infinispan-query-libs.mod | 11 ++ .../modules/infinispan-query.mod | 16 ++ .../infinispan/EmbeddedQueryManager.java | 41 +++++ .../EmbeddedQueryManagerFactory.java | 19 ++ .../infinispan/EmbeddedQueryManagerTest.java | 112 ++++++++++++ jetty-infinispan/infinispan-embedded/pom.xml | 44 +++++ .../infinispan/infinispan-embedded.xml | 17 ++ .../config/modules/infinispan-embedded.mod | 13 ++ .../session-store-infinispan-embedded.mod | 24 +++ .../infinispan.xml | 5 + .../infinispan-remote-query/pom.xml | 169 ++++++++++++++++++ .../src/main/assembly/config.xml | 27 +++ .../infinispan/infinispan-remote-query.xml} | 60 ++++--- .../modules/infinispan-remote-query-libs.mod | 11 ++ .../infinispan-remote-query-serverclasses.mod | 16 ++ .../modules/infinispan-remote-query.mod | 24 +++ .../hotrod-client.properties | 1 + .../other_proto_marshallers.xml | 39 ++++ .../infinispan/RemoteQueryManager.java | 67 +++++++ .../infinispan/RemoteQueryManagerFactory.java | 38 ++++ .../infinispan/RemoteQueryManagerTest.java | 131 ++++++++++++++ jetty-infinispan/infinispan-remote/pom.xml | 44 +++++ .../sessions/infinispan/infinispan-remote.xml | 76 ++++++++ .../main/config/modules/infinispan-remote.mod | 13 ++ .../session-store-infinispan-remote.mod} | 19 +- .../resources/hotrod-client.properties | 0 jetty-infinispan/pom.xml | 73 ++------ .../etc/sessions/infinispan/default.xml | 30 ---- .../session-store-infinispan-embedded-910.mod | 35 ---- .../session-store-infinispan-embedded.mod | 35 ---- .../session-store-infinispan-remote.mod | 36 ---- .../org/eclipse/jetty/server/MultiParts.java | 3 + .../test-hazelcast-sessions/pom.xml | 2 + .../HazelcastSessionDataStoreTest.java | 107 +++++------ .../session/HazelcastTestHelper.java | 22 ++- .../client/HazelcastSessionDataStoreTest.java | 164 +++++++++++++++++ .../test-infinispan-sessions/pom.xml | 26 ++- .../InfinispanSessionDataStoreTest.java | 41 +++++ .../server/session/InfinispanTestSupport.java | 57 ++++-- .../RemoteClusteredSessionScavengingTest.java | 1 + .../RemoteInfinispanSessionDataStoreTest.java | 59 +++--- .../remote/RemoteInfinispanTestSupport.java | 31 +++- 71 files changed, 2215 insertions(+), 409 deletions(-) rename {jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded => jetty-home/etc}/infinispan-embedded.xml (90%) create mode 100644 jetty-home/start.ini create mode 100644 jetty-infinispan/infinispan-common/pom.xml create mode 100644 jetty-infinispan/infinispan-common/src/main/config/etc/sessions/infinispan/infinispan-common.xml create mode 100644 jetty-infinispan/infinispan-common/src/main/config/modules/infinispan-common.mod rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionData.java (100%) rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java (68%) rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java (83%) rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionLegacyConverter.java (100%) create mode 100644 jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/NullQueryManagerFactory.java create mode 100644 jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManager.java create mode 100644 jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManagerFactory.java rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/SessionDataMarshaller.java (100%) rename jetty-infinispan/{ => infinispan-common}/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java (100%) rename jetty-infinispan/{ => infinispan-common}/src/main/resources/session.proto (100%) create mode 100644 jetty-infinispan/infinispan-embedded-query/pom.xml create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/assembly/config.xml create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/config-template/etc/sessions/infinispan/infinispan-embedded-query.xml create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-embedded-query.mod create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query-libs.mod create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query.mod create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManager.java create mode 100644 jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java create mode 100644 jetty-infinispan/infinispan-embedded-query/src/test/java/org/eclipse/jetty/server/session/infinispan/EmbeddedQueryManagerTest.java create mode 100644 jetty-infinispan/infinispan-embedded/pom.xml create mode 100644 jetty-infinispan/infinispan-embedded/src/main/config/etc/sessions/infinispan/infinispan-embedded.xml create mode 100644 jetty-infinispan/infinispan-embedded/src/main/config/modules/infinispan-embedded.mod create mode 100644 jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded.mod create mode 100644 jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded/infinispan.xml create mode 100644 jetty-infinispan/infinispan-remote-query/pom.xml create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/assembly/config.xml rename jetty-infinispan/{src/main/config/etc/sessions/infinispan/remote.xml => infinispan-remote-query/src/main/config-template/etc/sessions/infinispan/infinispan-remote-query.xml} (64%) create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-libs.mod create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-serverclasses.mod create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query.mod create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/hotrod-client.properties create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/other_proto_marshallers.xml create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManager.java create mode 100644 jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java create mode 100644 jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java create mode 100644 jetty-infinispan/infinispan-remote/pom.xml create mode 100644 jetty-infinispan/infinispan-remote/src/main/config/etc/sessions/infinispan/infinispan-remote.xml create mode 100644 jetty-infinispan/infinispan-remote/src/main/config/modules/infinispan-remote.mod rename jetty-infinispan/{src/main/config/modules/session-store-infinispan-remote-910.mod => infinispan-remote/src/main/config/modules/session-store-infinispan-remote.mod} (56%) rename jetty-infinispan/{ => infinispan-remote}/src/main/config/modules/session-store-infinispan-remote/resources/hotrod-client.properties (100%) delete mode 100644 jetty-infinispan/src/main/config/etc/sessions/infinispan/default.xml delete mode 100644 jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded-910.mod delete mode 100644 jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod delete mode 100644 jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod create mode 100644 tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/client/HazelcastSessionDataStoreTest.java diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc index 11eaf71790e5..ba8bfd985e5f 100644 --- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc +++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc @@ -84,6 +84,7 @@ Opening the `start.d/session-store-hazelcast-remote.ini` will show a list of all #jetty.session.hazelcast.mapName=jetty_sessions #jetty.session.hazelcast.onlyClient=true #jetty.session.hazelcast.configurationLocation= +jetty.session.hazelcast.scavengeZombies=false #jetty.session.gracePeriod.seconds=3600 #jetty.session.savePeriod.seconds=0 ---- @@ -94,6 +95,8 @@ jetty.session.hazelcast.onlyClient:: Hazelcast instance will be configured in client mode jetty.session.hazelcast.configurationLocation:: Path to an an Hazelcast xml configuration file +jetty.session.hazelcast.scavengeZombies:: +True/False. `False` by default. If `true`, jetty will use hazelcast queries to find sessions that are no longer being used on any jetty node and whose expiry time has passed. If you enable this option, and your session stores attributes that reference classes from inside your webapp, or jetty classes, you will need to ensure that these classes are available on each of your hazelcast instances. jetty.session.gracePeriod.seconds:: Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it. jetty.session.savePeriod.seconds=0:: @@ -106,6 +109,8 @@ Configuring `savePeriod` is useful if your persistence technology is very slow/c In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds. This allows the possibility that a node may prematurely expire the session, even though it is in use by another node. Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`. + +Be aware using the `scavengeZombies` option that if your session attributes contain classes from inside your webapp (or jetty classes) then you will need to put these classes onto the classpath of all of your hazelcast instances. ____ ==== Configuring Embedded Hazelcast Clustering @@ -165,15 +170,18 @@ Opening the `start.d/start.d/session-store-hazelcast-embedded.ini` will show a l #jetty.session.hazelcast.mapName=jetty_sessions #jetty.session.hazelcast.configurationLocation= +jetty.session.hazelcast.scavengeZombies=false #jetty.session.gracePeriod.seconds=3600 #jetty.session.savePeriod.seconds=0 ---- jetty.session.hazelcast.mapName:: Name of the Map in Hazelcast where sessions will be stored. -jetty.session.gracePeriod.seconds:: -Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it. jetty.session.hazelcast.configurationLocation:: Path to an an Hazelcast xml configuration file +jetty.session.hazelcast.scavengeZombies:: +True/False. `False` by default. If `true`, jetty will use hazelcast queries to find sessions that are no longer being used on any jetty node and whose expiry time has passed. If you enable this option, and your sessions contain attributes that reference classes from inside your webapp (or jetty classes) you will need to ensure that these classes are available on each of your hazelcast instances. +jetty.session.gracePeriod.seconds:: +Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it. jetty.session.savePeriod.seconds=0:: By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time. A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written. @@ -184,4 +192,6 @@ Configuring `savePeriod` is useful if your persistence technology is very slow/c In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds. This allows the possibility that a node may prematurely expire the session, even though it is in use by another node. Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`. + +Be aware using the `scavengeZombies` option that if your session attributes contain classes from inside your webapp (or jetty classes) then you will need to put these classes onto the classpath of all of your hazelcast instances. In the cast of embedded hazelcast, as it is started before your webapp, it will NOT have access to your webapp's classes - you will need to extract these classes and put them onto the jetty server's classpath. ____ diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc index 8647aa9ce1c8..e8274075591a 100644 --- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc +++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc @@ -24,10 +24,6 @@ When using the Jetty distribution, you will first need to enable the `session-store-infinispan-remote` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line. -____ -[IMPORTANT] -If you are running Jetty with JDK 9 or greater, enable `session-store-infinispan-remote-910.mod` instead. -____ [source, screen, subs="{sub-order}"] ---- @@ -52,7 +48,7 @@ INFO : server transitively enabled, ini template available with --add- INFO : sessions transitively enabled, ini template available with --add-to-start=sessions INFO : session-store-infinispan-remote initialized in ${jetty.base}/start.d/session-store-infinispan-remote.ini MKDIR : ${jetty.base}/lib/infinispan -DOWNLD: https://repo1.maven.org/maven2/org/infinispan/infinispan-remote/7.1.1.Final/infinispan-remote-7.1.1.Final.jar to ${jetty.base}/lib/infinispan/infinispan-remote-7.1.1.Final.jar +DOWNLD: https://repo1.maven.org/maven2/org/infinispan/infinispan-remote-it/9.4.8.Final/infinispan-remote-it-9.4.8.Final.jar to ${jetty.base}/lib/infinispan/infinispan-remote-it-9.4.8.Final.jar MKDIR : ${jetty.base}/resources COPY : ${jetty.home}/modules/session-store-infinispan-remote/resources/hotrod-client.properties to ${jetty.base}/resources/hotrod-client.properties INFO : Base directory was modified @@ -93,13 +89,17 @@ Opening the `start.d/session-store-infinispan-remote.ini` will show a list of al jetty.session.infinispan.remoteCacheName:: Name of the cache in Infinispan where sessions will be stored. jetty.session.infinispan.idleTimeout.seconds:: -Amount of time, in seconds, that the system allows the connector to remain idle before closing the connection. +Amount of time, in seconds, that a session entry in infinispan can be idle (ie not read or written) before infinispan will delete its entry. +Usually, you do *not* want to set a value for this, as you want jetty to handle all session expiration (and call any SessionListeners). +However, if there is the possibility that sessions can be left in infinispan but no longer referenced by any jetty node (so called "zombie" or "orphan" sessions), then you might want to use this feature. +You should make sure that the number of seconds you specify is sufficiently large to avoid the situation where a session is still being referenced by jetty, but is rarely accessed and thus deleted by infinispan. +Alternatively, you can enable the `infinispan-remote-query` module, which will allow jetty to search the infinispan session cache to proactively find and properly (ie calling any SessionListeners) scavenge defunct sessions. jetty.session.gracePeriod.seconds:: Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it. jetty.session.savePeriod.seconds=0:: By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time. A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written. -+ + ____ [NOTE] Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes. @@ -108,6 +108,19 @@ This allows the possibility that a node may prematurely expire the session, even Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`. ____ +==== Configuring the Remote Infinispan Query Module + +Enabling this module allows jetty to search infinispan for expired sessions that are no longer being referenced by any jetty node. +Note that this is an *additional* module, to be used in conjuction with the `session-store-infinispan-remote` module. + +[source, screen, subs="{sub-order}"] +---- +java -jar ../start.jar --add-to-start=infinispan-remote-query +---- + +There are no configuration properties associated with this module. + + ==== Configuring Embedded Inifinspan Clustering During testing, it can be helpful to run an in-process instance of Infinispan. @@ -137,7 +150,7 @@ Proceed (y/N)? y INFO : server initialised (transitively) in ${jetty.base}/start.d/server.ini INFO : sessions initialised (transitively) in ${jetty.base}/start.d/sessions.ini INFO : session-store-infinispan-embedded initialised in ${jetty.base}/start.d/session-store-infinispan-embedded.ini -DOWNLOAD: https://repo1.maven.org/maven2/org/infinispan/infinispan-embedded/7.1.1.Final/infinispan-embedded-7.1.1.Final.jar to ${jetty.base}/lib/infinispan/infinispan-embedded-7.1.1.Final.jar +DOWNLOAD: https://repo1.maven.org/maven2/org/infinispan/infinispan-embedded-it/9.4.8.Final/infinispan-embedded-it-9.4.8.Final.jar to ${jetty.base}/lib/infinispan/infinispan-embedded-it-9.4.8.Final.jar INFO : Base directory was modified ---- @@ -180,6 +193,19 @@ This allows the possibility that a node may prematurely expire the session, even Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`. ____ + +==== Configuring Inifinspan Embedded Query + +Similarly to the `session-store-infinispan-remote` module, the `session-store-infinispan-embedded` module has an adjunct module `infinispan-embedded-query`, which when enabled, will allow jetty to detect and properly scavenge defunct sessions stranded in infinispan. + +[source, screen, subs="{sub-order}"] +---- +java -jar ../start.jar --add-to-start=infinispan-embedded-query +---- + +There are no configuration properties associated with this module. + + ==== Converting session format for jetty-9.4.13 From jetty-9.4.13 onwards, we have changed the format of the serialized session when using a remote cache (ie using hotrod). diff --git a/jetty-hazelcast/pom.xml b/jetty-hazelcast/pom.xml index 48e211e3d04d..dfad9a45152f 100644 --- a/jetty-hazelcast/pom.xml +++ b/jetty-hazelcast/pom.xml @@ -12,7 +12,7 @@ Jetty :: Hazelcast Session Manager - 3.9.3 + 3.9.4 ${project.groupId}.hazelcast diff --git a/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/default.xml b/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/default.xml index 680e6f07d3f4..9df3c69c5e3e 100644 --- a/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/default.xml +++ b/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/default.xml @@ -13,6 +13,7 @@ + diff --git a/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/remote.xml b/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/remote.xml index ca32fcff21d6..355d73707e7f 100644 --- a/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/remote.xml +++ b/jetty-hazelcast/src/main/config/etc/sessions/hazelcast/remote.xml @@ -12,6 +12,7 @@ + diff --git a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-embedded.mod b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-embedded.mod index 405b5209aafa..374493bc509b 100644 --- a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-embedded.mod +++ b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-embedded.mod @@ -13,7 +13,7 @@ session-store sessions [files] -maven://com.hazelcast/hazelcast/3.9.3|lib/hazelcast/hazelcast-3.9.3.jar +maven://com.hazelcast/hazelcast/3.9.4|lib/hazelcast/hazelcast-3.9.4.jar [xml] etc/sessions/hazelcast/default.xml @@ -32,5 +32,6 @@ http://www.apache.org/licenses/LICENSE-2.0.html jetty.session.hazelcast.mapName=jetty-distributed-session-map jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE #jetty.session.hazelcast.configurationLocation= +jetty.session.hazelcast.scavengeZombies=false jetty.session.gracePeriod.seconds=3600 jetty.session.savePeriod.seconds=0 diff --git a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod index 29e3a2531718..3796bef5924d 100644 --- a/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod +++ b/jetty-hazelcast/src/main/config/modules/session-store-hazelcast-remote.mod @@ -13,8 +13,8 @@ session-store sessions [files] -maven://com.hazelcast/hazelcast/3.9.3|lib/hazelcast/hazelcast-3.9.3.jar -maven://com.hazelcast/hazelcast-client/3.9.3|lib/hazelcast/hazelcast-client-3.9.3.jar +maven://com.hazelcast/hazelcast/3.9.4|lib/hazelcast/hazelcast-3.9.4.jar +maven://com.hazelcast/hazelcast-client/3.9.4|lib/hazelcast/hazelcast-client-3.9.4.jar [xml] etc/sessions/hazelcast/remote.xml @@ -33,6 +33,7 @@ http://www.apache.org/licenses/LICENSE-2.0.html jetty.session.hazelcast.mapName=jetty-distributed-session-map jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE jetty.session.hazelcast.onlyClient=true +jetty.session.hazelcast.scavengeZombies=false #jetty.session.hazelcast.configurationLocation= jetty.session.gracePeriod.seconds=3600 jetty.session.savePeriod.seconds=0 diff --git a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java index dfed4934cb47..a728ccd658fc 100644 --- a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java +++ b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java @@ -18,7 +18,8 @@ package org.eclipse.jetty.hazelcast.session; -import java.util.Collections; +import java.util.Collection; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -33,6 +34,9 @@ import org.eclipse.jetty.util.log.Logger; import com.hazelcast.core.IMap; +import com.hazelcast.query.EntryObject; +import com.hazelcast.query.Predicate; +import com.hazelcast.query.PredicateBuilder; /** * Session data stored in Hazelcast @@ -47,9 +51,35 @@ public class HazelcastSessionDataStore private IMap sessionDataMap; + private boolean _scavengeZombies; + public HazelcastSessionDataStore() { - // no op + } + + /** Control whether or not to execute queries to find + * "zombie" sessions - ie sessions that are no longer + * actively referenced by any jetty instance and should + * be expired. + * + * If you use this feature, be aware that if your session + * stores any attributes that use classes from within your + * webapp, or from within jetty, you will need to make sure + * those classes are available to all of your hazelcast + * instances, whether embedded or remote. + * + * @param scavengeZombies true means unreferenced sessions + * will be actively sought and expired. False means that they + * will remain in hazelcast until some other mechanism removes them. + */ + public void setScavengeZombieSessions (boolean scavengeZombies) + { + _scavengeZombies = scavengeZombies; + } + + public boolean isScavengeZombies() + { + return _scavengeZombies; } @Override @@ -97,7 +127,9 @@ public void setSessionDataMap( IMap sessionDataMap ) public void initialize( SessionContext context ) throws Exception { - _context = context; + super.initialize(context); + if (isScavengeZombies()) + sessionDataMap.addIndex("expiry", true); } @Override @@ -113,16 +145,13 @@ public boolean isPassivating() return true; } + @Override public Set doGetExpired( Set candidates ) { - if (candidates == null || candidates.isEmpty()) - { - return Collections.emptySet(); - } - long now = System.currentTimeMillis(); - return candidates.stream().filter( candidate -> { + + Set expiredSessionIds = candidates.stream().filter( candidate -> { if (LOG.isDebugEnabled()) LOG.debug( "Checking expiry for candidate {}", candidate ); @@ -184,7 +213,49 @@ public Set doGetExpired( Set candidates ) } return false; } ).collect( Collectors.toSet() ); + + if (isScavengeZombies()) + { + //Now find other sessions in hazelcast that have expired + final AtomicReference> reference = new AtomicReference<>(); + final AtomicReference exception = new AtomicReference<>(); + + _context.run(()-> + { + try + { + Set ids = new HashSet<>(); + EntryObject eo = new PredicateBuilder().getEntryObject(); + Predicate predicate = eo.get("expiry").greaterThan(0).and(eo.get("expiry").lessEqual(now)); + Collection results = sessionDataMap.values(predicate); + if (results != null) + { + for (SessionData sd: results) + ids.add(sd.getId()); + } + reference.set(ids); + } + catch (Exception e) + { + exception.set(e); + } + }); + + if (exception.get() != null) + { + LOG.warn("Error querying for expired sessions {}", exception.get()); + return expiredSessionIds; + } + + if (reference.get() != null) + { + expiredSessionIds.addAll(reference.get()); + } + } + + return expiredSessionIds; } + @Override public boolean exists( String id ) diff --git a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java index f1524102cef4..08a941bd8bac 100644 --- a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java +++ b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java @@ -54,8 +54,20 @@ public class HazelcastSessionDataStoreFactory private HazelcastInstance hazelcastInstance; private MapConfig mapConfig; + + private boolean scavengeZombies = false; + public boolean isScavengeZombies() + { + return scavengeZombies; + } + + public void setScavengeZombies(boolean scavengeZombies) + { + this.scavengeZombies = scavengeZombies; + } + @Override public SessionDataStore getSessionDataStore( SessionHandler handler ) throws Exception @@ -122,9 +134,10 @@ public SessionDataStore getSessionDataStore( SessionHandler handler ) } } // initialize the map - hazelcastSessionDataStore.setSessionDataMap(hazelcastInstance.getMap( mapName ) ); - hazelcastSessionDataStore.setGracePeriodSec( getGracePeriodSec() ); - hazelcastSessionDataStore.setSavePeriodSec( getSavePeriodSec() ); + hazelcastSessionDataStore.setSessionDataMap(hazelcastInstance.getMap( mapName )); + hazelcastSessionDataStore.setGracePeriodSec(getGracePeriodSec()); + hazelcastSessionDataStore.setSavePeriodSec(getSavePeriodSec()); + hazelcastSessionDataStore.setScavengeZombieSessions(scavengeZombies); return hazelcastSessionDataStore; } diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded/infinispan-embedded.xml b/jetty-home/etc/infinispan-embedded.xml similarity index 90% rename from jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded/infinispan-embedded.xml rename to jetty-home/etc/infinispan-embedded.xml index 5adc356b1f60..07d8ca214cbb 100644 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded/infinispan-embedded.xml +++ b/jetty-home/etc/infinispan-embedded.xml @@ -2,4 +2,4 @@ - + \ No newline at end of file diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index 70e3d3deea92..5b981b9ec975 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -440,12 +440,41 @@ org.eclipse.jetty,org.eclipse.jetty.websocket + infinispan-embedded,infinispan-remote config false META-INF/** ${assembly-directory} + + unpack-infinispan-config + generate-resources + + unpack + + + + + org.eclipse.jetty + infinispan-embedded + ${project.version} + config + jar + + + org.eclipse.jetty + infinispan-remote + ${project.version} + config + jar + + + true + META-INF/** + ${assembly-directory} + + @@ -699,7 +728,24 @@ org.eclipse.jetty - jetty-infinispan + infinispan-embedded + ${project.version} + pom + + + org.eclipse.jetty + infinispan-embedded-query + ${project.version} + + + org.eclipse.jetty + infinispan-remote + ${project.version} + pom + + + org.eclipse.jetty + infinispan-remote-query ${project.version} diff --git a/jetty-home/start.ini b/jetty-home/start.ini new file mode 100644 index 000000000000..215582944fea --- /dev/null +++ b/jetty-home/start.ini @@ -0,0 +1,9 @@ +# --------------------------------------- +# Module: session-store-infinispan-embedded +# Enables session data store in a local Infinispan cache +# --------------------------------------- +--module=session-store-infinispan-embedded + +#jetty.session.gracePeriod.seconds=3600 +#jetty.session.savePeriod.seconds=0 + diff --git a/jetty-infinispan/infinispan-common/pom.xml b/jetty-infinispan/infinispan-common/pom.xml new file mode 100644 index 000000000000..5b4a89e04d9e --- /dev/null +++ b/jetty-infinispan/infinispan-common/pom.xml @@ -0,0 +1,74 @@ + + + org.eclipse.jetty + infinispan-parent + 9.4.16-SNAPSHOT + + 4.0.0 + infinispan-common + Jetty :: Infinispan Session Manager Common + http://www.eclipse.org/jetty + + ${project.groupId}.infinispan.common + + + + + install + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + + + org.infinispan + infinispan-core + ${infinispan.version} + true + + + org.infinispan.protostream + protostream + 4.2.2.Final + true + + + org.eclipse.jetty + jetty-server + ${project.version} + + + org.infinispan + infinispan-client-hotrod + ${infinispan.version} + provided + + + org.infinispan + infinispan-remote-query-client + ${infinispan.version} + provided + + + junit + junit + test + + + diff --git a/jetty-infinispan/infinispan-common/src/main/config/etc/sessions/infinispan/infinispan-common.xml b/jetty-infinispan/infinispan-common/src/main/config/etc/sessions/infinispan/infinispan-common.xml new file mode 100644 index 000000000000..cd994df21ddd --- /dev/null +++ b/jetty-infinispan/infinispan-common/src/main/config/etc/sessions/infinispan/infinispan-common.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-infinispan/infinispan-common/src/main/config/modules/infinispan-common.mod b/jetty-infinispan/infinispan-common/src/main/config/modules/infinispan-common.mod new file mode 100644 index 000000000000..babac9c900cf --- /dev/null +++ b/jetty-infinispan/infinispan-common/src/main/config/modules/infinispan-common.mod @@ -0,0 +1,20 @@ +[description] +Common to all infinispan modules + +[tags] +session + +[depend] +sessions + +[lib] +lib/infinispan-common-${jetty.version}.jar +lib/infinispan/*.jar + +[ini] +infinispan.version?=9.1.0.Final + +[license] +Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. +http://infinispan.org/ +http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionData.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionData.java similarity index 100% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionData.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionData.java diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java similarity index 68% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java index fa21de58ade6..0761bd15bdf7 100644 --- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java @@ -1,6 +1,6 @@ // // ======================================================================== -// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 @@ -34,6 +34,7 @@ import org.eclipse.jetty.util.log.Logger; import org.infinispan.commons.api.BasicCache; + /** * InfinispanSessionDataStore * @@ -48,10 +49,14 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore /** * Clustered cache of sessions */ - private BasicCache _cache; + private BasicCache _cache; private int _infinispanIdleTimeoutSec; + + + + private QueryManager _queryManager; private boolean _passivating; @@ -61,7 +66,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore * * @return the cache */ - public BasicCache getCache() + public BasicCache getCache() { return _cache; } @@ -73,13 +78,26 @@ public BasicCache getCache() * * @param cache the cache */ - public void setCache (BasicCache cache) + public void setCache (BasicCache cache) { this._cache = cache; } + public QueryManager getQueryManager() + { + return _queryManager; + } + + public void setQueryManager (QueryManager queryManager) + { + _queryManager = queryManager; + } + + /** + * @see org.eclipse.jetty.server.session.SessionDataStore#load(String) + */ @Override protected void doStart() throws Exception { @@ -139,67 +157,92 @@ public boolean delete(String id) throws Exception @Override public Set doGetExpired(Set candidates) { - if (candidates == null || candidates.isEmpty()) - return candidates; - + long now = System.currentTimeMillis(); Set expired = new HashSet<>(); - //TODO if there is NOT an idle timeout set on entries in infinispan, need to check other sessions - //that are not currently in the SessionDataStore (eg they've been passivated) - for (String candidate:candidates) + /* + * 1. Select sessions managed by this node for our context that have expired + */ + if(candidates != null) { - if (LOG.isDebugEnabled()) - LOG.debug("Checking expiry for candidate {}", candidate); - try + for (String candidate:candidates) { - SessionData sd = load(candidate); - - //if the session no longer exists - if (sd == null) - { - expired.add(candidate); - if (LOG.isDebugEnabled()) - LOG.debug("Session {} does not exist in infinispan", candidate); - } - else + if (LOG.isDebugEnabled()) + LOG.debug("Checking expiry for candidate {}", candidate); + try { - if (_context.getWorkerName().equals(sd.getLastNode())) + SessionData sd = load(candidate); + + //if the session no longer exists + if (sd == null) { - //we are its manager, add it to the expired set if it is expired now - if ((sd.getExpiry() > 0 ) && sd.getExpiry() <= now) - { - expired.add(candidate); - if (LOG.isDebugEnabled()) - LOG.debug("Session {} managed by {} is expired", candidate, _context.getWorkerName()); - } + expired.add(candidate); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} does not exist in infinispan", candidate); } else { - //if we are not the session's manager, only expire it iff: - // this is our first expiryCheck and the session expired a long time ago - //or - //the session expired at least one graceperiod ago - if (_lastExpiryCheckTime <=0) + if (_context.getWorkerName().equals(sd.getLastNode())) { - if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * (3 * _gracePeriodSec)))) + //we are its manager, add it to the expired set if it is expired now + if ((sd.getExpiry() > 0 ) && sd.getExpiry() <= now) + { expired.add(candidate); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} managed by {} is expired", candidate, _context.getWorkerName()); + } } else { - if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * _gracePeriodSec))) - expired.add(candidate); + //if we are not the session's manager, only expire it iff: + // this is our first expiryCheck and the session expired a long time ago + //or + //the session expired at least one graceperiod ago + if (_lastExpiryCheckTime <=0) + { + if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * (3 * _gracePeriodSec)))) + expired.add(candidate); + } + else + { + if ((sd.getExpiry() > 0 ) && sd.getExpiry() < (now - (1000L * _gracePeriodSec))) + expired.add(candidate); + } } } } + catch (Exception e) + { + LOG.warn("Error checking if candidate {} is expired", candidate, e); + } } - catch (Exception e) + } + + + /* + * 2. Select sessions for any node or context that have expired + * at least 1 graceperiod since the last expiry check. If we haven't done previous expiry checks, then check + * those that have expired at least 3 graceperiod ago. + */ + if(_queryManager != null) + { + long upperBound = now; + if (_lastExpiryCheckTime <= 0) + upperBound = (now - (3*(1000L * _gracePeriodSec))); + else + upperBound = _lastExpiryCheckTime - (1000L * _gracePeriodSec); + + if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}", _context.getWorkerName(), upperBound); + + for (String sessionId : _queryManager.queryExpiredSessions(upperBound)) { - LOG.warn("Error checking if candidate {} is expired", candidate, e); + expired.add(sessionId); + if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId); } } - + return expired; } @@ -233,8 +276,8 @@ public boolean isPassivating() { return _passivating; } - - + + @Override public boolean exists(String id) throws Exception diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java similarity index 83% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java index 291b80a6dc9a..71129ea911e0 100644 --- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.session.infinispan; import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory; +import org.eclipse.jetty.server.session.SessionData; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.server.session.SessionDataStore; import org.infinispan.commons.api.BasicCache; @@ -32,8 +33,8 @@ public class InfinispanSessionDataStoreFactory extends AbstractSessionDataStoreFactory { int _infinispanIdleTimeoutSec; - BasicCache _cache; - + BasicCache _cache; + protected QueryManager _queryManager; /** * @return the infinispanIdleTimeoutSec @@ -62,6 +63,7 @@ public SessionDataStore getSessionDataStore (SessionHandler handler) throws Exce store.setInfinispanIdleTimeoutSec(getInfinispanIdleTimeoutSec()); store.setCache(getCache()); store.setSavePeriodSec(getSavePeriodSec()); + store.setQueryManager(getQueryManager()); return store; } @@ -70,7 +72,7 @@ public SessionDataStore getSessionDataStore (SessionHandler handler) throws Exce * * @return the cache */ - public BasicCache getCache() + public BasicCache getCache() { return _cache; } @@ -82,10 +84,20 @@ public BasicCache getCache() * * @param cache the cache */ - public void setCache (BasicCache cache) + public void setCache (BasicCache cache) { this._cache = cache; } + public QueryManager getQueryManager() + { + return _queryManager; + } + + public void setQueryManager(QueryManager queryManager) + { + _queryManager = queryManager; + } + } diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionLegacyConverter.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionLegacyConverter.java similarity index 100% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionLegacyConverter.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionLegacyConverter.java diff --git a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/NullQueryManagerFactory.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/NullQueryManagerFactory.java new file mode 100644 index 000000000000..764ac2405bf5 --- /dev/null +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/NullQueryManagerFactory.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.commons.api.BasicCache; + +/** + * NullQueryManagerFactory + * + * Trivial impl of the QueryManagerFactory that does not support doing queries. + */ +public class NullQueryManagerFactory implements QueryManagerFactory +{ + @Override + public QueryManager getQueryManager(BasicCache cache) + { + return null; + } +} diff --git a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManager.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManager.java new file mode 100644 index 000000000000..33711c3a506c --- /dev/null +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManager.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import java.util.Set; + +public interface QueryManager +{ + Set queryExpiredSessions(); + Set queryExpiredSessions(long currentTime); +} diff --git a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManagerFactory.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManagerFactory.java new file mode 100644 index 000000000000..6475614a290c --- /dev/null +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/QueryManagerFactory.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.commons.api.BasicCache; + +public interface QueryManagerFactory +{ + public QueryManager getQueryManager(BasicCache cache); +} diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/SessionDataMarshaller.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/SessionDataMarshaller.java similarity index 100% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/SessionDataMarshaller.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/SessionDataMarshaller.java diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java similarity index 100% rename from jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java rename to jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java diff --git a/jetty-infinispan/src/main/resources/session.proto b/jetty-infinispan/infinispan-common/src/main/resources/session.proto similarity index 100% rename from jetty-infinispan/src/main/resources/session.proto rename to jetty-infinispan/infinispan-common/src/main/resources/session.proto diff --git a/jetty-infinispan/infinispan-embedded-query/pom.xml b/jetty-infinispan/infinispan-embedded-query/pom.xml new file mode 100644 index 000000000000..d1f4bb36479a --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/pom.xml @@ -0,0 +1,125 @@ + + + org.eclipse.jetty + infinispan-parent + 9.4.16-SNAPSHOT + + 4.0.0 + infinispan-embedded-query + Jetty :: Infinispan Session Manager Embedded with Querying + http://www.eclipse.org/jetty + + ${project.groupId}.infinispan.embedded.query + + + install + + + org.apache.maven.plugins + maven-dependency-plugin + + + build-deps-file + generate-resources + + list + + + false + ${project.build.directory}/deps.txt + true + org.eclipse.jetty,javax.servlet,org.slf4j + true + runtime + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + process-deps + process-resources + + run + + + + + + + + + + process-mod + process-resources + + run + + + + + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + src/main/assembly/config.xml + + + + + + + + + + org.eclipse.jetty + infinispan-common + ${project.version} + + + org.infinispan + infinispan-core + + + org.infinispan + infinispan-commons + + + + + org.infinispan + infinispan-query + ${infinispan.version} + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/assembly/config.xml b/jetty-infinispan/infinispan-embedded-query/src/main/assembly/config.xml new file mode 100644 index 000000000000..7234f9b9e4ea --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/assembly/config.xml @@ -0,0 +1,27 @@ + + + config + false + + jar + + + + src/main/config-template + + + ** + + + **/infinispan-query-libs.mod + + + + target + modules + + infinispan-query-libs.mod + + + + diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/config-template/etc/sessions/infinispan/infinispan-embedded-query.xml b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/etc/sessions/infinispan/infinispan-embedded-query.xml new file mode 100644 index 000000000000..9fe9caefbf5a --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/etc/sessions/infinispan/infinispan-embedded-query.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + expiry + + + + + + + + + + + + + + + + /etc/infinispan.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jetty-query-sessions + + + + + + + + + + diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-embedded-query.mod b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-embedded-query.mod new file mode 100644 index 000000000000..73706051703f --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-embedded-query.mod @@ -0,0 +1,20 @@ +[description] +Enables querying with the Infinispan cache + +[tags] +session + +[provides] +infinispan-embedded + +[depends] +infinispan-query-libs + +[lib] +lib/infinispan/*.jar +lib/infinispan-embedded-query-${jetty.version}.jar + +[xml] +etc/sessions/infinispan/infinispan-embedded-query.xml +etc/sessions/infinispan/infinispan-common.xml + diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query-libs.mod b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query-libs.mod new file mode 100644 index 000000000000..54b8a22e5c77 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query-libs.mod @@ -0,0 +1,11 @@ +[description] +The Infinispan query libraries + +[tags] +3rdparty +infinispan + +[depends] +infinispan-query + + diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query.mod b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query.mod new file mode 100644 index 000000000000..5a262b322ac1 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/config-template/modules/infinispan-query.mod @@ -0,0 +1,16 @@ +[description] +Enables querying with the Infinispan cache + +[tags] +session +3rdparty +infinispan + +[license] +Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. +http://infinispan.org/ +http://www.apache.org/licenses/LICENSE-2.0.html + +[ini] +## Hide the infinispan libraries from deployed webapps +jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManager.java b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManager.java new file mode 100644 index 000000000000..e1810fb2c982 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManager.java @@ -0,0 +1,41 @@ +package org.eclipse.jetty.session.infinispan; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.Cache; +import org.infinispan.query.Search; +import org.infinispan.query.dsl.Query; +import org.infinispan.query.dsl.QueryFactory; + +public class EmbeddedQueryManager implements QueryManager +{ + private Cache _cache; + + public EmbeddedQueryManager(Cache cache) + { + _cache = cache; + } + + @Override + public Set queryExpiredSessions(long time) + { + QueryFactory qf = Search.getQueryFactory(_cache); + Query q = qf.from(SessionData.class).select("id").having("expiry").lte(time).build(); + + List list = q.list(); + Set ids = new HashSet<>(); + for(Object[] sl : list) + ids.add((String)sl[0]); + return ids; + } + + + @Override + public Set queryExpiredSessions() + { + return queryExpiredSessions(System.currentTimeMillis()); + } + +} diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java new file mode 100644 index 000000000000..f33f4dcd8c32 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java @@ -0,0 +1,19 @@ +package org.eclipse.jetty.session.infinispan; + +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.Cache; +import org.infinispan.commons.api.BasicCache; + +public class EmbeddedQueryManagerFactory implements QueryManagerFactory +{ + + @Override + public QueryManager getQueryManager(BasicCache cache) + { + if (!(cache instanceof Cache)) + throw new IllegalArgumentException("Argument was not of type Cache"); + + return new EmbeddedQueryManager((Cache)cache); + } + +} diff --git a/jetty-infinispan/infinispan-embedded-query/src/test/java/org/eclipse/jetty/server/session/infinispan/EmbeddedQueryManagerTest.java b/jetty-infinispan/infinispan-embedded-query/src/test/java/org/eclipse/jetty/server/session/infinispan/EmbeddedQueryManagerTest.java new file mode 100644 index 000000000000..6e6f59cf96ef --- /dev/null +++ b/jetty-infinispan/infinispan-embedded-query/src/test/java/org/eclipse/jetty/server/session/infinispan/EmbeddedQueryManagerTest.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.session.infinispan; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import java.lang.annotation.ElementType; +import java.util.HashSet; +import java.util.Properties; +import java.util.Random; +import java.util.Set; + +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.session.infinispan.EmbeddedQueryManager; +import org.eclipse.jetty.session.infinispan.QueryManager; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.hibernate.search.cfg.Environment; +import org.hibernate.search.cfg.SearchMapping; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.cache.Index; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; + + +public class EmbeddedQueryManagerTest +{ + public static final String DEFAULT_CACHE_NAME = "session_test_cache"; + + + @Test + public void test() throws Exception + { + + String _name = DEFAULT_CACHE_NAME+System.currentTimeMillis(); + EmbeddedCacheManager _manager; + + _manager = new DefaultCacheManager(new GlobalConfigurationBuilder().globalJmxStatistics().allowDuplicateDomains(true).build()); + + //TODO verify that this is being indexed properly, if you change expiry to something that is not a valid field it still passes the tests + SearchMapping mapping = new SearchMapping(); + mapping.entity(SessionData.class).indexed().providedId().property("expiry", ElementType.FIELD).field(); + Properties properties = new Properties(); + properties.put(Environment.MODEL_MAPPING, mapping); + properties.put("hibernate.search.default.indexBase", MavenTestingUtils.getTargetTestingDir().getAbsolutePath()); + + Configuration dcc = _manager.getDefaultCacheConfiguration(); + ConfigurationBuilder b = new ConfigurationBuilder(); + if (dcc != null) + b = b.read(dcc); + + b.indexing().index(Index.ALL).addIndexedEntity(SessionData.class).withProperties(properties); + Configuration c = b.build(); + + _manager.defineConfiguration(_name, c); + Cache _cache = _manager.getCache(_name); + + //put some sessions into the cache + int numSessions = 10; + long currentTime = 500; + int maxExpiryTime = 1000; + Set expiredSessions = new HashSet<>(); + Random r = new Random(); + + for (int i=0; i queryResult = qm.queryExpiredSessions(currentTime); + + // Check that the result is correct + assertEquals(expiredSessions.size(), queryResult.size()); + for (String s : expiredSessions) + { + assertTrue(queryResult.contains(s)); + } + } +} diff --git a/jetty-infinispan/infinispan-embedded/pom.xml b/jetty-infinispan/infinispan-embedded/pom.xml new file mode 100644 index 000000000000..57a46adfed21 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded/pom.xml @@ -0,0 +1,44 @@ + + + org.eclipse.jetty + infinispan-parent + 9.4.16-SNAPSHOT + + 4.0.0 + infinispan-embedded + pom + Jetty :: Infinispan Session Manager Embedded + http://www.eclipse.org/jetty + + ${project.groupId}.infinispan.embedded + + + install + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + + + org.eclipse.jetty + infinispan-common + ${project.version} + + + diff --git a/jetty-infinispan/infinispan-embedded/src/main/config/etc/sessions/infinispan/infinispan-embedded.xml b/jetty-infinispan/infinispan-embedded/src/main/config/etc/sessions/infinispan/infinispan-embedded.xml new file mode 100644 index 000000000000..a3a6b98510d6 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded/src/main/config/etc/sessions/infinispan/infinispan-embedded.xml @@ -0,0 +1,17 @@ + + + + + + + + + + /etc/infinispan.xml + + + + + + + diff --git a/jetty-infinispan/infinispan-embedded/src/main/config/modules/infinispan-embedded.mod b/jetty-infinispan/infinispan-embedded/src/main/config/modules/infinispan-embedded.mod new file mode 100644 index 000000000000..51c14f4832c4 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded/src/main/config/modules/infinispan-embedded.mod @@ -0,0 +1,13 @@ +[description] +Setup infinispan embedded without querying + +[tags] +session + +[provides] +infinispan-embedded + +[xml] +etc/sessions/infinispan/infinispan-embedded.xml +etc/sessions/infinispan/infinispan-common.xml + diff --git a/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded.mod b/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded.mod new file mode 100644 index 000000000000..e6cdd5ca1eb1 --- /dev/null +++ b/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded.mod @@ -0,0 +1,24 @@ +[description] +Enables session data store in a local Infinispan cache + +[tags] +session + +[provides] +session-store + +[depend] +infinispan-common +infinispan-embedded + +[files] +basehome:modules/session-store-infinispan-embedded/infinispan.xml|etc/infinispan.xml +maven://org.infinispan/infinispan-embedded-it/${infinispan.version}|lib/infinispan/infinispan-embedded-it-${infinispan.version}.jar + +[ini] +infinispan.version?=9.4.8.Final + +[ini-template] +#jetty.session.infinispan.idleTimeout.seconds=0 +#jetty.session.gracePeriod.seconds=3600 +#jetty.session.savePeriod.seconds=0 diff --git a/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded/infinispan.xml b/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded/infinispan.xml new file mode 100644 index 000000000000..07d8ca214cbb --- /dev/null +++ b/jetty-infinispan/infinispan-embedded/src/main/config/modules/session-store-infinispan-embedded/infinispan.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jetty-infinispan/infinispan-remote-query/pom.xml b/jetty-infinispan/infinispan-remote-query/pom.xml new file mode 100644 index 000000000000..45fedbfa7ea1 --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/pom.xml @@ -0,0 +1,169 @@ + + + org.eclipse.jetty + infinispan-parent + 9.4.16-SNAPSHOT + + 4.0.0 + infinispan-remote-query + Jetty :: Infinispan Session Manager Remote + http://www.eclipse.org/jetty + + ${project.groupId}.infinispan.remote.query + + + install + + + org.apache.maven.plugins + maven-dependency-plugin + + + build-deps-file + generate-resources + + list + + + false + ${project.build.directory}/deps.txt + true + org.eclipse.jetty,javax.servlet + true + runtime + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + process-deps + process-resources + + run + + + + + + + + + + + process-mod + process-resources + + run + + + + + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + src/main/assembly/config.xml + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + org.eclipse.jetty + infinispan-common + ${project.version} + + + org.infinispan + infinispan-core + + + org.infinispan + infinispan-commons + + + + + org.infinispan + infinispan-query + ${infinispan.version} + + + org.infinispan + infinispan-client-hotrod + ${infinispan.version} + + + org.infinispan + infinispan-remote-query-client + ${infinispan.version} + + + junit + junit + test + + + + + remote + + + hotrod.enabled + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + + diff --git a/jetty-infinispan/infinispan-remote-query/src/main/assembly/config.xml b/jetty-infinispan/infinispan-remote-query/src/main/assembly/config.xml new file mode 100644 index 000000000000..e51bc6ca97ca --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/assembly/config.xml @@ -0,0 +1,27 @@ + + + config + false + + jar + + + + src/main/config-template + + + ** + + + **/infinispan-remote-query-libs.mod + + + + target + modules + + infinispan-remote-query-libs.mod + + + + diff --git a/jetty-infinispan/src/main/config/etc/sessions/infinispan/remote.xml b/jetty-infinispan/infinispan-remote-query/src/main/config-template/etc/sessions/infinispan/infinispan-remote-query.xml similarity index 64% rename from jetty-infinispan/src/main/config/etc/sessions/infinispan/remote.xml rename to jetty-infinispan/infinispan-remote-query/src/main/config-template/etc/sessions/infinispan/infinispan-remote-query.xml index 356cd48b29c3..e3d7d4806a91 100644 --- a/jetty-infinispan/src/main/config/etc/sessions/infinispan/remote.xml +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/etc/sessions/infinispan/infinispan-remote-query.xml @@ -3,23 +3,38 @@ - - - - + - + + + org.eclipse.jetty.server.session.SessionData + + + + expiry + + + + + + + + - + /resources/hotrod-client.properties + + + + - + @@ -30,13 +45,12 @@ - - - + + - + @@ -60,29 +74,17 @@ - + - + - + - - - - - - - - - - - - - - - + + + diff --git a/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-libs.mod b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-libs.mod new file mode 100644 index 000000000000..aed7e5a4d41f --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-libs.mod @@ -0,0 +1,11 @@ +[description] +The Infinispan remote query libraries + +[tags] +3rdparty +infinispan + +[depends] +infinispan-remote-query-serverclasses + + diff --git a/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-serverclasses.mod b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-serverclasses.mod new file mode 100644 index 000000000000..12571f52dfcb --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query-serverclasses.mod @@ -0,0 +1,16 @@ +[description] +Enables querying with a remote Infinispan cache + +[tags] +session +3rdparty +infinispan + +[license] +Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. +http://infinispan.org/ +http://www.apache.org/licenses/LICENSE-2.0.html + +[ini] +## Hide the infinispan libraries from deployed webapps +jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ diff --git a/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query.mod b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query.mod new file mode 100644 index 000000000000..a465ae1d0bea --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query.mod @@ -0,0 +1,24 @@ +[description] +Enables querying with a remote Infinispan cache + +[tags] +session + +[provides] +infinispan-remote + +[depends] +infinispan-remote-query-libs + +[files] +basehome:modules/infinispan-remote-query/hotrod-client.properties|resources/hotrod-client.properties +basehome:modules/infinispan-remote-query/other_proto_marshallers.xml|etc/other_proto_marshallers.xml + +[lib] +lib/infinispan/*.jar +lib/infinispan-remote-query-${jetty.version}.jar + +[xml] +etc/sessions/infinispan/infinispan-remote-query.xml +etc/other_proto_marshallers.xml +etc/sessions/infinispan/infinispan-common.xml diff --git a/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/hotrod-client.properties b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/hotrod-client.properties new file mode 100644 index 000000000000..bb774cd9c525 --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/hotrod-client.properties @@ -0,0 +1 @@ +#infinispan.client.hotrod.server_list diff --git a/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/other_proto_marshallers.xml b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/other_proto_marshallers.xml new file mode 100644 index 000000000000..494755ae7871 --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/config-template/modules/infinispan-remote-query/other_proto_marshallers.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + diff --git a/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManager.java b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManager.java new file mode 100644 index 000000000000..c30e089e423b --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManager.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.client.hotrod.Search; +import org.infinispan.query.dsl.Query; +import org.infinispan.query.dsl.QueryFactory; + +/** + * RemoteQueryManager + * + * A QueryManager impl that supports doing queries against remote infinispan server. + * + */ +public class RemoteQueryManager implements QueryManager +{ + private RemoteCache _cache; + + public RemoteQueryManager(RemoteCache cache) + { + _cache = cache; + } + + @Override + public Set queryExpiredSessions(long time) + { + // TODO can the QueryFactory be created only once + QueryFactory qf = Search.getQueryFactory(_cache); + Query q = qf.from(InfinispanSessionData.class).select("id").having("expiry").lte(time).build(); + + List list = q.list(); + Set ids = new HashSet<>(); + for(Object[] sl : list) + ids.add((String)sl[0]); + + return ids; + } + + + @Override + public Set queryExpiredSessions() + { + return queryExpiredSessions(System.currentTimeMillis()); + } + +} diff --git a/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java new file mode 100644 index 000000000000..21bea54b821d --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import org.eclipse.jetty.server.session.SessionData; +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.commons.api.BasicCache; + +public class RemoteQueryManagerFactory implements QueryManagerFactory +{ + + @Override + public QueryManager getQueryManager(BasicCache cache) + { + System.err.println(cache.getClass().getName()); + if (!RemoteCache.class.isAssignableFrom(cache.getClass())) + throw new IllegalArgumentException("Argument is not of type RemoteCache"); + + return new RemoteQueryManager((RemoteCache)cache); + } + +} diff --git a/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java b/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java new file mode 100644 index 000000000000..b67e86c9c207 --- /dev/null +++ b/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java @@ -0,0 +1,131 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.session.infinispan; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.annotation.ElementType; +import java.util.HashSet; +import java.util.Properties; +import java.util.Random; +import java.util.Set; + +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.session.infinispan.QueryManager; +import org.eclipse.jetty.session.infinispan.RemoteQueryManager; +import org.eclipse.jetty.session.infinispan.SessionDataMarshaller; +import org.eclipse.jetty.util.IO; +import org.hibernate.search.cfg.Environment; +import org.hibernate.search.cfg.SearchMapping; +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; +import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller; +import org.infinispan.protostream.FileDescriptorSource; +import org.infinispan.protostream.SerializationContext; +import org.junit.Test; + + + +public class RemoteQueryManagerTest +{ + public static final String DEFAULT_CACHE_NAME = "remote-session-test"; + + + @Test + public void test() throws Exception + { + + SearchMapping mapping = new SearchMapping(); + mapping.entity(SessionData.class).indexed().providedId().property("expiry", ElementType.FIELD).field(); + + Properties properties = new Properties(); + properties.put(Environment.MODEL_MAPPING, mapping); + + ConfigurationBuilder clientBuilder = new ConfigurationBuilder(); + clientBuilder.withProperties(properties).addServer().host("127.0.0.1").marshaller(new ProtoStreamMarshaller()); + + RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build()); + + + FileDescriptorSource fds = new FileDescriptorSource(); + fds.addProtoFiles("/session.proto"); + + SerializationContext serCtx = ProtoStreamMarshaller.getSerializationContext(remoteCacheManager); + serCtx.registerProtoFiles(fds); + serCtx.registerMarshaller(new SessionDataMarshaller()); + + RemoteCache _cache = remoteCacheManager.getCache(DEFAULT_CACHE_NAME); + + + ByteArrayOutputStream baos; + try(InputStream is = RemoteQueryManagerTest.class.getClassLoader().getResourceAsStream("session.proto")) + { + if (is == null) + throw new IllegalStateException("inputstream is null"); + + baos = new ByteArrayOutputStream(); + IO.copy(is, baos); + is.close(); + } + + String content = baos.toString("UTF-8"); + remoteCacheManager.getCache("___protobuf_metadata").put("session.proto", content); + + //put some sessions into the remote cache + int numSessions = 10; + long currentTime = 500; + int maxExpiryTime = 1000; + Set expiredSessions = new HashSet<>(); + Random r = new Random(); + + for(int i=0; i queryResult = qm.queryExpiredSessions(currentTime); + + // Check that the result is correct + assertEquals(expiredSessions.size(), queryResult.size()); + for(String s : expiredSessions) + { + assertTrue(queryResult.contains(s)); + } + } +} diff --git a/jetty-infinispan/infinispan-remote/pom.xml b/jetty-infinispan/infinispan-remote/pom.xml new file mode 100644 index 000000000000..174722a800ac --- /dev/null +++ b/jetty-infinispan/infinispan-remote/pom.xml @@ -0,0 +1,44 @@ + + + org.eclipse.jetty + infinispan-parent + 9.4.16-SNAPSHOT + + 4.0.0 + infinispan-remote + pom + Jetty :: Infinispan Session Manager Remote + http://www.eclipse.org/jetty + + ${project.groupId}.infinispan.remote + + + install + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + config + + + + + + + + + + org.eclipse.jetty + infinispan-common + ${project.version} + + + diff --git a/jetty-infinispan/infinispan-remote/src/main/config/etc/sessions/infinispan/infinispan-remote.xml b/jetty-infinispan/infinispan-remote/src/main/config/etc/sessions/infinispan/infinispan-remote.xml new file mode 100644 index 000000000000..8fad6a21c292 --- /dev/null +++ b/jetty-infinispan/infinispan-remote/src/main/config/etc/sessions/infinispan/infinispan-remote.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + /resources/hotrod-client.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /session.proto + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-infinispan/infinispan-remote/src/main/config/modules/infinispan-remote.mod b/jetty-infinispan/infinispan-remote/src/main/config/modules/infinispan-remote.mod new file mode 100644 index 000000000000..aa596b0dc24d --- /dev/null +++ b/jetty-infinispan/infinispan-remote/src/main/config/modules/infinispan-remote.mod @@ -0,0 +1,13 @@ +[description] +Default setup for the remote infinispan cache without queries + +[tags] +session + +[provides] +infinispan-remote + + +[xml] +etc/sessions/infinispan/infinispan-remote.xml +etc/sessions/infinispan/infinispan-common.xml diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote-910.mod b/jetty-infinispan/infinispan-remote/src/main/config/modules/session-store-infinispan-remote.mod similarity index 56% rename from jetty-infinispan/src/main/config/modules/session-store-infinispan-remote-910.mod rename to jetty-infinispan/infinispan-remote/src/main/config/modules/session-store-infinispan-remote.mod index 93903bd41a33..6525708731d5 100644 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote-910.mod +++ b/jetty-infinispan/infinispan-remote/src/main/config/modules/session-store-infinispan-remote.mod @@ -1,5 +1,3 @@ -DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html - [description] Enables session data store in a remote Infinispan cache @@ -8,30 +6,27 @@ session [provides] session-store -session-store-infinispan-remote [depend] -sessions +infinispan-common +infinispan-remote [files] -maven://org.infinispan/infinispan-remote/9.1.0.Final|lib/infinispan/infinispan-remote-9.1.0.Final.jar -basehome:modules/session-store-infinispan-remote/ +maven://org.infinispan/infinispan-remote-it/${infinispan.version}|lib/infinispan/infinispan-remote-it-${infinispan.version}.jar +basehome:modules/session-store-infinispan-remote/resources/hotrod-client.properties|resources/hotrod-client.properties -[xml] -etc/sessions/infinispan/remote.xml +[ini] +infinispan.version?=9.4.8.Final -[lib] -lib/jetty-infinispan-${jetty.version}.jar -lib/infinispan/*.jar [license] Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. http://infinispan.org/ http://www.apache.org/licenses/LICENSE-2.0.html - [ini-template] #jetty.session.infinispan.remoteCacheName=sessions #jetty.session.infinispan.idleTimeout.seconds=0 #jetty.session.gracePeriod.seconds=3600 #jetty.session.savePeriod.seconds=0 + diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote/resources/hotrod-client.properties b/jetty-infinispan/infinispan-remote/src/main/config/modules/session-store-infinispan-remote/resources/hotrod-client.properties similarity index 100% rename from jetty-infinispan/src/main/config/modules/session-store-infinispan-remote/resources/hotrod-client.properties rename to jetty-infinispan/infinispan-remote/src/main/config/modules/session-store-infinispan-remote/resources/hotrod-client.properties diff --git a/jetty-infinispan/pom.xml b/jetty-infinispan/pom.xml index eddf3d5ac8aa..8d1ca11f2095 100644 --- a/jetty-infinispan/pom.xml +++ b/jetty-infinispan/pom.xml @@ -1,69 +1,26 @@ + org.eclipse.jetty jetty-project 9.4.16-SNAPSHOT + 4.0.0 - jetty-infinispan - Jetty :: Infinispan Session Managers - http://www.eclipse.org/jetty + org.eclipse.jetty + infinispan-parent + pom + Jetty :: Infinispan + ${project.groupId}.infinispan - - install - - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - - config - - - - - - - - - - org.infinispan - infinispan-core - ${infinispan.version} - - - org.infinispan.protostream - protostream - 4.2.2.Final - - - org.eclipse.jetty - jetty-server - ${project.version} - - - org.jboss.logging - jboss-logging - - - org.infinispan - infinispan-client-hotrod - ${infinispan.version} - provided - - - org.infinispan - infinispan-remote-query-client - ${infinispan.version} - provided - - + + infinispan-common + infinispan-embedded + infinispan-remote + infinispan-embedded-query + infinispan-remote-query + + diff --git a/jetty-infinispan/src/main/config/etc/sessions/infinispan/default.xml b/jetty-infinispan/src/main/config/etc/sessions/infinispan/default.xml deleted file mode 100644 index efc565e2fad3..000000000000 --- a/jetty-infinispan/src/main/config/etc/sessions/infinispan/default.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - /etc/infinispan-embedded.xml - - - - - - - - - - - - - - - - - - diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded-910.mod b/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded-910.mod deleted file mode 100644 index e7c8f425f8ae..000000000000 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded-910.mod +++ /dev/null @@ -1,35 +0,0 @@ -DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html - -[description] -Enables session data store in a local Infinispan cache - -[tags] -session - -[provides] -session-store -session-store-infinispan-embedded - -[depend] -sessions - -[files] -maven://org.infinispan/infinispan-embedded/9.1.0.Final|lib/infinispan/infinispan-embedded-9.1.0.Final.jar -basehome:modules/session-store-infinispan-embedded/infinispan-embedded.xml|etc/infinispan-embedded.xml - - -[xml] -etc/sessions/infinispan/default.xml - -[lib] -lib/jetty-infinispan-${jetty.version}.jar -lib/infinispan/*.jar - -[license] -Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. -http://infinispan.org/ -http://www.apache.org/licenses/LICENSE-2.0.html - -[ini-template] -#jetty.session.gracePeriod.seconds=3600 -#jetty.session.savePeriod.seconds=0 diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod b/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod deleted file mode 100644 index 21dc1e65c9bd..000000000000 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-embedded.mod +++ /dev/null @@ -1,35 +0,0 @@ -DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html - -[description] -Enables session data store in a local Infinispan cache - -[tags] -session - -[provides] -session-store -session-store-infnispan-embedded - -[depend] -sessions - -[files] -maven://org.infinispan/infinispan-embedded/7.1.1.Final|lib/infinispan/infinispan-embedded-7.1.1.Final.jar -basehome:modules/session-store-infinispan-embedded/infinispan-embedded.xml|etc/infinispan-embedded.xml - - -[xml] -etc/sessions/infinispan/default.xml - -[lib] -lib/jetty-infinispan-${jetty.version}.jar -lib/infinispan/*.jar - -[license] -Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. -http://infinispan.org/ -http://www.apache.org/licenses/LICENSE-2.0.html - -[ini-template] -#jetty.session.gracePeriod.seconds=3600 -#jetty.session.savePeriod.seconds=0 diff --git a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod b/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod deleted file mode 100644 index 844b47323cbc..000000000000 --- a/jetty-infinispan/src/main/config/modules/session-store-infinispan-remote.mod +++ /dev/null @@ -1,36 +0,0 @@ -DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html - -[description] -Enables session data store in a remote Infinispan cache - -[tags] -session - -[provides] -session-store - -[depend] -sessions - -[files] -maven://org.infinispan/infinispan-remote/7.1.1.Final|lib/infinispan/infinispan-remote-7.1.1.Final.jar -basehome:modules/session-store-infinispan-remote/ - -[xml] -etc/sessions/infinispan/remote.xml - -[lib] -lib/jetty-infinispan-${jetty.version}.jar -lib/infinispan/*.jar - -[license] -Infinispan is an open source project hosted on Github and released under the Apache 2.0 license. -http://infinispan.org/ -http://www.apache.org/licenses/LICENSE-2.0.html - - -[ini-template] -#jetty.session.infinispan.remoteCacheName=sessions -#jetty.session.infinispan.idleTimeout.seconds=0 -#jetty.session.gracePeriod.seconds=3600 -#jetty.session.savePeriod.seconds=0 \ No newline at end of file diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java index f28b9453b3cb..8a74f0921488 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java @@ -133,7 +133,10 @@ public MultiPartsUtilParser(InputStream in, String contentType, MultipartConfigE for(NonCompliance nc : nonComplianceWarnings) violations.add(nc.name()+": "+nc.getURL()); + + System.out.println(violations); } + } @Override diff --git a/tests/test-sessions/test-hazelcast-sessions/pom.xml b/tests/test-sessions/test-hazelcast-sessions/pom.xml index fa01d58fe70b..f253e3a7dd6b 100644 --- a/tests/test-sessions/test-hazelcast-sessions/pom.xml +++ b/tests/test-sessions/test-hazelcast-sessions/pom.xml @@ -53,6 +53,8 @@ org.apache.maven.plugins maven-surefire-plugin + 45 + 240 ${project.build.testOutputDirectory}/logging.properties diff --git a/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreTest.java b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreTest.java index a19efa11ae51..9d915148ac79 100644 --- a/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreTest.java +++ b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreTest.java @@ -16,7 +16,6 @@ // ======================================================================== // - package org.eclipse.jetty.hazelcast.session; import static org.junit.jupiter.api.Assertions.fail; @@ -31,6 +30,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * HazelcastSessionDataStoreTest @@ -39,16 +39,15 @@ */ public class HazelcastSessionDataStoreTest extends AbstractSessionDataStoreTest { - + HazelcastTestHelper _testHelper; - @Override public SessionDataStoreFactory createSessionDataStoreFactory() - { + { return _testHelper.createSessionDataStoreFactory(false); } - + @BeforeEach public void setUp() { @@ -61,58 +60,91 @@ public void shutdown() _testHelper.tearDown(); } - @Override public void persistSession(SessionData data) throws Exception { _testHelper.createSession(data); } - @Override public void persistUnreadableSession(SessionData data) throws Exception { - //not used by testLoadSessionFails() + // not used by testLoadSessionFails() } - @Override public boolean checkSessionExists(SessionData data) throws Exception { return _testHelper.checkSessionExists(data); } + + + @Test + @Override + public void testGetExpiredDifferentNode() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } + + @Test + @Override + public void testGetExpiredPersistedAndExpiredOnly() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } - /** + + + @Override + public void testStoreSession() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } + + /** * - * This test deliberately sets the sessionDataMap to null - * for the HazelcastSessionDataStore to provoke an exception - * in the load() method. + * This test deliberately sets the sessionDataMap to null for the + * HazelcastSessionDataStore to provoke an exception in the load() method. */ @Override + @Test public void testLoadSessionFails() throws Exception { - //create the SessionDataStore + // create the SessionDataStore ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); + context.setContextPath("/test"); SessionDataStoreFactory factory = createSessionDataStoreFactory(); - ((AbstractSessionDataStoreFactory)factory).setGracePeriodSec(GRACE_PERIOD_SEC); + ((AbstractSessionDataStoreFactory) factory).setGracePeriodSec(GRACE_PERIOD_SEC); SessionDataStore store = factory.getSessionDataStore(context.getSessionHandler()); SessionContext sessionContext = new SessionContext("foo", context.getServletContext()); store.initialize(sessionContext); - - //persist a session + // persist a session long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("222", 100, now, now-1, -1); + SessionData data = store.newSessionData("222", 100, now, now - 1, -1); data.setLastNode(sessionContext.getWorkerName()); persistSession(data); - + store.start(); - - ((HazelcastSessionDataStore)store).setSessionDataMap(null); - - //test that loading it fails + ((HazelcastSessionDataStore) store).setSessionDataMap(null); + + // test that loading it fails try { store.load("222"); @@ -120,34 +152,9 @@ public void testLoadSessionFails() throws Exception } catch (UnreadableSessionDataException e) { - //expected exception + // expected exception } } - - - /** - * This test currently won't work for Hazelcast - there is currently no - * means to query it to find sessions that have expired. - * - */ - @Override - public void testGetExpiredPersistedAndExpiredOnly() throws Exception - { - //ignore - } - - - - - /** - * This test currently won't work for Hazelcast - there is currently no - * means to query it to find sessions that have expired. - */ - @Override - public void testGetExpiredDifferentNode() throws Exception - { - //ignore - } @Override public boolean checkSessionPersisted(SessionData data) throws Exception diff --git a/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastTestHelper.java b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastTestHelper.java index 08d7eae7304e..d295263d3377 100644 --- a/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastTestHelper.java +++ b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/HazelcastTestHelper.java @@ -22,8 +22,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Collections; import java.util.concurrent.TimeUnit; +import com.hazelcast.client.HazelcastClient; +import com.hazelcast.client.config.ClientConfig; +import com.hazelcast.client.config.ClientNetworkConfig; import com.hazelcast.config.JoinConfig; import com.hazelcast.config.MulticastConfig; import com.hazelcast.config.NetworkConfig; @@ -73,8 +77,22 @@ public SessionDataStoreFactory createSessionDataStoreFactory(boolean onlyClient) HazelcastSessionDataStoreFactory factory = new HazelcastSessionDataStoreFactory(); factory.setOnlyClient( onlyClient ); factory.setMapName(_name); - factory.setHazelcastInstance(_instance); - + if(onlyClient){ + ClientNetworkConfig clientNetworkConfig = new ClientNetworkConfig() + .setAddresses(Collections.singletonList("localhost:"+_instance.getConfig().getNetworkConfig().getPort())); + ClientConfig clientConfig = new ClientConfig() + .setNetworkConfig(clientNetworkConfig); + + SerializerConfig sc = new SerializerConfig(). + setImplementation(new SessionDataSerializer()). + setTypeClass(SessionData.class); + clientConfig.getSerializationConfig().addSerializerConfig(sc); + + factory.setHazelcastInstance(HazelcastClient.newHazelcastClient(clientConfig)); + + } else { + factory.setHazelcastInstance(_instance); + } return factory; } diff --git a/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/client/HazelcastSessionDataStoreTest.java b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/client/HazelcastSessionDataStoreTest.java new file mode 100644 index 000000000000..801ef48fcdf2 --- /dev/null +++ b/tests/test-sessions/test-hazelcast-sessions/src/test/java/org/eclipse/jetty/hazelcast/session/client/HazelcastSessionDataStoreTest.java @@ -0,0 +1,164 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.hazelcast.session.client; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.eclipse.jetty.hazelcast.session.HazelcastSessionDataStore; +import org.eclipse.jetty.hazelcast.session.HazelcastTestHelper; +import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory; +import org.eclipse.jetty.server.session.AbstractSessionDataStoreTest; +import org.eclipse.jetty.server.session.SessionContext; +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.server.session.SessionDataStore; +import org.eclipse.jetty.server.session.SessionDataStoreFactory; +import org.eclipse.jetty.server.session.UnreadableSessionDataException; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * HazelcastSessionDataStoreTest + * + * + */ +public class HazelcastSessionDataStoreTest extends AbstractSessionDataStoreTest +{ + + HazelcastTestHelper _testHelper; + + @Override + public SessionDataStoreFactory createSessionDataStoreFactory() + { + return _testHelper.createSessionDataStoreFactory(true); + } + + @BeforeEach + public void setUp() + { + _testHelper = new HazelcastTestHelper(); + } + + @AfterEach + public void shutdown() + { + _testHelper.tearDown(); + } + + @Override + public void persistSession(SessionData data) throws Exception + { + _testHelper.createSession(data); + } + + @Override + public void persistUnreadableSession(SessionData data) throws Exception + { + // not used by testLoadSessionFails() + } + + @Override + public boolean checkSessionExists(SessionData data) throws Exception + { + return _testHelper.checkSessionExists(data); + } + + + @Test + @Override + public void testGetExpiredDifferentNode() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } + + @Test + @Override + public void testGetExpiredPersistedAndExpiredOnly() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } + + @Override + public void testStoreSession() throws Exception + { + //This test will not work for hazelcast because we can't enable + //HazelcastSessionDataStore.setScavengeZombieSessions, as it's + //too difficult to get the required classes onto the embedded + //hazelcast instance: these classes are required to handle + //the serialization/deserialization that hazelcast performs when querying + //to find zombie sessions. + } + + /** + * + * This test deliberately sets the sessionDataMap to null for the + * HazelcastSessionDataStore to provoke an exception in the load() method. + */ + @Override + @Test + public void testLoadSessionFails() throws Exception + { + // create the SessionDataStore + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + SessionDataStoreFactory factory = createSessionDataStoreFactory(); + ((AbstractSessionDataStoreFactory) factory).setGracePeriodSec(GRACE_PERIOD_SEC); + SessionDataStore store = factory.getSessionDataStore(context.getSessionHandler()); + SessionContext sessionContext = new SessionContext("foo", context.getServletContext()); + store.initialize(sessionContext); + + // persist a session + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("222", 100, now, now - 1, -1); + data.setLastNode(sessionContext.getWorkerName()); + persistSession(data); + + store.start(); + + ((HazelcastSessionDataStore) store).setSessionDataMap(null); + + // test that loading it fails + try + { + store.load("222"); + fail("Session should be unreadable"); + } + catch (UnreadableSessionDataException e) + { + // expected exception + } + } + + @Override + public boolean checkSessionPersisted(SessionData data) throws Exception + { + return _testHelper.checkSessionPersisted(data); + } +} diff --git a/tests/test-sessions/test-infinispan-sessions/pom.xml b/tests/test-sessions/test-infinispan-sessions/pom.xml index 2b8b22b0649d..6439f5247e00 100644 --- a/tests/test-sessions/test-infinispan-sessions/pom.xml +++ b/tests/test-sessions/test-infinispan-sessions/pom.xml @@ -83,14 +83,8 @@ org.eclipse.jetty - jetty-infinispan + infinispan-remote-query ${project.version} - - - org.infinispan - infinispan-core - - org.eclipse.jetty @@ -109,6 +103,12 @@ ${infinispan.version} test + + org.infinispan + infinispan-query + ${infinispan.version} + test + org.infinispan infinispan-client-hotrod @@ -121,6 +121,18 @@ ${infinispan.version} test + + org.infinispan.protostream + protostream + 4.2.2.Final + test + + + org.slf4j + slf4j-jdk14 + 1.7.25 + test + diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanSessionDataStoreTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanSessionDataStoreTest.java index 45d957efa554..cbfdbf992320 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanSessionDataStoreTest.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanSessionDataStoreTest.java @@ -20,12 +20,25 @@ package org.eclipse.jetty.server.session; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStore; import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory; + + +import org.infinispan.Cache; +import org.infinispan.query.Search; +import org.infinispan.query.dsl.Query; +import org.infinispan.query.dsl.QueryFactory; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * InfinispanSessionDataStoreTest @@ -164,4 +177,32 @@ public boolean checkSessionPersisted(SessionData data) throws Exception } } + + @Test + public void testQuery() throws Exception + { + Cache cache = __testSupport.getCache(); + + SessionData sd1 = new SessionData("sd1", "", "", 0, 0, 0, 0); + SessionData sd2 = new SessionData("sd2", "", "", 0, 0, 0, 1000); + sd2.setExpiry(100L); //long ago + SessionData sd3 = new SessionData("sd3", "", "", 0, 0, 0, 0); + + cache.put("session1", sd1); + cache.put("session2", sd2); + cache.put("session3", sd3); + + QueryFactory qf = Search.getQueryFactory(cache); + Query q = qf.from(SessionData.class).select("id").having("expiry").lte(System.currentTimeMillis()).and().having("expiry").gt(0).toBuilder().build(); + + List list = q.list(); + + List ids = new ArrayList<>(); + for(Object[] sl : list) + ids.add((String)sl[0]); + + assertFalse(ids.isEmpty()); + assertTrue(1==ids.size()); + assertTrue(ids.contains("sd2")); + } } diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java index 57ecb1f80d30..b3bfd46ea6b1 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java @@ -23,11 +23,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.lang.annotation.ElementType; +import java.util.Properties; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.IO; +import org.hibernate.search.cfg.Environment; +import org.hibernate.search.cfg.SearchMapping; import org.infinispan.Cache; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.cache.Index; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; @@ -89,19 +95,44 @@ public Cache getCache () public void setup () throws Exception { - if (_useFileStore) - { - _tmpdir = File.createTempFile("infini", "span"); - _tmpdir.delete(); - _tmpdir.mkdir(); - Configuration config = _builder.persistence().addSingleFileStore().location(_tmpdir.getAbsolutePath()).storeAsBinary().build(); - _manager.defineConfiguration(_name, config); - } - else - { - _manager.defineConfiguration(_name, _builder.build()); - } - _cache = _manager.getCache(_name); + File testdir = MavenTestingUtils.getTargetTestingDir(); + File tmp = new File (testdir, "indexes"); + IO.delete(tmp); + tmp.mkdirs(); + + SearchMapping mapping = new SearchMapping(); + mapping.entity(SessionData.class).indexed().providedId().property("expiry", ElementType.FIELD).field(); + Properties properties = new Properties(); + properties.put(Environment.MODEL_MAPPING, mapping); + properties.put("hibernate.search.default.indexBase", tmp.getAbsolutePath()); + + if (_useFileStore) + { + _tmpdir = File.createTempFile("infini", "span"); + _tmpdir.delete(); + _tmpdir.mkdir(); + + Configuration config = _builder.indexing() + .index(Index.ALL) + .addIndexedEntity(SessionData.class) + .withProperties(properties) + .persistence() + .addSingleFileStore() + .location(_tmpdir.getAbsolutePath()) + .storeAsBinary() + .build(); + + _manager.defineConfiguration(_name, config); + } + else + { + _manager.defineConfiguration(_name, _builder.indexing() + .withProperties(properties) + .index(Index.ALL) + .addIndexedEntity(SessionData.class) + .build()); + } + _cache = _manager.getCache(_name); } diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClusteredSessionScavengingTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClusteredSessionScavengingTest.java index b612f7bae182..ee9910008816 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClusteredSessionScavengingTest.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteClusteredSessionScavengingTest.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; + /** * ClusteredSessionScavengingTest * diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanSessionDataStoreTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanSessionDataStoreTest.java index b51acf95aed0..53ad1f94c321 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanSessionDataStoreTest.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanSessionDataStoreTest.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server.session.remote; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory; @@ -29,10 +30,16 @@ import org.eclipse.jetty.server.session.SessionDataStoreFactory; import org.eclipse.jetty.server.session.UnreadableSessionDataException; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.session.infinispan.InfinispanSessionData; import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStore; import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory; +import org.eclipse.jetty.session.infinispan.RemoteQueryManager; +import org.infinispan.client.hotrod.Search; +import org.infinispan.query.dsl.Query; +import org.infinispan.query.dsl.QueryFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * RemoteInfinispanSessionDataStoreTest @@ -67,6 +74,7 @@ public SessionDataStoreFactory createSessionDataStoreFactory() { InfinispanSessionDataStoreFactory factory = new InfinispanSessionDataStoreFactory(); factory.setCache(__testSupport.getCache()); + factory.setQueryManager(new RemoteQueryManager(__testSupport.getCache())); return factory; } @@ -110,29 +118,6 @@ public boolean checkSessionPersisted(SessionData data) throws Exception - /** - * This test currently won't work for Infinispan - there is currently no - * means to query it to find sessions that have expired. - * - * @see org.eclipse.jetty.server.session.AbstractSessionDataStoreTest#testGetExpiredPersistedAndExpiredOnly() - */ - @Override - public void testGetExpiredPersistedAndExpiredOnly() throws Exception - { - - } - - - - /** - * This test won't work for Infinispan - there is currently no - * means to query infinispan to find other expired sessions. - */ - @Override - public void testGetExpiredDifferentNode() throws Exception - { - //Ignore - } /** @@ -175,4 +160,32 @@ public void testLoadSessionFails() throws Exception } } + @Test + public void testQuery() throws Exception + { + InfinispanSessionData sd1 = new InfinispanSessionData("sd1", "", "", 0, 0, 0, 1000); + sd1.setLastNode("fred1"); + __testSupport.getCache().put("session1", sd1); + + InfinispanSessionData sd2 = new InfinispanSessionData("sd2", "", "", 0, 0, 0, 2000); + sd2.setLastNode("fred2"); + __testSupport.getCache().put("session2", sd2); + + InfinispanSessionData sd3 = new InfinispanSessionData("sd3", "", "", 0, 0, 0, 3000); + sd3.setLastNode("fred3"); + __testSupport.getCache().put("session3", sd3); + + QueryFactory qf = Search.getQueryFactory(__testSupport.getCache()); + + + for(int i=0; i<=3; i++) + { + long now = System.currentTimeMillis(); + Query q = qf.from(InfinispanSessionData.class).having("expiry").lt(now).build(); + assertEquals(i, q.list().size()); + Thread.sleep(1000); + } + } } + + diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java index 450fdaf9096e..b1ddeebe1242 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInfinispanTestSupport.java @@ -23,11 +23,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.annotation.ElementType; import java.util.Properties; import org.eclipse.jetty.server.session.SessionData; import org.eclipse.jetty.session.infinispan.InfinispanSessionData; import org.eclipse.jetty.session.infinispan.SessionDataMarshaller; +import org.eclipse.jetty.util.IO; +import org.hibernate.search.cfg.Environment; +import org.hibernate.search.cfg.SearchMapping; import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.client.hotrod.RemoteCacheManager; @@ -45,7 +51,7 @@ public class RemoteInfinispanTestSupport { public static final String DEFAULT_CACHE_NAME = "session_test_cache"; - public RemoteCache _cache; + public RemoteCache _cache; private String _name; public static RemoteCacheManager _manager; @@ -54,8 +60,14 @@ public class RemoteInfinispanTestSupport try { String host = System.getProperty("hotrod.host","127.0.0.1"); + + SearchMapping mapping = new SearchMapping(); + mapping.entity(SessionData.class).indexed().providedId() + .property("expiry", ElementType.METHOD).field(); + Properties properties = new Properties(); - + properties.put(Environment.MODEL_MAPPING, mapping); + ConfigurationBuilder clientBuilder = new ConfigurationBuilder(); clientBuilder.withProperties(properties).addServer().host(host).marshaller(new ProtoStreamMarshaller()); @@ -67,6 +79,19 @@ public class RemoteInfinispanTestSupport SerializationContext serCtx = ProtoStreamMarshaller.getSerializationContext(_manager); serCtx.registerProtoFiles(fds); serCtx.registerMarshaller(new SessionDataMarshaller()); + + ByteArrayOutputStream baos; + try(InputStream is = RemoteInfinispanSessionDataStoreTest.class.getClassLoader().getResourceAsStream("session.proto")) + { + if (is == null) + throw new IllegalStateException("inputstream is null"); + + baos = new ByteArrayOutputStream(); + IO.copy(is, baos); + } + + String content = baos.toString("UTF-8"); + _manager.getCache("___protobuf_metadata").put("session.proto", content); } catch (Exception e) { @@ -89,7 +114,7 @@ public RemoteInfinispanTestSupport(String cacheName) - public RemoteCache getCache () + public RemoteCache getCache () { return _cache; }