Merge branch 'jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Jesse McConnell 2016-06-07 11:56:27 -05:00
commit 20c1301203
25 changed files with 515 additions and 587 deletions

View File

@ -25,6 +25,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.FragmentDescriptor;
import org.eclipse.jetty.webapp.WebAppContext;
@ -40,7 +41,7 @@ public class TestAnnotationConfiguration
@Test
public void testGetFragmentFromJar() throws Exception
{
String dir = System.getProperty("basedir", ".");
String dir = MavenTestingUtils.getTargetTestingDir("getFragmentFromJar").getAbsolutePath();
File file = new File(dir);
file=new File(file.getCanonicalPath());
URL url=file.toURL();

View File

@ -1,9 +1,9 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.jboss.LEVEL=DEBUG
# org.jboss.LEVEL=DEBUG
org.eclipse.jetty.LEVEL=INFO
org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
org.eclipse.jetty.cdi.LEVEL=DEBUG
# org.eclipse.jetty.util.DecoratedObjectFactory.LEVEL=DEBUG
# org.eclipse.jetty.cdi.LEVEL=DEBUG
# org.eclipse.jetty.LEVEL=DEBUG
# org.eclipse.jetty.websocket.LEVEL=DEBUG

View File

@ -17,144 +17,97 @@
[[session-clustering-gcloud-datastore]]
=== Session Clustering with Google Cloud Datastore
Jetty can support session clustering by persisting sessions to
https://cloud.google.com/datastore/docs/concepts/overview[Google Cloud
Datastore]. Each Jetty instance locally caches sessions for which it has
received requests, writing any changes to the session through to the
Datastore as the request exits the server. Sessions must obey the
Serialization contract, and servlets must call the
Session.setAttribute() method to ensure that changes are persisted.
Jetty can support session clustering by persisting sessions to https://cloud.google.com/datastore/docs/concepts/overview[Google Cloud Datastore].
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the Datastore as the request exits the server.
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
The persistent session mechanism works in conjunction with a load
balancer that supports stickiness. Stickiness can be based on various
data items, such as source IP address or characteristics of the session
ID or a load-balancer specific mechanism. For those load balancers that
examine the session ID, the Jetty persistent session mechanism appends a
node ID to the session ID, which can be used for routing.
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
==== Configuration
There are two components to session management in Jetty: a session ID
manager and a session manager.
There are two components to session management in Jetty: a session ID manager and a session manager.
* The session ID manager ensures that session IDs are unique across all
webapps hosted on a Jetty instance, and thus there can only be one
session ID manager per Jetty instance.
* The session manager handles the session lifecycle
(create/update/invalidate/expire) on behalf of a web application, so
there is one session manager per web application instance.
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
These managers also cooperate and collaborate with the
`org.eclipse.jetty.server.session.SessionHandler` to enable
cross-context dispatch.
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
==== The gcloud-sessions Module
When using the jetty distribution, to enable Cloud Datastore session
persistence, you will first need to enable the `gcloud-sessions`
link:#startup-modules[module] for your link:#creating-jetty-base[base]
using the --add-to-start or --add-to-startd argument to the
link:#startup-overview[start.jar].
When using the jetty distribution, to enable Cloud Datastore session persistence, you will first need to enable the `gcloud-sessions` link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
As part of the module installation, the necessary jars will be
dynamically downloaded and installed to your `${jetty.base}/lib/gcloud`
directory. If you need to up or downgrade the version of the jars, then
you can delete the jars that were automatically installed and replace
them. Once you've done that, you will need to prevent jetty's startup
checks from detecting the missing jars. To do that, you can use
`--skip-file-validation=glcoud-sessions` argument to start.jar on the
command line, or place that line inside `${jetty.base}/start.ini` to
ensure it is used for every start.
As part of the module installation, the necessary jars will be dynamically downloaded and installed to your `${jetty.base}/lib/gcloud` directory.
If you need to up or downgrade the version of the jars, then you can delete the jars that were automatically installed and replace them.
Once you've done that, you will need to prevent jetty's startup checks from detecting the missing jars.
To do that, you can use `--skip-file-validation=glcoud-sessions` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
===== Configuring the GCloudSessionIdManager
The gcloud-sessions module will have installed file called
`${jetty.home}/etc/jetty-gcloud-sessions.xml`. This file configures an
instance of the GCloudSessionIdManager that will be shared across all
webapps deployed on that server. It looks like this:
The gcloud-sessions module will have installed file called `${jetty.home}/etc/jetty-gcloud-sessions.xml`.
This file configures an instance of the `GCloudSessionIdManager` that will be shared across all webapps deployed on that server. It looks like this:
[source,xml]
----
include::{SRCDIR}/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-session-store.xml[]
include::{SRCDIR}/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml[]
----
You configure it by setting values for properties. The properties will
either be inserted as commented out in your `start.ini`, or your
`start.d/gcloud-sessions.ini` file, depending on how you enabled the
module.
You configure it by setting values for properties.
The properties will either be inserted as commented out in your `start.ini`, or your `start.d/gcloud-sessions.ini` file, depending on how you enabled the module.
The only property you always need to set is the name of the node in the
cluster:
The only property you always need to set is the name of the node in the cluster:
jetty.gcloudSession.workerName::
The name that uniquely identifies this node in the cluster. This value
will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on
which you enable gcloud datastore session clustering.
The name that uniquely identifies this node in the cluster.
This value will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on which you enable gcloud datastore session clustering.
Which other properties you need to set depends on the execution
environment:
Which other properties you need to set depends on the execution environment:
====== Running Within Google Infrastructure
When you upload your webapp to run in Compute Engine, you do not need to
set any other properties for jetty. If you follow the instructions in
the https://cloud.google.com/datastore/docs/activate[Cloud Datastore
documentation], all authorizations etc will be provided by the runtime
environment.
When you upload your webapp to run in Compute Engine, you do not need to set any other properties for Jetty.
If you follow the instructions in the https://cloud.google.com/datastore/docs/activate[Cloud Datastore documentation], all authorizations...etc. will be provided by the runtime environment.
====== Running Externally to Google Infrastructure
When your app is executing outside of Google, you can either contact a
remote Cloud Datastore instance, or a
https://cloud.google.com/datastore/docs/tools/devserver[local test dev
server] provided by the sdk. The choice determines which properties you
need to set:
When your app is executing outside of Google, you can either contact a remote Cloud Datastore instance, or a https://cloud.google.com/datastore/docs/tools/devserver[local test dev server] provided by the sdk.
The choice determines which properties you need to set:
Contacting an sdk dev server for testing:::
In this case, you need to set up either some _System_ properties or
_environment variables_ - NOT jetty properties!
+
In this case, you need to set up either some _System_ properties or _environment variables_ - *NOT* Jetty properties!
DATASTORE_DATASET;;
This must be the name of your (test) project.
DATASTORE_HOST;;
This is the url of the dev server as described at
https://cloud.google.com/datastore/docs/tools/devserver#setting_environment_variables.
An example may be "http://localhost:9999"
This is the url of the dev server as described https://cloud.google.com/datastore/docs/tools/devserver#setting_environment_variables[in Google's documentation].
An example may be "http://localhost:9999".
Contacting a remote Cloud Datastore:::
In this case, you need to provide all of the authentication and
authorization information explicitly via jetty properties in the ini
file:
+
In this case, you need to provide all of the authentication and authorization information explicitly via jetty properties in the ini file:
jetty.gcloudSession.projectId;;
This is the name of your project.
jetty.gcloudSession.p12File;;
This is the location of the p12 key file that is associated with
your project.
This is the location of the p12 key file that is associated with your project.
jetty.gcloudSession.serviceAccount;;
This is the email address that defines your service account for the
Cloud Datastore.
This is the email address that defines your service account for the Cloud Datastore.
jetty.gcloudSession.password;;
This is the password associated with the p12 key file.
===== Configuring the GCloudSessionManager
As mentioned elsewhere, there should be one GCloudSessionManager per
context (ie webapp). It will need to reference the single
GCloudSessionIdManager from which it derives the Cloud Datastore
configuration information.
As mentioned elsewhere, there should be one `GCloudSessionManager` per context (e.g. webapp).
It will need to reference the single `GCloudSessionIdManager` from which it derives the Cloud Datastore configuration information.
The way you configure a GCloudSessionManager depends on whether you're
configuring from a context xml file or a `jetty-web.xml` file or code.
The basic difference is how you get a reference to the Jetty
`org.eclipse.jetty.server.Server` instance.
The way you configure a `GCloudSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
From a context xml file, you reference the Server instance as a Ref:
[source,xml]
----
----
<!-- Get a reference to the GCloudSessionIdManager -->
<Ref id="Server">
<Call id="idMgr" name="getSessionIdManager"/>
@ -175,12 +128,10 @@ From a context xml file, you reference the Server instance as a Ref:
</Set>
----
From a `WEB-INF/jetty-web.xml` file, you can reference the Server
instance directly:
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
[source,xml]
----
<!-- Reference the server directly -->
<Get name="server">
<Get id="idMgr" name="sessionIdManager"/>
@ -201,19 +152,17 @@ instance directly:
</Set>
----
The GCloudSessionManager supports the following configuration setters:
The `GCloudSessionManager` supports the following configuration setters:
scavengeIntervalSec::
Time in seconds between runs of a scavenger task that looks for
expired old sessions to delete. The default is 10 minutes. If set to
0, no scavenging is done.
Time in seconds between runs of a scavenger task that looks for expired old sessions to delete.
The default is 10 minutes.
If set to 0, no scavenging is done.
staleIntervalSec::
The length of time a session can be in memory without being checked
against the cluster. A value of 0 indicates that the session is never
checked against the cluster - the current node is considered to be the
master for the session.
The length of time a session can be in memory without being checked against the cluster.
A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
maxQueryResults::
The maximum number of results to return for a query to find expired
sessions. For efficiency it is important to limit the size of the
result. The default is 100. If 0 or negative numbers are set, the
default is used instead.
The maximum number of results to return for a query to find expired sessions.
For efficiency it is important to limit the size of the result.
The default is 100.
If 0 or negative numbers are set, the default is used instead.

View File

@ -17,58 +17,33 @@
[[session-clustering-infinispan]]
=== Session Clustering with Infinispan
Jetty can support session clustering by persisting sessions to
http://www.infinispan.org[Infinispan]. Each Jetty instance locally
caches sessions for which it has received requests, writing any changes
to the session through to Infinispan as the request exits the server.
Sessions must obey the Serialization contract, and servlets must call
the Session.setAttribute() method to ensure that changes are persisted.
Jetty can support session clustering by persisting sessions to http://www.infinispan.org[Infinispan].
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to Infinispan as the request exits the server.
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
The persistent session mechanism works in conjunction with a load
balancer that supports stickiness. Stickiness can be based on various
data items, such as source IP address or characteristics of the session
ID or a load-balancer specific mechanism. For those load balancers that
examine the session ID, the Jetty persistent session mechanism appends a
node ID to the session ID, which can be used for routing.
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
==== Configuration
There are two components to session management in Jetty: a session ID
manager and a session manager.
There are two components to session management in Jetty: a session ID manager and a session manager.
* The session ID manager ensures that session IDs are unique across all
webapps hosted on a Jetty instance, and thus there can only be one
session ID manager per Jetty instance.
* The session manager handles the session lifecycle
(create/update/invalidate/expire) on behalf of a web application, so
there is one session manager per web application instance.
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
These managers also cooperate and collaborate with the
`org.eclipse.jetty.server.session.SessionHandler` to enable
cross-context dispatch.
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
==== The infinispan Module
==== The Infinispan Module
When using the jetty distribution, to enable Infinispan session
persistence, you will first need to enable the `infinispan`
link:#startup-modules[module] for your link:#creating-jetty-base[base]
using the --add-to-start or --add-to-startd argument to the
link:#startup-overview[start.jar].
When using the jetty distribution, to enable Infinispan session persistence, you will first need to enable the Infinispan link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
As part of the module installation, the necessary infinispan jars will
be dynamically downloaded and installed to your
`${jetty.base}/lib/infinispan` directory. If you need to up or downgrade
the version of the infinispan jars, then you can delete the jars that
were automatically installed and replace them. Once you've done that,
you will need to prevent jetty's startup checks from detecting the
missing jars. To do that, you can use
`--skip-file-validation=infinispan` argument to start.jar on the command
line, or place that line inside `${jetty.base}/start.ini` to ensure it
is used for every start.
As part of the module installation, the necessary Infinispan jars will be dynamically downloaded and installed to your `${jetty.base}/lib/infinispan` directory.
If you need to up or downgrade the version of the Infinispan jars, then you can delete the jars that were automatically installed and replace them.
Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars.
To do that, you can use `--skip-file-validation=infinispan` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
You will also find the following properties, either in your base's
`start.d/infinispan.ini` file or appended to your `start.ini`, depending
on how you enabled the module:
You will also find the following properties, either in your base's `start.d/infinispan.ini` file or appended to your `start.ini`, depending on how you enabled the module:
....
## Unique identifier for this node in the cluster
@ -76,61 +51,41 @@ jetty.infinispanSession.workerName=node1
....
jetty.infinispanSession.workerName::
The name that uniquely identifies this node in the cluster. This value
will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on
which you enable infinispan session clustering.
The name that uniquely identifies this node in the cluster.
This value will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on which you enable Infinispan session clustering.
These properties are applied to the InfinispanSessionIdManager described
below.
These properties are applied to the `InfinispanSessionIdManager` described below.
===== Configuring the InfinispanSessionIdManager
The infinispan module will have installed file called
$\{jetty.home}/etc/jetty-infinispan.xml. This file configures an
instance of the InfinispanSessionIdManager that will be shared across
all webapps deployed on that server. It looks like this:
The Infinispan module will have installed file called `$\{jetty.home}/etc/jetty-infinispan.xml`.
This file configures an instance of the `InfinispanSessionIdManager` that will be shared across all webapps deployed on that server.
It looks like this:
[source,xml]
----
include::{SRCDIR}/jetty-infinispan/src/main/config/etc/jetty-default-infinispan-store.xml[]
include::{SRCDIR}/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml[]
----
As you can see, you configure the Infinispan
http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_the_cache_apis[Cache]
instance that the InfinispanSessionIdManager should use in this file. By
default, the infinispan
http://infinispan.org/docs/7.1.x/getting_started/getting_started.html#_running_infinispan_on_a_single_node[Default
cache] instance is used (ie on the local node). You can instead use a
custom Cache setup - the jetty-infinispan.xml file shows you how to
configure a remote Cache (using the
http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_using_hot_rod_server[hotrod
java client]).
As you can see, you configure the Infinispan http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_the_cache_apis[Cache] instance that the `InfinispanSessionIdManager` should use in this file.
By default, the Infinispan http://infinispan.org/docs/7.1.x/getting_started/getting_started.html#_running_infinispan_on_a_single_node[Default cache] instance is used (e.g. on the local node).
You can instead use a custom Cache setup - the `jetty-infinispan.xml` file shows you how to configure a remote Cache (using the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_using_hot_rod_server[hotrod java client]).
The InfinispanSessionIdManager can be configured by calling setters:
The `InfinispanSessionIdManager` can be configured by calling setters:
idleExpiryMultiple::
Sessions that are not immortal, ie they have an expiry time, have
their ids stored into Infinispan with an
http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle
expiry timeout] equivalent to double the session's timeout. This
should be sufficient to ensure that a session id that is in-use by a
session is never accidentally removed. However, should you wish to,
you can configure this to any integral value to effectively increase
the
http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle
expiry] timeout.
Sessions that are not immortal, e.g. they have an expiry time, have their ids stored into Infinispan with an http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle expiry timeout] equivalent to double the session's timeout.
This should be sufficient to ensure that a session id that is in-use by a session is never accidentally removed.
However, should you wish to, you can configure this to any integral value to effectively increase the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle expiry] timeout.
===== Configuring the InfinispanSessionManager
As mentioned elsewhere, there should be one InfinispanSessionManager per
context (ie webapp). It will need to reference the single
InfinispanSessionIdManager configured previously for the Server.
As mentioned elsewhere, there should be one `InfinispanSessionManager` per context (e.g. webapp).
It will need to reference the single `InfinispanSessionIdManager` configured previously for the Server.
The way you configure a InfinispanSessionManager depends on whether
you're configuring from a context xml file or a `jetty-web.xml` file or
code. The basic difference is how you get a reference to the Jetty
`org.eclipse.jetty.server.Server` instance.
The way you configure a `InfinispanSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
From a context xml file, you reference the Server instance as a Ref:
@ -171,8 +126,7 @@ From a context xml file, you reference the Server instance as a Ref:
</Set>
----
From a `WEB-INF/jetty-web.xml` file, you can reference the Server
instance directly:
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
[source,xml]
----
@ -213,25 +167,19 @@ instance directly:
The InfinispanSessionManager can be provided by calling setters:
scavengeInterval::
Time in seconds between runs of a scavenger task that looks for
expired old sessions to delete. The default is 10 minutes.
Time in seconds between runs of a scavenger task that looks for expired old sessions to delete.
The default is 10 minutes.
staleIntervalSec::
The length of time a session can be in memory without being checked
against the cluster. A value of 0 indicates that the session is never
checked against the cluster - the current node is considered to be the
master for the session.
The length of time a session can be in memory without being checked against the cluster.
A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
===== Using HotRod
If you're using the hotrod client - where serialization will be required
- you will need to ensure that the hotrod marshalling software works
with jetty classloading. To do this, firstly ensure that you have
included the lines containing the `prependServerClass` to your context
xml file as shown above.
If you're using the hotrod client - where serialization will be required - you will need to ensure that the hotrod marshalling software works with Jetty classloading.
To do this, firstly ensure that you have included the lines containing the `prependServerClass` to your context xml file as shown above.
Then, create the file
`${jetty.base}/resources/hotrod-client.properties`. Add the following
line to this file:
Then, create the file `${jetty.base}/resources/hotrod-client.properties`.
Add the following line to this file:
....
infinispan.client.hotrod.marshaller=org.eclipse.jetty.session.infinispan.WebAppMarshaller

View File

@ -17,57 +17,35 @@
[[session-clustering-jdbc]]
=== Session Clustering with a Database
Jetty can support session clustering by persisting sessions to a shared
database. Each Jetty instance locally caches sessions for which it has
received requests, writing any changes to the session through to the
database as the request exits the server. Sessions must obey the
Serialization contract, and servlets must call the
Session.setAttribute() method to ensure that changes are persisted.
Jetty can support session clustering by persisting sessions to a shared database.
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the database as the request exits the server.
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
The persistent session mechanism works in conjunction with a load
balancer that supports stickiness. Stickiness can be based on various
data items, such as source IP address or characteristics of the session
ID or a load-balancer specific mechanism. For those load balancers that
examine the session ID, the Jetty persistent session mechanism appends a
node ID to the session ID, which can be used for routing.
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
In this type of solution, the database can become both a bottleneck and
a single point of failure. Jetty takes steps to reduce the load on the
database (discussed below), but in a heavily loaded environment you
might need to investigate other optimization strategies such as local
caching and database replication. You should also consult your database
vendor's documentation for information on how to ensure high
availability and failover of your database.
In this type of solution, the database can become both a bottleneck and a single point of failure.
Jetty takes steps to reduce the load on the database (discussed below), but in a heavily loaded environment you might need to investigate other optimization strategies such as local caching and database replication.
You should also consult your database vendor's documentation for information on how to ensure high availability and failover of your database.
==== Configuration
There are two components to session management in Jetty: a session ID
manager and a session manager.
There are two components to session management in Jetty: a session ID manager and a session manager.
* The session ID manager ensures that session IDs are unique across all
webapps hosted on a Jetty instance, and thus there can only be one
session ID manager per Jetty instance.
* The session manager handles the session lifecycle
(create/update/invalidate/expire) on behalf of a web application, so
there is one session manager per web application instance.
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
These managers also cooperate and collaborate with the
`org.eclipse.jetty.server.session.SessionHandler` to enable
cross-context dispatch.
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
==== The jdbc-session Module
When using the jetty distribution, to enable jdbc session persistence,
you will first need to enable the jdbc-session
link:#startup-modules[module] for your link:#creating-jetty-base[base]
using the --add-to-start or --add-to-startd argument to the
link:#startup-overview[start.jar].
When using the jetty distribution, to enable jdbc session persistence, you will first need to enable the jdbc-session link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
You will also find the following properties, either in your base's
start.d/jdbc-session.ini file or appended to your start.ini, depending
on how you enabled the module:
You will also find the following properties, either in your base's start.d/jdbc-session.ini file or appended to your start.ini, depending on how you enabled the module:
....
[source,java]
----
## Unique identifier for this node in the cluster
jetty.jdbcSession.workerName=node1
@ -75,53 +53,42 @@ jetty.jdbcSession.workerName=node1
#jetty.jdbcSession.datasource=sessions
jetty.jdbcSession.driverClass=org.apache.derby.jdbc.EmbeddedDriver
jetty.jdbcSession.connectionURL=jdbc:derby:sessions;create=true
....
----
jetty.jdbcSession.workerName::
The name that uniquely identifies this node in the cluster. This value
will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on
which you enable jdbc session clustering.
The name that uniquely identifies this node in the cluster.
This value will also be used by the sticky load balancer to identify the node.
Don't forget to change the value of this property on *each* node on which you enable jdbc session clustering.
jetty.jdbcSession.scavenge::
The time in seconds between sweeps of a task which scavenges old
expired sessions. The default is 10 mins. We don't recommend you
increase the frequency bcause doing so increases the load on the
database with very little gain.
The time in seconds between sweeps of a task which scavenges old expired sessions.
The default is 10 minutess.
Increasing the frequency is not recommended as doing so increases the load on the database with very little gain.
jetty.jdbcSession.datasource::
The name of a javax.sql.DataSource that gives access to the database
that holds the session information. You should configure *either* this
or the jdbc driver information described next.
The name of a `javax.sql.DataSource` that gives access to the database that holds the session information.
You should configure *either* this or the jdbc driver information described next.
jetty.jdbcSession.datasource and jetty.jdbcSession.connectionURL::
This is the name of the jdbc driver class, and a jdbc connection url
suitable for that driver. You should configure *either* this or the
jdbc datasource name described above.
This is the name of the jdbc driver class, and a jdbc connection url suitable for that driver.
You should configure *either* this or the jdbc datasource name described above.
These properties are applied to the JDBCSessionIdManager described
below.
These properties are applied to the `JDBCSessionIdManager` described below.
===== Configuring the JDBCSessionIdManager
The jdbc-session module will have installed file called
$\{jetty.home}/etc/jetty-jdbc-sessions.xml. This file configures an
instance of the JDBCSessionIdManager that will be shared across all
webapps deployed on that server. It looks like this:
The jdbc-session module will have installed file called `$\{jetty.home}/etc/jetty-jdbc-sessions.xml`.
This file configures an instance of the `JDBCSessionIdManager` that will be shared across all webapps deployed on that server.
It looks like this:
[source,xml]
----
include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-jdbc-session-store.xml[]
include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml[]
----
As well as uncommenting and setting up appropriate values for the
properties we discussed above, you will also need to edit this file and
uncomment *either* the datasource or the driver info elements.
As well as uncommenting and setting up appropriate values for the properties discussed above, you will also need to edit this file and uncomment *either* the data source or the driver info elements.
As Jetty configuration files are direct mappings of XML to Java, it is
straightforward to see how to do this in code, but here's an example
anyway:
As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
[source,java]
----
----
Server server = new Server();
...
JDBCSessionIdManager idMgr = new JDBCSessionIdManager(server);
@ -134,19 +101,18 @@ server.setSessionIdManager(idMgr);
====== Configuring the Database Schema
You may find it necessary to change the names of the tables and columns
that the JDBC Session management uses to store the session information.
You may find it necessary to change the names of the tables and columns that the JDBC Session management uses to store the session information.
The defaults used are:
.Default Values for Session Id Table
[cols=",",]
[options="header"]
|===========================
|table name |JettySessionIds
|columns |id
|columns |id
|===========================
.Default Values for Session Table
[cols=",",]
[options="header"]
|=======================================================================
|table name |JettySessions
@ -155,16 +121,11 @@ accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime,
expiryTime, maxInterval, map
|=======================================================================
To change these values, use the
link:{JDURL}/org/eclipse/jetty/server/session/SessionIdTableSchema.html[org.eclipse.jetty.server.session.SessionIdTableSchema]
and
link:{JDURL}/org/eclipse/jetty/server/session/SessionTableSchema.html[org.eclipse.jetty.server.session.SessionTableSchema]
classes. These classes have getter/setter methods for the table name and
all columns.
To change these values, use the link:{JDURL}/org/eclipse/jetty/server/session/SessionIdTableSchema.html[org.eclipse.jetty.server.session.SessionIdTableSchema] and link:{JDURL}/org/eclipse/jetty/server/session/SessionTableSchema.html[org.eclipse.jetty.server.session.SessionTableSchema] classes.
These classes have getter/setter methods for the table name and all columns.
Here's an example of changing the name of JettySessionsId table and its
single column. This example will use java code, but as explained above,
you may also do this via a jetty xml configuration file:
Here's an example of changing the name of `JettySessionsId` table and its single column.
This example will use java code, but as explained above, you may also do this via a Jetty xml configuration file:
[source,java]
----
@ -176,10 +137,8 @@ idTableSchema.setIdColumn("theid");
idManager.setSessionIdTableSchema(idTableSchema);
----
In a similar fashion, you can change the names of the table and columns
for the JettySessions table. *Note* that both the SessionIdTableSchema
and the SessionTableSchema instances are set on the JDBCSessionIdManager
class.
In a similar fashion, you can change the names of the table and columns for the `JettySessions` table.
*Note* that both the `SessionIdTableSchema` and the `SessionTableSchema` instances are set on the `JDBCSessionIdManager` class.
[source,java]
----
@ -203,14 +162,11 @@ idManager.setSessionTableSchema(sessionTableSchema);
===== Configuring the JDBCSessionManager
As mentioned elsewhere, there should be one JDBCSessionManager per
context (ie webapp). It will need to reference the single
JDBCSessionIdManager configured previously for the Server.
As mentioned elsewhere, there should be one `JDBCSessionManager` per context (e.g. webapp).
It will need to reference the single `JDBCSessionIdManager` configured previously for the Server.
The way you configure a JDBCSessionManager depends on whether you're
configuring from a context xml file or a `jetty-web.xml` file or code.
The basic difference is how you get a reference to the Jetty
`org.eclipse.jetty.server.Server` instance.
The way you configure a `JDBCSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
From a context xml file, you reference the Server instance as a Ref:
@ -232,8 +188,7 @@ From a context xml file, you reference the Server instance as a Ref:
</Set>
----
From a `WEB-INF/jetty-web.xml` file, you can reference the Server
instance directly:
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
[source,xml]
----

View File

@ -17,108 +17,72 @@
[[session-clustering-mongodb]]
=== Session Clustering with MongoDB
Jetty can support session clustering by persisting sessions into
http://www.mogodb.org[MongoDB]. Each Jetty instance locally caches
sessions for which it has received requests, writing any changes to the
session through to the cluster as the request exits the server. Sessions
must obey the Serialization contract, and servlets must call the
Session.setAttribute() method to ensure that changes are persisted.
Jetty can support session clustering by persisting sessions into http://www.mogodb.org[MongoDB].
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the cluster as the request exits the server.
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
The session persistence mechanism works in conjunction with a load
balancer that supports stickiness. Stickiness can be based on various
data items, such as source IP address or characteristics of the session
ID or a load-balancer specific mechanism. For those load balancers that
examine the session ID, the Jetty persistent session mechanism appends a
node ID to the session ID, which can be used for routing.
The session persistence mechanism works in conjunction with a load balancer that supports stickiness.
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
In this type of solution, the traffic on the network needs to be
carefully watched and tends to be the bottleneck. You are probably
investigating this solution in order to scale to large amount of users
and sessions, so careful attention should be paid to your usage
scenario. Applications with a heavy write profile to their sessions will
consume more network bandwidth than profiles that are predominately read
oriented. We recommend using this session manager with largely read
based session scenarios.
In this type of solution, the traffic on the network needs to be carefully watched and tends to be the bottleneck.
You are probably investigating this solution in order to scale to large amount of users and sessions, so careful attention should be paid to your usage scenario.
Applications with a heavy write profile to their sessions will consume more network bandwidth than profiles that are predominately read oriented.
We recommend using this session manager with largely read based session scenarios.
==== Configuration
There are two components to session management in Jetty: a session ID
manager and a session manager.
There are two components to session management in Jetty: a session ID manager and a session manager.
* The session ID manager ensures that session IDs are unique across all
webapps hosted on a Jetty instance, and thus there should only be one
session ID manager per Jetty instance.
* The session manager handles the session lifecycle
(create/update/invalidate/expire) on behalf of a web application, so
there is one session manager per web application instance.
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
These managers also cooperate and collaborate with the
`org.eclipse.jetty.server.session.SessionHandler` to enable
cross-context dispatch.
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
==== The nosql Module
When using the jetty distribution, to enable the mongodb session
persistence mechanism, you will first need to enable the nosql
link:#startup-modules[module] for your link:#creating-jetty-base[base]
using the --add-to-start or --add-to-startd argument to the
link:#startup-overview[start.jar]. This module will automatically
download the mongodb-java-driver and install it to your base's lib/nosql
directory.
When using the jetty distribution, to enable the MongoDB session persistence mechanism, you will first need to enable the nosql link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` or `--add-to-startd` argument to the link:#startup-overview[start.jar].
This module will automatically download the `mongodb-java-driver` and install it to your base's `lib/nosql` directory.
As part of the module installation, the necessary mongo java driver jars
will be dynamically downloaded and installed to your
`${jetty.base}/lib/nosql` directory. If you need to up or downgrade the
version of these jars, then you can delete the jars that were
automatically installed and replace them. Once you've done that, you
will need to prevent jetty's startup checks from detecting the missing
jars. To do that, you can use `--skip-file-validation=nosql` argument to
start.jar on the command line, or place that line inside
`${jetty.base}/start.ini` to ensure it is used for every start.
As part of the module installation, the necessary mongo java driver jars will be dynamically downloaded and installed to your `${jetty.base}/lib/nosql` directory.
If you need to up or downgrade the version of these jars, then you can delete the jars that were automatically installed and replace them.
Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars.
To do that, you can use `--skip-file-validation=nosql` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
You will also find the following properties, either in your base's
start.d/nosql.ini file or appended to your start.ini, depending on how
you enabled the module:
You will also find the following properties, either in your base's `start.d/nosql.ini` file or appended to your `start.ini`, depending on how you enabled the module:
....
[source,xml]
----
## Unique identifier for this node in the cluster
jetty.nosqlSession.workerName=node1
## Interval in seconds between scavenging expired sessions
jetty.nosqlSession.scavenge=1800
....
----
The `jetty.nosqlSession.workerName` is the unique name for this jetty
Server instance. It will be used by the sticky load balancer to uniquely
identify the node. You should change this value on *each* node to which
you install mongodb session management.
The `jetty.nosqlSession.workerName` is the unique name for this Jetty Server instance.
It will be used by the sticky load balancer to uniquely identify the node.
You should change this value on *each* node to which you install MongoDB session management.
The `jetty.nosqlSession.scavenge` property defines the time in seconds
between runs of the scavengeer: the scavenger is a task which runs
periodically to clean out sessions that have expired but become stranded
in the database for whatever reason.
The `jetty.nosqlSession.scavenge` property defines the time in seconds between runs of the scavenger: the scavenger is a task which runs periodically to clean out sessions that have expired but become stranded in the database for whatever reason.
These properties are substituted into the configuration of the
MongoDBSessionIdManager and MongoSessionManager.
These properties are substituted into the configuration of the `MongoDBSessionIdManager` and `MongoSessionManager`.
===== Configuring the MongoSessionIdManager
The nosql module will have installed file called
$\{jetty.home}/etc/jetty-nosql.xml. This file configures an instance of
the MongoSessionIdManager that will be shared across all webapps
deployed on that server. It looks like this:
The nosql module will have installed file called `$\{jetty.home}/etc/jetty-nosql.xml`.
This file configures an instance of the `MongoSessionIdManager` that will be shared across all webapps deployed on that server.
It looks like this:
[source,xml]
----
include::{SRCDIR}/jetty-nosql/src/main/config/etc/jetty-mongo-session-store.xml[]
include::{SRCDIR}/jetty-nosql/src/main/config/etc/jetty-nosql.xml[]
----
The MongoSessionIdManager needs access to a mongodb cluster, and the
jetty-nosql.xml file assumes the defaults of localhost and default
mongodb port. If you need to configure something else, you will need to
edit this file. Here's an example of a more complex setup to use a
remote mongodb instance:
The `MongoSessionIdManager` needs access to a MongoDB cluster, and the `jetty-nosql.xml` file assumes the defaults of localhost and default MongoDB port.
If you need to configure something else, you will need to edit this file.
Here's an example of a more complex setup to use a remote MongoDB instance:
[source,xml]
----
@ -162,9 +126,7 @@ remote mongodb instance:
----
As Jetty configuration files are direct mappings of XML to Java, it is
straightforward to see how to do this in code, but here's an example
anyway:
As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
[source,java]
----
@ -178,71 +140,52 @@ anyway:
----
The MongoSessionIdManager has slightly different options than some of
our more traditional session options. The MongoDBSessionIdManager has
the same scavenge timers which govern the setting of a valid session to
invalid after a certain period of inactivity. New to this session id
manager is the extra purge setting which governs removal from the
mongodb cluster. This can be configured through the 'purge' option.
Purge is by default set to true and by default runs daily for each node
on the cluster. Also able to be configured is the age in which an
invalid session will be retained which is set to 1 day by default. This
means that invalid sessions will be removed after lingering in the
mongodb instance for a day. There is also an option for purging valid
sessions that have not been used recently. The default time for this is
1 week. You can disable these behaviors by setting purge to false.
The MongoSessionIdManager has slightly different options than some of our more traditional session options.
The `MongoDBSessionIdManager` has the same scavenge timers which govern the setting of a valid session to invalid after a certain period of inactivity.
New to this session id manager is the extra purge setting which governs removal from the MongoDB cluster.
This can be configured through the 'purge' option. Purge is by default set to true and by default runs daily for each node on the cluster.
Also able to be configured is the age in which an invalid session will be retained which is set to 1 day by default.
This means that invalid sessions will be removed after lingering in the MongoDB instance for a day.
There is also an option for purging valid sessions that have not been used recently.
The default time for this is 1 week. You can disable these behaviors by setting purge to false.
scavengeDelay::
How long to delay before periodically looking for sessions to
scavenge?
How long to delay before periodically looking for sessions to scavenge?
scavengePeriod::
How much time after a scavenge has completed should you wait before
doing it again?
How much time after a scavenge has completed should you wait before doing it again?
scavengeBlockSize::
Number of session ids to which to limit each scavenge query. If you
have a very large number of sessions in memory then setting this to a
non 0 value may help speed up scavenging by breaking the scavenge into
multiple, queries. The default is 0, which means that all session ids
are considered in a single query.
Number of session ids to which to limit each scavenge query.
If you have a very large number of sessions in memory then setting this to a non 0 value may help speed up scavenging by breaking the scavenge into multiple, queries.
The default is 0, which means that all session ids are considered in a single query.
purge (Boolean)::
Do you want to purge (delete) sessions that are invalid from the
session store completely?
Do you want to purge (delete) sessions that are invalid from the session store completely?
purgeDelay::
How often do you want to perform this purge operation?
purgeInvalidAge::
How old should an invalid session be before it is eligible to be
purged?
How old should an invalid session be before it is eligible to be purged?
purgeValidAge::
How old should a valid session be before it is eligible to be marked
invalid and purged? Should this occur at all?
How old should a valid session be before it is eligible to be marked invalid and purged?
Should this occur at all?
purgeLimit::
Integer value that represents how many items to return from a purge
query. The default is 0, which is unlimited. If you have a lot of old
expired orphaned sessions then setting this value may speed up the
purge process.
Integer value that represents how many items to return from a purge query.
The default is 0, which is unlimited.
If you have a lot of old expired orphaned sessions then setting this value may speed up the purge process.
preserveOnStop::
Whether or not to retain all sessions when the session manager stops.
Default is `true`.
===== Configuring a MongoSessionManager
As mentioned elsewhere, there should be one MongoSessionManager per
context (ie webapp). It will need to reference the single
MongoSessionIdManager configured previously for the Server.
As mentioned elsewhere, there should be one `MongoSessionManager` per context (e.g. webapp).
It will need to reference the single `MongoSessionIdManager` configured previously for the Server.
The way you configure a
link:{JDURL}/org/eclipse/jetty/nosql/MongoSessionManager.html[org.eclipse.jetty.nosql.mongodb.MongoSessionManager]
depends on whether you're configuring from a
link:#deployable-descriptor-file[context xml] file or a
link:#jetty-web-xml-config[jetty-web.xml] file or code. The basic
difference is how you get a reference to the Jetty
`org.eclipse.jetty.server.Server` instance.
The way you configure a link:{JDURL}/org/eclipse/jetty/nosql/MongoSessionManager.html[org.eclipse.jetty.nosql.mongodb.MongoSessionManager] depends on whether you're configuring from a link:#deployable-descriptor-file[context xml] file or a link:#jetty-web-xml-config[jetty-web.xml] file or code.
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
From a context xml file, you reference the Server instance as a Ref:
[source,xml]
----
----
<Ref name="Server" id="Server">
<Call id="mongoIdMgr" name="getSessionIdManager"/>
</Ref>
@ -258,15 +201,12 @@ From a context xml file, you reference the Server instance as a Ref:
</Arg>
</New>
</Set>
----
From a `WEB-INF/jetty-web.xml` file, you can reference the Server
instance directly:
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
[source,xml]
----
<Get name="server">
<Get id="mongoIdMgr" name="sessionIdManager"/>
</Get>
@ -287,7 +227,6 @@ If you're embedding this in code:
[source,java]
----
//assuming you have already set up the MongoSessionIdManager as shown earlier
//and have a reference to the Server instance:
@ -296,5 +235,4 @@ If you're embedding this in code:
MongoSessionManager mongoMgr = new MongoSessionManager();
mongoMgr.setSessionIdManager(server.getSessionIdManager());
wac.setSessionHandler(new SessionHandler(mongoMgr));
----

