Merge branch `jetty-9.4.x` into `jetty-10.0.x`
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> # Conflicts: # jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/WebAppMarshaller.java # jetty-infinispan/pom.xml # jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java # jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
This commit is contained in:
commit
c2e81f5584
|
@ -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.
|
||||
____
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<name>Jetty :: Hazelcast Session Manager</name>
|
||||
|
||||
<properties>
|
||||
<hazelcast.version>3.9.3</hazelcast.version>
|
||||
<hazelcast.version>3.9.4</hazelcast.version>
|
||||
<bundle-symbolic-name>${project.groupId}.hazelcast</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<New id="sessionDataStoreFactory" class="org.eclipse.jetty.hazelcast.session.HazelcastSessionDataStoreFactory">
|
||||
<Set name="mapName"><Property name="jetty.session.hazelcast.mapName" default="jetty-distributed-session-map" /></Set>
|
||||
<Set name="hazelcastInstanceName"><Property name="jetty.session.hazelcast.hazelcastInstanceName" default="JETTY_DISTRIBUTED_SESSION_INSTANCE" /></Set>
|
||||
<Set name="scavengeZombies"><Property name="jetty.session.hazelcast.scavengeZombies" default="false"/></Set>
|
||||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
</New>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<New id="sessionDataStoreFactory" class="org.eclipse.jetty.hazelcast.session.HazelcastSessionDataStoreFactory">
|
||||
<Set name="mapName"><Property name="jetty.session.hazelcast.mapName" default="jetty-distributed-session-map" /></Set>
|
||||
<Set name="hazelcastInstanceName"><Property name="jetty.session.hazelcast.hazelcastInstanceName" default="JETTY_DISTRIBUTED_SESSION_INSTANCE" /></Set>
|
||||
<Set name="scavengeZombies"><Property name="jetty.session.hazelcast.scavengeZombies" default="false"/></Set>
|
||||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
<Set name="onlyClient"><Property name="jetty.session.hazelcast.onlyClient" default="true" /></Set>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,11 +18,16 @@
|
|||
|
||||
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;
|
||||
|
||||
import com.hazelcast.core.IMap;
|
||||
import com.hazelcast.query.EntryObject;
|
||||
import com.hazelcast.query.Predicate;
|
||||
import com.hazelcast.query.PredicateBuilder;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
||||
import org.eclipse.jetty.server.session.SessionContext;
|
||||
import org.eclipse.jetty.server.session.SessionData;
|
||||
|
@ -32,8 +37,6 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
import com.hazelcast.core.IMap;
|
||||
|
||||
/**
|
||||
* Session data stored in Hazelcast
|
||||
*/
|
||||
|
@ -47,9 +50,35 @@ public class HazelcastSessionDataStore
|
|||
|
||||
private IMap<String, SessionData> 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 +126,9 @@ public class HazelcastSessionDataStore
|
|||
public void initialize( SessionContext context )
|
||||
throws Exception
|
||||
{
|
||||
_context = context;
|
||||
super.initialize(context);
|
||||
if (isScavengeZombies())
|
||||
sessionDataMap.addIndex("expiry", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,16 +144,13 @@ public class HazelcastSessionDataStore
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> doGetExpired( Set<String> candidates )
|
||||
{
|
||||
if (candidates == null || candidates.isEmpty())
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
return candidates.stream().filter( candidate -> {
|
||||
|
||||
Set<String> expiredSessionIds = candidates.stream().filter( candidate -> {
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug( "Checking expiry for candidate {}", candidate );
|
||||
|
@ -184,7 +212,49 @@ public class HazelcastSessionDataStore
|
|||
}
|
||||
return false;
|
||||
} ).collect( Collectors.toSet() );
|
||||
|
||||
if (isScavengeZombies())
|
||||
{
|
||||
//Now find other sessions in hazelcast that have expired
|
||||
final AtomicReference<Set<String>> reference = new AtomicReference<>();
|
||||
final AtomicReference<Exception> exception = new AtomicReference<>();
|
||||
|
||||
_context.run(()->
|
||||
{
|
||||
try
|
||||
{
|
||||
Set<String> ids = new HashSet<>();
|
||||
EntryObject eo = new PredicateBuilder().getEntryObject();
|
||||
Predicate<?, ?> predicate = eo.get("expiry").greaterThan(0).and(eo.get("expiry").lessEqual(now));
|
||||
Collection<SessionData> 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 )
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.hazelcast.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.hazelcast.client.HazelcastClient;
|
||||
import com.hazelcast.client.config.ClientConfig;
|
||||
import com.hazelcast.client.config.XmlClientConfigBuilder;
|
||||
|
@ -33,8 +35,6 @@ import org.eclipse.jetty.server.session.SessionDataStore;
|
|||
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Factory to construct {@link HazelcastSessionDataStore}
|
||||
*/
|
||||
|
@ -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 class HazelcastSessionDataStoreFactory
|
|||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
<cache-container default-cache="jetty-sessions">
|
||||
<local-cache name="jetty-sessions"/>
|
||||
</cache-container>
|
||||
</infinispan>
|
||||
</infinispan>
|
|
@ -432,12 +432,41 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
|
||||
<excludeArtifactIds>infinispan-embedded,infinispan-remote</excludeArtifactIds>
|
||||
<classifier>config</classifier>
|
||||
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
|
||||
<excludes>META-INF/**</excludes>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>unpack-infinispan-config</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<failOnMissingClassifierArtifact>true</failOnMissingClassifierArtifact>
|
||||
<excludes>META-INF/**</excludes>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
@ -681,7 +710,24 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-infinispan</artifactId>
|
||||
<artifactId>infinispan-embedded</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-embedded-query</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-remote-query</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -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
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<name>Jetty :: Infinispan Session Manager Common</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan.common</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>config</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan.protostream</groupId>
|
||||
<artifactId>protostream</artifactId>
|
||||
<version>4.2.2.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-client-hotrod</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-remote-query-client</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="sessionDataStoreFactory" class="org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory">
|
||||
<Set name="cache"><Ref refid="cache"/></Set>
|
||||
<Set name="infinispanIdleTimeoutSec"><Property name="jetty.session.infinispan.idleTimeout.seconds" default="0" /></Set>
|
||||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- get QueryManager using the QueryManagerFactory -->
|
||||
<Ref refid="queryMgrFactory">
|
||||
<Call id="queryManager" name="getQueryManager">
|
||||
<Arg>
|
||||
<Ref refid="cache"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
|
||||
<!-- set QueryManager in SessionDataStoreFactory -->
|
||||
<Ref refid="sessionDataStoreFactory">
|
||||
<Call name="setQueryManager">
|
||||
<Arg>
|
||||
<Ref refid="queryManager"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
|
||||
|
||||
|
||||
</Configure>
|
|
@ -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
|
|
@ -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.Log;
|
|||
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<String, Object> _cache;
|
||||
private BasicCache<String, SessionData> _cache;
|
||||
|
||||
|
||||
private int _infinispanIdleTimeoutSec;
|
||||
|
||||
|
||||
|
||||
private QueryManager _queryManager;
|
||||
|
||||
private boolean _passivating;
|
||||
|
||||
|
@ -61,7 +66,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
|||
*
|
||||
* @return the cache
|
||||
*/
|
||||
public BasicCache<String, Object> getCache()
|
||||
public BasicCache<String, SessionData> getCache()
|
||||
{
|
||||
return _cache;
|
||||
}
|
||||
|
@ -73,13 +78,26 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
|||
*
|
||||
* @param cache the cache
|
||||
*/
|
||||
public void setCache (BasicCache<String, Object> cache)
|
||||
public void setCache (BasicCache<String, SessionData> 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 class InfinispanSessionDataStore extends AbstractSessionDataStore
|
|||
@Override
|
||||
public Set<String> doGetExpired(Set<String> candidates)
|
||||
{
|
||||
if (candidates == null || candidates.isEmpty())
|
||||
return candidates;
|
||||
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Set<String> 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 (LOG.isDebugEnabled())
|
||||
LOG.debug("Checking expiry for candidate {}", candidate);
|
||||
try
|
||||
{
|
||||
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 (_context.getWorkerName().equals(sd.getLastNode()))
|
||||
//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)
|
||||
{
|
||||
LOG.warn("Error checking if candidate {} is expired", candidate, 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))
|
||||
{
|
||||
expired.add(sessionId);
|
||||
if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
return expired;
|
||||
}
|
||||
|
||||
|
@ -233,8 +276,8 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
|||
{
|
||||
return _passivating;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean exists(String id) throws Exception
|
|
@ -20,8 +20,9 @@
|
|||
package org.eclipse.jetty.session.infinispan;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.server.session.SessionData;
|
||||
import org.eclipse.jetty.server.session.SessionDataStore;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.infinispan.commons.api.BasicCache;
|
||||
|
||||
/**
|
||||
|
@ -32,8 +33,8 @@ import org.infinispan.commons.api.BasicCache;
|
|||
public class InfinispanSessionDataStoreFactory extends AbstractSessionDataStoreFactory
|
||||
{
|
||||
int _infinispanIdleTimeoutSec;
|
||||
BasicCache<String, Object> _cache;
|
||||
|
||||
BasicCache<String, SessionData> _cache;
|
||||
protected QueryManager _queryManager;
|
||||
|
||||
/**
|
||||
* @return the infinispanIdleTimeoutSec
|
||||
|
@ -62,6 +63,7 @@ public class InfinispanSessionDataStoreFactory extends AbstractSessionDataStoreF
|
|||
store.setInfinispanIdleTimeoutSec(getInfinispanIdleTimeoutSec());
|
||||
store.setCache(getCache());
|
||||
store.setSavePeriodSec(getSavePeriodSec());
|
||||
store.setQueryManager(getQueryManager());
|
||||
return store;
|
||||
}
|
||||
|
||||
|
@ -70,7 +72,7 @@ public class InfinispanSessionDataStoreFactory extends AbstractSessionDataStoreF
|
|||
*
|
||||
* @return the cache
|
||||
*/
|
||||
public BasicCache<String, Object> getCache()
|
||||
public BasicCache<String, SessionData> getCache()
|
||||
{
|
||||
return _cache;
|
||||
}
|
||||
|
@ -82,10 +84,20 @@ public class InfinispanSessionDataStoreFactory extends AbstractSessionDataStoreF
|
|||
*
|
||||
* @param cache the cache
|
||||
*/
|
||||
public void setCache (BasicCache<String, Object> cache)
|
||||
public void setCache (BasicCache<String, SessionData> cache)
|
||||
{
|
||||
this._cache = cache;
|
||||
}
|
||||
|
||||
public QueryManager getQueryManager()
|
||||
{
|
||||
return _queryManager;
|
||||
}
|
||||
|
||||
public void setQueryManager(QueryManager queryManager)
|
||||
{
|
||||
_queryManager = queryManager;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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<String, SessionData> cache)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -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<String> queryExpiredSessions();
|
||||
Set<String> queryExpiredSessions(long currentTime);
|
||||
}
|
|
@ -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<String, SessionData> cache);
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>infinispan-embedded-query</artifactId>
|
||||
<name>Jetty :: Infinispan Session Manager Embedded with Querying</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan.embedded.query</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-deps-file</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>list</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendOutput>false</appendOutput>
|
||||
<outputFile>${project.build.directory}/deps.txt</outputFile>
|
||||
<sort>true</sort>
|
||||
<excludeGroupIds>org.eclipse.jetty,javax.servlet,org.slf4j</excludeGroupIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>process-deps</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match=" *(.*):(.*):jar:(.*):.*$"
|
||||
replace="maven://\1/\2/\3|lib/infinispan/\2-\3.jar"
|
||||
byline="true"
|
||||
/>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match="The following files have been resolved:"
|
||||
replace="[files]"
|
||||
/>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>process-mod</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<concat destfile="${project.build.directory}/infinispan-query-libs.mod">
|
||||
<fileset file="src/main/config-template/modules/infinispan-query-libs.mod"/>
|
||||
<fileset file="${project.build.directory}/deps.txt"/>
|
||||
</concat>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/config.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-commons</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-query</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly>
|
||||
<id>config</id>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/config-template</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/infinispan-query-libs.mod</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>modules</outputDirectory>
|
||||
<includes>
|
||||
<include>infinispan-query-libs.mod</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Get a reference to the default local cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
<!-- TODO allow users to add more properties -->
|
||||
<New id="properties" class="java.util.Properties">
|
||||
<Call name="put">
|
||||
<Arg>
|
||||
<Get class="org.hibernate.search.cfg.Environment" name="MODEL_MAPPING"/>
|
||||
</Arg>
|
||||
<Arg>
|
||||
<New class="org.hibernate.search.cfg.SearchMapping">
|
||||
<Call name="entity">
|
||||
<Arg>
|
||||
<Get class="org.eclipse.jetty.server.session.SessionData" name="class"/>
|
||||
</Arg>
|
||||
<Call name="indexed">
|
||||
<Call name="providedId">
|
||||
<Call name="property">
|
||||
<Arg type="String">expiry</Arg>
|
||||
<Arg>
|
||||
<Get class="java.lang.annotation.ElementType" name="FIELD"/>
|
||||
</Arg>
|
||||
<Call name="field"/>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
<New id="cacheMgr" class="org.infinispan.manager.DefaultCacheManager">
|
||||
<Arg>
|
||||
<Property name="jetty.base" default="."/>/etc/infinispan.xml
|
||||
</Arg>
|
||||
<Get id="defaultConfig" name="defaultCacheConfiguration"/>
|
||||
</New>
|
||||
|
||||
|
||||
<New class="org.infinispan.configuration.cache.ConfigurationBuilder">
|
||||
<Call name="read">
|
||||
<Arg>
|
||||
<Ref refid="defaultConfig"/>
|
||||
</Arg>
|
||||
<Call name="indexing">
|
||||
<Call name="index">
|
||||
<Arg>
|
||||
<Get class="org.infinispan.configuration.cache.Index" name="ALL"/>
|
||||
</Arg>
|
||||
<Call name="addIndexedEntity">
|
||||
<Arg>
|
||||
<Get class="org.eclipse.jetty.server.session.SessionData" name="class"/>
|
||||
</Arg>
|
||||
<Call name="withProperties">
|
||||
<Arg>
|
||||
<Ref refid="properties"/>
|
||||
</Arg>
|
||||
<Call id="config" name="build"/>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
<Ref refid="cacheMgr">
|
||||
<Call name="defineConfiguration">
|
||||
<Arg>jetty-query-sessions</Arg>
|
||||
<Arg><Ref refid="config"/></Arg>
|
||||
</Call>
|
||||
|
||||
<Get id="cache" name="cache"/>
|
||||
</Ref>
|
||||
|
||||
<!-- set queryMgrFactory reference to EmbeddedQueryManagerFactory -->
|
||||
<New id="queryMgrFactory" class="org.eclipse.jetty.session.infinispan.EmbeddedQueryManagerFactory"/>
|
||||
|
||||
</Configure>
|
|
@ -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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[description]
|
||||
The Infinispan query libraries
|
||||
|
||||
[tags]
|
||||
3rdparty
|
||||
infinispan
|
||||
|
||||
[depends]
|
||||
infinispan-query
|
||||
|
||||
|
|
@ -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/
|
|
@ -0,0 +1,42 @@
|
|||
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<String, SessionData> _cache;
|
||||
|
||||
public EmbeddedQueryManager(Cache<String, SessionData> cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> queryExpiredSessions(long time)
|
||||
{
|
||||
QueryFactory qf = Search.getQueryFactory(_cache);
|
||||
Query q = qf.from(SessionData.class).select("id").having("expiry").lte(time).build();
|
||||
|
||||
List<Object[]> list = q.list();
|
||||
Set<String> ids = new HashSet<>();
|
||||
for(Object[] sl : list)
|
||||
ids.add((String)sl[0]);
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> queryExpiredSessions()
|
||||
{
|
||||
return queryExpiredSessions(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, SessionData> cache)
|
||||
{
|
||||
if (!(cache instanceof Cache))
|
||||
throw new IllegalArgumentException("Argument was not of type Cache");
|
||||
|
||||
return new EmbeddedQueryManager((Cache<String, SessionData>)cache);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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 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;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
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<String, SessionData> _cache = _manager.getCache(_name);
|
||||
|
||||
//put some sessions into the cache
|
||||
int numSessions = 10;
|
||||
long currentTime = 500;
|
||||
int maxExpiryTime = 1000;
|
||||
Set<String> expiredSessions = new HashSet<>();
|
||||
Random r = new Random();
|
||||
|
||||
for (int i=0; i<numSessions; i++)
|
||||
{
|
||||
//create new sessiondata with random expiry time
|
||||
long expiryTime = r.nextInt(maxExpiryTime);
|
||||
SessionData sd = new SessionData("sd"+i, "", "", 0, 0, 0, 0);
|
||||
sd.setExpiry(expiryTime);
|
||||
|
||||
//if this entry has expired add it to expiry list
|
||||
if (expiryTime <= currentTime)
|
||||
expiredSessions.add("sd"+i);
|
||||
|
||||
//add to cache
|
||||
_cache.put("sd"+i,sd);
|
||||
}
|
||||
|
||||
//run the query
|
||||
QueryManager qm = new EmbeddedQueryManager(_cache);
|
||||
Set<String> queryResult = qm.queryExpiredSessions(currentTime);
|
||||
|
||||
// Check that the result is correct
|
||||
assertEquals(expiredSessions.size(), queryResult.size());
|
||||
for (String s : expiredSessions)
|
||||
{
|
||||
assertTrue(queryResult.contains(s));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>infinispan-embedded</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Jetty :: Infinispan Session Manager Embedded</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan.embedded</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>config</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Get a reference to the default local cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
<New id="cacheMgr" class="org.infinispan.manager.DefaultCacheManager">
|
||||
<Arg><Property name="jetty.base" default="."/>/etc/infinispan.xml</Arg>
|
||||
<Get id="cache" name="cache"></Get>
|
||||
</New>
|
||||
|
||||
<!-- set queryMgrFactory reference to NullQueryManagerFactory -->
|
||||
<New id="queryMgrFactory" class="org.eclipse.jetty.session.infinispan.NullQueryManagerFactory"/>
|
||||
|
||||
</Configure>
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
<infinispan>
|
||||
<cache-container default-cache="jetty-sessions">
|
||||
<local-cache name="jetty-sessions"/>
|
||||
</cache-container>
|
||||
</infinispan>
|
|
@ -0,0 +1,169 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>infinispan-remote-query</artifactId>
|
||||
<name>Jetty :: Infinispan Session Manager Remote</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan.remote.query</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-deps-file</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>list</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<appendOutput>false</appendOutput>
|
||||
<outputFile>${project.build.directory}/deps.txt</outputFile>
|
||||
<sort>true</sort>
|
||||
<excludeGroupIds>org.eclipse.jetty,javax.servlet</excludeGroupIds>
|
||||
<prependGroupId>true</prependGroupId>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>process-deps</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match=" *(.*):(.*):jar:(.*):(.*):.*$"
|
||||
replace="maven://\1/\2/\4/jar/\3|lib/infinispan/\2-\3-\4.jar"
|
||||
byline="true"
|
||||
/>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match=" *(.*):(.*):jar:(.*):.*$"
|
||||
replace="maven://\1/\2/\3|lib/infinispan/\2-\3.jar"
|
||||
byline="true"
|
||||
/>
|
||||
<replaceregexp file="${project.build.directory}/deps.txt"
|
||||
match="The following files have been resolved:"
|
||||
replace="[files]"
|
||||
/>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>process-mod</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<concat destfile="${project.build.directory}/infinispan-remote-query-libs.mod">
|
||||
<fileset file="src/main/config-template/modules/infinispan-remote-query-libs.mod"/>
|
||||
<fileset file="${project.build.directory}/deps.txt"/>
|
||||
</concat>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/config.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-commons</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-query</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-client-hotrod</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-remote-query-client</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>remote</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>hotrod.enabled</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly>
|
||||
<id>config</id>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/config-template</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/infinispan-remote-query-libs.mod</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory>modules</outputDirectory>
|
||||
<includes>
|
||||
<include>infinispan-remote-query-libs.mod</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
|
@ -3,23 +3,38 @@
|
|||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Set up infinispan to use protobuf marshalling instead of default. -->
|
||||
<!-- Avoids classloading issues when marshalling/demarshalling webapps -->
|
||||
<!-- that use jetty server classes in their attributes. -->
|
||||
<!-- Get a reference to the remote cache manager. -->
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
<New id="mapping" class="org.hibernate.search.cfg.SearchMapping">
|
||||
<Call name="entity">
|
||||
<Arg><Call class="java.lang.Class" name="forName"><Arg>org.eclipse.jetty.server.session.SessionData</Arg></Call></Arg>
|
||||
<Call name="indexed">
|
||||
<Call name="providedId">
|
||||
<Call name="property">
|
||||
<Arg type="String">expiry</Arg>
|
||||
<Arg><Get class="java.lang.annotation.ElementType" name="FIELD"/></Arg>
|
||||
<Call name="field"/>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
<New id="properties" class="java.util.Properties">
|
||||
<Call name="load">
|
||||
<Arg>
|
||||
<New class="java.io.FileInputStream">
|
||||
<New class="java.io.FileInputStream">
|
||||
<Arg><Property name="jetty.base" default="."/>/resources/hotrod-client.properties</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
<Call name="put">
|
||||
<Arg><Get class="org.hibernate.search.cfg.Environment" name="MODEL_MAPPING"/></Arg>
|
||||
<Arg><Ref refid="mapping"/></Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
|
||||
<New class="org.infinispan.client.hotrod.configuration.ConfigurationBuilder">
|
||||
<Call name="withProperties">
|
||||
<Arg><Ref refid="properties"/></Arg>
|
||||
|
@ -30,13 +45,12 @@
|
|||
</Arg>
|
||||
</Call>
|
||||
<Call id="config" name="build"/>
|
||||
</New>
|
||||
|
||||
|
||||
</New>
|
||||
|
||||
<New id="remoteCacheManager" class="org.infinispan.client.hotrod.RemoteCacheManager">
|
||||
<Arg><Ref refid="config"/></Arg>
|
||||
</New>
|
||||
|
||||
|
||||
<Call id="serial_context" class="org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller" name="getSerializationContext">
|
||||
<Arg>
|
||||
<Ref refid="remoteCacheManager"/>
|
||||
|
@ -60,29 +74,17 @@
|
|||
</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Get a reference to the remote cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
<!-- ===================================================================== -->
|
||||
<Ref refid="remoteCacheManager">
|
||||
<Call id="remoteCache" name="getCache">
|
||||
<Call id="cache" name="getCache">
|
||||
<Arg><Property name="jetty.session.infinispan.remoteCacheName" default="sessions"/></Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Configure a factory for InfinispanSessionDataStore using an -->
|
||||
<!-- Infinispan remote cache -->
|
||||
<!-- ===================================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="sessionDataStoreFactory" class="org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory">
|
||||
<Set name="cache"><Ref id="remoteCache"/></Set>
|
||||
<Set name="infinispanIdleTimeoutSec"><Property name="jetty.session.infinispan.idleTimeout.seconds" default="0" /></Set>
|
||||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
<!-- set queryMgrFactory reference to RemoteQueryManagerFactory -->
|
||||
<New id="queryMgrFactory" class="org.eclipse.jetty.session.infinispan.RemoteQueryManagerFactory"/>
|
||||
|
||||
</Configure>
|
|
@ -0,0 +1,11 @@
|
|||
[description]
|
||||
The Infinispan remote query libraries
|
||||
|
||||
[tags]
|
||||
3rdparty
|
||||
infinispan
|
||||
|
||||
[depends]
|
||||
infinispan-remote-query-serverclasses
|
||||
|
||||
|
|
@ -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/
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#infinispan.client.hotrod.server_list
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- If your sessions contain any objects of classes from your -->
|
||||
<!-- from your application, you need to describe each class in a -->
|
||||
<!-- .proto file, and supply a marshaller for each to read/write -->
|
||||
<!-- instances. These classes will need to exist on the server's -->
|
||||
<!-- classpath because they are referenced BEFORE your webapp is -->
|
||||
<!-- started. -->
|
||||
<!-- ============================================================= -->
|
||||
<Ref refid="serial_context">
|
||||
<!--
|
||||
<Call name="registerProtoFiles">
|
||||
<Arg>
|
||||
<New class="org.infinispan.protostream.FileDescriptorSource">
|
||||
<Call name="addProtoFile">
|
||||
<Arg>my.proto</Arg>
|
||||
<Arg>
|
||||
<New class="java.io.File">
|
||||
<Arg><Property name="jetty.base" default="."/>/etc/my.proto</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
-->
|
||||
<!--
|
||||
<Call name="registerMarshaller">
|
||||
<Arg>
|
||||
<New class="com.acme.MyMarshaller"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
-->
|
||||
</Ref>
|
||||
</Configure>
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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<String, SessionData> _cache;
|
||||
|
||||
public RemoteQueryManager(RemoteCache<String, SessionData> cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> 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<Object[]> list = q.list();
|
||||
Set<String> ids = new HashSet<>();
|
||||
for(Object[] sl : list)
|
||||
ids.add((String)sl[0]);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> queryExpiredSessions()
|
||||
{
|
||||
return queryExpiredSessions(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, SessionData> 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<String, SessionData>)cache);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
|
||||
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<String, SessionData> _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<String> expiredSessions = new HashSet<>();
|
||||
Random r = new Random();
|
||||
|
||||
for(int i=0; i<numSessions; i++)
|
||||
{
|
||||
String id = "sd"+i;
|
||||
//create new sessiondata with random expiry time
|
||||
long expiryTime = r.nextInt(maxExpiryTime);
|
||||
SessionData sd = new SessionData(id, "", "", 0, 0, 0, 0);
|
||||
sd.setLastNode("lastNode");
|
||||
sd.setExpiry(expiryTime);
|
||||
|
||||
//if this entry has expired add it to expiry list
|
||||
if (expiryTime <= currentTime)
|
||||
expiredSessions.add(id);
|
||||
|
||||
//add to cache
|
||||
_cache.put(id, sd);
|
||||
assertNotNull(_cache.get(id));
|
||||
}
|
||||
|
||||
//run the query
|
||||
QueryManager qm = new RemoteQueryManager(_cache);
|
||||
Set<String> queryResult = qm.queryExpiredSessions(currentTime);
|
||||
|
||||
// Check that the result is correct
|
||||
assertEquals(expiredSessions.size(), queryResult.size());
|
||||
for(String s : expiredSessions)
|
||||
{
|
||||
assertTrue(queryResult.contains(s));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>infinispan-remote</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Jetty :: Infinispan Session Manager Remote</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan.remote</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>config</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Read hotrod-client.properties file -->
|
||||
<!-- ===================================================================== -->
|
||||
<New id="properties" class="java.util.Properties">
|
||||
<Call name="load">
|
||||
<Arg>
|
||||
<New class="java.io.FileInputStream">
|
||||
<Arg><Property name="jetty.base" default="."/>/resources/hotrod-client.properties</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Convert properties to configuration -->
|
||||
<!-- ===================================================================== -->
|
||||
<New class="org.infinispan.client.hotrod.configuration.ConfigurationBuilder">
|
||||
<Call name="withProperties">
|
||||
<Arg><Ref refid="properties"/></Arg>
|
||||
</Call>
|
||||
<Call name="marshaller">
|
||||
<Arg>
|
||||
<New class="org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
<Call id="config" name="build"/>
|
||||
</New>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Get a reference to the remote cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
<New id="hotrodMgr" class="org.infinispan.client.hotrod.RemoteCacheManager">
|
||||
<Arg><Ref refid="config"/></Arg>
|
||||
</New>
|
||||
|
||||
|
||||
<Call id="serial_context" class="org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller" name="getSerializationContext">
|
||||
<Arg>
|
||||
<Ref refid="hotrodMgr"/>
|
||||
</Arg>
|
||||
<Call name="registerProtoFiles">
|
||||
<Arg>
|
||||
<New class="org.infinispan.protostream.FileDescriptorSource">
|
||||
<Call name="addProtoFiles">
|
||||
<Arg>
|
||||
<Array type="java.lang.String">
|
||||
<Item>/session.proto</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
</Call>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
<Call name="registerMarshaller">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.session.infinispan.SessionDataMarshaller"/>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
|
||||
|
||||
<Ref refid="hotrodMgr">
|
||||
<Call id="cache" name="getCache">
|
||||
<Arg><Property name="jetty.session.infinispan.remoteCacheName" default="sessions"/></Arg>
|
||||
</Call>
|
||||
</Ref>
|
||||
|
||||
<!-- set queryMgrFactory reference to NullQueryManagerFactory -->
|
||||
<New id="queryMgrFactory" class="org.eclipse.jetty.session.infinispan.NullQueryManagerFactory"/>
|
||||
|
||||
</Configure>
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -1,69 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-project</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-infinispan</artifactId>
|
||||
<name>Jetty :: Infinispan Session Managers</name>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>infinispan-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Jetty :: Infinispan</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.infinispan</bundle-symbolic-name>
|
||||
</properties>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>config</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan.protostream</groupId>
|
||||
<artifactId>protostream</artifactId>
|
||||
<version>4.2.2.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-client-hotrod</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-remote-query-client</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<modules>
|
||||
<module>infinispan-common</module>
|
||||
<module>infinispan-embedded</module>
|
||||
<module>infinispan-remote</module>
|
||||
<module>infinispan-embedded-query</module>
|
||||
<module>infinispan-remote-query</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Get a reference to the default local cache. -->
|
||||
<!-- ===================================================================== -->
|
||||
<New id="local" class="org.infinispan.manager.DefaultCacheManager">
|
||||
<Arg><Property name="jetty.base" default="."/>/etc/infinispan-embedded.xml</Arg>
|
||||
<Get id="cache" name="cache"></Get>
|
||||
</New>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Configure a factory for InfinispanSessionDataStore using the -->
|
||||
<!-- Infinispan DefaultCache -->
|
||||
<!-- ===================================================================== -->
|
||||
<Call name="addBean">
|
||||
<Arg>
|
||||
<New id="sessionDataStoreFactory" class="org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory">
|
||||
<Set name="cache"><Ref id="cache"/></Set>
|
||||
<Set name="infinispanIdleTimeoutSec"><Property name="jetty.session.infinispan.idleTimeout.seconds" default="0" /></Set>
|
||||
<Set name="gracePeriodSec"><Property name="jetty.session.gracePeriod.seconds" default="3600" /></Set>
|
||||
<Set name="savePeriodSec"><Property name="jetty.session.savePeriod.seconds" default="0" /></Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -18,10 +18,11 @@
|
|||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -32,12 +33,15 @@ import org.eclipse.jetty.http.MimeTypes;
|
|||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Default Handler.
|
||||
|
@ -118,64 +122,87 @@ public class DefaultHandler extends AbstractHandler
|
|||
}
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.setContentType(MimeTypes.Type.TEXT_HTML_8859_1.asString());
|
||||
response.setContentType(MimeTypes.Type.TEXT_HTML_UTF_8.toString());
|
||||
|
||||
try (ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);)
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(outputStream, UTF_8))
|
||||
{
|
||||
writer.write("<HTML>\n<HEAD>\n<TITLE>Error 404 - Not Found");
|
||||
writer.write("</TITLE>\n<BODY>\n<H2>Error 404 - Not Found.</H2>\n");
|
||||
writer.write("No context on this server matched or handled this request.<BR>");
|
||||
writer.write("Contexts known to this server are: <ul>");
|
||||
writer.append("<!DOCTYPE html>\n");
|
||||
writer.append("<html lang=\"en\">\n<head>\n");
|
||||
writer.append("<title>Error 404 - Not Found</title>\n");
|
||||
writer.append("<meta charset=\"utf-8\">\n");
|
||||
writer.append("<style>body { font-family: sans-serif; } table, td { border: 1px solid #333; } td, th { padding: 5px; } thead, tfoot { background-color: #333; color: #fff; } </style>\n");
|
||||
writer.append("</head>\n<body>\n");
|
||||
writer.append("<h2>Error 404 - Not Found.</h2>\n");
|
||||
writer.append("<p>No context on this server matched or handled this request.</p>\n");
|
||||
writer.append("<p>Contexts known to this server are:</p>\n");
|
||||
|
||||
Server server = getServer();
|
||||
Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class);
|
||||
|
||||
writer.append("<table class=\"contexts\"><thead><tr>");
|
||||
writer.append("<th>Context Path</th>");
|
||||
writer.append("<th>Display Name</th>");
|
||||
writer.append("<th>Status</th>");
|
||||
writer.append("<th>LifeCycle</th>");
|
||||
writer.append("</tr></thead><tbody>\n");
|
||||
|
||||
for (int i=0;handlers!=null && i<handlers.length;i++)
|
||||
{
|
||||
writer.append("<tr><td>");
|
||||
// Context Path
|
||||
ContextHandler context = (ContextHandler)handlers[i];
|
||||
|
||||
String contextPath = context.getContextPath();
|
||||
String href = URIUtil.encodePath(contextPath);
|
||||
if (contextPath.length() > 1 && !contextPath.endsWith("/"))
|
||||
{
|
||||
href += '/';
|
||||
}
|
||||
|
||||
if (context.isRunning())
|
||||
{
|
||||
writer.write("<li><a href=\"");
|
||||
if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
|
||||
writer.write(request.getScheme()+"://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
|
||||
writer.write(context.getContextPath());
|
||||
if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/"))
|
||||
writer.write("/");
|
||||
writer.write("\">");
|
||||
writer.write(context.getContextPath());
|
||||
if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
|
||||
writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
|
||||
writer.write(" ---> ");
|
||||
writer.write(context.toString());
|
||||
writer.write("</a></li>\n");
|
||||
writer.append("<a href=\"").append(href).append("\">");
|
||||
}
|
||||
writer.append(contextPath.replaceAll("%", "%"));
|
||||
if (context.isRunning())
|
||||
{
|
||||
writer.append("</a>");
|
||||
}
|
||||
writer.append("</td><td>");
|
||||
// Display Name
|
||||
|
||||
if (StringUtil.isNotBlank(context.getDisplayName()))
|
||||
{
|
||||
writer.append(StringUtil.sanitizeXmlString(context.getDisplayName()));
|
||||
}
|
||||
writer.append(" </td><td>");
|
||||
// Available
|
||||
|
||||
if (context.isAvailable())
|
||||
{
|
||||
writer.append("Available");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.write("<li>");
|
||||
writer.write(context.getContextPath());
|
||||
if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
|
||||
writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
|
||||
writer.write(" ---> ");
|
||||
writer.write(context.toString());
|
||||
if (context.isFailed())
|
||||
writer.write(" [failed]");
|
||||
if (context.isStopped())
|
||||
writer.write(" [stopped]");
|
||||
writer.write("</li>\n");
|
||||
writer.append("<em>Not</em> Available");
|
||||
}
|
||||
writer.append("</td><td>");
|
||||
// State
|
||||
writer.append(context.getState());
|
||||
writer.append("</td></tr>\n");
|
||||
}
|
||||
|
||||
writer.write("</ul><hr>");
|
||||
|
||||
baseRequest.getHttpChannel().getHttpConfiguration()
|
||||
.writePoweredBy(writer,"<a href=\"http://eclipse.org/jetty\"><img border=0 src=\"/favicon.ico\"/></a> ","<hr/>\n");
|
||||
|
||||
writer.write("\n</BODY>\n</HTML>\n");
|
||||
writer.append("</tbody></table><hr/>\n");
|
||||
writer.append("<a href=\"http://eclipse.org/jetty\"><img alt=\"icon\" src=\"/favicon.ico\"/></a> ");
|
||||
writer.append("<a href=\"http://eclipse.org/jetty\">Powered by Eclipse Jetty:// Server</a><hr/>\n");
|
||||
writer.append("</body>\n</html>\n");
|
||||
writer.flush();
|
||||
response.setContentLength(writer.size());
|
||||
try (OutputStream out=response.getOutputStream())
|
||||
byte content[] = outputStream.toByteArray();
|
||||
response.setContentLength(content.length);
|
||||
try (OutputStream out = response.getOutputStream())
|
||||
{
|
||||
writer.writeTo(out);
|
||||
out.write(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.servlet;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.tools.HttpTester;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.resource.PathResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
public class DefaultHandlerTest
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector localConnector;
|
||||
private File baseA;
|
||||
private File baseFoo;
|
||||
|
||||
@BeforeEach
|
||||
public void startServer() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
localConnector = new LocalConnector(server);
|
||||
server.addConnector(localConnector);
|
||||
|
||||
File docRoot = MavenTestingUtils.getTargetTestingDir(DefaultHandlerTest.class.getName());
|
||||
FS.ensureDirExists(docRoot);
|
||||
|
||||
baseA = new File(docRoot, "baseA");
|
||||
FS.ensureDirExists(baseA);
|
||||
|
||||
baseFoo = new File(docRoot, "baseFoo");
|
||||
FS.ensureDirExists(baseFoo);
|
||||
|
||||
ServletContextHandler contextA = new ServletContextHandler();
|
||||
contextA.setContextPath("/a");
|
||||
contextA.setBaseResource(new PathResource(baseA));
|
||||
|
||||
ServletContextHandler contextFoo = new ServletContextHandler();
|
||||
contextFoo.setContextPath("/foo");
|
||||
contextFoo.setBaseResource(new PathResource(baseFoo));
|
||||
|
||||
HandlerList handlers = new HandlerList();
|
||||
handlers.addHandler(contextA);
|
||||
handlers.addHandler(contextFoo);
|
||||
handlers.addHandler(new DefaultHandler());
|
||||
|
||||
server.setHandler(handlers);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotRevealBaseResource() throws Exception
|
||||
{
|
||||
StringBuilder req = new StringBuilder();
|
||||
req.append("GET / HTTP/1.0\r\n");
|
||||
req.append("\r\n");
|
||||
|
||||
String rawResponse = localConnector.getResponse(req.toString());
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.NOT_FOUND_404));
|
||||
|
||||
String body = response.getContent();
|
||||
assertThat(body, containsString("No context on this server matched or handled this request"));
|
||||
assertThat(body, containsString("Contexts known to this server are"));
|
||||
assertThat(body, containsString("<a href=\"/a/\">"));
|
||||
assertThat(body, containsString("<a href=\"/foo/\">"));
|
||||
assertThat(body, not(containsString(baseA.toString())));
|
||||
assertThat(body, not(containsString(baseFoo.toString())));
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.servlet;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
@ -33,7 +35,6 @@ import java.util.function.Consumer;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
|
@ -52,12 +53,14 @@ import org.eclipse.jetty.http.tools.HttpTester;
|
|||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.ResourceContentFactory;
|
||||
import org.eclipse.jetty.server.ResourceService;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.resource.PathResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -81,6 +84,7 @@ import static org.hamcrest.Matchers.not;
|
|||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
|
@ -90,6 +94,9 @@ public class DefaultServletTest
|
|||
|
||||
public Path docRoot;
|
||||
|
||||
// The name of the odd-jar used for testing "jar:file://" based resource access.
|
||||
private static final String ODD_JAR = "jar-resource-odd.jar";
|
||||
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
private ServletContextHandler context;
|
||||
|
@ -105,10 +112,17 @@ public class DefaultServletTest
|
|||
connector = new LocalConnector(server);
|
||||
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
|
||||
|
||||
File extraJarResources = MavenTestingUtils.getTestResourceFile(ODD_JAR);
|
||||
URL urls[] = new URL[] { extraJarResources.toURI().toURL() };
|
||||
|
||||
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
URLClassLoader extraClassLoader = new URLClassLoader(urls, parentClassLoader);
|
||||
|
||||
context = new ServletContextHandler();
|
||||
context.setBaseResource(new PathResource(docRoot));
|
||||
context.setContextPath("/context");
|
||||
context.setWelcomeFiles(new String[]{"index.html", "index.jsp", "index.htm"});
|
||||
context.setClassLoader(extraClassLoader);
|
||||
|
||||
server.setHandler(context);
|
||||
server.addConnector(connector);
|
||||
|
@ -211,6 +225,136 @@ public class DefaultServletTest
|
|||
assertThat(body, containsString("f??r"));
|
||||
}
|
||||
|
||||
/**
|
||||
* A regression on windows allowed the directory listing show
|
||||
* the fully qualified paths within the directory listing.
|
||||
* This test ensures that this behavior will not arise again.
|
||||
*/
|
||||
@Test
|
||||
public void testListingFilenamesOnly() throws Exception
|
||||
{
|
||||
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/*");
|
||||
defholder.setInitParameter("dirAllowed", "true");
|
||||
defholder.setInitParameter("redirectWelcome", "false");
|
||||
defholder.setInitParameter("gzip", "false");
|
||||
|
||||
/* create some content in the docroot */
|
||||
FS.ensureDirExists(docRoot);
|
||||
Path one = docRoot.resolve("one");
|
||||
FS.ensureDirExists(one);
|
||||
Path deep = one.resolve("deep");
|
||||
FS.ensureDirExists(deep);
|
||||
FS.touch(deep.resolve("foo"));
|
||||
FS.ensureDirExists(docRoot.resolve("two"));
|
||||
FS.ensureDirExists(docRoot.resolve("three"));
|
||||
|
||||
String resBasePath = docRoot.toAbsolutePath().toString();
|
||||
defholder.setInitParameter("resourceBase", resBasePath);
|
||||
|
||||
StringBuffer req1 = new StringBuffer();
|
||||
req1.append("GET /context/one/deep/ HTTP/1.0\n");
|
||||
req1.append("\n");
|
||||
|
||||
String rawResponse = connector.getResponse(req1.toString());
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
String body = response.getContent();
|
||||
assertThat(body, containsString("/foo"));
|
||||
assertThat(body, not(containsString(resBasePath)));
|
||||
}
|
||||
|
||||
/**
|
||||
* A regression on windows allowed the directory listing show
|
||||
* the fully qualified paths within the directory listing.
|
||||
* This test ensures that this behavior will not arise again.
|
||||
*/
|
||||
@Test
|
||||
public void testListingFilenamesOnly_UrlResource() throws Exception
|
||||
{
|
||||
URL extraResource = context.getClassLoader().getResource("rez/one");
|
||||
assertNotNull(extraResource, "Must have extra jar resource in classloader");
|
||||
|
||||
String extraResourceBaseString = extraResource.toURI().toASCIIString();
|
||||
extraResourceBaseString = extraResourceBaseString.substring(0, extraResourceBaseString.length() - "/one".length());
|
||||
|
||||
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/extra/*");
|
||||
defholder.setInitParameter("resourceBase", extraResourceBaseString);
|
||||
defholder.setInitParameter("pathInfoOnly", "true");
|
||||
defholder.setInitParameter("dirAllowed", "true");
|
||||
defholder.setInitParameter("redirectWelcome", "false");
|
||||
defholder.setInitParameter("gzip", "false");
|
||||
|
||||
StringBuffer req1;
|
||||
String rawResponse;
|
||||
HttpTester.Response response;
|
||||
String body;
|
||||
|
||||
// Test that GET works first.
|
||||
req1 = new StringBuffer();
|
||||
req1.append("GET /context/extra/one HTTP/1.0\n");
|
||||
req1.append("\n");
|
||||
|
||||
rawResponse = connector.getResponse(req1.toString());
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
body = response.getContent();
|
||||
assertThat(body, containsString("is this the one?"));
|
||||
|
||||
// Typical directory listing of location in jar:file:// URL
|
||||
req1 = new StringBuffer();
|
||||
req1.append("GET /context/extra/deep/ HTTP/1.0\r\n");
|
||||
req1.append("\r\n");
|
||||
|
||||
rawResponse = connector.getResponse(req1.toString());
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
body = response.getContent();
|
||||
assertThat(body, containsString("/xxx"));
|
||||
assertThat(body, containsString("/yyy"));
|
||||
assertThat(body, containsString("/zzz"));
|
||||
|
||||
assertThat(body, not(containsString(extraResourceBaseString)));
|
||||
assertThat(body, not(containsString(ODD_JAR)));
|
||||
|
||||
// Get deep resource
|
||||
req1 = new StringBuffer();
|
||||
req1.append("GET /context/extra/deep/yyy HTTP/1.0\r\n");
|
||||
req1.append("\r\n");
|
||||
|
||||
rawResponse = connector.getResponse(req1.toString());
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
body = response.getContent();
|
||||
assertThat(body, containsString("a file named yyy"));
|
||||
|
||||
// Convoluted directory listing of location in jar:file:// URL
|
||||
// This exists to test proper encoding output
|
||||
req1 = new StringBuffer();
|
||||
req1.append("GET /context/extra/oddities/ HTTP/1.0\r\n");
|
||||
req1.append("\r\n");
|
||||
|
||||
rawResponse = connector.getResponse(req1.toString());
|
||||
response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(HttpStatus.OK_200));
|
||||
body = response.getContent();
|
||||
assertThat(body, containsString(">#hashcode <")); // text on page
|
||||
assertThat(body, containsString("/oddities/%23hashcode")); // generated link
|
||||
|
||||
assertThat(body, containsString(">other%2fkind%2Fof%2fslash <")); // text on page
|
||||
assertThat(body, containsString("/oddities/other%252fkind%252Fof%252fslash")); // generated link
|
||||
|
||||
assertThat(body, containsString(">a file with a space <")); // text on page
|
||||
assertThat(body, containsString("/oddities/a%20file%20with%20a%20space")); // generated link
|
||||
|
||||
assertThat(body, not(containsString(extraResourceBaseString)));
|
||||
assertThat(body, not(containsString(ODD_JAR)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListingProperUrlEncoding() throws Exception
|
||||
{
|
||||
|
@ -1744,11 +1888,13 @@ public class DefaultServletTest
|
|||
ServletHolder defholder = context.addServlet(DefaultServlet.class, "/");
|
||||
defholder.setInitParameter("resourceBase", docRoot.toFile().getAbsolutePath());
|
||||
|
||||
String rawResponse = connector.getResponse("GET /context/%0a HTTP/1.1\r\nHost: local\r\nConnection: close\r\n\r\n");
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
System.out.println(response + "\n" + response.getContent());
|
||||
assertThat("Response.status", response.getStatus(), anyOf(is(HttpServletResponse.SC_NOT_FOUND), is(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)));
|
||||
assertThat("Response.content", response.getContent(), is(not(containsString(docRoot.toString()))));
|
||||
try (StacklessLogging ignore = new StacklessLogging(ResourceService.class))
|
||||
{
|
||||
String rawResponse = connector.getResponse("GET /context/%0a HTTP/1.1\r\nHost: local\r\nConnection: close\r\n\r\n");
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat("Response.status", response.getStatus(), anyOf(is(HttpServletResponse.SC_NOT_FOUND), is(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)));
|
||||
assertThat("Response.content", response.getContent(), is(not(containsString(docRoot.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.LEVEL=INFO
|
||||
org.eclipse.jetty.LEVEL=WARN
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.servlet.LEVEL=DEBUG
|
||||
|
|
|
@ -45,9 +45,11 @@ import org.eclipse.jetty.util.UrlEncoded;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
/**
|
||||
* Abstract resource class.
|
||||
* <p>
|
||||
* This class provides a resource abstraction, where a resource may be
|
||||
|
@ -76,7 +78,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return __defaultUseCaches;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a uri.
|
||||
* @param uri A URI.
|
||||
|
@ -88,7 +90,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return newResource(uri.toURL());
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a url.
|
||||
* @param url A URL.
|
||||
|
@ -98,8 +100,8 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return newResource(url, __defaultUseCaches);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Construct a resource from a url.
|
||||
* @param url the url for which to make the resource
|
||||
|
@ -137,8 +139,8 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
return new URLResource(url,null,useCaches);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a string.
|
||||
* @param resource A URL or filename.
|
||||
|
@ -149,7 +151,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return newResource(resource, __defaultUseCaches);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Construct a resource from a string.
|
||||
* @param resource A URL or filename.
|
||||
|
@ -218,7 +220,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
/** Construct a system resource from a string.
|
||||
* The resource is tried as classloader resource before being
|
||||
* treated as a normal resource.
|
||||
* @param resource Resource as string representation
|
||||
* @param resource Resource as string representation
|
||||
* @return The new Resource
|
||||
* @throws IOException Problem accessing resource.
|
||||
*/
|
||||
|
@ -254,17 +256,17 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
url=loader.getResource(resource.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (url==null)
|
||||
{
|
||||
url=ClassLoader.getSystemResource(resource);
|
||||
if (url==null && resource.startsWith("/"))
|
||||
url=ClassLoader.getSystemResource(resource.substring(1));
|
||||
}
|
||||
|
||||
|
||||
if (url==null)
|
||||
return null;
|
||||
|
||||
|
||||
return newResource(url);
|
||||
}
|
||||
|
||||
|
@ -286,21 +288,21 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
* Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources.
|
||||
* @param name The relative name of the resource
|
||||
* @param useCaches True if URL caches are to be used.
|
||||
* @param checkParents True if forced searching of parent Classloaders is performed to work around
|
||||
* @param checkParents True if forced searching of parent Classloaders is performed to work around
|
||||
* loaders with inverted priorities
|
||||
* @return Resource or null
|
||||
*/
|
||||
public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents)
|
||||
{
|
||||
URL url=Resource.class.getResource(name);
|
||||
|
||||
|
||||
if (url==null)
|
||||
url=Loader.getResource(name);
|
||||
if (url==null)
|
||||
return null;
|
||||
return newResource(url,useCaches);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
|
||||
{
|
||||
|
@ -309,7 +311,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Release any temporary resources held by the resource.
|
||||
*/
|
||||
|
@ -321,7 +323,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
* @return true if the represented resource exists.
|
||||
*/
|
||||
public abstract boolean exists();
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -334,7 +336,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Time resource was last modified.
|
||||
*
|
||||
*
|
||||
* @return the last modified time as milliseconds since unix epoch
|
||||
*/
|
||||
public abstract long lastModified();
|
||||
|
@ -343,15 +345,15 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Length of the resource.
|
||||
*
|
||||
*
|
||||
* @return the length of the resource
|
||||
*/
|
||||
public abstract long length();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* URI representing the resource.
|
||||
*
|
||||
*
|
||||
* @return an URI representing the given resource
|
||||
*/
|
||||
public abstract URI getURI();
|
||||
|
@ -359,38 +361,38 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* File representing the given resource.
|
||||
*
|
||||
*
|
||||
* @return an File representing the given resource or NULL if this
|
||||
* is not possible.
|
||||
* @throws IOException if unable to get the resource due to permissions
|
||||
* @throws IOException if unable to get the resource due to permissions
|
||||
*/
|
||||
public abstract File getFile()
|
||||
throws IOException;
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* The name of the resource.
|
||||
*
|
||||
*
|
||||
* @return the name of the resource
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Input stream to the resource
|
||||
*
|
||||
*
|
||||
* @return an input stream to the resource
|
||||
* @throws IOException if unable to open the input stream
|
||||
*/
|
||||
public abstract InputStream getInputStream()
|
||||
throws IOException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Readable ByteChannel for the resource.
|
||||
*
|
||||
*
|
||||
* @return an readable bytechannel to the resource or null if one is not available.
|
||||
* @throws IOException if unable to open the readable bytechannel for the resource.
|
||||
*/
|
||||
|
@ -402,11 +404,11 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
* Deletes the given resource
|
||||
* @return true if resource was found and successfully deleted, false if resource didn't exist or was unable to
|
||||
* be deleted.
|
||||
* @throws SecurityException if unable to delete due to permissions
|
||||
* @throws SecurityException if unable to delete due to permissions
|
||||
*/
|
||||
public abstract boolean delete()
|
||||
throws SecurityException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Rename the given resource
|
||||
|
@ -416,7 +418,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
*/
|
||||
public abstract boolean renameTo(Resource dest)
|
||||
throws SecurityException;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* list of resource names contained in the given resource.
|
||||
|
@ -482,7 +484,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return getAlias()!=null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return The canonical Alias of this resource or null if none.
|
||||
|
@ -491,7 +493,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the resource list as a HTML directory listing.
|
||||
* @param base The base URL
|
||||
|
@ -671,7 +673,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
buf.append("<tbody>\n");
|
||||
|
||||
String encodedBase = hrefEncodeURI(base);
|
||||
|
||||
|
||||
if (parent)
|
||||
{
|
||||
// Name
|
||||
|
@ -689,12 +691,12 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
DateFormat.MEDIUM);
|
||||
for (Resource item: items)
|
||||
{
|
||||
String name = item.getName();
|
||||
int slashIdx = name.lastIndexOf('/');
|
||||
if (slashIdx != -1)
|
||||
String name = item.getFileName();
|
||||
if (StringUtil.isBlank(name))
|
||||
{
|
||||
name = name.substring(slashIdx + 1);
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
if (item.isDirectory() && !name.endsWith("/"))
|
||||
{
|
||||
name += URIUtil.SLASH;
|
||||
|
@ -711,13 +713,21 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
|
||||
// Last Modified
|
||||
buf.append("<td class=\"lastmodified\">");
|
||||
buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
buf.append("</td>");
|
||||
long lastModified = item.lastModified();
|
||||
if (lastModified > 0)
|
||||
{
|
||||
buf.append(dfmt.format(new Date(item.lastModified())));
|
||||
}
|
||||
buf.append(" </td>");
|
||||
|
||||
// Size
|
||||
buf.append("<td class=\"size\">");
|
||||
buf.append(String.format("%,d", item.length()));
|
||||
buf.append(" bytes </td></tr>\n");
|
||||
long length = item.length();
|
||||
if (length >= 0)
|
||||
{
|
||||
buf.append(String.format("%,d bytes", item.length()));
|
||||
}
|
||||
buf.append(" </td></tr>\n");
|
||||
}
|
||||
buf.append("</tbody>\n");
|
||||
buf.append("</table>\n");
|
||||
|
@ -725,7 +735,56 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the raw (decoded if possible) Filename for this Resource.
|
||||
* This is the last segment of the path.
|
||||
* @return the raw / decoded filename for this resource
|
||||
*/
|
||||
private String getFileName()
|
||||
{
|
||||
try
|
||||
{
|
||||
// if a Resource supports File
|
||||
File file = getFile();
|
||||
if (file != null)
|
||||
{
|
||||
return file.getName();
|
||||
}
|
||||
}
|
||||
catch (Throwable ignore)
|
||||
{
|
||||
}
|
||||
|
||||
// All others use raw getName
|
||||
try
|
||||
{
|
||||
String rawName = getName(); // gets long name "/foo/bar/xxx"
|
||||
int idx = rawName.lastIndexOf('/');
|
||||
if (idx == rawName.length()-1)
|
||||
{
|
||||
// hit a tail slash, aka a name for a directory "/foo/bar/"
|
||||
idx = rawName.lastIndexOf('/', idx-1);
|
||||
}
|
||||
|
||||
String encodedFileName;
|
||||
if (idx >= 0)
|
||||
{
|
||||
encodedFileName = rawName.substring(idx + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
encodedFileName = rawName; // entire name
|
||||
}
|
||||
return UrlEncoded.decodeString(encodedFileName, 0, encodedFileName.length(), UTF_8);
|
||||
}
|
||||
catch (Throwable ignore)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode any characters that could break the URI string in an HREF.
|
||||
* Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
|
||||
|
@ -790,7 +849,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param out the output stream to write to
|
||||
* @param out the output stream to write to
|
||||
* @param start First byte to write
|
||||
* @param count Bytes to write or -1 for all of them.
|
||||
* @throws IOException if unable to copy the Resource to the output
|
||||
|
@ -813,7 +872,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
* Copy the Resource to the new destination file.
|
||||
* <p>
|
||||
* Will not replace existing destination file.
|
||||
*
|
||||
*
|
||||
* @param destination the destination file to create
|
||||
* @throws IOException if unable to copy the resource
|
||||
*/
|
||||
|
@ -822,7 +881,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
if (destination.exists())
|
||||
throw new IllegalArgumentException(destination + " exists");
|
||||
|
||||
|
||||
try (OutputStream out = new FileOutputStream(destination))
|
||||
{
|
||||
writeTo(out,0,-1);
|
||||
|
@ -832,14 +891,14 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Generate a weak ETag reference for this Resource.
|
||||
*
|
||||
*
|
||||
* @return the weak ETag reference for this resource.
|
||||
*/
|
||||
public String getWeakETag()
|
||||
{
|
||||
return getWeakETag("");
|
||||
}
|
||||
|
||||
|
||||
public String getWeakETag(String suffix)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkedProcessExitTimeoutInSeconds>45</forkedProcessExitTimeoutInSeconds>
|
||||
<forkedProcessTimeoutInSeconds>240</forkedProcessTimeoutInSeconds>
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.config.file>${project.build.testOutputDirectory}/logging.properties</java.util.logging.config.file>
|
||||
</systemPropertyVariables>
|
||||
|
|
|
@ -16,11 +16,8 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
|
||||
package org.eclipse.jetty.hazelcast.session;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreTest;
|
||||
import org.eclipse.jetty.server.session.SessionContext;
|
||||
|
@ -31,6 +28,9 @@ 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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* HazelcastSessionDataStoreTest
|
||||
|
@ -39,16 +39,15 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
*/
|
||||
public class HazelcastSessionDataStoreTest extends AbstractSessionDataStoreTest
|
||||
{
|
||||
|
||||
|
||||
HazelcastTestHelper _testHelper;
|
||||
|
||||
|
||||
@Override
|
||||
public SessionDataStoreFactory createSessionDataStoreFactory()
|
||||
{
|
||||
{
|
||||
return _testHelper.createSessionDataStoreFactory(false);
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp()
|
||||
{
|
||||
|
@ -61,58 +60,91 @@ public class HazelcastSessionDataStoreTest extends AbstractSessionDataStoreTest
|
|||
_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
|
||||
store.start();
|
||||
|
||||
((HazelcastSessionDataStore) store).setSessionDataMap(null);
|
||||
|
||||
// test that loading it fails
|
||||
try
|
||||
{
|
||||
store.load("222");
|
||||
|
@ -120,34 +152,9 @@ public class HazelcastSessionDataStoreTest extends AbstractSessionDataStoreTest
|
|||
}
|
||||
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
|
||||
|
|
|
@ -19,23 +19,25 @@
|
|||
|
||||
package org.eclipse.jetty.hazelcast.session;
|
||||
|
||||
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.Config;
|
||||
import com.hazelcast.config.JoinConfig;
|
||||
import com.hazelcast.config.MapConfig;
|
||||
import com.hazelcast.config.MulticastConfig;
|
||||
import com.hazelcast.config.NetworkConfig;
|
||||
import com.hazelcast.config.SerializerConfig;
|
||||
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import org.eclipse.jetty.server.session.SessionData;
|
||||
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.config.MapConfig;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* HazelcastTestHelper
|
||||
|
@ -73,8 +75,22 @@ public class HazelcastTestHelper
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -83,14 +83,8 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-infinispan</artifactId>
|
||||
<artifactId>infinispan-remote-query</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -109,6 +103,12 @@
|
|||
<version>${infinispan.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-query</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-client-hotrod</artifactId>
|
||||
|
@ -121,6 +121,18 @@
|
|||
<version>${infinispan.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan.protostream</groupId>
|
||||
<artifactId>protostream</artifactId>
|
||||
<version>4.2.2.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<!-- to test hotrod, configure a cache called "remote-session-test" -->
|
||||
|
|
|
@ -19,13 +19,23 @@
|
|||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* InfinispanSessionDataStoreTest
|
||||
|
@ -164,4 +174,32 @@ public class InfinispanSessionDataStoreTest extends AbstractSessionDataStoreTest
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testQuery() throws Exception
|
||||
{
|
||||
Cache<String, SessionData> 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<Object[]> list = q.list();
|
||||
|
||||
List<String> ids = new ArrayList<>();
|
||||
for(Object[] sl : list)
|
||||
ids.add((String)sl[0]);
|
||||
|
||||
assertFalse(ids.isEmpty());
|
||||
assertTrue(1==ids.size());
|
||||
assertTrue(ids.contains("sd2"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,19 +19,25 @@
|
|||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* InfinispanTestSupport
|
||||
*
|
||||
|
@ -89,19 +95,44 @@ public class InfinispanTestSupport
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory;
|
|||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
|
||||
/**
|
||||
* ClusteredSessionScavengingTest
|
||||
*
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
package org.eclipse.jetty.server.session.remote;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreTest;
|
||||
import org.eclipse.jetty.server.session.SessionContext;
|
||||
|
@ -29,10 +27,19 @@ 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.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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* RemoteInfinispanSessionDataStoreTest
|
||||
|
@ -67,6 +74,7 @@ public class RemoteInfinispanSessionDataStoreTest extends AbstractSessionDataSto
|
|||
{
|
||||
InfinispanSessionDataStoreFactory factory = new InfinispanSessionDataStoreFactory();
|
||||
factory.setCache(__testSupport.getCache());
|
||||
factory.setQueryManager(new RemoteQueryManager(__testSupport.getCache()));
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
@ -110,29 +118,6 @@ public class RemoteInfinispanSessionDataStoreTest extends AbstractSessionDataSto
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* 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 class RemoteInfinispanSessionDataStoreTest extends AbstractSessionDataSto
|
|||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,22 +19,28 @@
|
|||
|
||||
package org.eclipse.jetty.server.session.remote;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
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.infinispan.client.hotrod.configuration.ConfigurationBuilder;
|
||||
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 static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -45,7 +51,7 @@ import org.infinispan.protostream.SerializationContext;
|
|||
public class RemoteInfinispanTestSupport
|
||||
{
|
||||
public static final String DEFAULT_CACHE_NAME = "session_test_cache";
|
||||
public RemoteCache<String,Object> _cache;
|
||||
public RemoteCache<String,SessionData> _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 class RemoteInfinispanTestSupport
|
|||
|
||||
|
||||
|
||||
public RemoteCache<String,Object> getCache ()
|
||||
public RemoteCache<String,SessionData> getCache ()
|
||||
{
|
||||
return _cache;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue