Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-4770-simplify-gzip-testing
This commit is contained in:
commit
e2ca0bae8f
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[eg-arch-bean]]
|
||||
=== Jetty Component Architecture
|
||||
|
||||
Applications that use the Jetty libraries (both client and server) create objects from Jetty classes and compose them together to obtain the desired functionalities.
|
||||
|
||||
A client application creates a `ClientConnector` instance, a `HttpClientTransport` instance and an `HttpClient` instance and compose them to have a working HTTP client that uses to call third party services.
|
||||
|
||||
A server application creates a `ThreadPool` instance, a `Server` instance, a `ServerConnector` instance, a `Handler` instance and compose them together to expose an HTTP service.
|
||||
|
||||
Internally, the Jetty libraries create even more instances of other components that also are composed together with the main ones created by applications.
|
||||
|
||||
The end result is that an application based on the Jetty libraries is a _tree_ of components.
|
||||
In server application the root of the component tree is a `Server` instance, while in client applications the root of the component tree is an `HttpClient` instance.
|
||||
|
||||
Having all the Jetty components in a tree is beneficial in a number of use cases.
|
||||
It makes possible to register the components in the tree as JMX MBeans (TODO: xref the JMX section) so that a JMX console can look at the internal state of the components.
|
||||
It also makes possible to dump the component tree (and therefore each component's internal state) to a log file or to the console for troubleshooting purposes (TODO: xref troubleshooting section).
|
||||
|
||||
[[eg-arch-bean-lifecycle]]
|
||||
==== Jetty Component Lifecycle
|
||||
|
||||
Jetty components typically have a life cycle: they can be started and stopped.
|
||||
The Jetty components that have a life cycle implement the `org.eclipse.jetty.util.component.LifeCycle` interface.
|
||||
|
||||
Jetty components that contain other components extend the `org.eclipse.jetty.util.component.ContainerLifeCycle` class.
|
||||
`ContainerLifeCycle` can contain these type of components, also called __bean__s:
|
||||
|
||||
* _managed_ beans, `LifeCycle` instances whose life cycle is tied to the life cycle of their container
|
||||
* _unmanaged_ beans, `LifeCycle` instances whose life cycle is _not_ tied to the life cycle of their container
|
||||
* _POJO_ (Plain Old Java Object) beans, instances that do not implement `LifeCycle`
|
||||
|
||||
`ContainerLifeCycle` uses the following logic to determine if a bean should be _managed_, _unmanaged_ or _POJO_:
|
||||
|
||||
* the bean implements `LifeCycle`
|
||||
** the bean is not started, add it as _managed_
|
||||
** the bean is started, add it as _unmanaged_
|
||||
* the bean does not implement `LifeCycle`, add it as _POJO_
|
||||
|
||||
When a `ContainerLifeCycle` is started, it also starts recursively all its managed beans (if they implement `LifeCycle`); unmanaged beans are not started during the `ContainerLifeCycle` start cycle.
|
||||
Likewise, stopping a `ContainerLifeCycle` stops recursively and in reverse order all its managed beans; unmanaged beans are not stopped during the `ContainerLifeCycle` stop cycle.
|
||||
|
||||
Components can also be started and stopped individually, therefore activating or deactivating the functionalities that they offer.
|
||||
|
||||
Applications should first compose components in the desired structure, and then start the root component:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{doc_code}/embedded/ComponentDocs.java[tags=start]
|
||||
----
|
||||
|
||||
The component tree is the following:
|
||||
|
||||
[source,screen]
|
||||
----
|
||||
Root
|
||||
├── Monitor (MANAGED)
|
||||
└── Service (MANAGED)
|
||||
└── ScheduledExecutorService (POJO)
|
||||
----
|
||||
|
||||
When the `Root` instance is created, also the `Monitor` instance is created and added as bean, so `Monitor` is the first bean of `Root`.
|
||||
`Monitor` is a _managed_ bean, because it has been explicitly added to `Root` via `ContainerLifeCycle.addManaged(...)`.
|
||||
|
||||
Then, the application creates a `Service` instance and adds it to `Root` via `ContainerLifeCycle.addBean(...)`, so `Service` is the second bean of `Root`.
|
||||
`Service` is a _managed_ bean too, because it is a `LifeCycle` and at the time it was added to `Root` is was not started.
|
||||
|
||||
The `ScheduledExecutorService` within `Service` does not implement `LifeCycle` so it is added as a _POJO_ to `Service`.
|
||||
|
||||
It is possible to stop and re-start any component in a tree, for example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{doc_code}/embedded/ComponentDocs.java[tags=restart]
|
||||
----
|
||||
|
||||
`Service` can be stopped independently of `Root`, and re-started.
|
||||
Starting and stopping a non-root component does not alter the structure of the component tree, just the state of the subtree starting from the component that has been stopped and re-started.
|
||||
|
||||
[[eg-arch-bean-listener]]
|
||||
==== Jetty Component Listeners
|
||||
|
||||
// TODO: LifeCycle.Listener
|
||||
// TODO: Container.Listener + InheritedListener
|
|
@ -16,14 +16,13 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
[appendix]
|
||||
[[eg-io-arch]]
|
||||
== Jetty I/O Architecture
|
||||
[[eg-arch-io]]
|
||||
=== Jetty I/O Architecture
|
||||
|
||||
Jetty libraries (both client and server) use Java NIO to handle I/O, so that at its core Jetty I/O is completely non-blocking.
|
||||
|
||||
[[eg-io-arch-selector-manager]]
|
||||
=== Jetty I/O: `SelectorManager`
|
||||
[[eg-arch-io-selector-manager]]
|
||||
==== Jetty I/O: `SelectorManager`
|
||||
|
||||
The core class of Jetty I/O is link:{JDURL}/org/eclipse/jetty/io/SelectorManager.html[`SelectorManager`].
|
||||
|
||||
|
@ -51,8 +50,8 @@ This example shows how a server accepts a client connection:
|
|||
include::{doc_code}/embedded/SelectorManagerDocs.java[tags=accept]
|
||||
----
|
||||
|
||||
[[eg-io-arch-endpoint-connection]]
|
||||
=== Jetty I/O: `EndPoint` and `Connection`
|
||||
[[eg-arch-io-endpoint-connection]]
|
||||
==== Jetty I/O: `EndPoint` and `Connection`
|
||||
|
||||
``SocketChannel``s that are passed to `SelectorManager` are wrapped into two related components: an link:{JDURL}/org/eclipse/jetty/io/EndPoint.html[`EndPoint`] and a link:{JDURL}/org/eclipse/jetty/io/Connection.html[`Connection`].
|
||||
|
||||
|
@ -85,8 +84,8 @@ On the server-side, the component that aggregates a `SelectorManager` with a set
|
|||
|
||||
On the client-side, the components that aggregates a `SelectorManager` with a set of ``ClientConnectionFactory``s are link:{JDURL}/org/eclipse/jetty/client/HttpClientTransport.html[`HttpClientTransport`] subclasses, see xref:eg-client-io-arch[].
|
||||
|
||||
[[eg-io-arch-endpoint]]
|
||||
=== Jetty I/O: `EndPoint`
|
||||
[[eg-arch-io-endpoint]]
|
||||
==== Jetty I/O: `EndPoint`
|
||||
|
||||
The Jetty I/O library use Java NIO to handle I/O, so that I/O is non-blocking.
|
||||
|
||||
|
@ -100,10 +99,10 @@ In order to be notified when a `SocketChannel` uncongests and it is therefore wr
|
|||
In the Jetty I/O library, you can call `EndPoint.write(Callback, ByteBuffer...)` to write the ``ByteBuffer``s and the `Callback` parameter is the object that is notified when the whole write is finished (i.e. _all_ ``ByteBuffer``s have been fully written, even if they are delayed by TCP congestion/uncongestion).
|
||||
|
||||
The `EndPoint` APIs abstract out the Java NIO details by providing non-blocking APIs based on `Callback` objects for I/O operations.
|
||||
The `EndPoint` APIs are typically called by `Connection` implementations, see xref:eg-io-arch-connection[this section].
|
||||
The `EndPoint` APIs are typically called by `Connection` implementations, see xref:eg-arch-io-connection[this section].
|
||||
|
||||
[[eg-io-arch-connection]]
|
||||
=== Jetty I/O: `Connection`
|
||||
[[eg-arch-io-connection]]
|
||||
==== Jetty I/O: `Connection`
|
||||
|
||||
`Connection` is the abstraction that deserializes incoming bytes into objects, for example a HTTP request object or a WebSocket frame object, that can be used by more abstract layers.
|
||||
|
||||
|
@ -123,10 +122,13 @@ The example below shows a typical implementation that extends `AbstractConnectio
|
|||
include::{doc_code}/embedded/SelectorManagerDocs.java[tags=connection]
|
||||
----
|
||||
|
||||
[[eg-arch-io-connection-listener]]
|
||||
===== Jetty I/O: `Connection.Listener`
|
||||
|
||||
// TODO: Introduce Connection.Listener
|
||||
|
||||
[[eg-io-arch-echo]]
|
||||
=== Jetty I/O: Network Echo
|
||||
[[eg-arch-io-echo]]
|
||||
==== Jetty I/O: Network Echo
|
||||
|
||||
With the concepts above it is now possible to write a simple, fully non-blocking, `Connection` implementation that simply echoes the bytes that it reads back to the other peer.
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[eg-arch-listener]]
|
||||
=== Jetty Listeners
|
||||
|
||||
The Jetty architecture is based on xref:eg-arch-bean[components], typically organized in a component tree.
|
||||
These components have an internal state that varies with the component life cycle (that is, whether the component is started or stopped), as well as with the component use at runtime.
|
||||
The typical example is a thread pool, whose internal state -- such as the number of pooled threads or the job queue size -- changes as the thread pool is used by the running client or server.
|
||||
|
||||
In many cases, the component state change produces an event that is broadcast to listeners.
|
||||
Applications can register listeners to these components to be notified of the events they produce.
|
||||
|
||||
This section lists the listeners available in the Jetty components, but the events and listener APIs are discussed in the component specific sections.
|
||||
|
||||
* xref:eg-arch-bean-listener[]
|
||||
* xref:eg-arch-io-connection-listener[]
|
||||
* xref:eg-server-http-channel-events[]
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[appendix]
|
||||
[[eg-arch]]
|
||||
== Jetty Architecture
|
||||
|
||||
include::arch-bean.adoc[]
|
||||
include::arch-listener.adoc[]
|
||||
include::arch-io.adoc[]
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
The Jetty client libraries provide the basic components and APIs to implement a network client.
|
||||
|
||||
They build on the common xref:eg-io-arch[Jetty I/O Architecture] and provide client specific concepts (such as establishing a connection to a server).
|
||||
They build on the common xref:eg-arch-io[Jetty I/O Architecture] and provide client specific concepts (such as establishing a connection to a server).
|
||||
|
||||
There are conceptually two layers that compose the Jetty client libraries:
|
||||
|
||||
|
@ -31,7 +31,7 @@ There are conceptually two layers that compose the Jetty client libraries:
|
|||
[[eg-client-io-arch-network]]
|
||||
==== Client Libraries Network Layer
|
||||
|
||||
The Jetty client libraries use the common I/O design described in link:#eg-io-arch[this section].
|
||||
The Jetty client libraries use the common I/O design described in link:#eg-arch-io[this section].
|
||||
The main client-side component is the link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[`ClientConnector`].
|
||||
|
||||
The `ClientConnector` primarily wraps the link:{JDURL}/org/eclipse/jetty/io/SelectorManager.html[`SelectorManager`] and aggregates other four components:
|
||||
|
@ -88,7 +88,7 @@ Please refer to the `ClientConnector` link:{JDURL}/org/eclipse/jetty/io/ClientCo
|
|||
|
||||
The protocol layer builds on top of the network layer to generate the bytes to be written to the network and to parse the bytes read from the network.
|
||||
|
||||
Recall from link:#eg-io-arch-connection[this section] that Jetty uses the `Connection` abstraction to produce and interpret the network bytes.
|
||||
Recall from link:#eg-arch-io-connection[this section] that Jetty uses the `Connection` abstraction to produce and interpret the network bytes.
|
||||
|
||||
On the client side, a `ClientConnectionFactory` implementation is the component that creates `Connection` instances based on the protocol that the client wants to "speak" with the server.
|
||||
|
||||
|
|
|
@ -59,4 +59,5 @@ endif::[]
|
|||
include::.asciidoctorconfig[]
|
||||
include::client/client.adoc[]
|
||||
include::server/server.adoc[]
|
||||
include::io-arch.adoc[]
|
||||
include::arch.adoc[]
|
||||
include::troubleshooting.adoc[]
|
||||
|
|
|
@ -23,7 +23,7 @@ Writing HTTP applications is typically simple, especially when using blocking AP
|
|||
However, there are subtle cases where it is worth clarifying what a server application should do to obtain the desired results when run by Jetty.
|
||||
|
||||
[[eg-server-http-application-1xx]]
|
||||
==== Managing 1xx Responses
|
||||
==== Sending 1xx Responses
|
||||
|
||||
The link:https://tools.ietf.org/html/rfc7231#section-5.1.1[HTTP/1.1 RFC] allows for `1xx` informational responses to be sent before a real content response.
|
||||
Unfortunately the servlet specification does not provide a way for these to be sent, so Jetty has had to provide non-standard handling of these headers.
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
|
||||
A `Connector` is the component that handles incoming requests from clients, and works in conjunction with `ConnectionFactory` instances.
|
||||
|
||||
The primary implementation is `org.eclipse.jetty.server.ServerConnector`.`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen to a TCP port and to accept TCP connections.
|
||||
The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
|
||||
`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen to a TCP port and to accept TCP connections.
|
||||
|
||||
Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured in a similar way, for example the port to listen to, the network address to bind to, etc.:
|
||||
|
||||
|
@ -31,19 +32,19 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configur
|
|||
----
|
||||
|
||||
The _acceptors_ are threads (typically only one) that compete to accept TCP connections on the listening port.
|
||||
When a connection is accepted, `ServerConnector` wraps the accepted `SocketChannel` and passes it to the xref:eg-io-arch-selector-manager[`SelectorManager`].
|
||||
Therefore, there is a little moment where the acceptor thread is not accepting new connections because it is busy wrapping the just accepted one to pass it to the `SelectorManager`.
|
||||
When a connection is accepted, `ServerConnector` wraps the accepted `SocketChannel` and passes it to the xref:eg-arch-io-selector-manager[`SelectorManager`].
|
||||
Therefore, there is a little moment where the acceptor thread is not accepting new connections because it is busy wrapping the just accepted connection to pass it to the `SelectorManager`.
|
||||
Connections that are ready to be accepted but are not accepted yet are queued in a bounded queue (at the OS level) whose capacity can be configured with the `ServerConnector.acceptQueueSize` parameter.
|
||||
|
||||
If your application must withstand a very high rate of connections opened, configuring more than one acceptor thread may be beneficial: when one acceptor thread accepts one connection, another acceptor thread can take over accepting connections.
|
||||
|
||||
The _selectors_ are components that manage a set of connected sockets, implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
|
||||
Each selector requires one thread and uses the Java NIO mechanism to efficiently handle the set of connected sockets.
|
||||
The _selectors_ are components that manage a set of connected sockets, implemented by xref:eg-arch-io-selector-manager[`ManagedSelector`].
|
||||
Each selector requires one thread and uses the Java NIO mechanism to efficiently handle a set of connected sockets.
|
||||
As a rule of thumb, a single selector can easily manage up to 1000-5000 sockets, although the number may vary greatly depending on the application.
|
||||
|
||||
For example, web site applications tend to use sockets for one or more HTTP requests to retrieve resources and then the socket is idle for most of the time.
|
||||
In this case a single selector may be able to manage many sockets because chances are that they will be idle most of the time.
|
||||
On the contrary, web messaging applications tend to send many small messages at a very high frequency so that the socket is rarely idle.
|
||||
On the contrary, web messaging applications tend to send many small messages at a very high frequency so that sockets are rarely idle.
|
||||
In this case a single selector may be able to manage less sockets because chances are that many of them will be active at the same time.
|
||||
|
||||
It is possible to configure more than one `ServerConnector`, each listening on a different port:
|
||||
|
@ -56,7 +57,7 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configur
|
|||
[[eg-server-http-connector-protocol]]
|
||||
==== Configuring Protocols
|
||||
|
||||
For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory` to create a `Connection` object that handles the network traffic on that TCP connection, parsing and generating bytes for a specific protocol (see xref:eg-io-arch[this section] for more details about `Connection` objects).
|
||||
For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory` to create a `Connection` object that handles the network traffic on that TCP connection, parsing and generating bytes for a specific protocol (see xref:eg-arch-io[this section] for more details about `Connection` objects).
|
||||
|
||||
A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
|
||||
If no `ConnectionFactory` is specified then `HttpConnectionFactory` is implicitly configured.
|
||||
|
|
|
@ -38,8 +38,19 @@ A client making a request to `+https://shop.domain.com/cart+` is directed by Jet
|
|||
Therefore, in general, a web application is deployed to a _context_ which can be seen as the pair `(virtual_host, context_path)`.
|
||||
In the first case the contexts were `(domain.com, /shop)` and `(domain.com, /api)`, while in the second case the contexts were `(shop.domain.com, /)` and `(api.domain.com, /)`.
|
||||
Server applications using the Jetty Server Libraries create and configure a _context_ for each web application.
|
||||
Many __context__s can be deployed together to enrich the web application offering -- for example a catalog context, a shop context, an API context, an administration context, etc.
|
||||
|
||||
[[eg-server-http-handler-use-context]]
|
||||
Web applications can be written using exclusively the Servlet APIs, since developers know well the Servlet API and because they guarantee better portability across Servlet container implementations.
|
||||
|
||||
Embedded web applications based on the Servlet APIs are described in xref:eg-server-http-handler-use-servlet[this section].
|
||||
|
||||
Embedded web applications may also require additional features such as access to Jetty specific APIs, or utility features such as redirection from HTTP to HTTPS, support for `gzip` content compression, etc.
|
||||
The Jetty Server Libraries provides a number of out-of-the-box __Handler__s that implement the most common functionalities and are described in xref:eg-server-http-handler-use-util[this section].
|
||||
|
||||
[[eg-server-http-handler-use-util]]
|
||||
==== Custom and Utility Handlers
|
||||
|
||||
[[eg-server-http-handler-use-util-context]]
|
||||
===== ContextHandler
|
||||
|
||||
`ContextHandler` is a `Handler` that represents a _context_ for a web application.
|
||||
|
@ -62,7 +73,7 @@ Server
|
|||
└── ShopHandler
|
||||
----
|
||||
|
||||
[[eg-server-http-handler-use-context-collection]]
|
||||
[[eg-server-http-handler-use-util-context-collection]]
|
||||
===== ContextHandlerCollection
|
||||
|
||||
Server applications may need to deploy to Jetty more than one web application.
|
||||
|
@ -93,63 +104,7 @@ Server
|
|||
└── RESTHandler
|
||||
----
|
||||
|
||||
[[eg-server-http-handler-use-servlet-context]]
|
||||
===== ServletContextHandler
|
||||
|
||||
``Handler``s are easy to write, but often web applications have already been written using the Servlet APIs, using ``Servlet``s and ``Filter``s.
|
||||
|
||||
`ServletContextHandler` is a `ContextHandler` that provides support for the Servlet APIs and implements the behaviors required by the Servlet specification.
|
||||
|
||||
The Maven artifact coordinates are:
|
||||
|
||||
[source,xml,subs=normal]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>{version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=servletContextHandler]
|
||||
----
|
||||
|
||||
The `Handler` and Servlet components tree structure looks like the following:
|
||||
|
||||
[source,screen,subs=normal]
|
||||
----
|
||||
Server
|
||||
└── ServletContextHandler /shop
|
||||
├── _ShopCartServlet /cart/*_
|
||||
└── _CrossOriginFilter /*_
|
||||
----
|
||||
|
||||
Note how the Servlet components (they are not ``Handler``s) are represented in _italic_.
|
||||
|
||||
Note also how adding a `Servlet` or a `Filter` returns a _holder_ object that can be used to specify additional configuration for that particular `Servlet` or `Filter`.
|
||||
|
||||
When a request arrives to `ServletContextHandler` the request URI will be matched against the ``Filter``s and ``Servlet`` mappings and only those that match will process the request, as dictated by the Servlet specification.
|
||||
|
||||
IMPORTANT: `ServletContextHandler` is a terminal `Handler`, that is it always calls `Request.setHandled(true)` when invoked.
|
||||
Server applications must be careful when creating the `Handler` tree to put ``ServletContextHandler``s as last ``Handler``s in a `HandlerList` or as children of `ContextHandlerCollection`.
|
||||
|
||||
[[eg-server-http-handler-use-webapp-context]]
|
||||
===== WebAppContext
|
||||
|
||||
`WebAppContext` is a `ServletContextHandler` that auto configures itself by reading a `web.xml` Servlet configuration file.
|
||||
|
||||
Server applications can specify a `+*.war+` file or a directory with the structure of a `+*.war+` file to `WebAppContext` to deploy a standard Servlet web application packaged as a `war` (as defined by the Servlet specification).
|
||||
|
||||
Where server applications using `ServletContextHandler` must manually invoke methods to add ``Servlet``s and ``Filter``s, `WebAppContext` reads `WEB-INF/web.xml` to add ``Servlet``s and ``Filter``s.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=webAppContextHandler]
|
||||
----
|
||||
|
||||
[[eg-server-http-handler-use-resource-handler]]
|
||||
[[eg-server-http-handler-use-util-resource-handler]]
|
||||
===== ResourceHandler -- Static Content
|
||||
|
||||
Static content such as images or files (HTML, JavaScript, CSS) can be sent by Jetty very efficiently because Jetty can write the content asynchronously, using direct ``ByteBuffer``s to minimize data copy, and using a memory cache for faster access to the data to send.
|
||||
|
@ -188,17 +143,6 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=multiple
|
|||
If the resource is not found, `ResourceHandler` will not call `Request.setHandled(true)` so what happens next depends on the `Handler` tree structure.
|
||||
See also xref:eg-server-http-handler-use-util-default-handler[how to use] `DefaultHandler`.
|
||||
|
||||
[[eg-server-http-handler-use-default-servlet]]
|
||||
===== DefaultServlet -- Static Content for Servlets
|
||||
|
||||
If you have a xref:eg-server-http-handler-use-servlet-context[Servlet web application], you may want to use a `DefaultServlet` instead of `ResourceHandler`.
|
||||
The features are similar, but `DefaultServlet` is more commonly used to serve static files for Servlet web applications.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultServlet]
|
||||
----
|
||||
|
||||
[[eg-server-http-handler-use-util-gzip-handler]]
|
||||
===== GzipHandler
|
||||
|
||||
|
@ -372,3 +316,77 @@ In the example above, `ContextHandlerCollection` will try to match a request to
|
|||
|
||||
NOTE: `DefaultHandler` just sends a nicer HTTP `404` response in case of wrong requests from clients.
|
||||
Jetty will send an HTTP `404` response anyway if `DefaultHandler` is not used.
|
||||
|
||||
[[eg-server-http-handler-use-servlet]]
|
||||
==== Servlet API Handlers
|
||||
|
||||
[[eg-server-http-handler-use-servlet-context]]
|
||||
===== ServletContextHandler
|
||||
|
||||
``Handler``s are easy to write, but often web applications have already been written using the Servlet APIs, using ``Servlet``s and ``Filter``s.
|
||||
|
||||
`ServletContextHandler` is a `ContextHandler` that provides support for the Servlet APIs and implements the behaviors required by the Servlet specification.
|
||||
|
||||
The Maven artifact coordinates are:
|
||||
|
||||
[source,xml,subs=normal]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>{version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=servletContextHandler]
|
||||
----
|
||||
|
||||
The `Handler` and Servlet components tree structure looks like the following:
|
||||
|
||||
[source,screen,subs=normal]
|
||||
----
|
||||
Server
|
||||
└── ServletContextHandler /shop
|
||||
├── _ShopCartServlet /cart/*_
|
||||
└── _CrossOriginFilter /*_
|
||||
----
|
||||
|
||||
Note how the Servlet components (they are not ``Handler``s) are represented in _italic_.
|
||||
|
||||
Note also how adding a `Servlet` or a `Filter` returns a _holder_ object that can be used to specify additional configuration for that particular `Servlet` or `Filter`.
|
||||
|
||||
When a request arrives to `ServletContextHandler` the request URI will be matched against the ``Filter``s and ``Servlet`` mappings and only those that match will process the request, as dictated by the Servlet specification.
|
||||
|
||||
IMPORTANT: `ServletContextHandler` is a terminal `Handler`, that is it always calls `Request.setHandled(true)` when invoked.
|
||||
Server applications must be careful when creating the `Handler` tree to put ``ServletContextHandler``s as last ``Handler``s in a `HandlerList` or as children of `ContextHandlerCollection`.
|
||||
|
||||
[[eg-server-http-handler-use-webapp-context]]
|
||||
===== WebAppContext
|
||||
|
||||
`WebAppContext` is a `ServletContextHandler` that auto configures itself by reading a `web.xml` Servlet configuration file.
|
||||
|
||||
Server applications can specify a `+*.war+` file or a directory with the structure of a `+*.war+` file to `WebAppContext` to deploy a standard Servlet web application packaged as a `war` (as defined by the Servlet specification).
|
||||
|
||||
Where server applications using `ServletContextHandler` must manually invoke methods to add ``Servlet``s and ``Filter``s, `WebAppContext` reads `WEB-INF/web.xml` to add ``Servlet``s and ``Filter``s.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=webAppContextHandler]
|
||||
----
|
||||
|
||||
// TODO: add a section on Configuration (system/server classes)
|
||||
// TODO: add a section about how to setup JSP support
|
||||
// TODO: add a section on ClassLoading (see old docs)
|
||||
|
||||
[[eg-server-http-handler-use-default-servlet]]
|
||||
===== DefaultServlet -- Static Content for Servlets
|
||||
|
||||
If you have a xref:eg-server-http-handler-use-servlet-context[Servlet web application], you may want to use a `DefaultServlet` instead of `ResourceHandler`.
|
||||
The features are similar, but `DefaultServlet` is more commonly used to serve static files for Servlet web applications.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultServlet]
|
||||
----
|
||||
|
|
|
@ -61,5 +61,7 @@ However, in some cases the additional features are not required, or additional c
|
|||
In these cases, implementing your own `Handler` may be a better solution.
|
||||
Refer to xref:eg-server-http-handler-implement[this section] for more information about how to write your own ``Handler``s.
|
||||
|
||||
// TODO: document ScopedHandler? Is this really necessary or just an implementation detail that application will never worry about?
|
||||
|
||||
include::server-http-handler-use.adoc[]
|
||||
include::server-http-handler-implement.adoc[]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[eg-server-http-security]]
|
||||
==== Securing HTTP Server Applications
|
||||
|
||||
// TODO: ConstraintSecurityHandler and Authenticators and LoginServices
|
||||
TODO
|
||||
|
|
@ -64,7 +64,12 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=simple]
|
|||
|
||||
The example above shows the simplest HTTP/1.1 server; it has no support for HTTP sessions, for HTTP authentication, or for any of the features required by the Servlet specification.
|
||||
|
||||
All these features are provided by the Jetty Server Libraries and server applications only need to put the required components together to provide all the required features, and it is discussed in details in xref:eg-server-http-handler-use[this section].
|
||||
All these features are provided by the Jetty Server Libraries, and server applications only need to put the required components together to provide all the required features.
|
||||
|
||||
The ``Handler``s provided by the Jetty Server Libraries allow writing server applications that have functionalities similar to Apache HTTPD or Nginx (for example: URL redirection, URL rewriting, serving static content, reverse proxying, etc.), as well as generating content dynamically by processing incoming requests.
|
||||
Read xref:eg-server-http-handler[this section] for further details.
|
||||
|
||||
If you are interested in writing your server application based on the Servlet APIs, jump to xref:eg-server-http-handler-use-servlet[this section].
|
||||
|
||||
[[eg-server-http-request-processing]]
|
||||
==== Server Request Processing
|
||||
|
@ -75,15 +80,6 @@ Request handing is slightly different for each protocol; in HTTP/2 Jetty takes i
|
|||
|
||||
However, the diagram below captures the essence of request handling that is common among all protocols that carry HTTP requests.
|
||||
|
||||
First, the Jetty I/O layer emits an event that a socket has data to read.
|
||||
This event is converted to a call to `AbstractConnection.onFillable()`, where the `Connection` first reads from the `EndPoint` into a `ByteBuffer`, and then calls a protocol specific parser to parse the bytes in the `ByteBuffer`.
|
||||
|
||||
The parser emit events such that are protocol specific; the HTTP/2 parser, for example, emits events for each HTTP/2 frame that has been parsed.
|
||||
The parser events are then converted to protocol independent events such as _"request start"_, _"request headers"_, _"request content chunk"_, etc.
|
||||
that in turn are converted into method calls to `HttpChannel`.
|
||||
|
||||
When enough of the HTTP request is arrived, the `Connection` calls `HttpChannel.handle()` that calls the `Handler` chain, that eventually calls the server application code.
|
||||
|
||||
[plantuml]
|
||||
----
|
||||
skinparam backgroundColor transparent
|
||||
|
@ -109,6 +105,15 @@ HttpChannel -> Server : handle()
|
|||
Server -> Handlers : handle()
|
||||
----
|
||||
|
||||
First, the Jetty I/O layer emits an event that a socket has data to read.
|
||||
This event is converted to a call to `AbstractConnection.onFillable()`, where the `Connection` first reads from the `EndPoint` into a `ByteBuffer`, and then calls a protocol specific parser to parse the bytes in the `ByteBuffer`.
|
||||
|
||||
The parser emit events that are protocol specific; the HTTP/2 parser, for example, emits events for each HTTP/2 frame that has been parsed.
|
||||
The parser events are then converted to protocol independent events such as _"request start"_, _"request headers"_, _"request content chunk"_, etc.
|
||||
that in turn are converted into method calls to `HttpChannel`.
|
||||
|
||||
When enough of the HTTP request is arrived, the `Connection` calls `HttpChannel.handle()` that calls the `Handler` chain, that eventually calls the server application code.
|
||||
|
||||
[[eg-server-http-channel-events]]
|
||||
===== HttpChannel Events
|
||||
|
||||
|
@ -141,7 +146,7 @@ Currently, the following events are available:
|
|||
|
||||
Please refer to the `HttpChannel.Listener` link:{JDURL}/org/eclipse/jetty/server/HttpChannel.Listener.html[javadocs] for the complete list of events.
|
||||
|
||||
Server applications can register `HttpChannel.Listener` by adding them as beans to the `Connector`:
|
||||
Server applications can register `HttpChannel.Listener` by adding them as xref:eg-arch-bean[beans] to the `Connector`:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
|
@ -150,4 +155,5 @@ include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=httpChan
|
|||
|
||||
include::server-http-connector.adoc[]
|
||||
include::server-http-handler.adoc[]
|
||||
include::server-http-security.adoc[]
|
||||
include::server-http-application.adoc[]
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
The Jetty server libraries provide the basic components and APIs to implement a network server.
|
||||
|
||||
They build on the common xref:eg-io-arch[Jetty I/O Architecture] and provide server specific concepts.
|
||||
They build on the common xref:eg-arch-io[Jetty I/O Architecture] and provide server specific concepts.
|
||||
|
||||
The main I/O server-side class is `org.eclipse.jetty.server.ServerConnector`.
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@ The Jetty server-side libraries provide:
|
|||
* HTTP/2 low-level support, for applications that want to explicitly handle low-level HTTP/2 _sessions_, _streams_ and _frames_, via the xref:eg-server-http2[HTTP/2 libraries]
|
||||
* WebSocket support, for applications that want to embed a WebSocket server, via the xref:eg-server-websocket[WebSocket libraries]
|
||||
|
||||
// TODO: add a section on lifecycle and the component tree.
|
||||
|
||||
include::http/server-http.adoc[]
|
||||
include::http2/server-http2.adoc[]
|
||||
include::websocket/server-websocket.adoc[]
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[appendix]
|
||||
[[eg-troubleshooting]]
|
||||
== Troubleshooting Jetty
|
||||
|
||||
[[eg-troubleshooting-logging]]
|
||||
=== Logging
|
||||
|
||||
The Jetty libraries (both client and server) use link:http://slf4j.org/[SLF4J] as logging APIs.
|
||||
You can therefore plug in any SLF4J logging implementation, and configure the logging category `org.eclipse.jetty` at the desired level.
|
||||
|
||||
When you have problems with Jetty, the first thing that you want to do is to enable DEBUG logging.
|
||||
This is helpful because by reading the DEBUG logs you get a better understanding of what is going on in the system (and that alone may give you the answers you need to fix the problem), and because Jetty developers will probably need the DEBUG logs to help you.
|
||||
|
||||
==== Jetty SLF4J Binding
|
||||
|
||||
The Jetty artifact `jetty-slf4j-impl` is a SLF4J binding, that is the Jetty implementation of the SLF4J APIs, and provides a number of easy-to-use features to configure logging.
|
||||
|
||||
The Jetty SLF4J binding only provides an appender that writes to `System.err`.
|
||||
For more advanced configurations (for example, logging to a file), use link:http://logback.qos.ch[LogBack], or link:https://logging.apache.org/log4j/2.x/[Log4J2], or your preferred SLF4J binding.
|
||||
|
||||
CAUTION: Only one binding can be present in the class-path or module-path. If you use the LogBack SLF4J binding or the Log4J2 SLF4J binding, remember to remove the Jetty SLF4J binding.
|
||||
|
||||
The Jetty SLF4J binding reads a file in the class-path (or module-path) called `jetty-logging.properties` that can be configured with the logging levels for various logger categories:
|
||||
|
||||
.jetty-logging.properties
|
||||
[source,screen]
|
||||
----
|
||||
# By default, log at INFO level all Jetty classes.
|
||||
org.eclipse.jetty.LEVEL=INFO
|
||||
|
||||
# However, the Jetty client classes are logged at DEBUG level.
|
||||
org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
----
|
||||
|
||||
Similarly to how you configure the `jetty-logging.properties` file, you can set the system property `org.eclipse.jetty[.<package_names>].LEVEL=DEBUG` to quickly change the logging level to DEBUG without editing any file.
|
||||
The system property can be set on the command line, or in your IDE when you run your tests or your Jetty-based application and will override the `jetty-logging.properties` file configuration.
|
||||
For example to enable DEBUG logging for all the Jetty classes (_very_ verbose):
|
||||
|
||||
[source,screen]
|
||||
----
|
||||
java -Dorg.eclipse.jetty.LEVEL=DEBUG --class-path ...
|
||||
----
|
||||
|
||||
If you want to enable DEBUG logging but only for the HTTP/2 classes:
|
||||
|
||||
[source,screen]
|
||||
----
|
||||
java -Dorg.eclipse.jetty.http2.LEVEL=DEBUG --class-path ...
|
||||
----
|
||||
|
||||
[[eg-troubleshooting-debugging]]
|
||||
=== Debugging
|
||||
|
||||
Sometimes, in order to figure out a problem, enabling xref:eg-troubleshooting-logging[DEBUG logging] is not enough and you really need to debug the code with a debugger.
|
||||
|
||||
Debugging an embedded Jetty application is most easily done from your preferred IDE, so refer to your IDE instruction for how to debug Java applications.
|
||||
|
||||
Remote debugging can be enabled in a Jetty application via command line options:
|
||||
|
||||
[source,screen]
|
||||
----
|
||||
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000 --class-path ...
|
||||
----
|
||||
|
||||
The example above enables remote debugging so that debuggers (for example, your preferred IDE) can connect to port `8000` on the host running the Jetty application to receive debugging events.
|
||||
|
||||
NOTE: More technically, remote debugging exchanges JVM Tools Interface (JVMTI) events and commands via the Java Debug Wire Protocol (JDWP).
|
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package embedded;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ComponentDocs
|
||||
{
|
||||
public void start() throws Exception
|
||||
{
|
||||
// tag::start[]
|
||||
class Monitor extends AbstractLifeCycle
|
||||
{
|
||||
}
|
||||
|
||||
class Root extends ContainerLifeCycle
|
||||
{
|
||||
// Monitor is an internal component.
|
||||
private final Monitor monitor = new Monitor();
|
||||
|
||||
public Root()
|
||||
{
|
||||
// The Monitor life cycle is managed by Root.
|
||||
addManaged(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
class Service extends ContainerLifeCycle
|
||||
{
|
||||
// An instance of the Java scheduler service.
|
||||
private ScheduledExecutorService scheduler;
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
// Java's schedulers cannot be restarted, so they must
|
||||
// be created anew every time their container is started.
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
// Even if Java scheduler does not implement
|
||||
// LifeCycle, make it part of the component tree.
|
||||
addBean(scheduler);
|
||||
// Start all the children beans.
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
// Perform the opposite operations that were
|
||||
// performed in doStart(), in reverse order.
|
||||
super.doStop();
|
||||
removeBean(scheduler);
|
||||
scheduler.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Root instance.
|
||||
Root root = new Root();
|
||||
|
||||
// Create a Service instance.
|
||||
Service service = new Service();
|
||||
|
||||
// Link the components.
|
||||
root.addBean(service);
|
||||
|
||||
// Start the root component to
|
||||
// start the whole component tree.
|
||||
root.start();
|
||||
// end::start[]
|
||||
}
|
||||
|
||||
public void restart() throws Exception
|
||||
{
|
||||
// tag::restart[]
|
||||
class Root extends ContainerLifeCycle
|
||||
{
|
||||
}
|
||||
|
||||
class Service extends ContainerLifeCycle
|
||||
{
|
||||
// An instance of the Java scheduler service.
|
||||
private ScheduledExecutorService scheduler;
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
// Java's schedulers cannot be restarted, so they must
|
||||
// be created anew every time their container is started.
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
// Even if Java scheduler does not implement
|
||||
// LifeCycle, make it part of the component tree.
|
||||
addBean(scheduler);
|
||||
// Start all the children beans.
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
// Perform the opposite operations that were
|
||||
// performed in doStart(), in reverse order.
|
||||
super.doStop();
|
||||
removeBean(scheduler);
|
||||
scheduler.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
Root root = new Root();
|
||||
Service service = new Service();
|
||||
root.addBean(service);
|
||||
|
||||
// Start the Root component.
|
||||
root.start();
|
||||
|
||||
// Stop temporarily Service without stopping the Root.
|
||||
service.stop();
|
||||
|
||||
// Restart Service.
|
||||
service.start();
|
||||
// end::restart[]
|
||||
}
|
||||
}
|
|
@ -124,11 +124,15 @@ INSTALLED 2
|
|||
60 org.eclipse.jetty.tests.webapp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-osgi/test-jetty-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.tests.webapp_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32
|
||||
61 PAXEXAM-PROBE-d9c5a341-5c98-4084-b814-8303880cb447 local 0.0.0 32
|
||||
|
||||
If one of them isn't active (32) and you think it should be, you can edit the src of the test and call a method to generate more information next time you run the test:
|
||||
|
||||
TestOSGiUtil.getBundle(BundleContext, String)
|
||||
If things didn't go so well, and some bundle that should be in state ACTIVE (32) isn't, then you'll see diagnosis like this:
|
||||
|
||||
Where BundleContext is the field called bundleContext in the unit test class, and the String is the symbolic name of the jar. For example, for jetty-util, the symbolic name is org.eclipse.jetty.util. You can find it on the list above. You can also look into the pom.xml for the relevant jetty module and find it. If it's a 3rd party jar, you'll have to look in the META-INF/MANIFEST.MF.
|
||||
Trying to start the bundle org.glassfish.web.javax.servlet.jsp.jstl that was supposed to be active or resolved.
|
||||
org.glassfish.web.javax.servlet.jsp.jstl failed to start
|
||||
org.osgi.framework.BundleException: Could not resolve module: org.glassfish.web.javax.servlet.jsp.jstl [57]
|
||||
Unresolved requirement: Import-Package: javax.servlet.jsp.jstl.core
|
||||
|
||||
The "Unresolved requirement" means either that some bundle that exports that package has not been deployed, or that it is deployed, but it's manifest is screwed up, and didn't expose that package. Check the test code for the mavenBundle() statements to ascertain if it has been deployed, and then check the manifest inside the jar for the Export-Package statements to verify the correct packages are exported.
|
||||
|
||||
|
||||
|
||||
|
@ -204,3 +208,13 @@ at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.notifyHooksPrivileg
|
|||
at org.eclipse.osgi.internal.weaving.WovenClassImpl.callHooks(WovenClassImpl.java:270)
|
||||
at org.eclipse.osgi.internal.weaving.WeavingHookConfigurator.processClass(WeavingHookConfigurator.java:71)
|
||||
... 35 more
|
||||
|
||||
|
||||
|
||||
|
||||
Other Useful Things
|
||||
-------------------
|
||||
|
||||
If you have an ordinary jar with no osgi manifest headers in it, or one with incorrect/incomplete osgi manifest headers in it, you can use the Pax Wrapped Bundle facility to add in/replace the headers so that the test will work.
|
||||
|
||||
See https://ops4j1.jira.com/wiki/spaces/PAXEXAM4/pages/54263890/Configuration+Options#ConfigurationOptions-wrappedBundle for more information. The wrappedBundle() itself uses an underlying Wrap Protocol mechanism, which has more details on configuration options, so also look at https://ops4j1.jira.com/wiki/spaces/paxurl/pages/3833898/Wrap+Protocol. For an example of it in use in the tests, look at TestOSGiUtil.java line 179.
|
||||
|
|
|
@ -80,7 +80,7 @@ public class TestJettyOSGiBootContextAsService
|
|||
public void testContextHandlerAsOSGiService() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
// now test the context
|
||||
HttpClient client = new HttpClient();
|
||||
|
|
|
@ -111,20 +111,21 @@ public class TestJettyOSGiBootHTTP2Conscrypt
|
|||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
Bundle conscrypt = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.conscrypt.server");
|
||||
TestOSGiUtil.diagnoseNonActiveOrNonResolvedBundle(conscrypt);
|
||||
assertNotNull(conscrypt);
|
||||
ServiceReference<?>[] services = conscrypt.getRegisteredServices();
|
||||
assertNotNull(services);
|
||||
assertTrue(services.length > 0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP2() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
{
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
Bundle conscrypt = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.conscrypt.server");
|
||||
assertNotNull(conscrypt);
|
||||
ServiceReference<?>[] services = conscrypt.getRegisteredServices();
|
||||
assertNotNull(services);
|
||||
assertTrue(services.length > 0);
|
||||
}
|
||||
|
||||
HTTP2Client client = new HTTP2Client();
|
||||
try
|
||||
|
|
|
@ -100,23 +100,19 @@ public class TestJettyOSGiBootHTTP2JDK9
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
Bundle javaAlpn = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.java.server");
|
||||
assertNotNull(javaAlpn);
|
||||
ServiceReference<?>[] services = javaAlpn.getRegisteredServices();
|
||||
assertNotNull(services);
|
||||
Bundle server = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.server");
|
||||
assertNotNull(server);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP2() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
{
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
Bundle javaAlpn = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.java.server");
|
||||
assertNotNull(javaAlpn);
|
||||
ServiceReference<?>[] services = javaAlpn.getRegisteredServices();
|
||||
assertNotNull(services);
|
||||
Bundle server = TestOSGiUtil.getBundle(bundleContext, "org.eclipse.jetty.alpn.server");
|
||||
assertNotNull(server);
|
||||
}
|
||||
|
||||
HttpClient httpClient = null;
|
||||
HTTP2Client http2Client = null;
|
||||
|
|
|
@ -94,17 +94,11 @@ public class TestJettyOSGiBootWebAppAsService
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBundle() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
ServiceReference<?>[] refs = bundleContext.getServiceReferences(WebAppContext.class.getName(), null);
|
||||
assertNotNull(refs);
|
||||
|
|
|
@ -93,17 +93,11 @@ public class TestJettyOSGiBootWithAnnotations
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndex() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.eclipse.jetty.client.HttpClient;
|
|||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.ops4j.pax.exam.Configuration;
|
||||
|
@ -95,16 +94,12 @@ public class TestJettyOSGiBootWithBundle
|
|||
return options;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testContextHandlerAsOSGiService() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
// now test the context
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
|
|
|
@ -96,12 +96,6 @@ public class TestJettyOSGiBootWithJavaxWebSocket
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebsocket() throws Exception
|
||||
{
|
||||
|
@ -111,7 +105,7 @@ public class TestJettyOSGiBootWithJavaxWebSocket
|
|||
startBundle(bundleContext, "org.eclipse.jetty.tests.webapp");
|
||||
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
String port = System.getProperty("boot.javax.websocket.port");
|
||||
assertNotNull(port);
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.osgi.test;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
@ -37,7 +38,6 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
|
||||
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
|
||||
|
||||
/**
|
||||
* Pax-Exam to make sure the jetty-osgi-boot can be started along with the
|
||||
|
@ -63,7 +63,6 @@ public class TestJettyOSGiBootWithJsp
|
|||
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res", "com.sun.org.apache.xml.internal.utils",
|
||||
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
|
||||
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
|
||||
|
||||
options.addAll(TestOSGiUtil.coreJettyDependencies());
|
||||
options.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-java-client").versionAsInProject().start());
|
||||
options.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-client").versionAsInProject().start());
|
||||
|
@ -80,17 +79,11 @@ public class TestJettyOSGiBootWithJsp
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
{
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJspDump() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
try
|
||||
|
|
|
@ -83,17 +83,16 @@ public class TestJettyOSGiBootWithWebSocket
|
|||
return res;
|
||||
}
|
||||
|
||||
public void assertAllBundlesActiveOrResolved()
|
||||
public void debugBundles()
|
||||
{
|
||||
TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
|
||||
TestOSGiUtil.debugBundles(bundleContext);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebsocket() throws Exception
|
||||
{
|
||||
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
|
||||
assertAllBundlesActiveOrResolved();
|
||||
TestOSGiUtil.diagnoseBundles(bundleContext);
|
||||
|
||||
String port = System.getProperty("boot.websocket.port");
|
||||
assertNotNull(port);
|
||||
|
|
|
@ -211,14 +211,6 @@ public class TestOSGiUtil
|
|||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> consoleDependencies()
|
||||
{
|
||||
List<Option> res = new ArrayList<>();
|
||||
res.add(systemProperty("osgi.console").value("6666"));
|
||||
res.add(systemProperty("osgi.console.enable.builtin").value("true"));
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> jspDependencies()
|
||||
{
|
||||
List<Option> res = new ArrayList<>();
|
||||
|
@ -234,14 +226,6 @@ public class TestOSGiUtil
|
|||
return res;
|
||||
}
|
||||
|
||||
public static List<Option> httpServiceJetty()
|
||||
{
|
||||
List<Option> res = new ArrayList<>();
|
||||
res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-httpservice").versionAsInProject().start());
|
||||
res.add(mavenBundle().groupId("org.eclipse.equinox.http").artifactId("servlet").versionAsInProject().start());
|
||||
return res;
|
||||
}
|
||||
|
||||
protected static Bundle getBundle(BundleContext bundleContext, String symbolicName)
|
||||
{
|
||||
Map<String, Bundle> bundles = new HashMap<>();
|
||||
|
@ -258,46 +242,40 @@ public class TestOSGiUtil
|
|||
return bundles.get(symbolicName);
|
||||
}
|
||||
|
||||
protected static void assertActiveBundle(BundleContext bundleContext, String symbolicName) throws Exception
|
||||
{
|
||||
Bundle b = getBundle(bundleContext, symbolicName);
|
||||
assertNotNull(b);
|
||||
assertEquals(b.getSymbolicName() + " must be active.", Bundle.ACTIVE, b.getState());
|
||||
}
|
||||
|
||||
protected static void assertActiveOrResolvedBundle(BundleContext bundleContext, String symbolicName) throws Exception
|
||||
{
|
||||
Bundle b = getBundle(bundleContext, symbolicName);
|
||||
assertNotNull(b);
|
||||
if (b.getHeaders().get("Fragment-Host") == null)
|
||||
diagnoseNonActiveOrNonResolvedBundle(b);
|
||||
assertTrue(b.getSymbolicName() + " must be active or resolved. It was " + b.getState(),
|
||||
b.getState() == Bundle.ACTIVE || b.getState() == Bundle.RESOLVED);
|
||||
}
|
||||
|
||||
protected static void assertAllBundlesActiveOrResolved(BundleContext bundleContext)
|
||||
protected static void diagnoseBundles(BundleContext bundleContext)
|
||||
{
|
||||
System.err.println("ACTIVE: " + Bundle.ACTIVE);
|
||||
System.err.println("RESOLVED: " + Bundle.RESOLVED);
|
||||
System.err.println("INSTALLED: " + Bundle.INSTALLED);
|
||||
for (Bundle b : bundleContext.getBundles())
|
||||
{
|
||||
if (b.getState() == Bundle.INSTALLED)
|
||||
switch (b.getState())
|
||||
{
|
||||
diagnoseNonActiveOrNonResolvedBundle(b);
|
||||
case Bundle.INSTALLED:
|
||||
{
|
||||
//can't start a fragment bundle
|
||||
if (b.getHeaders().get("Fragment-Host") == null)
|
||||
{
|
||||
diagnoseNonActiveOrNonResolvedBundle(b);
|
||||
}
|
||||
dumpBundle(b);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
dumpBundle(b);
|
||||
}
|
||||
}
|
||||
assertTrue("Bundle: " + b +
|
||||
" (state should be " +
|
||||
"ACTIVE[" +
|
||||
Bundle.ACTIVE +
|
||||
"] or RESOLVED[" +
|
||||
Bundle.RESOLVED +
|
||||
"]" +
|
||||
", but was [" +
|
||||
b.getState() +
|
||||
"])", (b.getState() == Bundle.ACTIVE) || (b.getState() == Bundle.RESOLVED));
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean diagnoseNonActiveOrNonResolvedBundle(Bundle b)
|
||||
|
||||
protected static void dumpBundle(Bundle b)
|
||||
{
|
||||
System.err.println(" " + b.getBundleId() + " " + b.getSymbolicName() + " " + b.getLocation() + " " + b.getVersion() + " " + b.getState());
|
||||
}
|
||||
|
||||
protected static void diagnoseNonActiveOrNonResolvedBundle(Bundle b)
|
||||
{
|
||||
if (b.getState() != Bundle.ACTIVE && b.getHeaders().get("Fragment-Host") == null)
|
||||
{
|
||||
try
|
||||
|
@ -305,30 +283,22 @@ public class TestOSGiUtil
|
|||
System.err.println("Trying to start the bundle " + b.getSymbolicName() + " that was supposed to be active or resolved.");
|
||||
b.start();
|
||||
System.err.println(b.getSymbolicName() + " did start");
|
||||
return true;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
System.err.println(b.getSymbolicName() + " failed to start");
|
||||
t.printStackTrace(System.err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
System.err.println(b.getSymbolicName() + " was already started");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static void debugBundles(BundleContext bundleContext)
|
||||
protected static void dumpBundles(BundleContext bundleContext)
|
||||
{
|
||||
Map<String, Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
|
||||
System.err.println("Active " + Bundle.ACTIVE);
|
||||
System.err.println("RESOLVED " + Bundle.RESOLVED);
|
||||
System.err.println("INSTALLED " + Bundle.INSTALLED);
|
||||
System.err.println("ACTIVE: " + Bundle.ACTIVE);
|
||||
System.err.println("RESOLVED: " + Bundle.RESOLVED);
|
||||
System.err.println("INSTALLED: " + Bundle.INSTALLED);
|
||||
for (Bundle b : bundleContext.getBundles())
|
||||
{
|
||||
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
|
||||
System.err.println(" " + b.getBundleId() + " " + b.getSymbolicName() + " " + b.getLocation() + " " + b.getVersion() + " " + b.getState());
|
||||
}
|
||||
dumpBundle(b);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
@ -344,67 +314,8 @@ public class TestOSGiUtil
|
|||
return sslContextFactory;
|
||||
}
|
||||
|
||||
public static List<Option> jettyLogging()
|
||||
{
|
||||
List<Option> options = new ArrayList<>();
|
||||
// SLF4J Specific (possible set of options)
|
||||
/*
|
||||
options.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject().start());
|
||||
options.add(mavenBundle().groupId("org.slf4j").artifactId("jul-to-slf4j").versionAsInProject().start());
|
||||
options.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-log4j12").versionAsInProject().start());
|
||||
options.add(mavenBundle().groupId("log4j").artifactId("log4j").versionAsInProject().start());
|
||||
*/
|
||||
options.add(systemProperty("org.eclipse.jetty.LEVEL").value("INFO"));
|
||||
return options;
|
||||
}
|
||||
|
||||
public static void assertContains(String message, String haystack, String needle)
|
||||
{
|
||||
assertTrue(message + "\nContains: <" + needle + ">\nIn:\n" + haystack, haystack.contains(needle));
|
||||
}
|
||||
|
||||
protected static void testHttpServiceGreetings(BundleContext bundleContext, String protocol, int port) throws Exception
|
||||
{
|
||||
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.boot");
|
||||
|
||||
assertActiveBundle(bundleContext, "org.eclipse.jetty.osgi.httpservice");
|
||||
assertActiveBundle(bundleContext, "org.eclipse.equinox.http.servlet");
|
||||
|
||||
// in the OSGi world this would be bad code and we should use a bundle
|
||||
// tracker.
|
||||
// here we purposely want to make sure that the httpService is actually
|
||||
// ready.
|
||||
ServiceReference<?> sr = bundleContext.getServiceReference(HttpService.class.getName());
|
||||
assertNotNull("The httpServiceOSGiBundle is started and should " + "have deployed a service reference for HttpService", sr);
|
||||
HttpService http = (HttpService)bundleContext.getService(sr);
|
||||
http.registerServlet("/greetings", new HttpServlet()
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.getWriter().write("Hello");
|
||||
}
|
||||
}, null, null);
|
||||
|
||||
// now test the servlet
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setSelectors(1);
|
||||
clientConnector.setSslContextFactory(newClientSslContextFactory());
|
||||
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
ContentResponse response = client.GET(protocol + "://127.0.0.1:" + port + "/greetings");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
||||
String content = new String(response.getContent());
|
||||
assertEquals("Hello", content);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -732,6 +732,10 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
|
||||
_listener = Objects.requireNonNull(readListener);
|
||||
|
||||
//illegal if async not started
|
||||
if (!_channelState.isAsync())
|
||||
throw new IllegalStateException("Async not started");
|
||||
|
||||
if (isError())
|
||||
{
|
||||
woken = _channelState.onReadReady();
|
||||
|
|
|
@ -88,11 +88,96 @@ public class HttpInputTest
|
|||
super.failed(x);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestHttpInput extends HttpInput
|
||||
{
|
||||
public TestHttpInput(HttpChannelState state)
|
||||
{
|
||||
super(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void produceContent() throws IOException
|
||||
{
|
||||
_history.add("produceContent " + _fillAndParseSimulate.size());
|
||||
|
||||
for (String s = _fillAndParseSimulate.poll(); s != null; s = _fillAndParseSimulate.poll())
|
||||
{
|
||||
if ("_EOF_".equals(s))
|
||||
_in.eof();
|
||||
else
|
||||
_in.addContent(new TContent(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blockForContent() throws IOException
|
||||
{
|
||||
_history.add("blockForContent");
|
||||
super.blockForContent();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestHttpChannelState extends HttpChannelState
|
||||
{
|
||||
private boolean _fakeAsyncState;
|
||||
|
||||
public TestHttpChannelState(HttpChannel channel)
|
||||
{
|
||||
super(channel);
|
||||
}
|
||||
|
||||
public boolean isFakeAsyncState()
|
||||
{
|
||||
return _fakeAsyncState;
|
||||
}
|
||||
|
||||
public void setFakeAsyncState(boolean fakeAsyncState)
|
||||
{
|
||||
_fakeAsyncState = fakeAsyncState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsync()
|
||||
{
|
||||
if (isFakeAsyncState())
|
||||
return true;
|
||||
return super.isAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadUnready()
|
||||
{
|
||||
_history.add("s.onReadUnready");
|
||||
super.onReadUnready();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReadPossible()
|
||||
{
|
||||
_history.add("s.onReadPossible");
|
||||
return super.onReadPossible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContentAdded()
|
||||
{
|
||||
_history.add("s.onDataAvailable");
|
||||
return super.onContentAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReadReady()
|
||||
{
|
||||
_history.add("s.onReadReady");
|
||||
return super.onReadReady();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void before()
|
||||
{
|
||||
_in = new HttpInput(new HttpChannelState(new HttpChannel(new MockConnector(), new HttpConfiguration(), null, null)
|
||||
_in = new TestHttpInput(new TestHttpChannelState(new HttpChannel(new MockConnector(), new HttpConfiguration(), null, null)
|
||||
{
|
||||
@Override
|
||||
public void onAsyncWaitForContent()
|
||||
|
@ -100,57 +185,7 @@ public class HttpInputTest
|
|||
_history.add("asyncReadInterested");
|
||||
}
|
||||
})
|
||||
{
|
||||
@Override
|
||||
public void onReadUnready()
|
||||
{
|
||||
_history.add("s.onReadUnready");
|
||||
super.onReadUnready();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReadPossible()
|
||||
{
|
||||
_history.add("s.onReadPossible");
|
||||
return super.onReadPossible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContentAdded()
|
||||
{
|
||||
_history.add("s.onDataAvailable");
|
||||
return super.onContentAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReadReady()
|
||||
{
|
||||
_history.add("s.onReadReady");
|
||||
return super.onReadReady();
|
||||
}
|
||||
})
|
||||
{
|
||||
@Override
|
||||
protected void produceContent() throws IOException
|
||||
{
|
||||
_history.add("produceContent " + _fillAndParseSimulate.size());
|
||||
|
||||
for (String s = _fillAndParseSimulate.poll(); s != null; s = _fillAndParseSimulate.poll())
|
||||
{
|
||||
if ("_EOF_".equals(s))
|
||||
_in.eof();
|
||||
else
|
||||
_in.addContent(new TContent(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blockForContent() throws IOException
|
||||
{
|
||||
_history.add("blockForContent");
|
||||
super.blockForContent();
|
||||
}
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
@ -326,7 +361,9 @@ public class HttpInputTest
|
|||
@Test
|
||||
public void testAsyncEmpty() throws Exception
|
||||
{
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
assertThat(_history.poll(), equalTo("produceContent 0"));
|
||||
assertThat(_history.poll(), equalTo("s.onReadUnready"));
|
||||
assertThat(_history.poll(), nullValue());
|
||||
|
@ -341,7 +378,10 @@ public class HttpInputTest
|
|||
@Test
|
||||
public void testAsyncRead() throws Exception
|
||||
{
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
|
||||
assertThat(_history.poll(), equalTo("produceContent 0"));
|
||||
assertThat(_history.poll(), equalTo("s.onReadUnready"));
|
||||
assertThat(_history.poll(), nullValue());
|
||||
|
@ -388,7 +428,9 @@ public class HttpInputTest
|
|||
@Test
|
||||
public void testAsyncEOF() throws Exception
|
||||
{
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
assertThat(_history.poll(), equalTo("produceContent 0"));
|
||||
assertThat(_history.poll(), equalTo("s.onReadUnready"));
|
||||
assertThat(_history.poll(), nullValue());
|
||||
|
@ -406,8 +448,10 @@ public class HttpInputTest
|
|||
|
||||
@Test
|
||||
public void testAsyncReadEOF() throws Exception
|
||||
{
|
||||
{
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
assertThat(_history.poll(), equalTo("produceContent 0"));
|
||||
assertThat(_history.poll(), equalTo("s.onReadUnready"));
|
||||
assertThat(_history.poll(), nullValue());
|
||||
|
@ -452,7 +496,9 @@ public class HttpInputTest
|
|||
@Test
|
||||
public void testAsyncError() throws Exception
|
||||
{
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
assertThat(_history.poll(), equalTo("produceContent 0"));
|
||||
assertThat(_history.poll(), equalTo("s.onReadUnready"));
|
||||
assertThat(_history.poll(), nullValue());
|
||||
|
@ -477,6 +523,42 @@ public class HttpInputTest
|
|||
|
||||
assertThat(_history.poll(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetListenerWithNull() throws Exception
|
||||
{
|
||||
//test can't be null
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
{
|
||||
_in.setReadListener(null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetListenerNotAsync() throws Exception
|
||||
{
|
||||
//test not async
|
||||
assertThrows(IllegalStateException.class, () ->
|
||||
{
|
||||
_in.setReadListener(_listener);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetListenerAlreadySet() throws Exception
|
||||
{
|
||||
//set up a listener
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(true);
|
||||
_in.setReadListener(_listener);
|
||||
//throw away any events generated by setting the listener
|
||||
_history.clear();
|
||||
((TestHttpChannelState)_in.getHttpChannelState()).setFakeAsyncState(false);
|
||||
//now test that you can't set another listener
|
||||
assertThrows(IllegalStateException.class, () ->
|
||||
{
|
||||
_in.setReadListener(_listener);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecycle() throws Exception
|
||||
|
|
28
pom.xml
28
pom.xml
|
@ -33,7 +33,6 @@
|
|||
<jmh.version>1.21</jmh.version>
|
||||
<jmhjar.name>benchmarks</jmhjar.name>
|
||||
<tycho-version>1.4.0</tycho-version>
|
||||
<cbi-plugins.version>1.1.7</cbi-plugins.version>
|
||||
<junit.version>5.5.1</junit.version>
|
||||
<maven.version>3.6.0</maven.version>
|
||||
<maven.resolver.version>1.3.1</maven.resolver.version>
|
||||
|
@ -1380,6 +1379,9 @@
|
|||
<name>eclipse-sign</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<cbi-plugins.version>1.1.7</cbi-plugins.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -1427,6 +1429,15 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>cbi-releases</id>
|
||||
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>snapshot-repositories</id>
|
||||
|
@ -1455,21 +1466,6 @@
|
|||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>cbi-repository</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>cbi-releases</id>
|
||||
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<issueManagement>
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.tests.distribution;
|
|||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -41,6 +40,7 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
|
|||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnJre;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
@ -440,7 +440,8 @@ public class DistributionTests extends AbstractDistributionTest
|
|||
try (DistributionTester.Run run2 = distribution.start(args2))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
|
||||
assertFalse(run2.getLogs().stream().anyMatch(s -> s.contains("LinkageError")));
|
||||
// we do not test that anymore because it doesn't work for java14
|
||||
//assertFalse(run2.getLogs().stream().anyMatch(s -> s.contains("LinkageError")));
|
||||
|
||||
startHttpClient();
|
||||
ContentResponse response = client.GET("http://localhost:" + port + "/test1/index.jsp");
|
||||
|
|
Loading…
Reference in New Issue