View File

@ -17,9 +17,7 @@
[[setting-session-characteristics]]
=== Setting Session Characteristics
To modify the session characteristics of a web application, you can use
the following parameters, applying them as in one of the example
configurations:
To modify the session characteristics of a web application, you can use the following parameters, applying them as in one of the example configurations:
[[using-init-parameters]]
==== Using Init Parameters
@ -61,14 +59,12 @@ urls with calls to encodeURL(). False by default.
[[applying-init-parameters]]
===== Applying Init Parameters
The following sections provide examples of how to apply the init
parameters.
The following sections provide examples of how to apply the init parameters.
[[context-parameter-example]]
====== Context Parameter Example
You can set these parameters as context parameters in a web
application's ` WEB-INF/web.xml` file:
You can set these parameters as context parameters in a web application's `WEB-INF/web.xml` file:
[source,xml]
----
@ -97,8 +93,7 @@ application's ` WEB-INF/web.xml` file:
[[web-application-examples]]
====== Web Application Examples
You can configure init parameters on a web application, either in code,
or in a Jetty context xml file equivalent:
You can configure init parameters on a web application, either in code, or in a Jetty context xml file equivalent:
[source,xml]
----
@ -125,8 +120,7 @@ or in a Jetty context xml file equivalent:
[[init-parameter-examples]]
====== SessionManager Examples
You can configure init parameters directly on a `SessionManager`
instance, either in code or the equivalent in xml:
You can configure init parameters directly on a `SessionManager` instance, either in code or the equivalent in xml:
[source,xml]
----
@ -152,24 +146,16 @@ instance, either in code or the equivalent in xml:
==== Using Servlet 3.0 Session Configuration
With the advent of http://jcp.org/en/jsr/detail?id=315[Servlet
Specification 3.0] there are new APIs for configuring session handling
characteristics. What was achievable before only via jetty-specific
link:#session-init-params[init-parameters] can now be achieved in a
container-agostic manner either in code, or via web.xml.
With the advent of http://jcp.org/en/jsr/detail?id=315[Servlet Specification 3.0] there are new APIs for configuring session handling characteristics.
What was achievable before only via Jetty-specific link:#session-init-params[init-parameters] can now be achieved in a container-agnostic manner either in code, or via `web.xml`.
[[session-cookie-configuration]]
===== SessionCookieConfiguration
The
http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javax.servlet.SessionCookieConfig]
class can be used to set up session handling characteristics. For full
details, consult the
http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javadoc].
The http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javax.servlet.SessionCookieConfig] class can be used to set up session handling characteristics.
For full details, consult the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javadoc].
Here's an example of how you use it: this is a ServletContextListener
that retrieves the SessionCookieConfig and sets up some new values for
it when the context is being initialized:
Below is an example of this implementation: a `ServletContextListener` retrieves the `SessionCookieConfig` and sets up some new values when the context is being initialized:
[source,java]
----
@ -209,9 +195,7 @@ public class TestListener implements ServletContextListener
}
----
You can also use web.xml to configure the session handling
characteristics instead: here's an example, doing exactly the same as we
did above in code:
You can also use `web.xml` to configure the session handling characteristics instead: here's an example doing exactly the same as above instead of using code:
[source,xml]
----
@ -240,14 +224,9 @@ did above in code:
[[session-tracking-modes]]
===== SessionTrackingModes
In addition to the configuration of
link:#session-cookie-configuration[session cookies], since Servlet 3.0
you can also use the
http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html[javax.servlet.SessionTrackingMode]
to configure session tracking.
In addition to the configuration of link:#session-cookie-configuration[session cookies], since Servlet 3.0 you can also use the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html[javax.servlet.SessionTrackingMode] to configure session tracking.
To determine what are the _default_ session tracking characteristics
used by the container, call:
To determine what are the _default_ session tracking characteristics used by the container, call:
[source,java]
----
@ -260,13 +239,12 @@ _default_ session tracking modes for Jetty are:
* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#COOKIE[SessionTrackingMode.COOKIE]
* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#URL[SessionTrackingMode.URL]
To see which session tracking modes are actually in effect for this
Context, the following call returns a java.util.Set of
javax.servlet.SessionTrackingMode:
To see which session tracking modes are actually in effect for this Context, the following call returns a `java.util.Set` of `javax.servlet.SessionTrackingMode`:
....
[source,java]
----
javax.servlet.SessionContext.getEffectiveSessionTrackingModes();
....
----
To change the session tracking modes, call:
@ -275,7 +253,7 @@ To change the session tracking modes, call:
javax.servlet.SessionContext.setSessionTrackingModes(Set<SessionTrackingMode>);
----
You may also set the tracking mode in web.xml, eg:
You may also set the tracking mode in `web.xml`, e.g.:
[source,xml]
----

