Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x

This commit is contained in:
Jan Bartel 2024-04-26 16:50:05 +10:00
commit 151fffb48e
141 changed files with 2231 additions and 4915 deletions

View File

@ -101,4 +101,34 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>eclipse-release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[22,)</version>
<message>[ERROR] OLD JDK [${java.version}] in use. Jetty Release ${project.version} MUST use JDK 22 or newer</message>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -131,7 +131,7 @@ reportMavenTestFailures() {
echo ""
if proceedyn "Are you sure you want to release using above? (y/N)" n; then
mvn clean install -pl build -Dmaven.build.cache.enabled=false
mvn clean install -pl build -Peclipse-release -Dmaven.build.cache.enabled=false
echo ""
if proceedyn "Update VERSION.txt for $VER_RELEASE? (Y/n)" y; then
mvn -N -Pupdate-version generate-resources -Dmaven.build.cache.enabled=false

View File

@ -0,0 +1,84 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
[[og-module-rewrite]]
===== Module `rewrite`
The `rewrite` module inserts the `RewriteHandler` at the beginning of the `Handler` chain, providing URI-rewriting features similar to the link:https://httpd.apache.org/docs/current/mod/mod_rewrite.html[Apache's mod_rewrite] or the link:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html[Nginx rewrite module].
The module properties are:
----
include::{jetty-home}/modules/rewrite.mod[tags=documentation]
----
A common use of the `rewrite` module is to redirect/rewrite old URI paths that have been renamed, for example from `+/old/*+` to `+/new/*+`; in this way, the old paths will not result in a `404` response, but rather be redirected/rewritten to the new paths.
`RewriteHandler` matches incoming requests against a set of rules that you can specify in the `$JETTY_BASE/etc/jetty-rewrite-rules.xml` file.
Rules can be matched against request data such as the request URI or the request headers; if there is a match, the rule is applied.
The rule file `$JETTY_BASE/etc/jetty-rewrite-rules.xml` is initially empty, but contains commented examples of rules that you can add.
The list of available rules can be found link:{javadoc-url}/org/eclipse/jetty/rewrite/handler/package-summary.html[here].
An example of `jetty-rewrite-rules.xml` is the following:
[source,xml]
.jetty-rewrite-rules.xml
----
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
<Call name="addRule">
<!-- Redirect with a 301 from /old/* to /new/* -->
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RedirectRegexRule">
<Set name="statusCode">301</Set>
<Set name="pattern">/old/(.*)</Set>
<Set name="location">/new/$1</Set>
</New>
</Arg>
</Call>
</Configure>
----
Rules can be scoped to a specific virtual host.
In the example below, the rule will only be evaluated if the virtual host matches `example.com`:
[source,xml]
.jetty-rewrite-rules.xml
----
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
<Configure id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RuleContainer">
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.VirtualHostRuleContainer">
<Call name="addVirtualHost">
<!-- Only match the example.com domain -->
<Arg>example.com</Arg>
</Call>
<Call name="addRule">
<Arg>
<New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
<Set name="pattern">/advice</Set>
<Set name="location">/support</Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
</Configure>
----

View File

@ -30,6 +30,7 @@ include::module-jmx.adoc[]
include::module-jmx-remote.adoc[]
include::module-requestlog.adoc[]
include::module-resources.adoc[]
include::module-rewrite.adoc[]
include::module-server.adoc[]
include::module-ssl.adoc[]
include::module-ssl-reload.adoc[]

View File

@ -0,0 +1,112 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
[[pg-server-http-request-customizers]]
==== Request Customizers
A request customizer is an instance of `HttpConfiguration.Customizer`, that can customize the HTTP request and/or the HTTP response headers _before_ the `Handler` chain is invoked.
Request customizers are added to a particular `HttpConfiguration` instance, and therefore are specific to a `Connector` instance: you can have two different ``Connector``s configured with different request customizers.
For example, it is common to configure a secure `Connector` with the `SecureRequestCustomizer` that customizes the HTTP request by adding attributes that expose TLS data associated with the secure communication.
A request customizer may:
* Inspect the received HTTP request method, URI, version and headers.
* Wrap the `Request` object to allow any method to be overridden and customized. Typically this is done to synthesize additional HTTP request headers, or to change the return value of overridden methods.
* Add or modify the HTTP response headers.
The out-of-the-box request customizers include:
* `ForwardedRequestCustomizer` -- to interpret the `Forwarded` (or the the obsolete ``+X-Forwarded-*+``) HTTP header added by a reverse proxy; see xref:pg-server-http-request-customizer-forwarded[this section].
* `HostHeaderCustomizer` -- to customize, or synthesize it when original absent, the HTTP `Host` header; see xref:pg-server-http-request-customizer-host[this section].
* `ProxyCustomizer` -- to expose as `Request` attributes the `ip:port` information carried by the PROXY protocol; see xref:pg-server-http-request-customizer-proxy[this section].
* `RewriteCustomizer` -- to rewrite the request URI; see xref:pg-server-http-request-customizer-rewrite[this section].
* `SecureRequestCustomizer` -- to expose TLS data via `Request` attributes; see xref:pg-server-http-request-customizer-secure[this section].
You can also write your own request customizers and add them to the `HttpConfiguration` instance along existing request customizers.
Multiple request customizers will be invoked in the order they have been added.
Below you can find an example of how to add a request customizer:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=requestCustomizer]
----
[[pg-server-http-request-customizer-forwarded]]
===== `ForwardedRequestCustomizer`
`ForwardedRequestCustomizer` should be added when Jetty receives requests from a reverse proxy on behalf of a remote client, and web applications need to access the remote client information.
The reverse proxy adds the `Forwarded` (or the obsolete ``+X-Forwarded-*+``) HTTP header to the request, and may offload TLS so that the request arrives in clear-text to Jetty.
Applications deployed in Jetty may need to access information related to the remote client, for example the remote IP address and port, or whether the request was sent through a secure communication channel.
However, the request is forwarded by the reverse proxy, so the direct information about the remote IP address is that of the proxy, not of the remote client.
Furthermore, the proxy may offload TLS and forward the request in clear-text, so that the URI scheme would be `http` as forwarded by the reverse proxy, not `https` as sent by the remote client.
`ForwardedRequestCustomizer` reads the `Forwarded` header where the reverse proxy saved the remote client information, and wraps the original `Request` so that applications will transparently see the remote client information when calling methods such as `Request.isSecure()`, or `Request.getConnectionMetaData().getRemoteSocketAddress()`, etc.
For more information about how to configure `ForwardedRequestCustomizer`, see also link:{javadoc-url}/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[the javadocs].
[[pg-server-http-request-customizer-host]]
===== `HostHeaderCustomizer`
`HostHeaderCustomizer` should be added when Jetty receives requests that may lack the `Host` HTTP header, such as HTTP/1.0, HTTP/2 or HTTP/3 requests, and web applications have logic that depends on the value of the `Host` HTTP header.
For HTTP/2 and HTTP/3, the `Host` HTTP header is missing because the authority information is carried by the `:authority` pseudo-header, as per the respective specifications.
`HostHeaderCustomizer` will look at the `:authority` pseudo-header, then wrap the original `Request` adding a `Host` HTTP header synthesized from the `:authority` pseudo-header.
In this way, web applications that rely on the presence of the `Host` HTTP header will work seamlessly in any HTTP protocol version.
`HostHeaderCustomizer` works also for the WebSocket protocol.
WebSocket over HTTP/2 or over HTTP/3 initiate the WebSocket communication with an HTTP request that only has the `:authority` pseudo-header.
`HostHeaderCustomizer` synthesizes the `Host` HTTP header for such requests, so that WebSocket web applications that inspect the initial HTTP request before the WebSocket communication will work seamlessly in any HTTP protocol version.
For more information about how to configure `HostHeaderCustomizer`, see also link:{javadoc-url}/org/eclipse/jetty/server/HostHeaderCustomizer.html[the javadocs].
[[pg-server-http-request-customizer-proxy]]
===== `ProxyCustomizer`
`ProxyCustomizer` should be added when Jetty receives requests from a reverse proxy on behalf of a remote client, prefixed by the PROXY protocol (see also this section about the xref:pg-server-http-connector-protocol-proxy-http11[PROXY protocol]).
`ProxyCustomizer` adds the reverse proxy IP address and port as `Request` attributes.
Web applications may use these attributes in conjunction with the data exposed by `ForwardedRequestCustomizer` (see xref:pg-server-http-request-customizer-forwarded[this section]).
For more information about how to configure `ProxyCustomizer`, see also link:{javadoc-url}/org/eclipse/jetty/server/ProxyCustomizer.html[the javadocs].
[[pg-server-http-request-customizer-rewrite]]
===== `RewriteCustomizer`
`RewriteCustomizer` is similar to `RewriteHandler` (see xref:pg-server-http-handler-use-rewrite[this section]), but a `RewriteCustomizer` cannot send a response or otherwise complete the request/response processing.
A `RewriteCustomizer` is mostly useful if you want to rewrite the request URI _before_ the `Handler` chain is invoked.
However, a very similar effect can be achieved by having the `RewriteHandler` as the first `Handler` (the child `Handler` of the `Server` instance).
Since `RewriteCustomizer` cannot send a response or complete the request/response processing, ``Rule``s that do so such as redirect rules have no effect and are ignored; only ``Rule``s that modify or wrap the `Request` will have effect and be applied.
Due to this limitation, it is often a better choice to use `RewriteHandler` instead of `RewriteCustomizer`.
For more information about how to configure `RewriteCustomizer`, see also link:{javadoc-url}/org/eclipse/jetty/rewrite/RewriteCustomizer.html[the javadocs].
[[pg-server-http-request-customizer-secure]]
===== `SecureRequestCustomizer`
`SecureRequestCustomizer` should be added when Jetty receives requests over a secure `Connector`.
`SecureRequestCustomizer` adds TLS information as request attributes, in particular an instance of `EndPoint.SslSessionData` that contains information about the negotiated TLS cipher suite and possibly client certificates, and an instance of `org.eclipse.jetty.util.ssl.X509` that contains information about the server certificate.
`SecureRequestCustomizer` also adds, if configured so, the `Strict-Transport-Security` HTTP response header (for more information about this header, see link:https://datatracker.ietf.org/doc/html/rfc6797[its specification]).
For more information about how to configure `SecureRequestCustomizer`, see also link:{javadoc-url}/org/eclipse/jetty/server/SecureRequestCustomizer.html[the javadocs].

View File

@ -0,0 +1,48 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
[[pg-server-http-request-logging]]
==== Request Logging
HTTP requests and responses can be logged to provide data that can be later analyzed with other tools.
These tools can provide information such as the most frequently accessed request URIs, the response status codes, the request/response content lengths, geographical information about the clients, etc.
The default request/response log line format is the link:https://en.wikipedia.org/wiki/Common_Log_Format[NCSA Format] extended with referrer data and user-agent data.
[NOTE]
====
Typically, the extended NCSA format is the is enough and it's the standard used and understood by most log parsing tools and monitoring tools.
To customize the request/response log line format see the link:{javadoc-url}/org/eclipse/jetty/server/CustomRequestLog.html[`CustomRequestLog` javadocs].
====
Request logging can be enabled at the `Server` level.
The request logging output can be directed to an SLF4J logger named `"org.eclipse.jetty.server.RequestLog"` at `INFO` level, and therefore to any logging library implementation of your choice (see also xref:pg-troubleshooting-logging[this section] about logging).
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=serverRequestLogSLF4J]
----
Alternatively, the request logging output can be directed to a daily rolling file of your choice, and the file name must contain `yyyy_MM_dd` so that rolled over files retain their date:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=serverRequestLogFile]
----
For maximum flexibility, you can log to multiple ``RequestLog``s using class `RequestLog.Collection`, for example by logging with different formats or to different outputs.
You can use `CustomRequestLog` with a custom `RequestLog.Writer` to direct the request logging output to your custom targets (for example, an RDBMS).
You can implement your own `RequestLog` if you want to have functionalities that are not implemented by `CustomRequestLog`.

View File

@ -123,9 +123,13 @@ 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, and similarly does the HTTP/3 parser.
The parser events are then converted to protocol independent events such as _"request start"_, _"request headers"_, _"request content chunk"_, etc.
The parser events are then converted to protocol independent events such as _"request start"_, _"request headers"_, _"request content chunk"_, etc. detailed in xref:pg-server-http-request-processing-events[this section].
When enough of the HTTP request is arrived, the `Connection` calls `HttpChannel.onRequest()` that calls the `Handler` chain starting from the `Server` instance, that eventually calls your web application code.
When enough of the HTTP request is arrived, the `Connection` calls `HttpChannel.onRequest()`.
`HttpChannel.onRequest()` calls the xref:pg-server-http-request-customizers[request customizers], that allow to customize the request and/or the response headers on a per-``Connector`` basis.
After request customization, if any, the `Handler` chain is invoked, starting from the `Server` instance, and eventually your web application code is invoked.
[[pg-server-http-request-processing-events]]
===== Request Processing Events
@ -135,42 +139,8 @@ A typical case is to know exactly _when_ the HTTP request/response processing st
This is conveniently implemented by `org.eclipse.jetty.server.handler.EventsHandler`, described in more details in xref:pg-server-http-handler-use-events[this section].
[[pg-server-http-request-logging]]
==== Request Logging
HTTP requests and responses can be logged to provide data that can be later analyzed with other tools.
These tools can provide information such as the most frequently accessed request URIs, the response status codes, the request/response content lengths, geographical information about the clients, etc.
The default request/response log line format is the link:https://en.wikipedia.org/wiki/Common_Log_Format[NCSA Format] extended with referrer data and user-agent data.
[NOTE]
====
Typically, the extended NCSA format is the is enough and it's the standard used and understood by most log parsing tools and monitoring tools.
To customize the request/response log line format see the link:{javadoc-url}/org/eclipse/jetty/server/CustomRequestLog.html[`CustomRequestLog` javadocs].
====
Request logging can be enabled at the `Server` level.
The request logging output can be directed to an SLF4J logger named `"org.eclipse.jetty.server.RequestLog"` at `INFO` level, and therefore to any logging library implementation of your choice (see also xref:pg-troubleshooting-logging[this section] about logging).
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=serverRequestLogSLF4J]
----
Alternatively, the request logging output can be directed to a daily rolling file of your choice, and the file name must contain `yyyy_MM_dd` so that rolled over files retain their date:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=serverRequestLogFile]
----
For maximum flexibility, you can log to multiple ``RequestLog``s using class `RequestLog.Collection`, for example by logging with different formats or to different outputs.
You can use `CustomRequestLog` with a custom `RequestLog.Writer` to direct the request logging output to your custom targets (for example, an RDBMS).
You can implement your own `RequestLog` if you want to have functionalities that are not implemented by `CustomRequestLog`.
include::server-http-request-logging.adoc[]
include::server-http-request-customizers.adoc[]
include::server-http-connector.adoc[]
include::server-http-handler.adoc[]
include::server-http-session.adoc[]

View File

@ -1593,4 +1593,32 @@ public class HTTPServerDocs
}
// end::continue100[]
}
public void requestCustomizer() throws Exception
{
// tag::requestCustomizer[]
Server server = new Server();
// Configure the secure connector.
HttpConfiguration httpsConfig = new HttpConfiguration();
// Add the SecureRequestCustomizer.
httpsConfig.addCustomizer(new SecureRequestCustomizer());
// Configure the SslContextFactory with the KeyStore information.
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("/path/to/keystore");
sslContextFactory.setKeyStorePassword("secret");
// Configure the Connector to speak HTTP/1.1 and HTTP/2.
HttpConnectionFactory h1 = new HttpConnectionFactory(httpsConfig);
HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
alpn.setDefaultProtocol(h1.getProtocol());
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
ServerConnector connector = new ServerConnector(server, ssl, alpn, h2, h1);
server.addConnector(connector);
server.start();
// end::requestCustomizer[]
}
}

View File

@ -896,8 +896,8 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(String name, String value)
{
if (value == null)
throw new IllegalArgumentException("null value");
Objects.requireNonNull(name);
Objects.requireNonNull(value);
return add(new HttpField(name, value));
}
@ -912,6 +912,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(String name, long value)
{
Objects.requireNonNull(name);
return add(new HttpField.LongValueHttpField(name, value));
}
@ -926,6 +927,8 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(HttpHeader header, HttpHeaderValue value)
{
Objects.requireNonNull(header);
Objects.requireNonNull(value);
return add(header, value.toString());
}
@ -940,8 +943,8 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(HttpHeader header, String value)
{
if (value == null)
throw new IllegalArgumentException("null value");
Objects.requireNonNull(header);
Objects.requireNonNull(value);
return add(new HttpField(header, value));
}
@ -956,6 +959,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(HttpHeader header, long value)
{
Objects.requireNonNull(header);
return add(new HttpField.LongValueHttpField(header, value));
}
@ -967,6 +971,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable add(HttpField field)
{
Objects.requireNonNull(field);
ListIterator<HttpField> i = listIterator(size());
i.add(field);
return this;
@ -998,8 +1003,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
default Mutable add(String name, List<String> list)
{
Objects.requireNonNull(name);
if (list == null)
throw new IllegalArgumentException("null list");
Objects.requireNonNull(list);
if (list.isEmpty())
return this;
if (list.size() == 1)
@ -1022,6 +1026,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable addCSV(HttpHeader header, String... values)
{
Objects.requireNonNull(header);
QuotedCSV existing = null;
for (HttpField f : this)
{
@ -1049,6 +1054,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable addCSV(String name, String... values)
{
Objects.requireNonNull(name);
QuotedCSV existing = null;
for (HttpField f : this)
{
@ -1076,6 +1082,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable addDateField(String name, long date)
{
Objects.requireNonNull(name);
add(name, DateGenerator.formatDate(date));
return this;
}
@ -1105,6 +1112,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default void ensureField(HttpField field)
{
Objects.requireNonNull(field);
HttpHeader header = field.getHeader();
// Is the field value multi valued?
if (field.getValue().indexOf(',') < 0)
@ -1136,6 +1144,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(HttpField field)
{
Objects.requireNonNull(field);
boolean put = false;
ListIterator<HttpField> i = listIterator();
while (i.hasNext())
@ -1170,6 +1179,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(String name, String value)
{
Objects.requireNonNull(name);
if (value == null)
return remove(name);
return put(new HttpField(name, value));
@ -1186,6 +1196,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(HttpHeader header, HttpHeaderValue value)
{
Objects.requireNonNull(header);
if (value == null)
return remove(header);
return put(new HttpField(header, value.toString()));
@ -1202,6 +1213,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(HttpHeader header, String value)
{
Objects.requireNonNull(header);
if (value == null)
return remove(header);
return put(new HttpField(header, value));
@ -1241,6 +1253,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable putDate(HttpHeader name, long date)
{
Objects.requireNonNull(name);
return put(name, DateGenerator.formatDate(date));
}
@ -1256,6 +1269,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable putDate(String name, long date)
{
Objects.requireNonNull(name);
return put(name, DateGenerator.formatDate(date));
}
@ -1269,6 +1283,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(HttpHeader header, long value)
{
Objects.requireNonNull(header);
if (value == 0 && header == HttpHeader.CONTENT_LENGTH)
return put(HttpFields.CONTENT_LENGTH_0);
return put(new HttpField.LongValueHttpField(header, value));
@ -1284,6 +1299,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable put(String name, long value)
{
Objects.requireNonNull(name);
if (value == 0 && HttpHeader.CONTENT_LENGTH.is(name))
return put(HttpFields.CONTENT_LENGTH_0);
return put(new HttpField.LongValueHttpField(name, value));
@ -1367,7 +1383,9 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable computeField(HttpHeader header, BiFunction<HttpHeader, List<HttpField>, HttpField> computeFn)
{
return put(computeFn.apply(header, stream().filter(f -> f.getHeader() == header).collect(Collectors.toList())));
Objects.requireNonNull(header);
HttpField result = computeFn.apply(header, stream().filter(f -> f.getHeader() == header).toList());
return result != null ? put(result) : remove(header);
}
/**
@ -1380,7 +1398,9 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable computeField(String name, BiFunction<String, List<HttpField>, HttpField> computeFn)
{
return put(computeFn.apply(name, stream().filter(f -> f.is(name)).collect(Collectors.toList())));
Objects.requireNonNull(name);
HttpField result = computeFn.apply(name, stream().filter(f -> f.is(name)).toList());
return result != null ? put(result) : remove(name);
}
/**
@ -1391,6 +1411,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable remove(HttpHeader header)
{
Objects.requireNonNull(header);
Iterator<HttpField> i = iterator();
while (i.hasNext())
{
@ -1428,6 +1449,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
*/
default Mutable remove(String name)
{
Objects.requireNonNull(name);
for (ListIterator<HttpField> i = listIterator(); i.hasNext(); )
{
HttpField f = i.next();
@ -1656,6 +1678,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
@Override
public Mutable put(HttpField field)
{
Objects.requireNonNull(field);
// rewrite put to ensure that removes are called before replace
int put = -1;
ListIterator<HttpField> i = _fields.listIterator();
@ -1675,7 +1698,7 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
{
field = onAddField(field);
if (field != null)
add(field);
_fields.add(field);
}
else
{

View File

@ -1004,7 +1004,7 @@ public class HttpFieldsTest
public void testAddNullValueList()
{
HttpFields.Mutable fields = HttpFields.build();
assertThrows(IllegalArgumentException.class, () -> fields.add("name", (List<String>)null));
assertThrows(NullPointerException.class, () -> fields.add("name", (List<String>)null));
assertThat(fields.size(), is(0));
List<String> list = new ArrayList<>();
fields.add("name", list);
@ -1374,4 +1374,56 @@ public class HttpFieldsTest
fields.ensureField(new HttpField("Test", "three, four"));
assertThat(fields.stream().map(HttpField::toString).collect(Collectors.toList()), contains("Test: one, two, three, four"));
}
@Test
public void testWrapperComputeFieldCallingOnField()
{
var wrapper = new HttpFields.Mutable.Wrapper(HttpFields.build())
{
final List<String> actions = new ArrayList<>();
@Override
public HttpField onAddField(HttpField field)
{
actions.add("onAddField");
return super.onAddField(field);
}
@Override
public boolean onRemoveField(HttpField field)
{
actions.add("onRemoveField");
return super.onRemoveField(field);
}
@Override
public HttpField onReplaceField(HttpField oldField, HttpField newField)
{
actions.add("onReplaceField");
return super.onReplaceField(oldField, newField);
}
};
wrapper.computeField("non-existent", (name, httpFields) -> null);
assertThat(wrapper.size(), is(0));
assertThat(wrapper.actions, is(List.of()));
wrapper.computeField("non-existent", (name, httpFields) -> new HttpField("non-existent", "a"));
wrapper.computeField("non-existent", (name, httpFields) -> new HttpField("non-existent", "b"));
wrapper.computeField("non-existent", (name, httpFields) -> null);
assertThat(wrapper.size(), is(0));
assertThat(wrapper.actions, is(List.of("onAddField", "onReplaceField", "onRemoveField")));
wrapper.actions.clear();
wrapper.computeField(HttpHeader.VARY, (name, httpFields) -> null);
assertThat(wrapper.size(), is(0));
assertThat(wrapper.actions, is(List.of()));
wrapper.computeField(HttpHeader.VARY, (name, httpFields) -> new HttpField(HttpHeader.VARY, "a"));
wrapper.computeField(HttpHeader.VARY, (name, httpFields) -> new HttpField(HttpHeader.VARY, "b"));
wrapper.computeField(HttpHeader.VARY, (name, httpFields) -> null);
assertThat(wrapper.size(), is(0));
assertThat(wrapper.actions, is(List.of("onAddField", "onReplaceField", "onRemoveField")));
wrapper.actions.clear();
}
}

View File

@ -99,8 +99,8 @@ public class HttpGeneratorClientTest
HttpFields.Mutable fields = HttpFields.build();
fields.add("Host", "something");
assertThrows(IllegalArgumentException.class, () -> fields.add("Null", (String)null));
assertThrows(IllegalArgumentException.class, () -> fields.add("Null", (List<String>)null));
assertThrows(NullPointerException.class, () -> fields.add("Null", (String)null));
assertThrows(NullPointerException.class, () -> fields.add("Null", (List<String>)null));
fields.add("Empty", "");
RequestInfo info = new RequestInfo("GET", "/index.html", fields);
assertFalse(gen.isChunking());

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-core</artifactId>
<version>12.0.9-SNAPSHOT</version>
</parent>
<artifactId>jetty-maven</artifactId>
<name>Core :: Maven</name>
<properties>
<bundle-symbolic-name>${project.groupId}.maven</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.jetty.maven.*</spotbugs.onlyAnalyze>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-tools-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-xml</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-xml</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-xml-impl</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-xml-meta</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine} ${jetty.surefire.argLine}
--add-reads org.eclipse.jetty.maven=org.eclipse.jetty.logging</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,208 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.maven;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JettyForkedChild
*
* This is the class that is executed when the jetty maven plugin
* forks a process when DeploymentMode=FORKED.
*/
public abstract class AbstractForkedChild extends ContainerLifeCycle
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractForkedChild.class);
protected AbstractJettyEmbedder jetty;
protected File tokenFile; // TODO: convert to Path
protected Scanner scanner;
protected File webAppPropsFile; // TODO: convert to Path
protected int scanInterval;
/**
* @param args arguments that were passed to main
* @throws Exception if unable to configure
*/
public AbstractForkedChild(String[] args)
throws Exception
{
jetty = newJettyEmbedder();
configure(args);
}
public AbstractJettyEmbedder getJettyEmbedder()
{
return jetty;
}
protected abstract AbstractJettyEmbedder newJettyEmbedder();
/**
* Based on the args passed to the program, configure jetty.
*
* @param args args that were passed to the program.
* @throws Exception if unable to load webprops
*/
public void configure(String[] args)
throws Exception
{
Map<String, String> jettyProperties = new HashMap<>();
for (int i = 0; i < args.length; i++)
{
//--stop-port
if ("--stop-port".equals(args[i]))
{
jetty.setStopPort(Integer.parseInt(args[++i]));
continue;
}
//--stop-key
if ("--stop-key".equals(args[i]))
{
jetty.setStopKey(args[++i]);
continue;
}
//--jettyXml
if ("--jetty-xml".equals(args[i]))
{
List<File> jettyXmls = new ArrayList<>();
String[] names = StringUtil.csvSplit(args[++i]);
for (int j = 0; names != null && j < names.length; j++)
{
jettyXmls.add(new File(names[j].trim()));
}
jetty.setJettyXmlFiles(jettyXmls);
continue;
}
//--webprops
if ("--webprops".equals(args[i]))
{
webAppPropsFile = new File(args[++i].trim());
jetty.setWebAppProperties(loadWebAppProps());
continue;
}
//--token
if ("--token".equals(args[i]))
{
tokenFile = new File(args[++i].trim());
continue;
}
if ("--scanInterval".equals(args[i]))
{
scanInterval = Integer.parseInt(args[++i].trim());
scanner = new Scanner();
scanner.setReportExistingFilesOnStartup(false);
scanner.setScanInterval(scanInterval);
scanner.addListener(new Scanner.BulkListener()
{
public void filesChanged(Set<String> changes)
{
if (!Objects.isNull(scanner))
{
try
{
scanner.stop();
jetty.redeployWebApp(loadWebAppProps());
scanner.start();
}
catch (Exception e)
{
LOG.error("Error reconfiguring/restarting webapp after change in watched files", e);
}
}
}
});
if (!Objects.isNull(webAppPropsFile))
scanner.addFile(webAppPropsFile.toPath());
continue;
}
//assume everything else is a jetty property to be passed in
String[] tmp = args[i].trim().split("=");
if (tmp.length == 2)
{
jettyProperties.put(tmp[0], tmp[1]);
}
}
jetty.setJettyProperties(jettyProperties);
jetty.setExitVm(true);
}
/**
* Load properties from a file describing the webapp if one is
* present.
*
* @return file contents as properties
* @throws FileNotFoundException if there is a file not found problem
* @throws IOException if there is an IO problem
*/
protected Properties loadWebAppProps() throws FileNotFoundException, IOException
{
Properties props = new Properties();
if (Objects.nonNull(webAppPropsFile))
props.load(new FileInputStream(webAppPropsFile));
return props;
}
/**
* Start a jetty instance and webapp. This thread will
* wait until jetty exits.
*/
public void doStart()
throws Exception
{
super.doStart();
//Start the embedded jetty instance
jetty.start();
//touch file to signify start of jetty
Path tokenPath = tokenFile.toPath();
Files.createFile(tokenPath);
//Start a watcher on a file that will change if the
//webapp is regenerated; stop the webapp, apply the
//properties and restart it.
if (scanner != null)
scanner.start();
//wait for jetty to finish
jetty.join();
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.File;
import java.util.List;

View File

@ -0,0 +1,428 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.maven;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
/**
* AbstractHomeForker
*
* Unpacks a jetty-home and configures it with a base that allows it
* to run an unassembled webapp.
*/
public abstract class AbstractHomeForker extends AbstractForker
{
protected String contextXml;
/**
* Location of existing jetty home directory
*/
protected File jettyHome;
/**
* Zip of jetty-home
*/
protected File jettyHomeZip;
/**
* Location of existing jetty base directory
*/
protected File jettyBase;
protected File baseDir;
/**
* Optional list of other modules to
* activate.
*/
protected String[] modules;
/*
* Optional jetty commands
*/
protected String jettyOptions;
protected List<File> libExtJarFiles;
protected Path modulesPath;
protected Path etcPath;
protected Path libPath;
protected Path webappPath;
protected Path mavenLibPath;
protected String version;
protected String environment;
public void setVersion(String version)
{
this.version = version;
}
public void setJettyOptions(String jettyOptions)
{
this.jettyOptions = jettyOptions;
}
public String getJettyOptions()
{
return jettyOptions;
}
public List<File> getLibExtJarFiles()
{
return libExtJarFiles;
}
public void setLibExtJarFiles(List<File> libExtJarFiles)
{
this.libExtJarFiles = libExtJarFiles;
}
public File getJettyHome()
{
return jettyHome;
}
public void setJettyHome(File jettyHome)
{
this.jettyHome = jettyHome;
}
public File getJettyBase()
{
return jettyBase;
}
public void setJettyBase(File jettyBase)
{
this.jettyBase = jettyBase;
}
public String[] getModules()
{
return modules;
}
public void setModules(String[] modules)
{
this.modules = modules;
}
public String getContextXmlFile()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public File getJettyHomeZip()
{
return jettyHomeZip;
}
public void setJettyHomeZip(File jettyHomeZip)
{
this.jettyHomeZip = jettyHomeZip;
}
public File getBaseDir()
{
return baseDir;
}
public void setBaseDir(File baseDir)
{
this.baseDir = baseDir;
}
@Override
protected ProcessBuilder createCommand()
{
List<String> cmd = new ArrayList<>();
cmd.add("java");
//add any args to the jvm
if (StringUtil.isNotBlank(jvmArgs))
{
Arrays.stream(jvmArgs.split(" ")).filter(a -> StringUtil.isNotBlank(a)).forEach((a) -> cmd.add(a.trim()));
}
cmd.add("-jar");
cmd.add(new File(jettyHome, "start.jar").getAbsolutePath());
if (systemProperties != null)
{
for (Map.Entry<String, String> e : systemProperties.entrySet())
{
cmd.add("-D" + e.getKey() + "=" + e.getValue());
}
}
cmd.add("-DSTOP.PORT=" + stopPort);
if (stopKey != null)
cmd.add("-DSTOP.KEY=" + stopKey);
//set up enabled jetty modules
StringBuilder tmp = new StringBuilder();
tmp.append("--module=");
tmp.append("server,http," + environment + "-webapp," + environment + "-deploy");
if (modules != null)
{
for (String m : modules)
{
if (tmp.indexOf(m) < 0)
tmp.append("," + m);
}
}
if (libExtJarFiles != null && !libExtJarFiles.isEmpty() && tmp.indexOf("ext") < 0)
tmp.append(",ext");
tmp.append("," + environment + "-maven");
cmd.add(tmp.toString());
//put any other jetty options onto the command line
if (StringUtil.isNotBlank(jettyOptions))
{
Arrays.stream(jettyOptions.split(" ")).filter(a -> StringUtil.isNotBlank(a)).forEach((a) -> cmd.add(a.trim()));
}
//put any jetty properties onto the command line
if (jettyProperties != null)
{
for (Map.Entry<String, String> e : jettyProperties.entrySet())
{
cmd.add(e.getKey() + "=" + e.getValue());
}
}
//existence of this file signals process started
cmd.add("jetty.token.file=" + tokenFile.getAbsolutePath().toString());
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(workDir);
PluginLog.getLog().info("Home process starting");
//set up extra environment vars if there are any
if (!env.isEmpty())
builder.environment().putAll(env);
if (waitForChild)
builder.inheritIO();
else
{
builder.redirectOutput(jettyOutputFile);
builder.redirectErrorStream(true);
}
return builder;
}
@Override
public void doStart() throws Exception
{
//set up a jetty-home
configureJettyHome();
if (jettyHome == null || !jettyHome.exists())
throw new IllegalStateException("No jetty home");
//set up a jetty-base
configureJettyBase();
//convert the webapp to properties
generateWebAppPropertiesFile();
super.doStart();
}
protected void redeployWebApp()
throws Exception
{
generateWebAppPropertiesFile();
webappPath.resolve("maven.xml").toFile().setLastModified(System.currentTimeMillis());
}
protected abstract void generateWebAppPropertiesFile() throws Exception;
/**
* Create or configure a jetty base.
*/
private void configureJettyBase() throws Exception
{
if (jettyBase != null && !jettyBase.exists())
throw new IllegalStateException(jettyBase.getAbsolutePath() + " does not exist");
File targetJettyBase = new File(baseDir, "jetty-base");
Path targetBasePath = targetJettyBase.toPath();
if (Files.exists(targetBasePath))
IO.delete(targetJettyBase);
targetJettyBase.mkdirs();
//jetty-base will be the working directory for the forked command
workDir = targetJettyBase;
//if there is an existing jetty base, copy parts of it
if (jettyBase != null)
{
Path jettyBasePath = jettyBase.toPath();
final File contextXmlFile = (contextXml == null ? null : FileSystems.getDefault().getPath(contextXml).toFile());
//copy the existing jetty base
Files.walkFileTree(jettyBasePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path targetDir = targetBasePath.resolve(jettyBasePath.relativize(dir));
try
{
Files.copy(dir, targetDir);
}
catch (FileAlreadyExistsException e)
{
if (!Files.isDirectory(targetDir)) //ignore attempt to recreate dir
throw e;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
if (contextXmlFile != null && Files.isSameFile(contextXmlFile.toPath(), file))
return FileVisitResult.CONTINUE; //skip copying the context xml file
Files.copy(file, targetBasePath.resolve(jettyBasePath.relativize(file)));
return FileVisitResult.CONTINUE;
}
});
}
//make the jetty base structure
modulesPath = Files.createDirectories(targetBasePath.resolve("modules"));
etcPath = Files.createDirectories(targetBasePath.resolve("etc"));
libPath = Files.createDirectories(targetBasePath.resolve("lib"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps"));
mavenLibPath = Files.createDirectories(libPath.resolve(environment + "-maven"));
//copy in the jetty-${ee}-maven-plugin jar
URI thisJar = TypeUtil.getLocationOfClass(this.getClass());
if (thisJar == null)
throw new IllegalStateException("Can't find jar for jetty-" + environment + "-maven-plugin");
try (InputStream jarStream = thisJar.toURL().openStream();
FileOutputStream fileStream = new FileOutputStream(mavenLibPath.resolve("jetty-" + environment + "-maven-plugin.jar").toFile()))
{
IO.copy(jarStream, fileStream);
}
//copy in the jetty-maven.jar for common classes
URI commonJar = TypeUtil.getLocationOfClass(AbstractHomeForker.class);
if (commonJar == null)
throw new IllegalStateException("Can't find jar for jetty-maven common classes");
try (InputStream jarStream = commonJar.toURL().openStream();
FileOutputStream fileStream = new FileOutputStream(mavenLibPath.resolve("jetty-maven.jar").toFile()))
{
IO.copy(jarStream, fileStream);
}
//copy in the maven-${ee}.xml webapp file
String mavenXml = "maven-" + environment + ".xml";
try (InputStream mavenXmlStream = getClass().getClassLoader().getResourceAsStream(mavenXml);
FileOutputStream fileStream = new FileOutputStream(webappPath.resolve(mavenXml).toFile()))
{
IO.copy(mavenXmlStream, fileStream);
}
Files.writeString(webappPath.resolve("maven-" + environment + ".properties"), "environment=" + environment);
//copy in the ${ee}-maven.mod file
String mavenMod = environment + "-maven.mod";
try (InputStream mavenModStream = getClass().getClassLoader().getResourceAsStream(mavenMod);
FileOutputStream fileStream = new FileOutputStream(modulesPath.resolve(mavenMod).toFile()))
{
IO.copy(mavenModStream, fileStream);
}
//copy in the jetty-${ee}-maven.xml file
String jettyMavenXml = "jetty-" + environment + "-maven.xml";
try (InputStream jettyMavenStream = getClass().getClassLoader().getResourceAsStream(jettyMavenXml);
FileOutputStream fileStream = new FileOutputStream(etcPath.resolve(jettyMavenXml).toFile()))
{
IO.copy(jettyMavenStream, fileStream);
}
//if there were plugin dependencies, copy them into lib/ext
if (libExtJarFiles != null && !libExtJarFiles.isEmpty())
{
Path libExtPath = Files.createDirectories(libPath.resolve("ext"));
for (File f : libExtJarFiles)
{
try (InputStream jarStream = new FileInputStream(f);
FileOutputStream fileStream = new FileOutputStream(libExtPath.resolve(f.getName()).toFile()))
{
IO.copy(jarStream, fileStream);
}
}
}
}
private void configureJettyHome()
throws Exception
{
if (jettyHome == null && jettyHomeZip == null)
throw new IllegalStateException("No jettyHome");
if (baseDir == null)
throw new IllegalStateException("No baseDir");
if (jettyHome == null)
{
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
{
Resource res = resourceFactory.newJarFileResource(jettyHomeZip.toPath().toUri());
res.copyTo(baseDir.toPath());
}
//zip will unpack to target/jetty-home-<VERSION>
jettyHome = new File(baseDir, "jetty-home-" + version);
}
}
}

View File

@ -0,0 +1,284 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ShutdownMonitor;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
/**
* AbstractJettyEmbedder
* Starts jetty within the current process.
*/
public abstract class AbstractJettyEmbedder extends ContainerLifeCycle
{
protected List<ContextHandler> contextHandlers;
protected List<LoginService> loginServices;
protected RequestLog requestLog;
protected MavenServerConnector httpConnector;
protected Server server;
protected boolean exitVm;
protected boolean stopAtShutdown;
protected List<File> jettyXmlFiles;
protected Map<String, String> jettyProperties;
protected ShutdownMonitor shutdownMonitor;
protected int stopPort;
protected String stopKey;
protected String contextXml;
protected Properties webAppProperties;
public List<ContextHandler> getContextHandlers()
{
return contextHandlers;
}
public void setContextHandlers(List<ContextHandler> contextHandlers)
{
if (contextHandlers == null)
this.contextHandlers = null;
else
this.contextHandlers = new ArrayList<>(contextHandlers);
}
public List<LoginService> getLoginServices()
{
return loginServices;
}
public void setLoginServices(List<LoginService> loginServices)
{
if (loginServices == null)
this.loginServices = null;
else
this.loginServices = new ArrayList<>(loginServices);
}
public RequestLog getRequestLog()
{
return requestLog;
}
public void setRequestLog(RequestLog requestLog)
{
this.requestLog = requestLog;
}
public MavenServerConnector getHttpConnector()
{
return httpConnector;
}
public void setHttpConnector(MavenServerConnector httpConnector)
{
this.httpConnector = httpConnector;
}
public Server getServer()
{
return server;
}
public void setServer(Server server)
{
this.server = server;
}
public boolean isExitVm()
{
return exitVm;
}
public void setExitVm(boolean exitVm)
{
this.exitVm = exitVm;
}
public boolean isStopAtShutdown()
{
return stopAtShutdown;
}
public void setStopAtShutdown(boolean stopAtShutdown)
{
this.stopAtShutdown = stopAtShutdown;
}
public List<File> getJettyXmlFiles()
{
return jettyXmlFiles;
}
public void setJettyXmlFiles(List<File> jettyXmlFiles)
{
this.jettyXmlFiles = jettyXmlFiles;
}
public Map<String, String> getJettyProperties()
{
return jettyProperties;
}
public void setJettyProperties(Map<String, String> jettyProperties)
{
this.jettyProperties = jettyProperties;
}
public ShutdownMonitor getShutdownMonitor()
{
return shutdownMonitor;
}
public void setShutdownMonitor(ShutdownMonitor shutdownMonitor)
{
this.shutdownMonitor = shutdownMonitor;
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public void setWebAppProperties(Properties props)
{
if (webAppProperties != null)
webAppProperties.clear();
if (props != null)
{
if (webAppProperties == null)
webAppProperties = new Properties();
webAppProperties.putAll(props);
}
}
public String getContextXml()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public void doStart() throws Exception
{
super.doStart();
configure();
configureShutdownMonitor();
server.start();
}
protected abstract void redeployWebApp() throws Exception;
public void redeployWebApp(Properties webaAppProperties) throws Exception
{
setWebAppProperties(webaAppProperties);
redeployWebApp();
}
public abstract void stopWebApp() throws Exception;
public void join() throws InterruptedException
{
server.join();
}
/**
* Configure the server and the webapp
* @throws Exception if there is an unspecified problem
*/
protected void configure() throws Exception
{
configureServer();
configureWebApp();
addWebAppToServer();
}
protected void configureServer() throws Exception
{
//apply any configs from jetty.xml files first
Server tmp = ServerSupport.applyXmlConfigurations(new Server(), jettyXmlFiles, jettyProperties);
if (tmp != null)
server = tmp;
server.setStopAtShutdown(stopAtShutdown);
//ensure there's a connector
if (httpConnector != null)
httpConnector.setServer(server);
ServerSupport.configureConnectors(server, httpConnector, jettyProperties);
//set up handler structure
ServerSupport.configureHandlers(server, contextHandlers, requestLog);
// set up security realms
ServerSupport.configureLoginServices(server, loginServices);
}
protected abstract void configureWebApp() throws Exception;
protected abstract void addWebAppToServer() throws Exception;
protected void applyWebAppProperties() throws Exception
{
//apply properties to the webapp if there are any
if (contextXml != null)
{
if (webAppProperties == null)
webAppProperties = new Properties();
webAppProperties.put("context.xml", contextXml);
}
}
private void configureShutdownMonitor()
{
if (stopPort > 0 && stopKey != null)
{
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
monitor.setPort(stopPort);
monitor.setKey(stopKey);
monitor.setExitVm(exitVm);
}
}
}

View File

@ -0,0 +1,276 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.server.Server;
/**
* AbstractServerForker
*
* Fork a jetty Server
*/
public abstract class AbstractServerForker extends AbstractForker
{
protected File forkWebXml;
protected Server server;
protected String containerClassPath;
protected File webAppPropsFile;
protected String contextXml;
protected int scanInterval;
protected String executionClassName;
/**
* @return the scan
*/
public boolean isScan()
{
return scanInterval > 0;
}
/**
* Set if true, the forked child will scan for changes at 1 second intervals.
* @param scan if true, the forked child will scan for changes at 1 second intervals
*/
public void setScan(boolean scan)
{
setScanInterval(scan ? 1 : 0);
}
public int getScanInterval()
{
return scanInterval;
}
public void setScanInterval(int sec)
{
scanInterval = sec;
}
public File getWebAppPropsFile()
{
return webAppPropsFile;
}
public void setWebAppPropsFile(File webAppPropsFile)
{
this.webAppPropsFile = webAppPropsFile;
}
public File getForkWebXml()
{
return forkWebXml;
}
public void setForkWebXml(File forkWebXml)
{
this.forkWebXml = forkWebXml;
}
public String getContextXml()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public String getContainerClassPath()
{
return containerClassPath;
}
public void setContainerClassPath(String containerClassPath)
{
this.containerClassPath = containerClassPath;
}
public Server getServer()
{
return server;
}
public void setServer(Server server)
{
this.server = server;
}
@Override
public void doStart()
throws Exception
{
generateWebApp();
super.doStart();
}
protected abstract void generateWebApp() throws Exception;
protected abstract void redeployWebApp() throws Exception;
public ProcessBuilder createCommand()
{
List<String> cmd = new ArrayList<String>();
cmd.add(getJavaBin());
if (jvmArgs != null)
{
String[] args = jvmArgs.split(" ");
for (int i = 0; args != null && i < args.length; i++)
{
if (args[i] != null && !"".equals(args[i]))
cmd.add(args[i].trim());
}
}
if (systemProperties != null)
{
for (Map.Entry<String, String> e:systemProperties.entrySet())
{
cmd.add("-D" + e.getKey() + "=" + e.getValue());
}
}
if (containerClassPath != null && containerClassPath.length() > 0)
{
cmd.add("-cp");
cmd.add(containerClassPath);
}
cmd.add(executionClassName);
if (stopPort > 0 && stopKey != null)
{
cmd.add("--stop-port");
cmd.add(Integer.toString(stopPort));
cmd.add("--stop-key");
cmd.add(stopKey);
}
if (jettyXmlFiles != null)
{
cmd.add("--jetty-xml");
StringBuilder tmp = new StringBuilder();
for (File jettyXml:jettyXmlFiles)
{
if (tmp.length() != 0)
tmp.append(",");
tmp.append(jettyXml.getAbsolutePath());
}
cmd.add(tmp.toString());
}
cmd.add("--webprops");
cmd.add(webAppPropsFile.getAbsolutePath());
cmd.add("--token");
cmd.add(tokenFile.getAbsolutePath());
if (scanInterval > 0)
{
cmd.add("--scanInterval");
cmd.add(Integer.toString(scanInterval));
}
if (jettyProperties != null)
{
for (Map.Entry<String, String> e:jettyProperties.entrySet())
{
cmd.add(e.getKey() + "=" + e.getValue());
}
}
ProcessBuilder command = new ProcessBuilder(cmd);
command.directory(workDir);
if (PluginLog.getLog().isDebugEnabled())
PluginLog.getLog().debug("Forked cli:" + command.command());
PluginLog.getLog().info("Forked process starting");
//set up extra environment vars if there are any
if (env != null && !env.isEmpty())
command.environment().putAll(env);
if (waitForChild)
{
command.inheritIO();
}
else
{
command.redirectOutput(jettyOutputFile);
command.redirectErrorStream(true);
}
return command;
}
/**
* Get the location of the java binary.
* @return the location of the java binary
*/
private String getJavaBin()
{
String[] javaexes = new String[]{"java", "java.exe"};
File javaHomeDir = new File(System.getProperty("java.home"));
for (String javaexe : javaexes)
{
File javabin = new File(javaHomeDir, fileSeparators("bin/" + javaexe));
if (javabin.exists() && javabin.isFile())
{
return javabin.getAbsolutePath();
}
}
return "java";
}
public static String fileSeparators(String path)
{
StringBuilder ret = new StringBuilder();
for (char c : path.toCharArray())
{
if ((c == '/') || (c == '\\'))
{
ret.append(File.separatorChar);
}
else
{
ret.append(c);
}
}
return ret.toString();
}
public static String pathSeparators(String path)
{
StringBuilder ret = new StringBuilder();
for (char c : path.toCharArray())
{
if ((c == ',') || (c == ':'))
{
ret.append(File.pathSeparatorChar);
}
else
{
ret.append(c);
}
}
return ret.toString();
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.Console;
import java.util.EventListener;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin.utils;
package org.eclipse.jetty.maven;
import java.io.File;
import java.nio.file.Path;
@ -35,8 +35,6 @@ import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.jetty.ee9.maven.plugin.OverlayManager;
import org.eclipse.jetty.ee9.maven.plugin.WarPluginInfo;
/**
* MavenProjectHelper

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.IOException;
import java.io.InputStream;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.util.Collection;
import java.util.List;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.File;
import java.io.IOException;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.util.ArrayList;
import java.util.Arrays;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.File;
import java.io.IOException;
@ -24,7 +24,7 @@ import java.util.Objects;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.eclipse.jetty.ee9.webapp.WebAppContext;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.MountedPathResource;
import org.eclipse.jetty.util.resource.Resource;
@ -45,41 +45,41 @@ public class OverlayManager
this.warPlugin = warPlugin;
}
public void applyOverlays(MavenWebAppContext webApp)
public void applyOverlays(ContextHandler contextHandler, boolean append)
throws Exception
{
List<Resource> resourceBases = new ArrayList<Resource>();
for (Overlay o : getOverlays(webApp))
for (Overlay o : getOverlays(contextHandler))
{
//can refer to the current project in list of overlays for ordering purposes
if (o.getConfig() != null && o.getConfig().isCurrentProject() && webApp.getBaseResource().exists())
if (o.getConfig() != null && o.getConfig().isCurrentProject() && contextHandler.getBaseResource().exists())
{
resourceBases.add(webApp.getBaseResource());
resourceBases.add(contextHandler.getBaseResource());
continue;
}
//add in the selectively unpacked overlay in the correct order to the webapp's resource base
resourceBases.add(unpackOverlay(webApp, o));
resourceBases.add(unpackOverlay(contextHandler, o));
}
if (!resourceBases.contains(webApp.getBaseResource()) && webApp.getBaseResource().exists())
if (!resourceBases.contains(contextHandler.getBaseResource()) && contextHandler.getBaseResource().exists())
{
if (webApp.getBaseAppFirst())
resourceBases.add(0, webApp.getBaseResource());
if (append)
resourceBases.add(0, contextHandler.getBaseResource());
else
resourceBases.add(webApp.getBaseResource());
resourceBases.add(contextHandler.getBaseResource());
}
webApp.setBaseResource(ResourceFactory.combine(resourceBases));
contextHandler.setBaseResource(ResourceFactory.combine(resourceBases));
}
/**
* Generate an ordered list of overlays
*/
protected List<Overlay> getOverlays(WebAppContext webApp)
private List<Overlay> getOverlays(ContextHandler contextHandler)
throws Exception
{
Objects.requireNonNull(webApp);
Objects.requireNonNull(contextHandler);
Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
List<Overlay> overlays = new ArrayList<Overlay>();
@ -104,7 +104,7 @@ public class OverlayManager
if (a != null)
{
matchedWarArtifacts.add(a);
Resource resource = webApp.getResourceFactory().newJarFileResource(a.getFile().toPath().toUri());
Resource resource = ResourceFactory.of(contextHandler).newJarFileResource(a.getFile().toPath().toUri());
SelectiveJarResource r = new SelectiveJarResource(resource);
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
@ -118,7 +118,7 @@ public class OverlayManager
{
if (!matchedWarArtifacts.contains(a))
{
Resource resource = webApp.getResourceFactory().newJarFileResource(a.getFile().toPath().toUri());
Resource resource = ResourceFactory.of(contextHandler).newJarFileResource(a.getFile().toPath().toUri());
Overlay overlay = new Overlay(null, resource);
overlays.add(overlay);
}
@ -133,10 +133,10 @@ public class OverlayManager
* @return the location to which it was unpacked
* @throws IOException if there is an IO problem
*/
protected Resource unpackOverlay(WebAppContext webApp, Overlay overlay)
protected Resource unpackOverlay(ContextHandler contextHandler, Overlay overlay)
throws IOException
{
Objects.requireNonNull(webApp);
Objects.requireNonNull(contextHandler);
Objects.requireNonNull(overlay);
if (overlay.getResource() == null)
@ -161,6 +161,6 @@ public class OverlayManager
overlay.unpackTo(unpackDir);
//use top level of unpacked content
return webApp.getResourceFactory().newResource(unpackDir.getCanonicalPath());
return ResourceFactory.of(contextHandler).newResource(unpackDir.getCanonicalPath());
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import org.apache.maven.plugin.logging.Log;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.util.Collections;
import java.util.List;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.File;
import java.nio.file.Path;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.IOException;
import java.io.InputStream;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.Writer;
import java.nio.file.AtomicMoveNotSupportedException;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.nio.file.Files;
import java.nio.file.Path;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.io.File;
import java.util.Enumeration;
@ -19,8 +19,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.ee9.webapp.Configurations;
import org.eclipse.jetty.ee9.webapp.WebAppContext;
import org.eclipse.jetty.maven.MavenServerConnector;
import org.eclipse.jetty.maven.PluginLog;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.RequestLog;
@ -37,12 +37,6 @@ import org.eclipse.jetty.xml.XmlConfiguration;
*/
public class ServerSupport
{
public static void configureDefaultConfigurationClasses(Server server)
{
Configurations.setServerDefault(server);
}
/**
* Set up the handler structure to receive a webapp.
* Also put in a DefaultHandler so we get a nicer page
@ -144,7 +138,7 @@ public class ServerSupport
* @param webapp the webapp to add
* @throws Exception if there is an unspecified problem
*/
public static void addWebApplication(Server server, WebAppContext webapp) throws Exception
public static void addWebApplication(Server server, ContextHandler webapp) throws Exception
{
if (server == null)
throw new IllegalArgumentException("Server is null");

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.maven.plugin;
package org.eclipse.jetty.maven;
import java.util.ArrayList;
import java.util.Collections;

View File

@ -1,8 +1,8 @@
# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/
[description]
Enables the jetty-rewrite handler.
Specific rewrite rules must be added to etc/jetty-rewrite-rules.xml.
Adds the RewriteHandler to the Handler chain.
Specific rewrite rules must be added to $JETTY_BASE/etc/jetty-rewrite-rules.xml.
[tags]
server
@ -24,5 +24,7 @@ etc/jetty-rewrite.xml
etc/jetty-rewrite-rules.xml
[ini-template]
# tag::documentation[]
## Request attribute name used to store the original request path.
# jetty.rewrite.originalPathAttribute=jetty.rewrite.originalRequestPath
# end::documentation[]

View File

@ -66,7 +66,7 @@ public class HeaderRegexRule extends RegexRule
}
/**
* Set true to add the response header, false to put the response header..
* Set true to add the response header, false to put the response header.
* @param add true to add the response header, false to put the response header.
*/
public void setAdd(boolean add)

View File

@ -53,7 +53,7 @@ public class RedirectPatternRule extends PatternRule
}
/**
* Set the location to redirect..
* Set the location to redirect.
* @param value the location to redirect.
*/
public void setLocation(String value)

View File

@ -55,7 +55,7 @@ public class RedirectRegexRule extends RegexRule
}
/**
* Set the location to redirect..
* Set the location to redirect.
* @param location the location to redirect.
*/
public void setLocation(String location)

View File

@ -43,7 +43,7 @@ import static java.lang.invoke.MethodType.methodType;
/**
* Customize Requests for Proxy Forwarding.
* <p>
* This customizer looks at at HTTP request for headers that indicate
* This customizer looks at HTTP request for headers that indicate
* it has been forwarded by one or more proxies. Specifically handled are
* <ul>
* <li>{@code Forwarded}, as defined by <a href="https://tools.ietf.org/html/rfc7239">rfc7239</a>
@ -80,7 +80,7 @@ import static java.lang.invoke.MethodType.methodType;
* <tbody style="text-align: left; vertical-align: top;">
* <tr>
* <td>1</td>
* <td><code>Forwarded</code> Header</td>
* <td>{@code Forwarded} Header</td>
* <td>"{@code host=<host>}" param (Required)</td>
* <td>"{@code host=<host>:<port>} param (Implied)</td>
* <td>"{@code proto=<value>}" param (Optional)</td>
@ -88,7 +88,7 @@ import static java.lang.invoke.MethodType.methodType;
* </tr>
* <tr>
* <td>2</td>
* <td><code>X-Forwarded-Host</code> Header</td>
* <td>{@code X-Forwarded-Host} Header</td>
* <td>Required</td>
* <td>Implied</td>
* <td>n/a</td>
@ -96,7 +96,7 @@ import static java.lang.invoke.MethodType.methodType;
* </tr>
* <tr>
* <td>3</td>
* <td><code>X-Forwarded-Port</code> Header</td>
* <td>{@code X-Forwarded-Port} Header</td>
* <td>n/a</td>
* <td>Required</td>
* <td>n/a</td>
@ -104,7 +104,7 @@ import static java.lang.invoke.MethodType.methodType;
* </tr>
* <tr>
* <td>4</td>
* <td><code>X-Forwarded-Server</code> Header</td>
* <td>{@code X-Forwarded-Server} Header</td>
* <td>Required</td>
* <td>Optional</td>
* <td>n/a</td>
@ -112,29 +112,29 @@ import static java.lang.invoke.MethodType.methodType;
* </tr>
* <tr>
* <td>5</td>
* <td><code>X-Forwarded-Proto</code> Header</td>
* <td>{@code X-Forwarded-Proto} Header</td>
* <td>n/a</td>
* <td>Implied from value</td>
* <td>Required</td>
* <td>
* <p>left-most value becomes protocol.</p>
* <ul>
* <li>Value of "<code>http</code>" means port=80.</li>
* <li>Value of "{@code http}" means port=80.</li>
* <li>Value of "{@link HttpConfiguration#getSecureScheme()}" means port={@link HttpConfiguration#getSecurePort()}.</li>
* </ul>
* </td>
* </tr>
* <tr>
* <td>6</td>
* <td><code>X-Proxied-Https</code> Header</td>
* <td>{@code X-Proxied-Https} Header</td>
* <td>n/a</td>
* <td>Implied from value</td>
* <td>boolean</td>
* <td>
* <p>left-most value determines protocol and port.</p>
* <ul>
* <li>Value of "<code>on</code>" means port={@link HttpConfiguration#getSecurePort()}, and protocol={@link HttpConfiguration#getSecureScheme()}).</li>
* <li>Value of "<code>off</code>" means port=80, and protocol=http.</li>
* <li>Value of "{@code on}" means port={@link HttpConfiguration#getSecurePort()}, and protocol={@link HttpConfiguration#getSecureScheme()}).</li>
* <li>Value of "{@code off}" means port=80, and protocol=http.</li>
* </ul>
* </td>
* </tr>
@ -799,12 +799,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("MutableHostPort{");
sb.append("host='").append(_host).append("'/").append(_hostSource);
sb.append(", port=").append(_port);
sb.append("/").append(_portSource);
sb.append('}');
return sb.toString();
return "%s@%x{host='%s'/%s, port=%d/%s}".formatted(getClass().getSimpleName(), hashCode(), _host, _hostSource, _port, _portSource);
}
}
@ -888,7 +883,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>Proxy-auth-cert</code>
* Called if header is {@code Proxy-auth-cert}
*/
public void handleCipherSuite(HttpField field)
{
@ -904,7 +899,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>Proxy-Ssl-Id</code>
* Called if header is {@code Proxy-Ssl-Id}
*/
public void handleSslSessionId(HttpField field)
{
@ -920,7 +915,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Forwarded-Host</code>
* Called if header is {@code X-Forwarded-Host}
*/
public void handleForwardedHost(HttpField field)
{
@ -928,7 +923,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Forwarded-For</code>
* Called if header is {@code X-Forwarded-For}
*/
public void handleForwardedFor(HttpField field)
{
@ -937,7 +932,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Forwarded-Server</code>
* Called if header is {@code X-Forwarded-Server}
*/
public void handleForwardedServer(HttpField field)
{
@ -947,7 +942,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Forwarded-Port</code>
* Called if header is {@code X-Forwarded-Port}
*/
public void handleForwardedPort(HttpField field)
{
@ -957,7 +952,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Forwarded-Proto</code>
* Called if header is {@code X-Forwarded-Proto}
*/
public void handleProto(HttpField field)
{
@ -965,7 +960,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>X-Proxied-Https</code>
* Called if header is {@code X-Proxied-Https}
*/
public void handleHttps(HttpField field)
{
@ -988,7 +983,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
}
/**
* Called if header is <code>Forwarded</code>
* Called if header is {@code Forwarded}
*/
public void handleRFC7239(HttpField field)
{

View File

@ -67,7 +67,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
/**
* @param sniHostCheck True if the SNI Host name must match.
* @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
* @param stsIncludeSubdomains If true, a include subdomain property is sent with any Strict-Transport-Security header
* @param stsIncludeSubdomains If true, an include subdomain property is sent with any Strict-Transport-Security header
*/
public SecureRequestCustomizer(
@Name("sniHostCheck") boolean sniHostCheck,
@ -81,7 +81,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
* @param sniRequired True if a SNI certificate is required.
* @param sniHostCheck True if the SNI Host name must match.
* @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent.
* @param stsIncludeSubdomains If true, a include subdomain property is sent with any Strict-Transport-Security header
* @param stsIncludeSubdomains If true, an include subdomain property is sent with any Strict-Transport-Security header
*/
public SecureRequestCustomizer(
@Name("sniRequired") boolean sniRequired,

View File

@ -109,6 +109,7 @@ public class SizeLimitHandler extends Handler.Wrapper
{
private final HttpFields.Mutable _httpFields;
private long _written = 0;
private HttpException.RuntimeException _failure;
public SizeLimitResponseWrapper(Request request, Response wrapped)
{
@ -119,7 +120,7 @@ public class SizeLimitHandler extends Handler.Wrapper
@Override
public HttpField onAddField(HttpField field)
{
if (field.getHeader().is(HttpHeader.CONTENT_LENGTH.asString()))
if (field.getHeader() == HttpHeader.CONTENT_LENGTH)
{
long contentLength = field.getLongValue();
if (_responseLimit >= 0 && contentLength > _responseLimit)
@ -139,12 +140,19 @@ public class SizeLimitHandler extends Handler.Wrapper
@Override
public void write(boolean last, ByteBuffer content, Callback callback)
{
if (_failure != null)
{
callback.failed(_failure);
return;
}
if (content != null && content.remaining() > 0)
{
if (_responseLimit >= 0 && (_written + content.remaining()) > _responseLimit)
{
String message = "Response body is too large: %d>%d".formatted(_written + content.remaining(), _responseLimit);
callback.failed(new HttpException.RuntimeException(HttpStatus.INTERNAL_SERVER_ERROR_500, message));
_failure = new HttpException.RuntimeException(HttpStatus.INTERNAL_SERVER_ERROR_500,
"Response body is too large: %d>%d".formatted(_written + content.remaining(), _responseLimit));
callback.failed(_failure);
return;
}
_written += content.remaining();

View File

@ -16,6 +16,7 @@ package org.eclipse.jetty.server;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@ -25,7 +26,10 @@ import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.DumpHandler;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.component.LifeCycle;
@ -96,6 +100,49 @@ public class RequestTest
assertEquals(HttpStatus.BAD_REQUEST_400, response.getStatus());
}
@Test
public void testAmbiguousPathSep() throws Exception
{
server.stop();
for (Connector connector: server.getConnectors())
{
HttpConnectionFactory httpConnectionFactory = connector.getConnectionFactory(HttpConnectionFactory.class);
if (httpConnectionFactory != null)
{
HttpConfiguration httpConfiguration = httpConnectionFactory.getHttpConfiguration();
httpConfiguration.setUriCompliance(UriCompliance.from(
EnumSet.of(UriCompliance.Violation.AMBIGUOUS_PATH_SEPARATOR)
));
}
}
ContextHandler fooContext = new ContextHandler();
fooContext.setContextPath("/foo");
fooContext.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
String pathInContext = Request.getPathInContext(request);
String msg = String.format("pathInContext=\"%s\"", pathInContext);
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain;charset=utf-8");
Content.Sink.write(response, true, msg, callback);
return true;
}
});
server.setHandler(fooContext);
server.start();
String request = """
GET /foo/zed%2Fbar HTTP/1.1\r
Host: local\r
Connection: close\r
\r
""";
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(request));
assertEquals(HttpStatus.OK_200, response.getStatus());
assertThat(response.getContent(), is("pathInContext=\"/zed%2Fbar\""));
}
@Test
public void testConnectRequestURLSameAsHost() throws Exception
{

View File

@ -28,6 +28,7 @@
<module>jetty-jmx</module>
<module>jetty-jndi</module>
<module>jetty-keystore</module>
<module>jetty-maven</module>
<module>jetty-openid</module>
<module>jetty-osgi</module>
<module>jetty-plus</module>

View File

@ -81,6 +81,11 @@
<artifactId>jetty-jndi</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -32,7 +32,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
</New>
</Arg>
</Call>

View File

@ -25,7 +25,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -5,7 +5,7 @@
<Ref refid="httpConnector">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -112,7 +112,7 @@
</jettyXmls>
<loginServices>
<loginService implementation="org.eclipse.jetty.security.HashLoginService">
<config implementation="org.eclipse.jetty.ee10.maven.plugin.MavenResource">
<config implementation="org.eclipse.jetty.maven.MavenResource">
<resourceAsString>${basedir}/src/config/login.xml</resourceAsString>
</config>
</loginService>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -5,7 +5,7 @@
<Ref refid="httpConnector">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -1,251 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractForker
*
* Base class for forking jetty.
*/
public abstract class AbstractForker extends AbstractLifeCycle
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractForker.class);
protected Map<String, String> env;
protected String jvmArgs;
protected boolean exitVm;
protected boolean stopAtShutdown;
protected List<File> jettyXmlFiles;
protected Map<String, String> jettyProperties;
protected int stopPort;
protected String stopKey;
protected File jettyOutputFile;
protected boolean waitForChild;
protected int maxChildStartChecks = 10; //check up to 10 times for child to start
protected long maxChildStartCheckMs = 200; //wait 200ms between checks
protected File tokenFile;
protected File workDir;
protected Map<String, String> systemProperties;
protected abstract ProcessBuilder createCommand();
protected abstract void redeployWebApp() throws Exception;
public File getWorkDir()
{
return workDir;
}
public void setWorkDir(File workDir)
{
this.workDir = workDir;
}
/**
* @return the systemProperties
*/
public Map<String, String> getSystemProperties()
{
return systemProperties;
}
/**
* Set the systemProperties to set.
* @param systemProperties the systemProperties to set
*/
public void setSystemProperties(Map<String, String> systemProperties)
{
this.systemProperties = systemProperties;
}
public Map<String, String> getEnv()
{
return env;
}
public void setEnv(Map<String, String> env)
{
this.env = env;
}
public String getJvmArgs()
{
return jvmArgs;
}
public void setJvmArgs(String jvmArgs)
{
this.jvmArgs = jvmArgs;
}
public boolean isExitVm()
{
return exitVm;
}
public void setExitVm(boolean exitVm)
{
this.exitVm = exitVm;
}
public boolean isStopAtShutdown()
{
return stopAtShutdown;
}
public void setStopAtShutdown(boolean stopAtShutdown)
{
this.stopAtShutdown = stopAtShutdown;
}
public List<File> getJettyXmlFiles()
{
return jettyXmlFiles;
}
public void setJettyXmlFiles(List<File> jettyXmlFiles)
{
this.jettyXmlFiles = jettyXmlFiles;
}
public Map<String, String> getJettyProperties()
{
return jettyProperties;
}
public void setJettyProperties(Map<String, String> jettyProperties)
{
this.jettyProperties = jettyProperties;
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public File getJettyOutputFile()
{
return jettyOutputFile;
}
public void setJettyOutputFile(File jettyOutputFile)
{
this.jettyOutputFile = jettyOutputFile;
}
public boolean isWaitForChild()
{
return waitForChild;
}
public void setWaitForChild(boolean waitForChild)
{
this.waitForChild = waitForChild;
}
public int getMaxChildtartChecks()
{
return maxChildStartChecks;
}
public void setMaxChildStartChecks(int maxChildStartChecks)
{
this.maxChildStartChecks = maxChildStartChecks;
}
public long getMaxChildStartCheckMs()
{
return maxChildStartCheckMs;
}
public void setMaxChildStartCheckMs(long maxChildStartCheckMs)
{
this.maxChildStartCheckMs = maxChildStartCheckMs;
}
public File getTokenFile()
{
return tokenFile;
}
public void setTokenFile(File tokenFile)
{
this.tokenFile = tokenFile;
}
public void doStart()
throws Exception
{
super.doStart();
//Create the command to fork
ProcessBuilder command = createCommand();
Process process = command.start();
if (waitForChild)
{
//keep executing until the child dies
process.waitFor();
}
else
{
//just wait until the child has started successfully
int attempts = maxChildStartChecks;
while (!tokenFile.exists() && attempts > 0)
{
Thread.sleep(maxChildStartCheckMs);
--attempts;
}
if (attempts <= 0)
LOG.info("Couldn't verify success of child startup");
}
}
}

View File

@ -24,6 +24,7 @@ import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.eclipse.jetty.maven.ScanPattern;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.Resources;
@ -123,7 +124,7 @@ public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo
*
* @throws IOException if there is an IO problem
*/
protected void configureUnassembledWebApp() throws IOException
protected void configureUnassembledWebApp() throws Exception
{
//Set up the location of the webapp.
//There are 2 parts to this: setWar() and setBaseResource(). On standalone jetty,
@ -208,7 +209,7 @@ public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo
//process any overlays and the war type artifacts, and
//sets up the base resource collection for the webapp
mavenProjectHelper.getOverlayManager().applyOverlays(webApp);
mavenProjectHelper.getOverlayManager().applyOverlays(webApp, webApp.getBaseAppFirst());
getLog().info("web.xml file = " + webApp.getDescriptor());
getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath());

View File

@ -48,7 +48,10 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.jetty.ee10.maven.plugin.utils.MavenProjectHelper;
import org.eclipse.jetty.maven.MavenProjectHelper;
import org.eclipse.jetty.maven.MavenServerConnector;
import org.eclipse.jetty.maven.PluginLog;
import org.eclipse.jetty.maven.ScanTargetPattern;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
@ -563,7 +566,7 @@ public abstract class AbstractWebAppMojo extends AbstractMojo
if (jettyHome == null)
jetty.setJettyHomeZip(jettyHomeZip != null ? jettyHomeZip : mavenProjectHelper.resolveArtifact(JETTY_HOME_GROUPID, JETTY_HOME_ARTIFACTID, plugin.getVersion(), "zip"));
jetty.version = plugin.getVersion();
jetty.setVersion(plugin.getVersion());
jetty.setJettyHome(jettyHome);
jetty.setJettyBase(jettyBase);
jetty.setBaseDir(target);

View File

@ -1,66 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.Console;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;
/**
* ConsoleReader
*
* Reads lines from the System console and supplies them
* to ConsoleReader.Listeners.
*/
public class ConsoleReader implements Runnable
{
public interface Listener extends EventListener
{
public void consoleEvent(String line);
}
public Set<ConsoleReader.Listener> listeners = new HashSet<>();
public void addListener(ConsoleReader.Listener listener)
{
listeners.add(listener);
}
public void removeListener(ConsoleReader.Listener listener)
{
listeners.remove(listener);
}
public void run()
{
Console console = System.console();
if (console == null)
return;
String line = "";
while (true && line != null)
{
line = console.readLine("%nHit <enter> to redeploy:%n%n");
if (line != null)
signalEvent(line);
}
}
private void signalEvent(String line)
{
for (ConsoleReader.Listener l:listeners)
l.consoleEvent(line);
}
}

View File

@ -13,220 +13,40 @@
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ShutdownMonitor;
import org.eclipse.jetty.ee10.webapp.Configurations;
import org.eclipse.jetty.maven.AbstractJettyEmbedder;
import org.eclipse.jetty.maven.ServerSupport;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
/**
* JettyEmbedded
* JettyEmbedder
*
* Starts jetty within the current process.
*/
public class JettyEmbedder extends AbstractLifeCycle
public class JettyEmbedder extends AbstractJettyEmbedder
{
protected List<ContextHandler> contextHandlers;
protected List<LoginService> loginServices;
protected RequestLog requestLog;
protected MavenServerConnector httpConnector;
protected Server server;
protected MavenWebAppContext webApp;
protected boolean exitVm;
protected boolean stopAtShutdown;
protected List<File> jettyXmlFiles;
protected Map<String, String> jettyProperties;
protected ShutdownMonitor shutdownMonitor;
protected int stopPort;
protected String stopKey;
private String contextXml;
private Properties webAppProperties;
public List<ContextHandler> getContextHandlers()
{
return contextHandlers;
}
public void setContextHandlers(List<ContextHandler> contextHandlers)
{
if (contextHandlers == null)
this.contextHandlers = null;
else
this.contextHandlers = new ArrayList<>(contextHandlers);
}
public List<LoginService> getLoginServices()
{
return loginServices;
}
public void setLoginServices(List<LoginService> loginServices)
{
if (loginServices == null)
this.loginServices = null;
else
this.loginServices = new ArrayList<>(loginServices);
}
public RequestLog getRequestLog()
{
return requestLog;
}
public void setRequestLog(RequestLog requestLog)
{
this.requestLog = requestLog;
}
public MavenServerConnector getHttpConnector()
{
return httpConnector;
}
public void setHttpConnector(MavenServerConnector httpConnector)
{
this.httpConnector = httpConnector;
}
public Server getServer()
{
return server;
}
public void setServer(Server server)
{
this.server = server;
}
public MavenWebAppContext getWebApp()
{
return webApp;
}
public boolean isExitVm()
{
return exitVm;
}
public void setExitVm(boolean exitVm)
{
this.exitVm = exitVm;
}
public boolean isStopAtShutdown()
{
return stopAtShutdown;
}
public void setStopAtShutdown(boolean stopAtShutdown)
{
this.stopAtShutdown = stopAtShutdown;
}
public List<File> getJettyXmlFiles()
{
return jettyXmlFiles;
}
public void setJettyXmlFiles(List<File> jettyXmlFiles)
{
this.jettyXmlFiles = jettyXmlFiles;
}
public Map<String, String> getJettyProperties()
{
return jettyProperties;
}
public void setJettyProperties(Map<String, String> jettyProperties)
{
this.jettyProperties = jettyProperties;
}
public ShutdownMonitor getShutdownMonitor()
{
return shutdownMonitor;
}
public void setShutdownMonitor(ShutdownMonitor shutdownMonitor)
{
this.shutdownMonitor = shutdownMonitor;
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public void setWebApp(MavenWebAppContext app)
{
webApp = app;
}
public void setWebAppProperties(Properties props)
{
if (webAppProperties != null)
webAppProperties.clear();
if (props != null)
{
if (webAppProperties == null)
webAppProperties = new Properties();
webAppProperties.putAll(props);
}
}
public String getContextXml()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public void doStart() throws Exception
{
super.doStart();
configure();
configureShutdownMonitor();
server.start();
}
protected void redeployWebApp() throws Exception
{
if (!webApp.isStopped())
webApp.stop();
stopWebApp();
//clear the ServletHandler, which may have
//remembered "durable" Servlets, Filters, Listeners
@ -239,40 +59,22 @@ public class JettyEmbedder extends AbstractLifeCycle
webApp.start();
}
protected void join() throws InterruptedException
@Override
public void stopWebApp() throws Exception
{
server.join();
if (webApp != null && !webApp.isStopped())
webApp.stop();
}
/**
* Configure the server and the webapp
* Configure the webapp
* @throws Exception if there is an unspecified problem
*/
private void configure() throws Exception
public void configureWebApp() throws Exception
{
// apply any configs from jetty.xml files first
Server tmp = ServerSupport.applyXmlConfigurations(new Server(), jettyXmlFiles, jettyProperties);
if (tmp != null)
server = tmp;
server.setStopAtShutdown(stopAtShutdown);
//ensure there's a connector
if (httpConnector != null)
httpConnector.setServer(server);
ServerSupport.configureConnectors(server, httpConnector, jettyProperties);
//set up handler structure
ServerSupport.configureHandlers(server, contextHandlers, requestLog);
//Set up list of default Configurations to apply to a webapp
ServerSupport.configureDefaultConfigurationClasses(server);
// set up security realms
ServerSupport.configureLoginServices(server, loginServices);
Configurations.setServerDefault(server);
/* Configure the webapp */
if (webApp == null)
@ -286,37 +88,22 @@ public class JettyEmbedder extends AbstractLifeCycle
Path qs = webApp.getTempDirectory().toPath().resolve("quickstart-web.xml");
if (Files.exists(qs) && Files.isRegularFile(qs))
{
webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, qs);
webApp.addConfiguration(new MavenQuickStartConfiguration());
webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, qs);
webApp.setAttribute(QuickStartConfiguration.MODE, Mode.QUICKSTART);
}
}
}
public void applyWebAppProperties() throws Exception
{
super.applyWebAppProperties();
WebAppPropertyConverter.fromProperties(webApp, webAppProperties, server, jettyProperties);
}
public void addWebAppToServer() throws Exception
{
//add the webapp to the server
ServerSupport.addWebApplication(server, webApp);
}
private void applyWebAppProperties() throws Exception
{
//apply properties to the webapp if there are any
if (contextXml != null)
{
if (webAppProperties == null)
webAppProperties = new Properties();
webAppProperties.put("context.xml", contextXml);
}
WebAppPropertyConverter.fromProperties(webApp, webAppProperties, server, jettyProperties);
}
private void configureShutdownMonitor()
{
if (stopPort > 0 && stopKey != null)
{
ShutdownMonitor monitor = ShutdownMonitor.getInstance();
monitor.setPort(stopPort);
monitor.setKey(stopKey);
monitor.setExitVm(exitVm);
}
}
}

View File

@ -26,6 +26,8 @@ import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import org.eclipse.jetty.maven.AbstractForkedChild;
import org.eclipse.jetty.maven.AbstractJettyEmbedder;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@ -38,174 +40,23 @@ import org.slf4j.LoggerFactory;
* This is the class that is executed when the jetty maven plugin
* forks a process when DeploymentMode=FORKED.
*/
public class JettyForkedChild extends ContainerLifeCycle
public class JettyForkedChild extends AbstractForkedChild
{
private static final Logger LOG = LoggerFactory.getLogger(JettyForkedChild.class);
protected JettyEmbedder jetty;
protected File tokenFile; // TODO: convert to Path
protected Scanner scanner;
protected File webAppPropsFile; // TODO: convert to Path
protected int scanInterval;
/**
* @param args arguments that were passed to main
* @throws Exception if unable to configure
*/
public JettyForkedChild(String[] args)
throws Exception
public JettyForkedChild(String[] args) throws Exception
{
jetty = new JettyEmbedder();
configure(args);
super(args);
}
/**
* Based on the args passed to the program, configure jetty.
*
* @param args args that were passed to the program.
* @throws Exception if unable to load webprops
*/
public void configure(String[] args)
throws Exception
@Override
protected AbstractJettyEmbedder newJettyEmbedder()
{
Map<String, String> jettyProperties = new HashMap<>();
for (int i = 0; i < args.length; i++)
{
//--stop-port
if ("--stop-port".equals(args[i]))
{
jetty.setStopPort(Integer.parseInt(args[++i]));
continue;
}
//--stop-key
if ("--stop-key".equals(args[i]))
{
jetty.setStopKey(args[++i]);
continue;
}
//--jettyXml
if ("--jetty-xml".equals(args[i]))
{
List<File> jettyXmls = new ArrayList<>();
String[] names = StringUtil.csvSplit(args[++i]);
for (int j = 0; names != null && j < names.length; j++)
{
jettyXmls.add(new File(names[j].trim()));
}
jetty.setJettyXmlFiles(jettyXmls);
continue;
}
//--webprops
if ("--webprops".equals(args[i]))
{
webAppPropsFile = new File(args[++i].trim());
jetty.setWebAppProperties(loadWebAppProps());
continue;
}
//--token
if ("--token".equals(args[i]))
{
tokenFile = new File(args[++i].trim());
continue;
}
if ("--scanInterval".equals(args[i]))
{
scanInterval = Integer.parseInt(args[++i].trim());
scanner = new Scanner();
scanner.setReportExistingFilesOnStartup(false);
scanner.setScanInterval(scanInterval);
scanner.addListener(new Scanner.BulkListener()
{
public void filesChanged(Set<String> changes)
{
if (!Objects.isNull(scanner))
{
try
{
scanner.stop();
if (!Objects.isNull(jetty.getWebApp()))
{
//stop the webapp
jetty.getWebApp().stop();
//reload the props
jetty.setWebAppProperties(loadWebAppProps());
jetty.setWebApp(jetty.getWebApp());
//restart the webapp
jetty.redeployWebApp();
//restart the scanner
scanner.start();
}
}
catch (Exception e)
{
LOG.error("Error reconfiguring/restarting webapp after change in watched files", e);
}
}
}
});
if (!Objects.isNull(webAppPropsFile))
scanner.addFile(webAppPropsFile.toPath());
continue;
}
//assume everything else is a jetty property to be passed in
String[] tmp = args[i].trim().split("=");
if (tmp.length == 2)
{
jettyProperties.put(tmp[0], tmp[1]);
}
}
jetty.setJettyProperties(jettyProperties);
jetty.setExitVm(true);
}
/**
* Load properties from a file describing the webapp if one is
* present.
*
* @return file contents as properties
* @throws IOException if there is an IO problem
*/
private Properties loadWebAppProps() throws IOException
{
Properties props = new Properties();
if (Objects.nonNull(webAppPropsFile))
props.load(new FileInputStream(webAppPropsFile));
return props;
}
/**
* Start a jetty instance and webapp. This thread will
* wait until jetty exits.
*/
public void doStart()
throws Exception
{
super.doStart();
//Start the embedded jetty instance
jetty.start();
//touch file to signify start of jetty
Path tokenPath = tokenFile.toPath();
Files.createFile(tokenPath);
//Start a watcher on a file that will change if the
//webapp is regenerated; stop the webapp, apply the
//properties and restart it.
if (scanner != null)
scanner.start();
//wait for jetty to finish
jetty.join();
return new JettyEmbedder();
}
public static void main(String[] args)

View File

@ -13,94 +13,21 @@
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.maven.AbstractServerForker;
/**
* JettyForker
*
* Uses quickstart to generate a webapp and forks a process to run it.
*/
public class JettyForker extends AbstractForker
public class JettyForker extends AbstractServerForker
{
protected File forkWebXml;
protected Server server;
protected MavenWebAppContext webApp;
protected String containerClassPath;
protected File webAppPropsFile;
protected String contextXml;
protected int scanInterval;
QuickStartGenerator generator;
/**
* @return the scan
*/
public boolean isScan()
public JettyForker()
{
return scanInterval > 0;
}
/**
* Set if true, the forked child will scan for changes at 1 second intervals.
* @param scan if true, the forked child will scan for changes at 1 second intervals
*/
public void setScan(boolean scan)
{
setScanInterval(scan ? 1 : 0);
}
public void setScanInterval(int sec)
{
scanInterval = sec;
}
public int getScanInterval()
{
return scanInterval;
}
public File getWebAppPropsFile()
{
return webAppPropsFile;
}
public void setWebAppPropsFile(File webAppPropsFile)
{
this.webAppPropsFile = webAppPropsFile;
}
public File getForkWebXml()
{
return forkWebXml;
}
public void setForkWebXml(File forkWebXml)
{
this.forkWebXml = forkWebXml;
}
public String getContextXml()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public String getContainerClassPath()
{
return containerClassPath;
}
public void setContainerClassPath(String containerClassPath)
{
this.containerClassPath = containerClassPath;
executionClassName = JettyForkedChild.class.getCanonicalName();
}
public void setWebApp(MavenWebAppContext app)
@ -108,19 +35,8 @@ public class JettyForker extends AbstractForker
webApp = app;
}
public Server getServer()
{
return server;
}
public void setServer(Server server)
{
this.server = server;
}
@Override
public void doStart()
throws Exception
public void generateWebApp() throws Exception
{
//Run the webapp to create the quickstart file and properties file
generator = new QuickStartGenerator(forkWebXml.toPath(), webApp);
@ -128,8 +44,6 @@ public class JettyForker extends AbstractForker
generator.setWebAppProps(webAppPropsFile.toPath());
generator.setServer(server);
generator.generate();
super.doStart();
}
protected void redeployWebApp()
@ -139,154 +53,4 @@ public class JettyForker extends AbstractForker
//which will redeploy the webapp
generator.generate();
}
public ProcessBuilder createCommand()
{
List<String> cmd = new ArrayList<String>();
cmd.add(getJavaBin());
if (jvmArgs != null)
{
String[] args = jvmArgs.split(" ");
for (int i = 0; args != null && i < args.length; i++)
{
if (args[i] != null && !"".equals(args[i]))
cmd.add(args[i].trim());
}
}
if (systemProperties != null)
{
for (Map.Entry<String, String> e:systemProperties.entrySet())
{
cmd.add("-D" + e.getKey() + "=" + e.getValue());
}
}
if (containerClassPath != null && containerClassPath.length() > 0)
{
cmd.add("-cp");
cmd.add(containerClassPath);
}
cmd.add(JettyForkedChild.class.getCanonicalName());
if (stopPort > 0 && stopKey != null)
{
cmd.add("--stop-port");
cmd.add(Integer.toString(stopPort));
cmd.add("--stop-key");
cmd.add(stopKey);
}
if (jettyXmlFiles != null)
{
cmd.add("--jetty-xml");
StringBuilder tmp = new StringBuilder();
for (File jettyXml:jettyXmlFiles)
{
if (tmp.length() != 0)
tmp.append(",");
tmp.append(jettyXml.getAbsolutePath());
}
cmd.add(tmp.toString());
}
cmd.add("--webprops");
cmd.add(webAppPropsFile.getAbsolutePath());
cmd.add("--token");
cmd.add(tokenFile.getAbsolutePath());
if (scanInterval > 0)
{
cmd.add("--scanInterval");
cmd.add(Integer.toString(scanInterval));
}
if (jettyProperties != null)
{
for (Map.Entry<String, String> e:jettyProperties.entrySet())
{
cmd.add(e.getKey() + "=" + e.getValue());
}
}
ProcessBuilder command = new ProcessBuilder(cmd);
command.directory(workDir);
if (PluginLog.getLog().isDebugEnabled())
PluginLog.getLog().debug("Forked cli:" + command.command());
PluginLog.getLog().info("Forked process starting");
//set up extra environment vars if there are any
if (env != null && !env.isEmpty())
command.environment().putAll(env);
if (waitForChild)
{
command.inheritIO();
}
else
{
command.redirectOutput(jettyOutputFile);
command.redirectErrorStream(true);
}
return command;
}
/**
* Get the location of the java binary.
* @return the location of the java binary
*/
private String getJavaBin()
{
String[] javaexes = new String[]{"java", "java.exe"};
File javaHomeDir = new File(System.getProperty("java.home"));
for (String javaexe : javaexes)
{
File javabin = new File(javaHomeDir, fileSeparators("bin/" + javaexe));
if (javabin.exists() && javabin.isFile())
{
return javabin.getAbsolutePath();
}
}
return "java";
}
public static String fileSeparators(String path)
{
StringBuilder ret = new StringBuilder();
for (char c : path.toCharArray())
{
if ((c == '/') || (c == '\\'))
{
ret.append(File.separatorChar);
}
else
{
ret.append(c);
}
}
return ret.toString();
}
public static String pathSeparators(String path)
{
StringBuilder ret = new StringBuilder();
for (char c : path.toCharArray())
{
if ((c == ',') || (c == ':'))
{
ret.append(File.pathSeparatorChar);
}
else
{
ret.append(c);
}
}
return ret.toString();
}
}

View File

@ -14,30 +14,8 @@
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.maven.AbstractHomeForker;
/**
* JettyHomeBaseForker
@ -45,121 +23,13 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
* Unpacks a jetty-home and configures it with a base that allows it
* to run an unassembled webapp.
*/
public class JettyHomeForker extends AbstractForker
public class JettyHomeForker extends AbstractHomeForker
{
protected MavenWebAppContext webApp;
protected String contextXml;
/**
* Location of existing jetty home directory
*/
protected File jettyHome;
/**
* Zip of jetty-home
*/
protected File jettyHomeZip;
/**
* Location of existing jetty base directory
*/
protected File jettyBase;
protected File baseDir;
/**
* Optional list of other modules to
* activate.
*/
protected String[] modules;
/*
* Optional jetty commands
*/
protected String jettyOptions;
protected List<File> libExtJarFiles;
protected Path modulesPath;
protected Path etcPath;
protected Path libPath;
protected Path webappPath;
protected Path mavenLibPath;
protected String version;
public void setJettyOptions(String jettyOptions)
public JettyHomeForker()
{
this.jettyOptions = jettyOptions;
}
public String getJettyOptions()
{
return jettyOptions;
}
public List<File> getLibExtJarFiles()
{
return libExtJarFiles;
}
public void setLibExtJarFiles(List<File> libExtJarFiles)
{
this.libExtJarFiles = libExtJarFiles;
}
public File getJettyHome()
{
return jettyHome;
}
public void setJettyHome(File jettyHome)
{
this.jettyHome = jettyHome;
}
public File getJettyBase()
{
return jettyBase;
}
public void setJettyBase(File jettyBase)
{
this.jettyBase = jettyBase;
}
public String[] getModules()
{
return modules;
}
public void setModules(String[] modules)
{
this.modules = modules;
}
public String getContextXmlFile()
{
return contextXml;
}
public void setContextXml(String contextXml)
{
this.contextXml = contextXml;
}
public File getJettyHomeZip()
{
return jettyHomeZip;
}
public void setJettyHomeZip(File jettyHomeZip)
{
this.jettyHomeZip = jettyHomeZip;
}
public MavenWebAppContext getWebApp()
{
return webApp;
environment = "ee10";
}
public void setWebApp(MavenWebAppContext webApp)
@ -177,106 +47,6 @@ public class JettyHomeForker extends AbstractForker
this.baseDir = baseDir;
}
@Override
protected ProcessBuilder createCommand()
{
List<String> cmd = new ArrayList<>();
cmd.add("java");
//add any args to the jvm
if (StringUtil.isNotBlank(jvmArgs))
{
Arrays.stream(jvmArgs.split(" ")).filter(StringUtil::isNotBlank).forEach((a) -> cmd.add(a.trim()));
}
cmd.add("-jar");
cmd.add(new File(jettyHome, "start.jar").getAbsolutePath());
if (systemProperties != null)
{
for (Map.Entry<String, String> e : systemProperties.entrySet())
{
cmd.add("-D" + e.getKey() + "=" + e.getValue());
}
}
cmd.add("-DSTOP.PORT=" + stopPort);
if (stopKey != null)
cmd.add("-DSTOP.KEY=" + stopKey);
//set up enabled jetty modules
StringBuilder tmp = new StringBuilder();
tmp.append("--module=");
tmp.append("server,http,ee10-webapp,ee10-deploy");
if (modules != null)
{
for (String m : modules)
{
if (tmp.indexOf(m) < 0)
tmp.append("," + m);
}
}
if (libExtJarFiles != null && !libExtJarFiles.isEmpty() && tmp.indexOf("ext") < 0)
tmp.append(",ext");
tmp.append(",ee10-maven");
cmd.add(tmp.toString());
//put any other jetty options onto the command line
if (StringUtil.isNotBlank(jettyOptions))
{
Arrays.stream(jettyOptions.split(" ")).filter(StringUtil::isNotBlank).forEach((a) -> cmd.add(a.trim()));
}
//put any jetty properties onto the command line
if (jettyProperties != null)
{
for (Map.Entry<String, String> e : jettyProperties.entrySet())
{
cmd.add(e.getKey() + "=" + e.getValue());
}
}
//existence of this file signals process started
cmd.add("jetty.token.file=" + tokenFile.getAbsolutePath().toString());
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(workDir);
PluginLog.getLog().info("Home process starting");
//set up extra environment vars if there are any
if (!env.isEmpty())
builder.environment().putAll(env);
if (waitForChild)
builder.inheritIO();
else
{
builder.redirectOutput(jettyOutputFile);
builder.redirectErrorStream(true);
}
return builder;
}
@Override
public void doStart() throws Exception
{
//set up a jetty-home
configureJettyHome();
if (jettyHome == null || !jettyHome.exists())
throw new IllegalStateException("No jetty home");
//set up a jetty-base
configureJettyBase();
//convert the webapp to properties
generateWebAppPropertiesFile();
super.doStart();
}
protected void redeployWebApp()
throws Exception
{
@ -284,142 +54,9 @@ public class JettyHomeForker extends AbstractForker
webappPath.resolve("maven.xml").toFile().setLastModified(System.currentTimeMillis());
}
private void generateWebAppPropertiesFile()
protected void generateWebAppPropertiesFile()
throws Exception
{
WebAppPropertyConverter.toProperties(webApp, etcPath.resolve("maven.props").toFile(), contextXml);
}
/**
* Create or configure a jetty base.
*/
private void configureJettyBase() throws Exception
{
if (jettyBase != null && !jettyBase.exists())
throw new IllegalStateException(jettyBase.getAbsolutePath() + " does not exist");
File targetJettyBase = new File(baseDir, "jetty-base");
Path targetBasePath = targetJettyBase.toPath();
if (Files.exists(targetBasePath))
IO.delete(targetJettyBase);
targetJettyBase.mkdirs();
//jetty-base will be the working directory for the forked command
workDir = targetJettyBase;
//if there is an existing jetty base, copy parts of it
if (jettyBase != null)
{
Path jettyBasePath = jettyBase.toPath();
final File contextXmlFile = (contextXml == null ? null : FileSystems.getDefault().getPath(contextXml).toFile());
//copy the existing jetty base
Files.walkFileTree(jettyBasePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path targetDir = targetBasePath.resolve(jettyBasePath.relativize(dir));
try
{
Files.copy(dir, targetDir);
}
catch (FileAlreadyExistsException e)
{
if (!Files.isDirectory(targetDir)) //ignore attempt to recreate dir
throw e;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
if (contextXmlFile != null && Files.isSameFile(contextXmlFile.toPath(), file))
return FileVisitResult.CONTINUE; //skip copying the context xml file
Files.copy(file, targetBasePath.resolve(jettyBasePath.relativize(file)));
return FileVisitResult.CONTINUE;
}
});
}
//make the jetty base structure
modulesPath = Files.createDirectories(targetBasePath.resolve("modules"));
etcPath = Files.createDirectories(targetBasePath.resolve("etc"));
libPath = Files.createDirectories(targetBasePath.resolve("lib"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps"));
mavenLibPath = Files.createDirectories(libPath.resolve("maven-ee10"));
//copy in the jetty-maven-plugin jar
URI thisJar = TypeUtil.getLocationOfClass(this.getClass());
if (thisJar == null)
throw new IllegalStateException("Can't find jar for jetty-ee10-maven-plugin");
try (InputStream jarStream = thisJar.toURL().openStream();
FileOutputStream fileStream = new FileOutputStream(mavenLibPath.resolve("plugin.jar").toFile()))
{
IO.copy(jarStream, fileStream);
}
// copy in the maven.xml webapp file
try (InputStream mavenXmlStream = getClass().getClassLoader().getResourceAsStream("maven-ee10.xml");
FileOutputStream fileStream = new FileOutputStream(webappPath.resolve("maven-ee10.xml").toFile()))
{
IO.copy(mavenXmlStream, fileStream);
}
Files.writeString(webappPath.resolve("maven-ee10.properties"), "environment=ee10");
//copy in the maven.mod file
try (InputStream mavenModStream = getClass().getClassLoader().getResourceAsStream("ee10-maven.mod");
FileOutputStream fileStream = new FileOutputStream(modulesPath.resolve("ee10-maven.mod").toFile()))
{
IO.copy(mavenModStream, fileStream);
}
//copy in the jetty-maven.xml file
try (InputStream jettyMavenStream = getClass().getClassLoader().getResourceAsStream("jetty-ee10-maven.xml");
FileOutputStream fileStream = new FileOutputStream(etcPath.resolve("jetty-ee10-maven.xml").toFile()))
{
IO.copy(jettyMavenStream, fileStream);
}
//if there were plugin dependencies, copy them into lib/ext
if (libExtJarFiles != null && !libExtJarFiles.isEmpty())
{
Path libExtPath = Files.createDirectories(libPath.resolve("ext"));
for (File f : libExtJarFiles)
{
try (InputStream jarStream = new FileInputStream(f);
FileOutputStream fileStream = new FileOutputStream(libExtPath.resolve(f.getName()).toFile()))
{
IO.copy(jarStream, fileStream);
}
}
}
}
private void configureJettyHome()
throws Exception
{
if (jettyHome == null && jettyHomeZip == null)
throw new IllegalStateException("No jettyHome");
if (baseDir == null)
throw new IllegalStateException("No baseDir");
if (jettyHome == null)
{
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
{
Resource res = resourceFactory.newJarFileResource(jettyHomeZip.toPath().toUri());
res.copyTo(baseDir.toPath()); // TODO: Resource.copyTo() cannot copy dir to dir, only file to file
}
//zip will unpack to target/jetty-home-<VERSION>
jettyHome = new File(baseDir, "jetty-home-" + version);
}
}
}

View File

@ -28,6 +28,7 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.maven.ConsoleReader;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.component.LifeCycle;
@ -355,7 +356,7 @@ public class JettyRunMojo extends AbstractUnassembledWebAppMojo
}
}
embedder.getWebApp().stop();
embedder.stopWebApp();
configureWebApp();
embedder.redeployWebApp();
if (scanner != null)

View File

@ -25,6 +25,7 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.jetty.maven.ConsoleReader;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.StringUtil;
@ -235,7 +236,7 @@ public class JettyRunWarMojo extends AbstractWebAppMojo
warArtifacts = null;
configureScanner();
}
embedder.getWebApp().stop();
embedder.stopWebApp();
configureWebApp();
embedder.redeployWebApp();
scanner.start();

View File

@ -1,220 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
/**
* MavenResource
*
* A helper class to allow Resources to be used in maven pom.xml configuration by
* providing a no-arg constructor and a setter that accepts a simple string as a
* file location. This class delegates to a real Resource obtained using a
* ResourceFactory.
*/
public class MavenResource extends Resource
{
private static final ResourceFactory __resourceFactory = ResourceFactory.root();
private String _resourceString;
private Resource _resource;
public MavenResource()
{
}
@Override
public void copyTo(Path destination) throws IOException
{
if (_resource == null)
return;
_resource.copyTo(destination);
}
@Override
public boolean exists()
{
if (_resource == null)
return false;
return _resource.exists();
}
@Override
public Collection<Resource> getAllResources()
{
if (_resource == null)
return null;
return _resource.getAllResources();
}
@Override
public String getFileName()
{
if (_resource == null)
return null;
return _resource.getFileName();
}
@Override
public String getName()
{
if (_resource == null)
return null;
return _resource.getName();
}
@Override
public Path getPath()
{
if (_resource == null)
return null;
return _resource.getPath();
}
@Override
public URI getRealURI()
{
if (_resource == null)
return null;
return _resource.getRealURI();
}
public String getResourceAsString()
{
return _resourceString;
}
public void setResourceAsString(String resourceString)
{
_resourceString = resourceString;
_resource = __resourceFactory.newResource(_resourceString);
}
@Override
public URI getURI()
{
if (_resource == null)
return null;
return _resource.getURI();
}
@Override
public boolean isAlias()
{
if (_resource == null)
return false;
return _resource.isAlias();
}
@Override
public boolean isContainedIn(Resource container)
{
if (_resource == null)
return false;
return _resource.isContainedIn(container);
}
@Override
public boolean contains(Resource other)
{
if (_resource == null)
return false;
return _resource.contains(other);
}
@Override
public Path getPathTo(Resource other)
{
if (_resource == null)
return null;
return _resource.getPathTo(other);
}
@Override
public boolean isDirectory()
{
if (_resource == null)
return false;
return _resource.isDirectory();
}
@Override
public boolean isReadable()
{
if (_resource == null)
return false;
return _resource.isReadable();
}
@Override
public Iterator<Resource> iterator()
{
if (_resource == null)
return null;
return _resource.iterator();
}
@Override
public Instant lastModified()
{
if (_resource == null)
return null;
return _resource.lastModified();
}
@Override
public long length()
{
if (_resource == null)
return -1;
return _resource.length();
}
@Override
public List<Resource> list()
{
if (_resource == null)
return null;
return _resource.list();
}
@Override
public InputStream newInputStream() throws IOException
{
if (_resource == null)
return null;
return _resource.newInputStream();
}
@Override
public Resource resolve(String subUriPath)
{
if (_resource == null)
return null;
return _resource.resolve(subUriPath);
}
}

View File

@ -1,221 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* MavenServerConnector
*
* As the ServerConnector class does not have a no-arg constructor, and moreover requires
* the server instance passed in to all its constructors, it cannot
* be referenced in the pom.xml. This class wraps a ServerConnector, delaying setting the
* server instance. Only a few of the setters from the ServerConnector class are supported.
*/
public class MavenServerConnector extends ContainerLifeCycle implements Connector
{
public static String PORT_SYSPROPERTY = "jetty.http.port";
public static final int DEFAULT_PORT = 8080;
public static final String DEFAULT_PORT_STR = String.valueOf(DEFAULT_PORT);
public static final int DEFAULT_MAX_IDLE_TIME = 30000;
private Server server;
private volatile ServerConnector delegate;
private String host;
private String name;
private int port;
private long idleTimeout;
public MavenServerConnector()
{
}
public void setServer(Server server)
{
this.server = server;
}
public void setHost(String host)
{
this.host = host;
}
public String getHost()
{
return this.host;
}
public void setPort(int port)
{
this.port = port;
}
public int getPort()
{
return this.port;
}
public void setName(String name)
{
this.name = name;
}
public void setIdleTimeout(long idleTimeout)
{
this.idleTimeout = idleTimeout;
}
@Override
protected void doStart() throws Exception
{
if (this.server == null)
throw new IllegalStateException("Server not set for MavenServerConnector");
this.delegate = new ServerConnector(this.server);
this.delegate.setName(this.name);
this.delegate.setPort(this.port);
this.delegate.setHost(this.host);
this.delegate.setIdleTimeout(idleTimeout);
this.delegate.start();
super.doStart();
}
@Override
protected void doStop() throws Exception
{
this.delegate.stop();
super.doStop();
this.delegate = null;
}
@Override
public CompletableFuture<Void> shutdown()
{
return checkDelegate().shutdown();
}
@Override
public boolean isShutdown()
{
return checkDelegate().isShutdown();
}
@Override
public Server getServer()
{
return this.server;
}
@Override
public Executor getExecutor()
{
return checkDelegate().getExecutor();
}
@Override
public Scheduler getScheduler()
{
return checkDelegate().getScheduler();
}
@Override
public ByteBufferPool getByteBufferPool()
{
return checkDelegate().getByteBufferPool();
}
@Override
public ConnectionFactory getConnectionFactory(String nextProtocol)
{
return checkDelegate().getConnectionFactory(nextProtocol);
}
@Override
public <T> T getConnectionFactory(Class<T> factoryType)
{
return checkDelegate().getConnectionFactory(factoryType);
}
@Override
public ConnectionFactory getDefaultConnectionFactory()
{
return checkDelegate().getDefaultConnectionFactory();
}
@Override
public Collection<ConnectionFactory> getConnectionFactories()
{
return checkDelegate().getConnectionFactories();
}
@Override
public List<String> getProtocols()
{
return checkDelegate().getProtocols();
}
@Override
@ManagedAttribute("maximum time a connection can be idle before being closed (in ms)")
public long getIdleTimeout()
{
return checkDelegate().getIdleTimeout();
}
@Override
public Object getTransport()
{
return checkDelegate().getTransport();
}
@Override
public Collection<EndPoint> getConnectedEndPoints()
{
return checkDelegate().getConnectedEndPoints();
}
@Override
public String getName()
{
return this.name;
}
public int getLocalPort()
{
return this.delegate.getLocalPort();
}
private ServerConnector checkDelegate() throws IllegalStateException
{
ServerConnector d = this.delegate;
if (d == null)
throw new IllegalStateException("MavenServerConnector delegate not ready");
return d;
}
}

View File

@ -36,6 +36,7 @@ import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.Configurations;
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.maven.Overlay;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;

View File

@ -1,103 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import org.eclipse.jetty.util.resource.Resource;
/**
* Overlay
*
* An Overlay represents overlay information derived from the
* maven-war-plugin.
*/
public class Overlay
{
private OverlayConfig _config;
private Resource _resource;
public Overlay(OverlayConfig config, Resource resource)
{
_config = config;
_resource = resource;
}
public Overlay(OverlayConfig config)
{
_config = config;
}
public void setResource(Resource r)
{
_resource = r;
}
public Resource getResource()
{
return _resource;
}
public OverlayConfig getConfig()
{
return _config;
}
@Override
public String toString()
{
StringBuilder strbuff = new StringBuilder();
if (_resource != null)
strbuff.append(_resource);
if (_config != null)
{
strbuff.append(" [");
strbuff.append(_config);
strbuff.append("]");
}
return strbuff.toString();
}
/**
* Unpack the overlay into the given directory. Only
* unpack if the directory does not exist, or the overlay
* has been modified since the dir was created.
*
* @param dir the directory into which to unpack the overlay
* @throws IOException
*/
public void unpackTo(File dir) throws IOException // TODO: change to Path
{
if (dir == null)
throw new IllegalStateException("No overly unpack directory");
Path pathDir = dir.toPath();
// only unpack if the overlay is newer
if (!Files.exists(pathDir))
{
// create directory
Files.createDirectories(pathDir);
getResource().copyTo(pathDir);
}
else
{
Instant dirLastModified = Files.getLastModifiedTime(pathDir).toInstant();
if (getResource().lastModified().isAfter(dirLastModified))
getResource().copyTo(pathDir);
}
}
}

View File

@ -1,337 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.jetty.util.StringUtil;
/**
* OverlayConfig
*
* The configuration of a war overlay in a pom. Used to help determine which resources
* from a project's dependent war should be included.
*/
public class OverlayConfig
{
private String targetPath;
private String groupId;
private String artifactId;
private String classifier;
private List<String> includes;
private List<String> excludes;
private boolean skip;
private boolean filtered;
public OverlayConfig()
{
}
public OverlayConfig(String fmt, List<String> defaultIncludes, List<String> defaultExcludes)
{
if (fmt == null)
return;
String[] atoms = StringUtil.csvSplit(fmt);
for (int i = 0; i < atoms.length; i++)
{
String s = atoms[i].trim();
switch (i)
{
case 0:
{
if (!"".equals(s))
groupId = s;
break;
}
case 1:
{
if (!"".equals(s))
artifactId = s;
break;
}
case 2:
{
if (!"".equals(s))
classifier = s;
break;
}
case 3:
{
if (!"".equals(s))
targetPath = s;
break;
}
case 4:
{
if ("".equals(s))
skip = false;
else
skip = Boolean.valueOf(s);
break;
}
case 5:
{
if ("".equals(s))
filtered = false;
else
filtered = Boolean.valueOf(s);
break;
}
case 6:
{
if ("".equals(s))
break;
String[] incs = s.split(";");
if (incs.length > 0)
includes = Arrays.asList(incs);
break;
}
case 7:
{
if ("".equals(s))
break;
String[] exs = s.split(";");
if (exs.length > 0)
excludes = Arrays.asList(exs);
break;
}
default:
break;
}
}
}
public OverlayConfig(Xpp3Dom root, List<String> defaultIncludes, List<String> defaultExcludes)
{
Xpp3Dom node = root.getChild("groupId");
setGroupId(node == null ? null : node.getValue());
node = root.getChild("artifactId");
setArtifactId(node == null ? null : node.getValue());
node = root.getChild("classifier");
setClassifier(node == null ? null : node.getValue());
node = root.getChild("targetPath");
setTargetPath(node == null ? null : node.getValue());
node = root.getChild("skip");
setSkip(node == null ? false : Boolean.valueOf(node.getValue()));
node = root.getChild("filtered");
setFiltered(node == null ? false : Boolean.valueOf(node.getValue()));
node = root.getChild("includes");
List<String> includes = null;
if (node != null && node.getChildCount() > 0)
{
Xpp3Dom[] list = node.getChildren("include");
for (int j = 0; list != null && j < list.length; j++)
{
if (includes == null)
includes = new ArrayList<>();
includes.add(list[j].getValue());
}
}
if (includes == null && defaultIncludes != null)
{
includes = new ArrayList<>();
includes.addAll(defaultIncludes);
}
setIncludes(includes);
node = root.getChild("excludes");
List<String> excludes = null;
if (node != null && node.getChildCount() > 0)
{
Xpp3Dom[] list = node.getChildren("exclude");
for (int j = 0; list != null && j < list.length; j++)
{
if (excludes == null)
excludes = new ArrayList<>();
excludes.add(list[j].getValue());
}
}
if (excludes == null && defaultExcludes != null)
{
excludes = new ArrayList<>();
excludes.addAll(defaultExcludes);
}
setExcludes(excludes);
}
public String getTargetPath()
{
return targetPath;
}
public void setTargetPath(String targetPath)
{
this.targetPath = targetPath;
}
public String getGroupId()
{
return groupId;
}
public void setGroupId(String groupId)
{
this.groupId = groupId;
}
public String getArtifactId()
{
return artifactId;
}
public void setArtifactId(String artifactId)
{
this.artifactId = artifactId;
}
public String getClassifier()
{
return classifier;
}
public void setClassifier(String classifier)
{
this.classifier = classifier;
}
public List<String> getIncludes()
{
return includes;
}
public void setIncludes(List<String> includes)
{
this.includes = includes;
}
public List<String> getExcludes()
{
return excludes;
}
public void setExcludes(List<String> excludes)
{
this.excludes = excludes;
}
public boolean isSkip()
{
return skip;
}
public void setSkip(boolean skip)
{
this.skip = skip;
}
public boolean isFiltered()
{
return filtered;
}
public void setFiltered(boolean filtered)
{
this.filtered = filtered;
}
public boolean isCurrentProject()
{
if (this.groupId == null && this.artifactId == null)
return true;
return false;
}
/**
* Check if this overlay configuration matches an Artifact's info
*
* @param gid Artifact groupId
* @param aid Artifact artifactId
* @param cls Artifact classifier
* @return true if matched
*/
public boolean matchesArtifact(String gid, String aid, String cls)
{
if (((getGroupId() == null && gid == null) || (getGroupId() != null && getGroupId().equals(gid))) &&
((getArtifactId() == null && aid == null) || (getArtifactId() != null && getArtifactId().equals(aid))) &&
((getClassifier() == null) || (getClassifier().equals(cls))))
return true;
return false;
}
/**
* Check if this overlay configuration matches an Artifact's info
*
* @param gid the group id
* @param aid the artifact id
* @return true if matched
*/
public boolean matchesArtifact(String gid, String aid)
{
if (((getGroupId() == null && gid == null) || (getGroupId() != null && getGroupId().equals(gid))) &&
((getArtifactId() == null && aid == null) || (getArtifactId() != null && getArtifactId().equals(aid))))
return true;
return false;
}
@Override
public String toString()
{
StringBuilder strbuff = new StringBuilder();
strbuff.append((groupId != null ? groupId : "") + ",");
strbuff.append((artifactId != null ? artifactId : "") + ",");
strbuff.append((classifier != null ? classifier : "") + ",");
strbuff.append((targetPath != null ? targetPath : "") + ",");
strbuff.append("" + skip + ",");
strbuff.append("" + filtered + ",");
if (includes != null)
{
Iterator<String> itor = includes.iterator();
while (itor.hasNext())
{
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(";");
}
}
strbuff.append(", ");
if (excludes != null)
{
Iterator<String> itor = excludes.iterator();
while (itor.hasNext())
{
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(";");
}
}
return strbuff.toString();
}
}

View File

@ -1,163 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.MountedPathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
/**
* OverlayManager
*
* Mediates information about overlays configured in a war plugin.
*
*/
public class OverlayManager
{
private WarPluginInfo warPlugin;
public OverlayManager(WarPluginInfo warPlugin)
{
this.warPlugin = warPlugin;
}
public void applyOverlays(MavenWebAppContext webApp) throws IOException
{
Objects.requireNonNull(webApp);
List<Resource> resourceBases = new ArrayList<Resource>();
for (Overlay o : getOverlays(webApp))
{
//can refer to the current project in list of overlays for ordering purposes
if (o.getConfig() != null && o.getConfig().isCurrentProject() && webApp.getBaseResource().exists())
{
resourceBases.add(webApp.getBaseResource());
continue;
}
//add in the selectively unpacked overlay in the correct order to the webapp's resource base
resourceBases.add(unpackOverlay(webApp, o));
}
if (!resourceBases.contains(webApp.getBaseResource()) && webApp.getBaseResource().exists())
{
if (webApp.getBaseAppFirst())
resourceBases.add(0, webApp.getBaseResource());
else
resourceBases.add(webApp.getBaseResource());
}
webApp.setBaseResource(ResourceFactory.combine(resourceBases));
}
/**
* Generate an ordered list of overlays
*/
protected List<Overlay> getOverlays(WebAppContext webApp)
{
Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
List<Overlay> overlays = new ArrayList<Overlay>();
//Check all of the overlay configurations
for (OverlayConfig config:warPlugin.getMavenWarOverlayConfigs())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = warPlugin.getWarArtifact(config.getGroupId(), config.getArtifactId(), config.getClassifier());
if (a != null)
{
matchedWarArtifacts.add(a);
Resource resource = webApp.getResourceFactory().newJarFileResource(a.getFile().toPath().toUri());
SelectiveJarResource r = new SelectiveJarResource(resource);
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts add them
for (Artifact a: warPlugin.getWarArtifacts())
{
if (!matchedWarArtifacts.contains(a))
{
Resource resource = webApp.getResourceFactory().newJarFileResource(a.getFile().toPath().toUri());
Overlay overlay = new Overlay(null, resource);
overlays.add(overlay);
}
}
return overlays;
}
/**
* Unpack a war overlay.
*
* @param overlay the war overlay to unpack
* @return the location to which it was unpacked
* @throws IOException if there is an IO problem
*/
protected Resource unpackOverlay(WebAppContext webApp, Overlay overlay)
throws IOException
{
Objects.requireNonNull(webApp);
if (overlay.getResource() == null)
return null; //nothing to unpack
//Get the name of the overlayed war and unpack it to a dir of the
//same name in the temporary directory.
//We know it is a war because it came from the maven repo
assert overlay.getResource() instanceof MountedPathResource;
Path p = Paths.get(URIUtil.unwrapContainer(overlay.getResource().getURI()));
String name = p.getName(p.getNameCount() - 1).toString();
name = name.replace('.', '_');
File overlaysDir = new File(warPlugin.getProject().getBuild().getDirectory(), "jetty_overlays");
File dir = new File(overlaysDir, name);
//if specified targetPath, unpack to that subdir instead
File unpackDir = dir;
if (overlay.getConfig() != null && overlay.getConfig().getTargetPath() != null)
unpackDir = new File(dir, overlay.getConfig().getTargetPath());
overlay.unpackTo(unpackDir);
//use top level of unpacked content
return webApp.getResourceFactory().newResource(unpackDir.getCanonicalPath());
}
}

View File

@ -1,37 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import org.apache.maven.plugin.logging.Log;
/**
* PluginLog
*
* Convenience class to provide access to the plugin
* Log for non-mojo classes.
*/
public class PluginLog
{
private static Log log = null;
public static void setLog(Log l)
{
log = l;
}
public static Log getLog()
{
return log;
}
}

View File

@ -18,6 +18,8 @@ import java.nio.file.Path;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.ee10.webapp.Configurations;
import org.eclipse.jetty.maven.ServerSupport;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
@ -148,7 +150,7 @@ public class QuickStartGenerator
//ensure handler structure enabled
ServerSupport.configureHandlers(server, null, null);
ServerSupport.configureDefaultConfigurationClasses(server);
Configurations.setServerDefault(server);
//if our server has a thread pool associated we can do annotation scanning multithreaded,
//otherwise scanning will be single threaded

View File

@ -1,48 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.util.Collections;
import java.util.List;
/**
* ScanPattern
*
* Ant-style pattern of includes and excludes.
*/
public class ScanPattern
{
private List<String> _includes = Collections.emptyList();
private List<String> _excludes = Collections.emptyList();
public void setIncludes(List<String> includes)
{
_includes = includes;
}
public void setExcludes(List<String> excludes)
{
_excludes = excludes;
}
public List<String> getIncludes()
{
return _includes;
}
public List<String> getExcludes()
{
return _excludes;
}
}

View File

@ -1,108 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.util.IncludeExcludeSet;
/**
* ScanTargetPattern
*
* Utility class to provide the ability for the mvn jetty:run
* mojo to be able to specify filesets of extra files to
* regularly scan for changes in order to redeploy the webapp.
*
* For example:
*
* &lt;scanTargetPattern&gt;
* &lt;directory&gt;/some/place&lt;/directory&gt;
* &lt;includes&gt;
* &lt;include&gt;some ant pattern here &lt;/include&gt;
* &lt;include&gt;some ant pattern here &lt;/include&gt;
* &lt;/includes&gt;
* &lt;excludes&gt;
* &lt;exclude&gt;some ant pattern here &lt;/exclude&gt;
* &lt;exclude&gt;some ant pattern here &lt;/exclude&gt;
* &lt;/excludes&gt;
* &lt;/scanTargetPattern&gt;
*/
public class ScanTargetPattern
{
private File _directory;
private ScanPattern _pattern;
/**
* Get the _directory.
* @return the _directory
*/
public File getDirectory()
{
return _directory;
}
/**
* Set the directory to set.
* @param directory the directory to set
*/
public void setDirectory(File directory)
{
this._directory = directory;
}
public void setIncludes(List<String> includes)
{
if (_pattern == null)
_pattern = new ScanPattern();
_pattern.setIncludes(includes);
}
public void setExcludes(List<String> excludes)
{
if (_pattern == null)
_pattern = new ScanPattern();
_pattern.setExcludes(excludes);
}
public List<String> getIncludes()
{
return (_pattern == null ? Collections.emptyList() : _pattern.getIncludes());
}
public List<String> getExcludes()
{
return (_pattern == null ? Collections.emptyList() : _pattern.getExcludes());
}
public void configureIncludesExcludeSet(IncludeExcludeSet<PathMatcher, Path> includesExcludes)
{
for (String include:getIncludes())
{
if (!include.startsWith("glob:"))
include = "glob:" + include;
includesExcludes.include(_directory.toPath().getFileSystem().getPathMatcher(include));
}
for (String exclude:getExcludes())
{
if (!exclude.startsWith("glob:"))
exclude = "glob:" + exclude;
includesExcludes.exclude(_directory.toPath().getFileSystem().getPathMatcher(exclude));
}
}
}

View File

@ -1,280 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.codehaus.plexus.util.SelectorUtils;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* SelectiveJarResource
*
* Selectively copies resources from a jar file based on includes/excludes.
* TODO: investigate if copyTo() can instead have an IncludeExcludeSet as a parameter?
* TODO: or have a smaller ResourceWrapper jetty-core class that can be overridden for specific behavior like in this class
*/
public class SelectiveJarResource extends Resource
{
private static final Logger LOG = LoggerFactory.getLogger(SelectiveJarResource.class);
/**
* Default matches every resource.
*/
public static final List<String> DEFAULT_INCLUDES =
Arrays.asList(new String[]{"**"});
/**
* Default is to exclude nothing.
*/
public static final List<String> DEFAULT_EXCLUDES = Collections.emptyList();
final Resource _delegate;
List<String> _includes = null;
List<String> _excludes = null;
boolean _caseSensitive = false;
public SelectiveJarResource(Resource resource)
{
_delegate = resource;
}
public void setCaseSensitive(boolean caseSensitive)
{
_caseSensitive = caseSensitive;
}
public void setIncludes(List<String> patterns)
{
_includes = patterns;
}
public void setExcludes(List<String> patterns)
{
_excludes = patterns;
}
protected boolean isIncluded(String name)
{
for (String include : _includes)
{
if (SelectorUtils.matchPath(include, name, "/", _caseSensitive))
{
return true;
}
}
return false;
}
protected boolean isExcluded(String name)
{
for (String exclude : _excludes)
{
if (SelectorUtils.matchPath(exclude, name, "/", _caseSensitive))
{
return true;
}
}
return false;
}
@Override
public Path getPath()
{
return _delegate.getPath();
}
@Override
public boolean isDirectory()
{
return _delegate.isDirectory();
}
@Override
public Instant lastModified()
{
return _delegate.lastModified();
}
@Override
public boolean isReadable()
{
return _delegate.isReadable();
}
@Override
public boolean isContainedIn(Resource container)
{
return _delegate.isContainedIn(container);
}
@Override
public boolean contains(Resource other)
{
return _delegate.contains(other);
}
@Override
public Path getPathTo(Resource other)
{
return _delegate.getPathTo(other);
}
@Override
public URI getURI()
{
return _delegate.getURI();
}
@Override
public String getName()
{
return _delegate.getName();
}
@Override
public String getFileName()
{
return _delegate.getFileName();
}
@Override
public Resource resolve(String subUriPath)
{
return _delegate.resolve(subUriPath);
}
@Override
public void copyTo(Path directory) throws IOException
{
if (_includes == null)
_includes = DEFAULT_INCLUDES;
if (_excludes == null)
_excludes = DEFAULT_EXCLUDES;
//Copy contents of the jar file to the given directory,
//using the includes and excludes patterns to control which
//parts of the jar file are copied
if (!exists())
return;
String urlString = this.getURI().toASCIIString().trim();
int endOfJarUrl = urlString.indexOf("!/");
int startOfJarUrl = (endOfJarUrl >= 0 ? 4 : 0);
if (endOfJarUrl < 0)
throw new IOException("Not a valid jar url: " + urlString);
URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
try (InputStream is = jarFileURL.openConnection().getInputStream();
JarInputStream jin = new JarInputStream(is))
{
JarEntry entry;
while ((entry = jin.getNextJarEntry()) != null)
{
String entryName = entry.getName();
LOG.debug("Looking at {}", entryName);
// make sure no access out of the root entry is present
if (URIUtil.isNotNormalWithinSelf(entryName))
{
LOG.info("Invalid entry: {}", entryName);
continue;
}
Path file = directory.resolve(entryName);
if (entry.isDirectory())
{
if (isIncluded(entryName))
{
if (!isExcluded(entryName))
{
// Make directory
if (!Files.exists(file))
Files.createDirectories(file);
}
else
LOG.debug("{} dir is excluded", entryName);
}
else
LOG.debug("{} dir is NOT included", entryName);
}
else
{
//entry is a file, is it included?
if (isIncluded(entryName))
{
if (!isExcluded(entryName))
{
// make directory (some jars don't list dirs)
Path dir = file.getParent();
if (!Files.exists(dir))
Files.createDirectories(dir);
// Make file
try (OutputStream fout = Files.newOutputStream(file))
{
IO.copy(jin, fout);
}
// touch the file.
if (entry.getTime() >= 0)
Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
}
else
LOG.debug("{} file is excluded", entryName);
}
else
LOG.debug("{} file is NOT included", entryName);
}
}
Manifest manifest = jin.getManifest();
if (manifest != null)
{
if (isIncluded("META-INF") && !isExcluded("META-INF"))
{
Path metaInf = directory.resolve("META-INF");
Files.createDirectory(metaInf);
Path f = metaInf.resolve("MANIFEST.MF");
try (OutputStream fout = Files.newOutputStream(f))
{
manifest.write(fout);
}
}
}
}
}
}

View File

@ -1,111 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.Writer;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
import org.eclipse.jetty.util.component.LifeCycle;
/**
* ServerConnectorListener
*
* This is for test support, where we need jetty to run on a random port, and we need
* a client to be able to find out which port was picked.
*/
public class ServerConnectorListener extends AbstractLifeCycleListener
{
private String _fileName;
private String _sysPropertyName;
@Override
public void lifeCycleStarted(LifeCycle event)
{
if (getFileName() != null)
{
try
{
Path tmp = Files.createTempFile("jettyport", ".tmp");
try (Writer writer = Files.newBufferedWriter(tmp))
{
writer.write(String.valueOf(((ServerConnector)event).getLocalPort()));
}
Path path = Paths.get(getFileName());
Files.deleteIfExists(path);
try
{
Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE);
}
catch (AtomicMoveNotSupportedException e) // can append on some os (windows).. so try again without the option
{
Files.move(tmp, path);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
if (getSysPropertyName() != null)
{
System.setProperty(_sysPropertyName, String.valueOf(((ServerConnector)event).getLocalPort()));
}
super.lifeCycleStarted(event);
}
/**
* Get the file name.
* @return the file name
*/
public String getFileName()
{
return _fileName;
}
/**
* Set the file name to set.
* @param name the file name to set
*/
public void setFileName(String name)
{
_fileName = name;
}
/**
* Get the sysPropertyName.
* @return the sysPropertyName
*/
public String getSysPropertyName()
{
return _sysPropertyName;
}
/**
* Set the sysPropertyName to set.
* @param sysPropertyName the sysPropertyName to set
*/
public void setSysPropertyName(String sysPropertyName)
{
_sysPropertyName = sysPropertyName;
}
}

View File

@ -1,59 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.jetty.util.component.LifeCycle;
/**
* ServerListener
*
* Listener to create a file that signals that the startup is completed.
* Used by the JettyRunHome maven goal to determine that the child
* process is started, and that jetty is ready.
*/
public class ServerListener implements LifeCycle.Listener
{
private String _tokenFile;
public void setTokenFile(String file)
{
_tokenFile = file;
}
public String getTokenFile()
{
return _tokenFile;
}
@Override
public void lifeCycleStarted(LifeCycle event)
{
if (_tokenFile != null)
{
try
{
// Using Path, as we need to reliably create/write a file.
Path path = Path.of(_tokenFile);
Files.createFile(path);
}
catch (Exception e)
{
throw new IllegalStateException(e);
}
}
}
}

View File

@ -1,236 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.ee10.webapp.Configurations;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
* ServerSupport
*
* Helps configure the Server instance.
*/
public class ServerSupport
{
public static void configureDefaultConfigurationClasses(Server server)
{
Configurations.setServerDefault(server);
}
/**
* Set up the handler structure to receive a webapp.
* Also put in a DefaultHandler so we get a nicer page
* than a 404 if we hit the root and the webapp's
* context isn't at root.
*
* @param server the server to use
* @param contextHandlers the context handlers to include
* @param requestLog a request log to use
*/
public static void configureHandlers(Server server, List<ContextHandler> contextHandlers, RequestLog requestLog)
{
if (server == null)
throw new IllegalArgumentException("Server is null");
if (requestLog != null)
server.setRequestLog(requestLog);
ContextHandlerCollection contexts = findContextHandlerCollection(server);
if (contexts == null)
{
contexts = new ContextHandlerCollection();
server.setHandler(contexts);
}
if (contextHandlers != null)
{
for (ContextHandler context:contextHandlers)
{
contexts.addHandler(context);
}
}
}
/**
* Configure at least one connector for the server
*
* @param server the server
* @param connector the connector
* @param properties jetty properties
*/
public static void configureConnectors(Server server, Connector connector, Map<String, String> properties)
{
if (server == null)
throw new IllegalArgumentException("Server is null");
//if a connector is provided, use it
if (connector != null)
{
server.addConnector(connector);
return;
}
// if the user hasn't configured the connectors in a jetty.xml file so use a default one
Connector[] connectors = server.getConnectors();
if (connectors == null || connectors.length == 0)
{
//Make a new default connector
MavenServerConnector tmp = new MavenServerConnector();
//use any jetty.http.port settings provided, trying system properties before jetty properties
String port = System.getProperty(MavenServerConnector.PORT_SYSPROPERTY);
if (port == null)
port = System.getProperty("jetty.port");
if (port == null)
port = (properties != null ? properties.get(MavenServerConnector.PORT_SYSPROPERTY) : null);
if (port == null)
port = MavenServerConnector.DEFAULT_PORT_STR;
tmp.setPort(Integer.parseInt(port.trim()));
tmp.setServer(server);
server.setConnectors(new Connector[]{tmp});
}
}
/**
* Set up any security LoginServices provided.
*
* @param server the server
* @param loginServices the login services
*/
public static void configureLoginServices(Server server, List<LoginService> loginServices)
{
if (server == null)
throw new IllegalArgumentException("Server is null");
if (loginServices != null)
{
for (LoginService loginService : loginServices)
{
PluginLog.getLog().debug(loginService.getClass().getName() + ": " + loginService.toString());
server.addBean(loginService);
}
}
}
/**
* Add a WebAppContext to a Server
* @param server the server to use
* @param webapp the webapp to add
*/
public static void addWebApplication(Server server, WebAppContext webapp)
{
if (server == null)
throw new IllegalArgumentException("Server is null");
ContextHandlerCollection contexts = findContextHandlerCollection(server);
if (contexts == null)
throw new IllegalStateException("ContextHandlerCollection is null");
contexts.addHandler(webapp);
}
/**
* Locate a ContextHandlerCollection for a Server.
*
* @param server the Server to check.
* @return The ContextHandlerCollection or null if not found.
*/
public static ContextHandlerCollection findContextHandlerCollection(Server server)
{
if (server == null)
return null;
return server.getDescendant(ContextHandlerCollection.class);
}
/**
* Apply xml files to server instance.
*
* @param server the server to apply the xml to
* @param files the list of xml files
* @param properties list of jetty properties
* @return the Server implementation, after the xml is applied
* @throws Exception if unable to apply the xml configuration
*/
public static Server applyXmlConfigurations(Server server, List<File> files, Map<String, String> properties)
throws Exception
{
if (files == null || files.isEmpty())
return server;
Map<String, Object> lastMap = new HashMap<>();
if (server != null)
lastMap.put("Server", server);
for (File xmlFile : files)
{
if (PluginLog.getLog() != null)
PluginLog.getLog().info("Configuring Jetty from xml configuration file = " + xmlFile.getCanonicalPath());
XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(server).newResource(xmlFile.toPath()));
//add in any properties
if (properties != null)
{
for (Map.Entry<String, String> e : properties.entrySet())
{
xmlConfiguration.getProperties().put(e.getKey(), e.getValue());
}
}
//chain ids from one config file to another
if (lastMap != null)
xmlConfiguration.getIdMap().putAll(lastMap);
//Set the system properties each time in case the config file set a new one
Enumeration<?> ensysprop = System.getProperties().propertyNames();
while (ensysprop.hasMoreElements())
{
String name = (String)ensysprop.nextElement();
xmlConfiguration.getProperties().put(name, System.getProperty(name));
}
xmlConfiguration.configure();
lastMap = xmlConfiguration.getIdMap();
}
return (Server)lastMap.get("Server");
}
/**
* Apply xml files to server instance.
*
* @param server the Server instance to configure
* @param files the xml configs to apply
* @return the Server after application of configs
* @throws Exception if there is an unspecified problem
*/
public static Server applyXmlConfigurations(Server server, List<File> files)
throws Exception
{
return applyXmlConfigurations(server, files, null);
}
}

View File

@ -1,239 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.jetty.util.StringUtil;
/**
* WarPluginInfo
*
* Information about the maven-war-plugin contained in the pom
*/
public class WarPluginInfo
{
private MavenProject _project;
private Plugin _plugin;
private List<String> _dependentMavenWarIncludes;
private List<String> _dependentMavenWarExcludes;
private List<OverlayConfig> _overlayConfigs;
private Set<Artifact> _warArtifacts;
public WarPluginInfo(MavenProject project)
{
_project = project;
if (_project.getArtifacts() != null)
{
_warArtifacts = _project.getArtifacts()
.stream()
.filter(a -> "war".equals(a.getType()) || "zip".equals(a.getType())).collect(Collectors.toSet());
}
else
_warArtifacts = Collections.emptySet();
}
/**
* Get the project.
* @return the project
*/
public MavenProject getProject()
{
return _project;
}
/**
* Get all dependent artifacts that are wars.
* @return all artifacts of type "war" or "zip"
*/
public Set<Artifact> getWarArtifacts()
{
return _warArtifacts;
}
/**
* Get an artifact of type war that matches the given coordinates.
* @param groupId the groupId to match
* @param artifactId the artifactId to match
* @param classifier the classified to match
* @return the matching Artifact or null if no match
*/
public Artifact getWarArtifact(String groupId, String artifactId, String classifier)
{
Optional<Artifact> o = _warArtifacts.stream()
.filter(a -> match(a, groupId, artifactId, classifier)).findFirst();
return o.orElse(null);
}
/**
* Find the maven-war-plugin, if one is configured
*
* @return the plugin
*/
public Plugin getWarPlugin()
{
if (_plugin == null)
{
List<Plugin> plugins = _project.getBuildPlugins();
if (plugins == null)
return null;
for (Plugin p : plugins)
{
if ("maven-war-plugin".equals(p.getArtifactId()))
{
_plugin = p;
break;
}
}
}
return _plugin;
}
/**
* Get value of dependentWarIncludes for maven-war-plugin
*
* @return the list of dependent war includes
*/
public List<String> getDependentMavenWarIncludes()
{
if (_dependentMavenWarIncludes == null)
{
getWarPlugin();
if (_plugin == null)
return null;
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return null;
node = node.getChild("dependentWarIncludes");
if (node == null)
return null;
String val = node.getValue();
_dependentMavenWarIncludes = StringUtil.csvSplit(null, val, 0, val.length());
}
return _dependentMavenWarIncludes;
}
/**
* Get value of dependentWarExcludes for maven-war-plugin
*
* @return the list of dependent war excludes
*/
public List<String> getDependentMavenWarExcludes()
{
if (_dependentMavenWarExcludes == null)
{
getWarPlugin();
if (_plugin == null)
return null;
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return null;
node = node.getChild("dependentWarExcludes");
if (node == null)
return null;
String val = node.getValue();
_dependentMavenWarExcludes = StringUtil.csvSplit(null, val, 0, val.length());
}
return _dependentMavenWarExcludes;
}
/**
* Get config for any overlays that have been declared for the maven-war-plugin.
*
* @return the list of overlay configs
*/
public List<OverlayConfig> getMavenWarOverlayConfigs()
{
if (_overlayConfigs == null)
{
getWarPlugin();
if (_plugin == null)
return Collections.emptyList();
getDependentMavenWarIncludes();
getDependentMavenWarExcludes();
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return Collections.emptyList();
node = node.getChild("overlays");
if (node == null)
return Collections.emptyList();
Xpp3Dom[] nodes = node.getChildren("overlay");
if (nodes == null)
return Collections.emptyList();
_overlayConfigs = new ArrayList<OverlayConfig>();
for (int i = 0; i < nodes.length; i++)
{
OverlayConfig overlayConfig = new OverlayConfig(nodes[i], _dependentMavenWarIncludes, _dependentMavenWarExcludes);
_overlayConfigs.add(overlayConfig);
}
}
return _overlayConfigs;
}
public boolean match(Artifact a, String gid, String aid, String cls)
{
if (a == null)
return (gid == null && aid == null && cls == null);
if (((a.getGroupId() == null && gid == null) || (a.getGroupId() != null && a.getGroupId().equals(gid))) &&
((a.getArtifactId() == null && aid == null) || (a.getArtifactId() != null && a.getArtifactId().equals(aid))) &&
((a.getClassifier() == null) || (a.getClassifier().equals(cls))))
return true;
return false;
}
/**
* Check if the given artifact matches the group and artifact coordinates.
*
* @param a the artifact to check
* @param gid the group id
* @param aid the artifact id
* @return true if matched false otherwise
*/
public boolean match(Artifact a, String gid, String aid)
{
if (a == null)
return (gid == null && aid == null);
if (((a.getGroupId() == null && gid == null) || (a.getGroupId() != null && a.getGroupId().equals(gid))) &&
((a.getArtifactId() == null && aid == null) || (a.getArtifactId() != null && a.getArtifactId().equals(aid))))
return true;
return false;
}
}

View File

@ -1,185 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.maven.plugin.utils;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.jetty.ee10.maven.plugin.OverlayManager;
import org.eclipse.jetty.ee10.maven.plugin.WarPluginInfo;
/**
* MavenProjectHelper
*
* A class to facilitate interacting with the build time maven environment.
*/
public class MavenProjectHelper
{
private MavenProject project;
private RepositorySystem repositorySystem;
private List<ArtifactRepository> remoteRepositories;
private MavenSession session;
private final Map<String, MavenProject> artifactToReactorProjectMap;
/**
* maven-war-plugin reference
*/
private WarPluginInfo warPluginInfo;
/**
* Helper for wrangling war overlays
*/
private OverlayManager overlayManager;
/**
* @param project the project being built
* @param repositorySystem a resolve for artifacts
* @param remoteRepositories repositories from which to resolve artifacts
* @param session the current maven build session
*/
public MavenProjectHelper(MavenProject project, RepositorySystem repositorySystem, List<ArtifactRepository> remoteRepositories, MavenSession session)
{
this.project = project;
this.repositorySystem = repositorySystem;
this.remoteRepositories = remoteRepositories;
this.session = session;
//work out which dependent projects are in the reactor
Set<MavenProject> mavenProjects = findDependenciesInReactor(project, new HashSet<>());
artifactToReactorProjectMap = mavenProjects.stream()
.collect(Collectors.toMap(MavenProject::getId, Function.identity()));
artifactToReactorProjectMap.put(project.getArtifact().getId(), project);
warPluginInfo = new WarPluginInfo(project);
overlayManager = new OverlayManager(warPluginInfo);
}
public MavenProject getProject()
{
return this.project;
}
public WarPluginInfo getWarPluginInfo()
{
return warPluginInfo;
}
public OverlayManager getOverlayManager()
{
return overlayManager;
}
/**
* Gets the maven project represented by the artifact iff it is in
* the reactor.
*
* @param artifact the artifact of the project to get
* @return {@link MavenProject} if artifact is referenced in reactor, otherwise null
*/
public MavenProject getMavenProjectFor(Artifact artifact)
{
if (artifact == null)
return null;
return artifactToReactorProjectMap.get(artifact.getId());
}
/**
* Gets path to artifact.
* If the artifact is referenced in the reactor, returns path to ${project.build.outputDirectory}.
* Otherwise, returns path to location in local m2 repo.
*
* Cannot return null - maven will complain about unsatisfied dependency during project build.
*
* @param artifact maven artifact to check
* @return path to artifact
*/
public Path getPathFor(Artifact artifact)
{
Path path = artifact.getFile().toPath();
MavenProject mavenProject = getMavenProjectFor(artifact);
if (mavenProject != null)
{
if ("test-jar".equals(artifact.getType()))
{
path = Paths.get(mavenProject.getBuild().getTestOutputDirectory());
}
else
{
path = Paths.get(mavenProject.getBuild().getOutputDirectory());
}
}
return path;
}
/**
* Given the coordinates for an artifact, resolve the artifact from the
* remote repositories.
*
* @param groupId the groupId of the artifact to resolve
* @param artifactId the artifactId of the artifact to resolve
* @param version the version of the artifact to resolve
* @param type the type of the artifact to resolve
* @return a File representing the location of the artifact or null if not resolved
*/
public File resolveArtifact(String groupId, String artifactId, String version, String type)
throws ArtifactResolutionException
{
ArtifactRequest request = new ArtifactRequest();
request.setRepositories(RepositoryUtils.toRepos(remoteRepositories));
request.setArtifact(new DefaultArtifact(groupId, artifactId, "", type, version));
ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
if (result.isResolved())
return result.getArtifact().getFile();
return null;
}
/**
* Recursively find projects in the reactor for all dependencies of the given project.
*
* @param project the project for which to find dependencies that are in the reactor
* @param visitedProjects the set of projects already seen
* @return unified set of all related projects in the reactor
*/
private static Set<MavenProject> findDependenciesInReactor(MavenProject project, Set<MavenProject> visitedProjects)
{
if (visitedProjects.contains(project))
return Collections.emptySet();
visitedProjects.add(project);
Collection<MavenProject> refs = project.getProjectReferences().values();
Set<MavenProject> availableProjects = new HashSet<>(refs);
for (MavenProject ref : refs)
{
availableProjects.addAll(findDependenciesInReactor(ref, visitedProjects));
}
return availableProjects;
}
}

View File

@ -12,7 +12,7 @@ ee10-webapp
ee10-annotations
[lib]
lib/maven-ee10/*.jar
lib/ee10-maven/*.jar
[xml]
etc/jetty-ee10-maven.xml

View File

@ -4,7 +4,7 @@
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.maven.plugin.ServerListener">
<New class="org.eclipse.jetty.maven.ServerListener">
<Set name="tokenFile"><Property name="jetty.token.file"/></Set>
</New>
</Arg>

View File

@ -88,7 +88,7 @@ public class TestForkedChild
webapp.setBaseResourceAsPath(baseDir.toPath());
WebAppPropertyConverter.toProperties(webapp, webappPropsFile, null);
child = new JettyForkedChild(cmd.toArray(new String[0]));
child.jetty.setExitVm(false); //ensure jetty doesn't stop vm for testing
child.getJettyEmbedder().setExitVm(false); //ensure jetty doesn't stop vm for testing
child.start();
}
catch (Exception e)

View File

@ -19,6 +19,8 @@ import java.util.List;
import java.util.Map;
import org.eclipse.jetty.ee10.servlet.ListenerHolder;
import org.eclipse.jetty.maven.MavenServerConnector;
import org.eclipse.jetty.maven.ServerSupport;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
@ -119,7 +121,7 @@ public class TestJettyEmbedder
assertTrue(contexts.contains(webApp));
//stop the webapp and check durable listener retained
jetty.getWebApp().stop();
jetty.stopWebApp();
boolean someListener = false;
for (ListenerHolder h : webApp.getServletHandler().getListeners())
{

View File

@ -18,6 +18,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.maven.SelectiveJarResource;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;

View File

@ -279,7 +279,9 @@ public class ServletChannel
public ServletContextResponse getServletContextResponse()
{
ServletContextRequest request = _servletContextRequest;
return request == null ? null : request.getServletContextResponse();
if (_servletContextRequest == null)
throw new IllegalStateException("Request/Response does not exist (likely recycled)");
return request.getServletContextResponse();
}
/**
@ -291,6 +293,8 @@ public class ServletChannel
*/
public Response getResponse()
{
if (_response == null)
throw new IllegalStateException("Response does not exist (likely recycled)");
return _response;
}

View File

@ -79,6 +79,7 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.ResponseUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
@ -103,6 +104,7 @@ import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
@ -692,6 +694,31 @@ public class ServletContextHandlerTest
_server.join();
}
@Test
public void testEnsureNotPersistent() throws Exception
{
ServletContextHandler root = new ServletContextHandler("/", ServletContextHandler.SESSIONS);
root.setContextPath("/");
root.addServlet(new ServletHolder(new HttpServlet()
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
{
Request request = ((ServletApiRequest)req).getRequest();
Response response = ((ServletApiResponse)resp).getResponse();
ResponseUtils.ensureNotPersistent(request, response);
}
}), "/ensureNotPersistent");
_server.setHandler(root);
_server.start();
String rawResponse = _connector.getResponse("GET /ensureNotPersistent HTTP/1.0\r\n\r\n");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response.getStatus(), is(200));
}
@Test
public void testInitParams() throws Exception
{

View File

@ -200,7 +200,7 @@ public class CreationTest
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> !contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
@ -245,7 +245,7 @@ public class CreationTest
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> !contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
@ -287,8 +287,8 @@ public class CreationTest
ContentResponse response = client.GET(url);
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
//check that the session does not exist
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
//check the session
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
assertThat(response.getHeaders().getValuesList(HttpHeader.SET_COOKIE).size(), Matchers.is(1));
}
finally
@ -338,8 +338,8 @@ public class CreationTest
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
//check that the sessions exist persisted
assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
assertTrue(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{
@ -387,9 +387,9 @@ public class CreationTest
ContentResponse response = client.GET(url + "?action=forwardinv");
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
//check that the session does not exist
assertFalse(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
assertFalse(ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
//check that the session does not exist
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> !contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
Awaitility.waitAtMost(5, TimeUnit.SECONDS).until(() -> !ctxB.getSessionHandler().getSessionCache().getSessionDataStore().exists(servlet._id));
}
finally
{

View File

@ -77,6 +77,7 @@ public class AttributeNameTest
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
cacheFactory.setSaveOnCreate(true);
MongoSessionDataStoreFactory storeFactory = MongoTestHelper.newSessionDataStoreFactory(DB_NAME, COLLECTION_NAME);
storeFactory.setGracePeriodSec(scavengePeriod);

View File

@ -86,6 +86,10 @@
<artifactId>jetty-jndi</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -32,7 +32,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
</New>
</Arg>
</Call>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -5,7 +5,7 @@
<Ref refid="httpConnector">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

View File

@ -140,7 +140,7 @@
</jettyXmls>
<loginServices>
<loginService implementation="org.eclipse.jetty.security.HashLoginService">
<config implementation="org.eclipse.jetty.ee8.maven.plugin.MavenResource">
<config implementation="org.eclipse.jetty.maven.MavenResource">
<resourceAsString>${basedir}/src/config/login.xml</resourceAsString>
</config>
</loginService>

View File

@ -26,7 +26,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee8.maven.plugin.ServerConnectorListener">
<New class="org.eclipse.jetty.maven.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>
</New>
</Arg>

Some files were not shown because too many files have changed in this diff Show More