View File

@ -17,21 +17,17 @@
[[using-persistent-sessions]]
=== Using Persistent Sessions
It is sometimes useful to preserve existing Sessions across restarts of
Jetty. The
link:{JDURL}/org/eclipse/jetty/server/session/HashSessionManager.html[`HashSessionManager`]
supports this feature. If you enable persistence, the
`HashSessionManager` saves all existing, valid Sessions to disk before
shutdown completes. On restart, Jetty restores the saved Sessions.
It is sometimes useful to preserve existing Sessions across restarts of Jetty.
The link:{JDURL}/org/eclipse/jetty/server/session/HashSessionManager.html[`HashSessionManager`] supports this feature.
If you enable persistence, the `HashSessionManager` saves all existing, valid Sessions to disk before shutdown completes.
On restart, Jetty restores the saved Sessions.
[[enabling-persistence]]
==== Enabling Persistence
A SessionManager does just what its name suggestsit manages the
lifecycle and state of sessions on behalf of a webapp. Each webapp must
have its own unique SessionManager instance. Enabling persistence is as
simple as configuring the `HashSessionManager` as the SessionManager for
a webapp and telling it where on disk to store the sessions:
A `SessionManager` does just what its name suggests it manages the lifecycle and state of sessions on behalf of a webapp.
Each webapp must have its own unique `SessionManager` instance.
Enabling persistence is as simple as configuring the `HashSessionManager` as the `SessionManager` for a webapp and telling it where on disk to store the sessions:
[source,xml]
----
@ -57,30 +53,25 @@ a webapp and telling it where on disk to store the sessions:
----
____
[TIP]
If you want to persist the sessions from multiple webapps:
1. Configure a separate HashSessionManager for each.
2. Assign to each a different value for 'storeDirectory'.
____
The above uses an example of a xref:intro-jetty-configuration-contexts[context configuration file].
The above example uses a configuration file suitable for the
link:{JDURL}/org/eclipse/jetty/deploy/providers/ContextProvider.html[ContextProvider],
thus you might want to check out xref:using-context-provider[].
[TIP]
====
If you want to persist the sessions from multiple webapps:
1. Configure a separate `HashSessionManager` for each.
2. Assign to each a different value for `storeDirectory`.
====
[[delaying-session-load]]
==== Delaying Session Load
You might need to ensure that the sessions are loaded AFTER the servlet
environment starts up (by default, Jetty eagerly loads sessions as part
of the container startup, but before it initializes the servlet
environment). For example, the Wicket web framework requires the servlet
environment to be available when sessions are activated.
You might need to ensure that the sessions are loaded AFTER the servlet environment starts up (by default, Jetty eagerly loads sessions as part of the container startup, but before it initializes the servlet environment).
For example, the Wicket web framework requires the servlet environment to be available when sessions are activated.
Using `SessionManager.setLazyLoad(true)`, Jetty loads sessions lazily
either when it receives the first request for a session, or the session
scavenger runs for the first time, whichever happens first. Here's how
the configuration looks in XML:
Using `SessionManager.setLazyLoad(true)`, Jetty loads sessions lazily either when it receives the first request for a session, or the session scavenger runs for the first time, whichever happens first.
Here's how the configuration looks in XML:
[source,xml]
----
@ -98,8 +89,7 @@ the configuration looks in XML:
[[enabling-persistence-for-jetty-maven-plugin]]
==== Enabling Persistence for the Jetty Maven Plugin
To enable session persistence for the Jetty Maven plugin, set up the
HashSessionManager in the configuration section like so:
To enable session persistence for the Jetty Maven plugin, set up the `HashSessionManager` in the configuration section like so:
[source,xml]
----

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javax.servlet.AsyncContext;
@ -62,8 +63,8 @@ public class LocalAsyncContextTest
_server.setHandler(session);
_server.start();
__completed.set(0);
__completed1.set(0);
__completed.set(null);
__completed1.set(null);
}
protected Connector initConnector()
@ -88,8 +89,8 @@ public class LocalAsyncContextTest
_handler.setCompleteAfter(-1);
response=process(null);
check(response,"TIMEOUT");
spinAssertEquals(1,__completed::get);
spinAssertEquals(1,__completed1::get);
spinAssertEquals(1,()->{return __completed.get()==null?0:1;});
spinAssertEquals(1,()->{return __completed1.get()==null?0:1;});
}
@Test
@ -210,9 +211,6 @@ public class LocalAsyncContextTest
{
String response;
__completed.set(0);
__completed1.set(0);
_handler.setRead(0);
_handler.setSuspendFor(1000);
_handler.setResumeAfter(100);
@ -222,8 +220,8 @@ public class LocalAsyncContextTest
_handler.setCompleteAfter2(-1);
response=process(null);
check(response,"STARTASYNC","DISPATCHED","startasync","STARTASYNC","DISPATCHED");
spinAssertEquals(1,__completed::get);
spinAssertEquals(0,__completed1::get);
spinAssertEquals(1,()->{return __completed.get()==null?0:1;});
spinAssertEquals(0,()->{return __completed1.get()==null?0:1;});
}
@ -544,21 +542,35 @@ public class LocalAsyncContextTest
}
}
static AtomicInteger __completed = new AtomicInteger();
static AtomicInteger __completed1 = new AtomicInteger();
static AtomicReference<Throwable> __completed = new AtomicReference<>();
static AtomicReference<Throwable> __completed1 = new AtomicReference<>();
private static AsyncListener __asyncListener = new AsyncListener()
{
@Override
public void onComplete(AsyncEvent event) throws IOException
{
__completed.incrementAndGet();
Throwable complete = new Throwable();
if (!__completed.compareAndSet(null,complete))
{
__completed.get().printStackTrace();
complete.printStackTrace();
__completed.set(null);
throw new IllegalStateException();
}
}
@Override
public void onError(AsyncEvent event) throws IOException
{
__completed.incrementAndGet();
Throwable complete = new Throwable();
if (!__completed.compareAndSet(null,complete))
{
__completed.get().printStackTrace();
complete.printStackTrace();
__completed.set(null);
throw new IllegalStateException();
}
}
@Override
@ -581,13 +593,29 @@ public class LocalAsyncContextTest
@Override
public void onComplete(AsyncEvent event) throws IOException
{
__completed1.incrementAndGet();
Throwable complete = new Throwable();
if (!__completed1.compareAndSet(null,complete))
{
__completed1.get().printStackTrace();
complete.printStackTrace();
__completed1.set(null);
throw new IllegalStateException();
}
}
@Override
public void onError(AsyncEvent event) throws IOException
{
Throwable complete = new Throwable();
if (!__completed1.compareAndSet(null,complete))
{
__completed1.get().printStackTrace();
complete.printStackTrace();
__completed1.set(null);
throw new IllegalStateException();
}
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{

View File

@ -35,7 +35,6 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.jsr356.EchoHandler;
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -91,7 +90,7 @@ public class MisbehavingClassTest
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
EndpointRuntimeOnOpen socket = new EndpointRuntimeOnOpen();
try (StacklessLogging logging = new StacklessLogging(EndpointRuntimeOnOpen.class,JsrEndpointEventDriver.class,WebSocketSession.class))
try (StacklessLogging logging = new StacklessLogging(EndpointRuntimeOnOpen.class, WebSocketSession.class))
{
// expecting ArrayIndexOutOfBoundsException during onOpen
Session session = container.connectToServer(socket,serverUri);
@ -112,7 +111,7 @@ public class MisbehavingClassTest
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
AnnotatedRuntimeOnOpen socket = new AnnotatedRuntimeOnOpen();
try (StacklessLogging logging = new StacklessLogging(AnnotatedRuntimeOnOpen.class,WebSocketSession.class))
try (StacklessLogging logging = new StacklessLogging(AnnotatedRuntimeOnOpen.class, WebSocketSession.class))
{
// expecting ArrayIndexOutOfBoundsException during onOpen
Session session = container.connectToServer(socket,serverUri);

View File

@ -88,6 +88,7 @@ public class OnPartialTest
@SuppressWarnings("resource")
JsrSession session = new JsrSession(container,id,requestURI,driver,connection);
session.setPolicy(policy);
session.start();
session.open();
return driver;
}

View File

@ -6,6 +6,8 @@ org.eclipse.jetty.LEVEL=WARN
# org.eclipse.jetty.websocket.LEVEL=WARN
# org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG
# org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG
# org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG
### Show state changes on BrowserDebugTool
# -- LEAVE THIS AT DEBUG LEVEL --
org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.websocket.api;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.Future;
@ -160,6 +161,13 @@ public interface RemoteEndpoint
* @see #flush()
*/
void setBatchMode(BatchMode mode);
/**
* Get the InetSocketAddress for the established connection.
*
* @return the InetSocketAddress for the established connection. (or null, if the connection is no longer established)
*/
InetSocketAddress getInetSocketAddress();
/**
* Flushes messages that may have been batched by the implementation.

View File

@ -0,0 +1,28 @@
//
// ========================================================================
// Copyright (c) 1995-2016 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.websocket.common;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
public interface RemoteEndpointFactory
{
RemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode);
}

View File

@ -216,9 +216,15 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
}
}
/**
* Get the InetSocketAddress for the established connection.
*
* @return the InetSocketAddress for the established connection. (or null, if the connection is no longer established)
*/
public InetSocketAddress getInetSocketAddress()
{
if(connection == null)
return null;
return connection.getRemoteAddress();
}

View File

@ -22,9 +22,11 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
@ -59,7 +61,7 @@ import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
@ManagedObject("A Jetty WebSocket Session")
public class WebSocketSession extends ContainerLifeCycle implements Session, WebSocketSessionScope, IncomingFrames, Connection.Listener, ConnectionStateListener
public class WebSocketSession extends ContainerLifeCycle implements Session, RemoteEndpointFactory, WebSocketSessionScope, IncomingFrames, Connection.Listener, ConnectionStateListener
{
private static final Logger LOG = Log.getLogger(WebSocketSession.class);
private static final Logger LOG_OPEN = Log.getLogger(WebSocketSession.class.getName() + "_OPEN");
@ -70,9 +72,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
private final Executor executor;
private ClassLoader classLoader;
private ExtensionFactory extensionFactory;
private RemoteEndpointFactory remoteEndpointFactory;
private String protocolVersion;
private Map<String, String[]> parameterMap = new HashMap<>();
private WebSocketRemoteEndpoint remote;
private RemoteEndpoint remote;
private IncomingFrames incomingHandler;
private OutgoingFrames outgoingHandler;
private WebSocketPolicy policy;
@ -141,6 +144,16 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
if(LOG.isDebugEnabled())
LOG.debug("starting - {}",this);
Iterator<RemoteEndpointFactory> iter = ServiceLoader.load(RemoteEndpointFactory.class).iterator();
if (iter.hasNext())
remoteEndpointFactory = iter.next();
if (remoteEndpointFactory == null)
remoteEndpointFactory = this;
if (LOG.isDebugEnabled())
LOG.debug("Using RemoteEndpointFactory: {}", remoteEndpointFactory);
super.doStart();
}
@ -457,6 +470,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
}
}
public WebSocketRemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode)
{
return new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
}
/**
* Open/Activate the session
*/
@ -477,7 +495,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Web
connection.getIOState().onConnected();
// Connect remote
remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
remote = remoteEndpointFactory.newRemoteEndpoint(connection,outgoingHandler,getBatchMode());
if(LOG_OPEN.isDebugEnabled())
LOG_OPEN.debug("[{}] {}.open() remote={}",policy.getBehavior(),this.getClass().getSimpleName(),remote);

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
import org.eclipse.jetty.websocket.common.frames.PingFrame;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.io.CloseableLocalWebSocketSession;
import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession;
import org.eclipse.jetty.websocket.common.scopes.SimpleContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
@ -67,12 +68,12 @@ public class EventDriverTest
}
@Test
public void testAdapter_ConnectClose() throws IOException
public void testAdapter_ConnectClose() throws Exception
{
AdapterConnectCloseSocket socket = new AdapterConnectCloseSocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.open();
driver.incomingFrame(new CloseInfo(StatusCode.NORMAL).asFrame());
@ -84,12 +85,12 @@ public class EventDriverTest
}
@Test
public void testAnnotated_ByteArray() throws IOException
public void testAnnotated_ByteArray() throws Exception
{
AnnotatedBinaryArraySocket socket = new AnnotatedBinaryArraySocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.open();
driver.incomingFrame(makeBinaryFrame("Hello World",true));
@ -103,12 +104,12 @@ public class EventDriverTest
}
@Test
public void testAnnotated_Error() throws IOException
public void testAnnotated_Error() throws Exception
{
AnnotatedTextSocket socket = new AnnotatedTextSocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.open();
driver.incomingError(new WebSocketException("oof"));
@ -122,12 +123,12 @@ public class EventDriverTest
}
@Test
public void testAnnotated_Frames() throws IOException
public void testAnnotated_Frames() throws Exception
{
AnnotatedFramesSocket socket = new AnnotatedFramesSocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.open();
driver.incomingFrame(new PingFrame().setPayload("PING"));
@ -151,7 +152,7 @@ public class EventDriverTest
AnnotatedBinaryStreamSocket socket = new AnnotatedBinaryStreamSocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.open();
driver.incomingFrame(makeBinaryFrame("Hello World",true));
@ -170,7 +171,7 @@ public class EventDriverTest
ListenerBasicSocket socket = new ListenerBasicSocket();
EventDriver driver = wrap(socket);
try (LocalWebSocketSession conn = new LocalWebSocketSession(container,testname,driver))
try (LocalWebSocketSession conn = new CloseableLocalWebSocketSession(container,testname,driver))
{
conn.start();
conn.open();

View File

@ -0,0 +1,57 @@
//
// ========================================================================
// Copyright (c) 1995-2016 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.websocket.common.io;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.junit.rules.TestName;
public class CloseableLocalWebSocketSession extends LocalWebSocketSession implements AutoCloseable
{
public CloseableLocalWebSocketSession(WebSocketContainerScope containerScope, TestName testname, EventDriver driver)
{
super(containerScope, testname, driver);
// LifeCycle start
try
{
start();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@Override
public void close()
{
// WebSocketSession.close();
super.close();
// LifeCycle Stop
try
{
stop();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}

View File

@ -61,13 +61,14 @@ public class MessageOutputStreamTest
private LocalWebSocketSession session;
@After
public void closeSession()
public void closeSession() throws Exception
{
session.close();
session.stop();
}
@Before
public void setupSession()
public void setupSession() throws Exception
{
policy = WebSocketPolicy.newServerPolicy();
policy.setInputBufferSize(1024);
@ -91,6 +92,8 @@ public class MessageOutputStreamTest
session.setPolicy(policy);
// talk to our remote socket
session.setOutgoingHandler(socketPipe);
// start session
session.start();
// open connection
session.open();
}

View File

@ -59,13 +59,14 @@ public class MessageWriterTest
private LocalWebSocketSession session;
@After
public void closeSession()
public void closeSession() throws Exception
{
session.close();
session.stop();
}
@Before
public void setupSession()
public void setupSession() throws Exception
{
policy = WebSocketPolicy.newServerPolicy();
policy.setInputBufferSize(1024);
@ -89,6 +90,8 @@ public class MessageWriterTest
session.setPolicy(policy);
// talk to our remote socket
session.setOutgoingHandler(socketPipe);
// start session
session.start();
// open connection
session.open();
}

View File

@ -18,8 +18,9 @@
package org.eclipse.jetty.websocket.server;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
@ -38,7 +39,6 @@ import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.test.BlockheadClient;
import org.eclipse.jetty.websocket.common.test.IBlockheadClient;
@ -257,7 +257,7 @@ public class WebSocketCloseTest
{
client.setProtocols("fastfail");
client.setTimeout(1,TimeUnit.SECONDS);
try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class,WebSocketSession.class))
try (StacklessLogging scope = new StacklessLogging(FastFailSocket.class, WebSocketSession.class))
{
client.connect();
client.sendStandardRequest();

View File

@ -39,11 +39,7 @@ public class AnnotatedRuntimeOnConnectSocket
public void onWebSocketConnect(Session sess)
{
// Intentional runtime exception.
int[] arr = new int[5];
for (int i = 0; i < 10; i++)
{
arr[i] = 222;
}
throw new RuntimeException("Intentional Exception from onWebSocketConnect");
}
@OnWebSocketClose

View File

@ -36,7 +36,7 @@ public class ListenerRuntimeOnConnectSocket extends WebSocketAdapter
{
super.onWebSocketConnect(sess);
throw new ArrayIndexOutOfBoundsException("Intentional Exception");
throw new RuntimeException("Intentional Exception from onWebSocketConnect");
}
@Override

View File

@ -18,6 +18,9 @@
package org.eclipse.jetty.websocket.server.misbehaving;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.EventQueue;
@ -64,72 +67,70 @@ public class MisbehavingClassTest
@Test
public void testListenerRuntimeOnConnect() throws Exception
{
try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
try (IBlockheadClient client = new BlockheadClient(server.getServerUri());
StacklessLogging scope = new StacklessLogging(ListenerRuntimeOnConnectSocket.class, WebSocketSession.class))
{
client.setProtocols("listener-runtime-connect");
client.setTimeout(1,TimeUnit.SECONDS);
try (StacklessLogging scope = new StacklessLogging(ListenerRuntimeOnConnectSocket.class, WebSocketSession.class))
{
ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
socket.reset();
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
socket.reset();
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(close.asFrame()); // respond with close
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
// ensure server socket got close event
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
client.write(close.asFrame()); // respond with close
// Validate errors
assertThat("socket.onErrors",socket.errors.size(),is(1));
Throwable cause = socket.errors.pop();
assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
}
// ensure server socket got close event
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
// Validate errors
assertThat("socket.onErrors",socket.errors.size(),is(1));
Throwable cause = socket.errors.pop();
assertThat("Error type",cause,instanceOf(RuntimeException.class));
}
}
@Test
public void testAnnotatedRuntimeOnConnect() throws Exception
{
try (IBlockheadClient client = new BlockheadClient(server.getServerUri()))
try (IBlockheadClient client = new BlockheadClient(server.getServerUri());
StacklessLogging scope = new StacklessLogging(AnnotatedRuntimeOnConnectSocket.class, WebSocketSession.class))
{
client.setProtocols("annotated-runtime-connect");
client.setTimeout(1,TimeUnit.SECONDS);
try (StacklessLogging scope = new StacklessLogging(AnnotatedRuntimeOnConnectSocket.class, WebSocketSession.class))
{
AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
socket.reset();
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
socket.reset();
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(close.asFrame()); // respond with close
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
// ensure server socket got close event
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
client.write(close.asFrame()); // respond with close
// Validate errors
assertThat("socket.onErrors",socket.errors.size(),is(1));
Throwable cause = socket.errors.pop();
assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
}
// ensure server socket got close event
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
// Validate errors
assertThat("socket.onErrors",socket.errors.size(),is(1));
Throwable cause = socket.errors.pop();
assertThat("Error type",cause,instanceOf(RuntimeException.class));
}
}
}

18
scripts/looptest.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
set
export PATH=$M3/bin:$PATH
mvn clean install -DskipTests
cd $MODULE
let count=0
while mvn $OPTS test -Dtest=$TESTNAME 2>&1 > target/lastbuild.log
do
now=`date`
echo "Test Run $count - $now"
let count=$count+1
done