Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-3428-decoderLists

This commit is contained in:
Lachlan Roberts 2020-05-28 10:29:53 +10:00
commit 2c1c25d5a7
250 changed files with 4798 additions and 1615 deletions

View File

@ -1,5 +1,31 @@
jetty-10.0.0-SNAPSHOT
jetty-9.4.29.v20200521 - 21 May 2020
+ 2188 Lock contention creating HTTP/2 streams
+ 4235 communicate the reason of failure to the OpenID error page
+ 4695 HttpChannel recycling in h2
+ 4764 HTTP2 Jetty Server does not send back content-length
+ 4778 Enforcing SNI when there are only non-wildcards certificates
+ 4787 Make org.eclipse.jetty.client.HttpRequest's host name writable
+ 4789 org.eclipse.jetty.util.thread.ShutdownThread should use an appropriate
name to identify itself in Thread dump
+ 4798 Better handling of fatal Selector failures
+ 4814 Allow a ConnectionFactory (eg SslConnectionFactory) to automatically
add a Customizer
+ 4820 Jetty OSGi DefaultJettyAtJettyHomeHelper refers to non-existent
config file
+ 4824 WebSocket server outgoing message queue memory growth
+ 4828 NIO ByteBuffer corruption in embedded Jetty server
+ 4835 GzipHandler and GzipHttpOutputInterceptor do not flush response when
body is empty
+ 4860 org.eclipse.jetty.server.HttpChannel busyloop on HttpFields
NullPointerException
+ 4861 Combine `AttributesMap` and `Attributes.Wrapper`
+ 4868 Update to asm 7.3.1
+ 4892 Non-blocking JSON parser
+ 4895 AbstractSessionCache.setFlushOnResponseCommit(true) can write an
invalid session to the backing store
jetty-10.0.0.alpha1 - 26 November 2019
+ 97 Permanent UnavailableException thrown during servlet request handling
should cause servlet destroy
@ -363,7 +389,7 @@ jetty-9.4.28.v20200408 - 08 April 2020
+ 4529 ErrorHandler showing servlet info, can not be disabled unless
overriding most of its functionality
+ 4542 servlet context root mapping incorrect
+ 4619 Inconsistent library versions notice.
+ 4619 Inconsistent library versions notice
+ 4620 Using console-capture with StdErrLog results in empty log file
+ 4621 jetty-jaspi in jetty-all uber aggregate artifact requires
javax.security.auth.message.AuthException which cannot be included

View File

@ -22,7 +22,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -61,6 +61,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.LoggerFactory;
public class Http2Server
{
@ -74,6 +75,8 @@ public class Http2Server
ManagementFactory.getPlatformMBeanServer());
server.addBean(mbContainer);
server.addBean(LoggerFactory.getILoggerFactory());
ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
Path docroot = Paths.get("src/main/resources/docroot");
if (!Files.exists(docroot))

View File

@ -211,7 +211,7 @@ public class WebServletAnnotation extends DiscoveredAnnotation
for (String p : urlPatternList)
{
ServletMapping existingMapping = _context.getServletHandler().getServletMapping(p);
if (existingMapping != null && existingMapping.isDefault())
if (existingMapping != null && existingMapping.isFromDefaultDescriptor())
{
String[] updatedPaths = ArrayUtil.removeFromArray(existingMapping.getPathSpecs(), p);
//if we removed the last path from a servletmapping, delete the servletmapping
@ -264,7 +264,7 @@ public class WebServletAnnotation extends DiscoveredAnnotation
return false;
for (ServletMapping m : mappings)
{
if (!m.isDefault())
if (!m.isFromDefaultDescriptor())
return true;
}
return false;

View File

@ -117,7 +117,7 @@ public class TestServletAnnotations
ServletMapping m = new ServletMapping();
m.setPathSpec("/");
m.setServletName("default");
m.setDefault(true); //this mapping will be from a default descriptor
m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
@ -150,13 +150,13 @@ public class TestServletAnnotations
ServletMapping m = new ServletMapping();
m.setPathSpec("/");
m.setServletName("default");
m.setDefault(true); //this mapping will be from a default descriptor
m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m);
ServletMapping m2 = new ServletMapping();
m2.setPathSpec("/other");
m2.setServletName("default");
m2.setDefault(true); //this mapping will be from a default descriptor
m2.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor
wac.getServletHandler().addServletMapping(m2);
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
@ -235,7 +235,7 @@ public class TestServletAnnotations
ServletMapping m = new ServletMapping();
m.setPathSpec("/default");
m.setDefault(true);
m.setFromDefaultDescriptor(true);
m.setServletName("DServlet");
wac.getServletHandler().addServletMapping(m);

View File

@ -381,7 +381,17 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-core</artifactId>
<artifactId>websocket-core-common</artifactId>
<version>10.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-core-client</artifactId>
<version>10.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-core-server</artifactId>
<version>10.0.0-SNAPSHOT</version>
</dependency>
<dependency>

View File

@ -93,4 +93,10 @@ public class HttpAuthenticationStore implements AuthenticationStore
}
return null;
}
@Override
public boolean hasAuthenticationResults()
{
return !results.isEmpty();
}
}

View File

@ -1113,7 +1113,7 @@ public class HttpClient extends ContainerLifeCycle
protected String normalizeHost(String host)
{
if (host != null && host.matches("\\[.*]"))
if (host != null && host.startsWith("[") && host.endsWith("]"))
return host.substring(1, host.length() - 1);
return host;
}

View File

@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.BytesRequestContent;
@ -138,13 +139,15 @@ public abstract class HttpConnection implements IConnection
request.path(path);
}
URI uri = request.getURI();
ProxyConfiguration.Proxy proxy = destination.getProxy();
if (proxy instanceof HttpProxy && !HttpClient.isSchemeSecure(request.getScheme()) && uri != null)
if (proxy instanceof HttpProxy && !HttpClient.isSchemeSecure(request.getScheme()))
{
path = uri.toString();
request.path(path);
URI uri = request.getURI();
if (uri != null)
{
path = uri.toString();
request.path(path);
}
}
// If we are HTTP 1.1, add the Host header
@ -185,9 +188,10 @@ public abstract class HttpConnection implements IConnection
// Cookies
CookieStore cookieStore = getHttpClient().getCookieStore();
if (cookieStore != null)
if (cookieStore != null && cookieStore.getClass() != HttpCookieStore.Empty.class)
{
StringBuilder cookies = null;
URI uri = request.getURI();
if (uri != null)
cookies = convertCookies(HttpCookieStore.matchPath(uri, cookieStore.get(uri)), null);
cookies = convertCookies(request.getCookies(), cookies);
@ -199,8 +203,8 @@ public abstract class HttpConnection implements IConnection
}
// Authentication
applyAuthentication(request, proxy != null ? proxy.getURI() : null);
applyAuthentication(request, uri);
applyProxyAuthentication(request, proxy);
applyRequestAuthentication(request);
}
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
@ -216,16 +220,31 @@ public abstract class HttpConnection implements IConnection
return builder;
}
private void applyAuthentication(Request request, URI uri)
private void applyProxyAuthentication(Request request, ProxyConfiguration.Proxy proxy)
{
if (uri != null)
if (proxy != null)
{
Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(uri);
Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(proxy.getURI());
if (result != null)
result.apply(request);
}
}
private void applyRequestAuthentication(Request request)
{
AuthenticationStore authenticationStore = getHttpClient().getAuthenticationStore();
if (authenticationStore.hasAuthenticationResults())
{
URI uri = request.getURI();
if (uri != null)
{
Authentication.Result result = authenticationStore.findAuthenticationResult(uri);
if (result != null)
result.apply(request);
}
}
}
public boolean onIdleTimeout(long idleTimeout)
{
synchronized (this)

View File

@ -117,9 +117,9 @@ public class HttpConversation extends AttributesMap
// will notify a listener that may send a new request and trigger
// another call to this method which will build different listeners
// which may be iterated over when the iteration continues.
List<Response.ResponseListener> listeners = new ArrayList<>();
HttpExchange firstExchange = exchanges.peekFirst();
HttpExchange lastExchange = exchanges.peekLast();
List<Response.ResponseListener> listeners = new ArrayList<>(firstExchange.getResponseListeners().size() + lastExchange.getResponseListeners().size());
if (firstExchange == lastExchange)
{
// We don't have a conversation, just a single request.

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@ -31,7 +32,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongConsumer;
import java.util.function.LongUnaryOperator;
import java.util.stream.Collectors;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
@ -693,10 +693,11 @@ public abstract class HttpReceiver
private ContentListeners(List<Response.ResponseListener> responseListeners)
{
listeners = responseListeners.stream()
listeners = new ArrayList<>(responseListeners.size());
responseListeners.stream()
.filter(Response.DemandedContentListener.class::isInstance)
.map(Response.DemandedContentListener.class::cast)
.collect(Collectors.toList());
.forEach(listeners::add);
}
private boolean isEmpty()

View File

@ -75,4 +75,12 @@ public interface AuthenticationStore
* @return the {@link Authentication.Result} that matches the given URI, or null
*/
public Authentication.Result findAuthenticationResult(URI uri);
/**
* @return false if there are no stored authentication results, true if there may be some.
*/
public default boolean hasAuthenticationResults()
{
return true;
}
}

View File

@ -73,7 +73,10 @@ public interface Request
* @param host the URI host of this request, such as "127.0.0.1" or "google.com"
* @return this request object
*/
Request host(String host);
default Request host(String host)
{
return this;
}
/**
* @return the URI port of this request such as 80 or 443
@ -81,11 +84,13 @@ public interface Request
int getPort();
/**
*
* @param port the URI port of this request such as 80 or 443
* @return this request object
*/
Request port(int port);
default Request port(int port)
{
return this;
}
/**
* @return the method of this request, such as GET or POST, as a String

View File

@ -67,6 +67,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
@ -88,8 +89,9 @@ import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.condition.OS.LINUX;
import static org.junit.jupiter.api.condition.OS.WINDOWS;
// This whole test is very specific to how TLS < 1.3 works.
@EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9, JRE.JAVA_10})
// Other JREs have slight differences in how TLS work
// and this test expects a very specific TLS behavior.
@EnabledOnJre(JRE.JAVA_11)
public class SslBytesServerTest extends SslBytesTest
{
private final AtomicInteger sslFills = new AtomicInteger();
@ -108,9 +110,9 @@ public class SslBytesServerTest extends SslBytesTest
@BeforeEach
public void init() throws Exception
{
threadPool = Executors.newCachedThreadPool();
server = new Server();
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
sslFills.set(0);
sslFlushes.set(0);
@ -119,6 +121,8 @@ public class SslBytesServerTest extends SslBytesTest
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.p12");
sslContextFactory = new SslContextFactory.Server();
// This whole test is very specific to how TLS < 1.3 works.
sslContextFactory.setIncludeProtocols("TLSv1.2");
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
sslContextFactory.setKeyStorePassword("storepwd");
@ -238,6 +242,7 @@ public class SslBytesServerTest extends SslBytesTest
sslContext = sslContextFactory.getSslContext();
threadPool = Executors.newCachedThreadPool();
proxy = new SimpleProxy(threadPool, "localhost", serverPort);
proxy.start();
logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort);
@ -1128,6 +1133,66 @@ public class SslBytesServerTest extends SslBytesTest
client.close();
}
@Test
public void testRequestResponseServerIdleTimeoutClientResets() throws Exception
{
SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
Future<Object> request = threadPool.submit(() ->
{
OutputStream clientOutput = client.getOutputStream();
clientOutput.write((
"GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n").getBytes(StandardCharsets.UTF_8));
clientOutput.flush();
return null;
});
// Application data
TLSRecord record = proxy.readFromClient();
proxy.flushToServer(record);
assertNull(request.get(5, TimeUnit.SECONDS));
// Application data
record = proxy.readFromServer();
assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8));
String line = reader.readLine();
assertNotNull(line);
assertTrue(line.startsWith("HTTP/1.1 200 "));
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
break;
}
// Wait for the server idle timeout.
Thread.sleep(idleTimeout);
// We expect that the server sends the TLS Alert.
record = proxy.readFromServer();
assertNotNull(record);
assertEquals(TLSRecord.Type.ALERT, record.getType());
// Send a RST to the server.
proxy.sendRSTToServer();
// Wait for the RST to be processed by the server.
Thread.sleep(1000);
// The server EndPoint must be closed.
assertFalse(serverEndPoint.get().isOpen());
client.close();
}
@Test
@EnabledOnOs(LINUX) // see message below
public void testRequestWithCloseAlertWithSplitBoundary() throws Exception

View File

@ -234,6 +234,7 @@ public abstract class SslBytesTest
public void flushToServer(TLSRecord record, long sleep) throws Exception
{
logger.debug("P --> S {}", record);
if (record == null)
{
server.shutdownOutput();
@ -272,6 +273,7 @@ public abstract class SslBytesTest
public void flushToClient(TLSRecord record) throws Exception
{
logger.debug("C <-- P {}", record);
if (record == null)
{
client.shutdownOutput();

View File

@ -85,6 +85,16 @@ db-connection-type=datasource
#jetty.session.jdbc.schema.maxIntervalColumn=maxInterval
#jetty.session.jdbc.schema.mapColumn=map
#jetty.session.jdbc.schema.table=JettySessions
# Optional name of the schema used to identify where the session table is defined in the database:
# "" - empty string, no schema name
# "INFERRED" - special string meaning infer from the current db connection
# name - a string defined by the user
#jetty.session.jdbc.schema.schemaName
# Optional name of the catalog used to identify where the session table is defined in the database:
# "" - empty string, no catalog name
# "INFERRED" - special string meaning infer from the current db connection
# name - a string defined by the user
#jetty.session.jdbc.schema.catalogName
----
jetty.session.gracePeriod.seconds::
@ -111,4 +121,14 @@ jetty.session.jdbc.driverUrl::
Url of the database which includes the driver type, host name and port, service name and any specific attributes unique to the database, such as a username.
As an example, here is a mysql connection with the username appended: `jdbc:mysql://127.0.0.1:3306/sessions?user=sessionsadmin`.
The `jetty.sessionTableSchema` values represent the names for the columns in the JDBC database and can be changed to suit your environment.
The `jetty.session.jdbc.schema.*` values represent the names of the table and columns in the JDBC database used to store sessions and can be changed to suit your environment.
There are also two special, optional properties: `jetty.session.jdbc.schema.schemaName` and `jetty.session.jdbc.schema.catalogName`.
The exact meaning of these two properties is dependent on your database vendor, but can broadly be described as further scoping for the session table name.
See https://en.wikipedia.org/wiki/Database_schema and https://en.wikipedia.org/wiki/Database_catalog.
These extra scoping names can come into play at startup time when jetty determines if the session table already exists, or otherwise creates it on-the-fly.
If you have employed either of these concepts when you pre-created the session table, or you want to ensure that jetty uses them when it auto-creates the session table, then you have two options: either set them explicitly, or let jetty infer them from a database connection (obtained using either a Datasource or Driver according to the `db-connection-type` you have configured).
To set them explicitly, uncomment and supply appropriate values for the `jetty.session.jdbc.schema.schemaName` and/or `jetty.session.jdbc.schema.catalogName` properties.
To allow jetty to infer them from a database connection, use the special string `INFERRED` instead.
If you leave them blank or commented out, then the sessions table will not be scoped by schema or catalog name.

View File

@ -55,7 +55,7 @@ You *must also install the Apache Aries SPI Fly bundles* as many parts of Jetty
[cols=",,",options="header",]
|=======================================================================
|Jar |Bundle Symbolic Name |Location
|org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle-1.2.jar |org.apache.aries.spifly.dynamic.bundle
|org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle-1.2.4.jar |org.apache.aries.spifly.dynamic.bundle
|https://repo1.maven.org/maven2/org/apache/aries/spifly/org.apache.aries.spifly.dynamic.bundle/[Maven central]
|=======================================================================

View File

@ -4,5 +4,7 @@ org.eclipse.jetty.LEVEL=INFO
#com.example.LEVEL=INFO
## Configure a level for specific logger
#com.example.MyComponent.LEVEL=INFO
## Configure JMX Context Name
# org.eclipse.jetty.logging.jmx.context=JettyServer
## Hide stacks traces in an arbitrary logger tree
#com.example.STACKS=false

View File

@ -43,7 +43,7 @@ public class HttpField
if (_header != null && name == null)
_name = _header.asString();
else
_name = Objects.requireNonNull(name);
_name = Objects.requireNonNull(name, "name");
_value = value;
}

View File

@ -27,6 +27,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
@ -855,6 +856,8 @@ public interface HttpFields extends Iterable<HttpField>
*/
public Mutable put(String name, List<String> list)
{
Objects.requireNonNull(name, "name must not be null");
Objects.requireNonNull(list, "list must not be null");
remove(name);
for (String v : list)
{
@ -1018,6 +1021,9 @@ public interface HttpFields extends Iterable<HttpField>
@Override
public void add(HttpField field)
{
if (field == null)
return;
_fields = Arrays.copyOf(_fields, _fields.length + 1);
System.arraycopy(_fields, _cursor, _fields, _cursor + 1, _size++);
_fields[_cursor++] = field;
@ -1083,7 +1089,10 @@ public interface HttpFields extends Iterable<HttpField>
{
if (_current < 0)
throw new IllegalStateException();
_fields[_current] = field;
if (field == null)
remove();
else
_fields[_current] = field;
}
}
}

View File

@ -28,6 +28,7 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpFieldTest
@ -165,6 +166,12 @@ public class HttpFieldTest
assertEquals("c", values[2]);
}
@Test
public void testFieldNameNull()
{
assertThrows(NullPointerException.class, () -> new HttpField((String)null, null));
}
@Test
public void testCachedField()
{

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
@ -49,7 +50,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpFieldsTest
{
@Test
public void testPut() throws Exception
public void testPut()
{
HttpFields.Mutable header = HttpFields.build()
.put("name0", "value:0")
@ -73,13 +74,13 @@ public class HttpFieldsTest
assertEquals(2, matches);
e = header.getValues("name0");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value:0");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
public void testPutTo() throws Exception
public void testPutTo()
{
HttpFields.Mutable header = HttpFields.build()
.put("name0", "value0")
@ -99,7 +100,7 @@ public class HttpFieldsTest
}
@Test
public void testImmutable() throws Exception
public void testImmutable()
{
HttpFields header = HttpFields.build()
.put("name0", "value0")
@ -109,24 +110,21 @@ public class HttpFieldsTest
assertEquals("value0", header.get("Name0"));
assertEquals("value1", header.get("name1"));
assertEquals("value1", header.get("Name1"));
assertEquals(null, header.get("Name2"));
assertNull(header.get("Name2"));
assertEquals("value0", header.getField("name0").getValue());
assertEquals("value0", header.getField("Name0").getValue());
assertEquals("value1", header.getField("name1").getValue());
assertEquals("value1", header.getField("Name1").getValue());
assertEquals(null, header.getField("Name2"));
assertNull(header.getField("Name2"));
assertEquals("value0", header.getField(0).getValue());
assertEquals("value1", header.getField(1).getValue());
assertThrows(NoSuchElementException.class, () ->
{
header.getField(2);
});
assertThrows(NoSuchElementException.class, () -> header.getField(2));
}
@Test
public void testMutable() throws Exception
public void testMutable()
{
HttpFields headers = HttpFields.build()
.add(HttpHeader.ETAG, "tag")
@ -147,20 +145,20 @@ public class HttpFieldsTest
}
@Test
public void testMap() throws Exception
public void testMap()
{
Map<HttpFields.Immutable,String> map = new HashMap<>();
map.put(HttpFields.build().add("X","1").add(HttpHeader.ETAG,"tag").asImmutable(),"1");
map.put(HttpFields.build().add("X","2").add(HttpHeader.ETAG,"other").asImmutable(),"2");
Map<HttpFields.Immutable, String> map = new HashMap<>();
map.put(HttpFields.build().add("X", "1").add(HttpHeader.ETAG, "tag").asImmutable(), "1");
map.put(HttpFields.build().add("X", "2").add(HttpHeader.ETAG, "other").asImmutable(), "2");
assertThat(map.get(HttpFields.build().add("X","1").add(HttpHeader.ETAG,"tag").asImmutable()), is("1"));
assertThat(map.get(HttpFields.build().add("X","2").add(HttpHeader.ETAG,"other").asImmutable()), is("2"));
assertThat(map.get(HttpFields.build().add("X","2").asImmutable()), nullValue());
assertThat(map.get(HttpFields.build().add("X","2").add(HttpHeader.ETAG,"tag").asImmutable()), nullValue());
assertThat(map.get(HttpFields.build().add("X", "1").add(HttpHeader.ETAG, "tag").asImmutable()), is("1"));
assertThat(map.get(HttpFields.build().add("X", "2").add(HttpHeader.ETAG, "other").asImmutable()), is("2"));
assertThat(map.get(HttpFields.build().add("X", "2").asImmutable()), nullValue());
assertThat(map.get(HttpFields.build().add("X", "2").add(HttpHeader.ETAG, "tag").asImmutable()), nullValue());
}
@Test
public void testGet() throws Exception
public void testGet()
{
HttpFields header = HttpFields.build()
.put("name0", "value0")
@ -170,24 +168,21 @@ public class HttpFieldsTest
assertEquals("value0", header.get("Name0"));
assertEquals("value1", header.get("name1"));
assertEquals("value1", header.get("Name1"));
assertEquals(null, header.get("Name2"));
assertNull(header.get("Name2"));
assertEquals("value0", header.getField("name0").getValue());
assertEquals("value0", header.getField("Name0").getValue());
assertEquals("value1", header.getField("name1").getValue());
assertEquals("value1", header.getField("Name1").getValue());
assertEquals(null, header.getField("Name2"));
assertNull(header.getField("Name2"));
assertEquals("value0", header.getField(0).getValue());
assertEquals("value1", header.getField(1).getValue());
assertThrows(NoSuchElementException.class, () ->
{
header.getField(2);
});
assertThrows(NoSuchElementException.class, () -> header.getField(2));
}
@Test
public void testGetKnown() throws Exception
public void testGetKnown()
{
HttpFields.Mutable header = HttpFields.build();
@ -200,12 +195,12 @@ public class HttpFieldsTest
assertEquals("value0", header.getField(HttpHeader.CONNECTION).getValue());
assertEquals("value1", header.getField(HttpHeader.ACCEPT).getValue());
assertEquals(null, header.getField(HttpHeader.AGE));
assertEquals(null, header.get(HttpHeader.AGE));
assertNull(header.getField(HttpHeader.AGE));
assertNull(header.get(HttpHeader.AGE));
}
@Test
public void testCRLF() throws Exception
public void testCRLF()
{
HttpFields.Mutable header = HttpFields.build();
@ -224,7 +219,7 @@ public class HttpFieldsTest
}
@Test
public void testCachedPut() throws Exception
public void testCachedPut()
{
HttpFields.Mutable header = HttpFields.build();
@ -244,7 +239,7 @@ public class HttpFieldsTest
}
@Test
public void testRePut() throws Exception
public void testRePut()
{
HttpFields.Mutable header = HttpFields.build();
@ -278,13 +273,13 @@ public class HttpFieldsTest
assertEquals(3, matches);
e = header.getValues("name1");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
public void testRemove() throws Exception
public void testRemove()
{
HttpFields.Mutable header = HttpFields.build(1)
.put("name0", "value0")
@ -326,11 +321,11 @@ public class HttpFieldsTest
assertEquals(2, matches);
e = header.getValues("name1");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
public void testAdd() throws Exception
public void testAdd()
{
HttpFields.Mutable fields = HttpFields.build();
@ -364,11 +359,11 @@ public class HttpFieldsTest
assertEquals(3, matches);
e = fields.getValues("name1");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "valueA");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "valueB");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
@ -412,7 +407,7 @@ public class HttpFieldsTest
}
@Test
public void testGetValues() throws Exception
public void testGetValues()
{
HttpFields.Mutable fields = HttpFields.build();
@ -422,37 +417,37 @@ public class HttpFieldsTest
fields.add("name1", "\"value1C\",\tvalue1D");
Enumeration<String> e = fields.getValues("name0");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0A,value0B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0C,value0D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
e = Collections.enumeration(fields.getCSV("name0", false));
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0A");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0C");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
e = Collections.enumeration(fields.getCSV("name1", false));
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1A");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value\t, 1B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1C");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
public void testGetCSV() throws Exception
public void testGetCSV()
{
HttpFields.Mutable fields = HttpFields.build();
@ -462,37 +457,37 @@ public class HttpFieldsTest
fields.add("name1", "\"value1C\",\tvalue1D");
Enumeration<String> e = fields.getValues("name0");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0A,value0B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0C,value0D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
e = Collections.enumeration(fields.getCSV("name0", false));
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0A");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0C");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value0D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
e = Collections.enumeration(fields.getCSV("name1", false));
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1A");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value\t, 1B");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1C");
assertEquals(true, e.hasMoreElements());
assertTrue(e.hasMoreElements());
assertEquals(e.nextElement(), "value1D");
assertEquals(false, e.hasMoreElements());
assertFalse(e.hasMoreElements());
}
@Test
public void testAddQuotedCSV() throws Exception
public void testAddQuotedCSV()
{
HttpFields.Mutable fields = HttpFields.build();
@ -540,7 +535,7 @@ public class HttpFieldsTest
}
@Test
public void testGetQualityCSV() throws Exception
public void testGetQualityCSV()
{
HttpFields.Mutable fields = HttpFields.build();
@ -562,7 +557,7 @@ public class HttpFieldsTest
}
@Test
public void testGetQualityCSVHeader() throws Exception
public void testGetQualityCSVHeader()
{
HttpFields.Mutable fields = HttpFields.build();
@ -584,7 +579,7 @@ public class HttpFieldsTest
}
@Test
public void testDateFields() throws Exception
public void testDateFields()
{
HttpFields.Mutable fields = HttpFields.build();
@ -626,7 +621,7 @@ public class HttpFieldsTest
}
@Test
public void testNegDateFields() throws Exception
public void testNegDateFields()
{
HttpFields.Mutable fields = HttpFields.build();
@ -644,7 +639,7 @@ public class HttpFieldsTest
}
@Test
public void testLongFields() throws Exception
public void testLongFields()
{
HttpFields.Mutable header = HttpFields.build();
@ -656,47 +651,12 @@ public class HttpFieldsTest
header.put("N2", "xx");
long i1 = header.getLongField("I1");
try
{
header.getLongField("I2");
assertTrue(false);
}
catch (NumberFormatException e)
{
assertTrue(true);
}
assertThrows(NumberFormatException.class, () -> header.getLongField("I2"));
long i3 = header.getLongField("I3");
try
{
header.getLongField("I4");
assertTrue(false);
}
catch (NumberFormatException e)
{
assertTrue(true);
}
try
{
header.getLongField("N1");
assertTrue(false);
}
catch (NumberFormatException e)
{
assertTrue(true);
}
try
{
header.getLongField("N2");
assertTrue(false);
}
catch (NumberFormatException e)
{
assertTrue(true);
}
assertThrows(NumberFormatException.class, () -> header.getLongField("I4"));
assertThrows(NumberFormatException.class, () -> header.getLongField("N1"));
assertThrows(NumberFormatException.class, () -> header.getLongField("N2"));
assertEquals(42, i1);
assertEquals(-44, i3);
@ -708,7 +668,7 @@ public class HttpFieldsTest
}
@Test
public void testContains() throws Exception
public void testContains()
{
HttpFields.Mutable header = HttpFields.build();
@ -768,6 +728,73 @@ public class HttpFieldsTest
assertFalse(fields.contains(keyName), "containsKey('" + keyName + "')");
}
@Test
public void testAddNullName()
{
HttpFields.Mutable fields = HttpFields.build();
assertThrows(NullPointerException.class, () -> fields.add((String)null, "bogus"));
assertThat(fields.size(), is(0));
assertThrows(NullPointerException.class, () -> fields.add((HttpHeader)null, "bogus"));
assertThat(fields.size(), is(0));
}
@Test
public void testPutNullName()
{
HttpFields.Mutable fields = HttpFields.build();
assertThrows(NullPointerException.class, () -> fields.put((String)null, "bogus"));
assertThat(fields.size(), is(0));
assertThrows(NullPointerException.class, () -> fields.put(null, (List<String>)null));
assertThat(fields.size(), is(0));
List<String> emptyList = new ArrayList<>();
assertThrows(NullPointerException.class, () -> fields.put(null, emptyList));
assertThat(fields.size(), is(0));
assertThrows(NullPointerException.class, () -> fields.put((HttpHeader)null, "bogus"));
assertThat(fields.size(), is(0));
}
@Test
public void testPutNullValueList()
{
HttpFields.Mutable fields = HttpFields.build();
assertThrows(NullPointerException.class, () -> fields.put("name", (List<String>)null));
assertThat(fields.size(), is(0));
}
@Test
public void testPreventNullFieldEntry()
{
// Attempt various ways that may have put a null field in the array that
// previously caused a NPE in put.
HttpFields.Mutable fields = HttpFields.build();
fields.add((HttpField)null); // should not result in field being added
assertThat(fields.size(), is(0));
fields.put(null); // should not result in field being added
assertThat(fields.size(), is(0));
fields.put("something", "else");
assertThat(fields.size(), is(1));
ListIterator<HttpField> iter = fields.listIterator();
iter.next();
iter.set(null); // set field to null - should result in noop
assertThat(fields.size(), is(0));
iter.add(null); // attempt to add null entry
assertThat(fields.size(), is(0));
fields.put("something", "other");
assertThat(fields.size(), is(1));
iter = fields.listIterator();
iter.next();
iter.remove(); // remove only entry
assertThat(fields.size(), is(0));
fields.put("something", "other");
assertThat(fields.size(), is(1));
fields.clear();
}
@Test
public void testPreventNullField()
{
@ -780,7 +807,7 @@ public class HttpFieldsTest
}
@Test
public void testIteration() throws Exception
public void testIteration()
{
HttpFields.Mutable header = HttpFields.build();
Iterator<HttpField> i = header.iterator();

View File

@ -21,6 +21,8 @@ package org.eclipse.jetty.http2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
@ -47,7 +49,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
private final Queue<WindowEntry> windows = new ArrayDeque<>();
private final Deque<Entry> entries = new ArrayDeque<>();
private final Queue<Entry> pendingEntries = new ArrayDeque<>();
private final Set<Entry> processedEntries = new HashSet<>();
private final Collection<Entry> processedEntries = new ArrayList<>();
private final HTTP2Session session;
private final ByteBufferPool.Lease lease;
private Throwable terminated;
@ -192,7 +194,10 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
progress = true;
processedEntries.add(entry);
// We use ArrayList contains() + add() instead of HashSet add()
// because that is faster for collections of size up to 250 entries.
if (!processedEntries.contains(entry))
processedEntries.add(entry);
if (entry.getDataBytesRemaining() == 0)
pending.remove();

View File

@ -395,12 +395,15 @@ public class HpackContext
{
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] evictAll", HpackContext.this.hashCode()));
_fieldMap.clear();
_nameMap.clear();
_offset = 0;
_size = 0;
_dynamicTableSizeInBytes = 0;
Arrays.fill(_entries, null);
if (size() > 0)
{
_fieldMap.clear();
_nameMap.clear();
_offset = 0;
_size = 0;
_dynamicTableSizeInBytes = 0;
Arrays.fill(_entries, null);
}
}
}

View File

@ -7,12 +7,11 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>infinispan-remote-query</artifactId>
<name>Jetty :: Infinispan Session Manager Remote with Querying</name>
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.infinispan.remote.query</bundle-symbolic-name>
<infinispan.docker.image.version>9.4.8.Final</infinispan.docker.image.version>
</properties>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -127,10 +126,20 @@
<artifactId>infinispan-remote-query-client</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>remote</id>
<id>remote-session-tests</id>
<activation>
<property>
<name>hotrod.enabled</name>
@ -144,6 +153,9 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>false</skipTests>
<systemPropertyVariables>
<infinispan.docker.image.version>${infinispan.docker.image.version}</infinispan.docker.image.version>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>

View File

@ -27,6 +27,7 @@ import java.util.Random;
import java.util.Set;
import org.eclipse.jetty.server.session.SessionData;
import org.eclipse.jetty.session.infinispan.InfinispanSessionData;
import org.eclipse.jetty.session.infinispan.QueryManager;
import org.eclipse.jetty.session.infinispan.RemoteQueryManager;
import org.eclipse.jetty.session.infinispan.SessionDataMarshaller;
@ -35,11 +36,19 @@ import org.hibernate.search.cfg.Environment;
import org.hibernate.search.cfg.SearchMapping;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.ClientIntelligence;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
import org.infinispan.protostream.FileDescriptorSource;
import org.infinispan.protostream.SerializationContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -49,8 +58,45 @@ public class RemoteQueryManagerTest
{
public static final String DEFAULT_CACHE_NAME = "remote-session-test";
private static final Logger LOG = LoggerFactory.getLogger(RemoteQueryManagerTest.class);
private static final Logger INFINISPAN_LOG =
LoggerFactory.getLogger("org.eclipse.jetty.server.session.infinispan.infinispanLogs");
private String host;
private int port;
GenericContainer infinispan =
new GenericContainer(System.getProperty("infinispan.docker.image.name", "jboss/infinispan-server") +
":" + System.getProperty("infinispan.docker.image.version", "9.4.8.Final"))
.withEnv("APP_USER","theuser")
.withEnv("APP_PASS","foobar")
.withEnv("MGMT_USER", "admin")
.withEnv("MGMT_PASS", "admin")
.waitingFor(new LogMessageWaitStrategy()
.withRegEx(".*Infinispan Server.*started in.*\\s"))
.withExposedPorts(4712,4713,8088,8089,8443,9990,9993,11211,11222,11223,11224)
.withLogConsumer(new Slf4jLogConsumer(INFINISPAN_LOG));
@BeforeEach
public void setup() throws Exception
{
long start = System.currentTimeMillis();
infinispan.start();
host = infinispan.getContainerIpAddress();
port = infinispan.getMappedPort(11222);
LOG.info("Infinispan container started for {}:{} - {}ms", host, port,
System.currentTimeMillis() - start);
}
@AfterEach
public void stop() throws Exception
{
infinispan.stop();
}
@Test
public void test() throws Exception
public void testQuery() throws Exception
{
SearchMapping mapping = new SearchMapping();
mapping.entity(SessionData.class).indexed().providedId().property("expiry", ElementType.FIELD).field();
@ -59,9 +105,13 @@ public class RemoteQueryManagerTest
properties.put(Environment.MODEL_MAPPING, mapping);
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
clientBuilder.withProperties(properties).addServer().host("127.0.0.1").marshaller(new ProtoStreamMarshaller());
clientBuilder.withProperties(properties).addServer()
.host(this.host).port(this.port)
.clientIntelligence(ClientIntelligence.BASIC)
.marshaller(new ProtoStreamMarshaller());
RemoteCacheManager remoteCacheManager = new RemoteCacheManager(clientBuilder.build());
remoteCacheManager.administration().getOrCreateCache("remote-session-test", (String)null);
FileDescriptorSource fds = new FileDescriptorSource();
fds.addProtoFiles("/session.proto");
@ -70,20 +120,17 @@ public class RemoteQueryManagerTest
serCtx.registerProtoFiles(fds);
serCtx.registerMarshaller(new SessionDataMarshaller());
RemoteCache<String, SessionData> cache = remoteCacheManager.getCache(DEFAULT_CACHE_NAME);
ByteArrayOutputStream baos;
try (InputStream is = RemoteQueryManagerTest.class.getClassLoader().getResourceAsStream("session.proto"))
try (InputStream is = RemoteQueryManagerTest.class.getClassLoader().getResourceAsStream("session.proto");
ByteArrayOutputStream baos = new ByteArrayOutputStream())
{
if (is == null)
throw new IllegalStateException("inputstream is null");
baos = new ByteArrayOutputStream();
IO.copy(is, baos);
String content = baos.toString("UTF-8");
remoteCacheManager.getCache("___protobuf_metadata").put("session.proto", content);
}
String content = baos.toString("UTF-8");
remoteCacheManager.getCache("___protobuf_metadata").put("session.proto", content);
RemoteCache<String, SessionData> cache = remoteCacheManager.getCache(DEFAULT_CACHE_NAME);
//put some sessions into the remote cache
int numSessions = 10;
@ -97,7 +144,7 @@ public class RemoteQueryManagerTest
String id = "sd" + i;
//create new sessiondata with random expiry time
long expiryTime = r.nextInt(maxExpiryTime);
SessionData sd = new SessionData(id, "", "", 0, 0, 0, 0);
InfinispanSessionData sd = new InfinispanSessionData(id, "", "", 0, 0, 0, 0);
sd.setLastNode("lastNode");
sd.setExpiry(expiryTime);

View File

@ -0,0 +1,3 @@
org.slf4j.simpleLogger.defaultLogLevel=info
org.slf4j.simpleLogger.log.org.eclipse.jetty.server.session.infinispan.infinispanLogs=debug
org.slf4j.simpleLogger.log.org.eclipse.jetty.server.session.infinispan.RemoteQueryManagerTest=debug

View File

@ -30,10 +30,12 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MXBean;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBean;
@ -151,6 +153,25 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
{
if (o == null)
return null;
if (o instanceof DynamicMBean)
return o;
Class<?> klass = o.getClass();
while (klass != Object.class)
{
MXBean mxbean = klass.getAnnotation(MXBean.class);
if (mxbean != null && mxbean.value())
return o;
String mbeanName = klass.getName() + "MBean";
String mxbeanName = klass.getName() + "MXBean";
Class<?>[] interfaces = klass.getInterfaces();
for (Class<?> type : interfaces)
{
String name = type.getName();
if (name.equals(mbeanName) || name.equals(mxbeanName))
return o;
}
klass = klass.getSuperclass();
}
Object mbean = findMetaData(container, o.getClass()).newInstance(o);
if (mbean instanceof ObjectMBean)
((ObjectMBean)mbean).setMBeanContainer(container);
@ -338,7 +359,9 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
StringBuilder buf = new StringBuilder();
String context = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectContextBasis()) : null;
String context = (mbean instanceof ObjectMBean)
? makeName(((ObjectMBean)mbean).getObjectContextBasis())
: makeName(reflectContextBasis(mbean));
if (context == null && parentObjectName != null)
context = parentObjectName.getKeyProperty("context");
@ -347,7 +370,9 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
buf.append("type=").append(type);
String name = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectNameBasis()) : context;
String name = (mbean instanceof ObjectMBean)
? makeName(((ObjectMBean)mbean).getObjectNameBasis())
: makeName(reflectNameBasis(mbean));
if (name != null && name.length() > 1)
buf.append(",").append("name=").append(name);
@ -394,6 +419,28 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
}
}
private String reflectContextBasis(Object mbean)
{
return reflectBasis(mbean, "jmxContext");
}
private String reflectNameBasis(Object mbean)
{
return reflectBasis(mbean, "jmxName");
}
private String reflectBasis(Object mbean, String methodName)
{
try
{
return (String)mbean.getClass().getMethod(methodName).invoke(mbean);
}
catch (Throwable x)
{
return null;
}
}
/**
* @param basis name to strip of special characters.
* @return normalized name

View File

@ -19,21 +19,11 @@
package org.eclipse.jetty.osgi.test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.util.StringUtil;
@ -43,16 +33,12 @@ import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.options.WrappedUrlProvisionOption.OverwriteMode;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
import org.ops4j.pax.url.mvn.internal.AetherBasedResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
@ -195,7 +181,9 @@ public class TestOSGiUtil
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-jndi").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-plus").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-annotations").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-core").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-core-server").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-core-client").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-core-common").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-util").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-util-server").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("websocket-jetty-api").versionAsInProject().start());

View File

@ -106,7 +106,7 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
{
String origin = node.getAttribute(_originAttributeName);
if (!StringUtil.isBlank(origin) && origin.startsWith(DefaultsDescriptor.class.getSimpleName()))
mapping.setDefault(true);
mapping.setFromDefaultDescriptor(true);
}
}

View File

@ -100,7 +100,7 @@ public class TestQuickStart
server.dumpStdErr();
//verify that FooServlet is now mapped to / and not the DefaultServlet
ServletHolder sh = webapp.getServletHandler().getMappedServlet("/").getResource();
ServletHolder sh = webapp.getServletHandler().getMappedServlet("/").getServletHolder();
assertNotNull(sh);
assertEquals("foo", sh.getName());
}

View File

@ -54,6 +54,12 @@
<Set name="mapColumn">
<Property name="jetty.session.jdbc.schema.mapColumn" default="map" />
</Set>
<Set name="schemaName">
<Property name="jetty.session.jdbc.schema.schemaName" />
</Set>
<Set name="catalogName">
<Property name="jetty.session.jdbc.schema.catalogName" />
</Set>
<Set name="tableName">
<Property name="jetty.session.jdbc.schema.table" default="JettySessions" />
</Set>

View File

@ -55,3 +55,14 @@ db-connection-type=datasource
#jetty.session.jdbc.schema.maxIntervalColumn=maxInterval
#jetty.session.jdbc.schema.mapColumn=map
#jetty.session.jdbc.schema.table=JettySessions
# Optional name of the schema used to identify where the session table is defined in the database:
# "" - empty string, no schema name
# "INFERRED" - special string meaning infer from the current db connection
# name - a string defined by the user
#jetty.session.jdbc.schema.schemaName
# Optional name of the catalog used to identify where the session table is defined in the database:
# "" - empty string, no catalog name
# "INFERRED" - special string meaning infer from the current db connection
# name - a string defined by the user
#jetty.session.jdbc.schema.catalogName

View File

@ -0,0 +1,99 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.AsyncContext;
import org.eclipse.jetty.util.Attributes;
class AsyncAttributes extends Attributes.Wrapper
{
private final String _requestURI;
private final String _contextPath;
private final String _pathInContext;
private ServletPathMapping _mapping;
private final String _queryString;
public AsyncAttributes(Attributes attributes, String requestUri, String contextPath, String pathInContext, ServletPathMapping mapping, String queryString)
{
super(attributes);
_requestURI = requestUri;
_contextPath = contextPath;
_pathInContext = pathInContext;
_mapping = mapping;
_queryString = queryString;
}
@Override
public Object getAttribute(String key)
{
switch (key)
{
case AsyncContext.ASYNC_REQUEST_URI:
return _requestURI;
case AsyncContext.ASYNC_CONTEXT_PATH:
return _contextPath;
case AsyncContext.ASYNC_SERVLET_PATH:
return _mapping == null ? null : _mapping.getServletPath();
case AsyncContext.ASYNC_PATH_INFO:
return _mapping == null ? _pathInContext : _mapping.getPathInfo();
case AsyncContext.ASYNC_QUERY_STRING:
return _queryString;
case AsyncContext.ASYNC_MAPPING:
return _mapping;
default:
return super.getAttribute(key);
}
}
@Override
public Set<String> getAttributeNameSet()
{
Set<String> set = new HashSet<>(super.getAttributeNameSet());
set.add(AsyncContext.ASYNC_REQUEST_URI);
set.add(AsyncContext.ASYNC_CONTEXT_PATH);
set.add(AsyncContext.ASYNC_SERVLET_PATH);
set.add(AsyncContext.ASYNC_PATH_INFO);
set.add(AsyncContext.ASYNC_QUERY_STRING);
set.add(AsyncContext.ASYNC_MAPPING);
return set;
}
@Override
public void setAttribute(String key, Object value)
{
switch (key)
{
case AsyncContext.ASYNC_REQUEST_URI:
case AsyncContext.ASYNC_CONTEXT_PATH:
case AsyncContext.ASYNC_SERVLET_PATH:
case AsyncContext.ASYNC_PATH_INFO:
case AsyncContext.ASYNC_QUERY_STRING:
case AsyncContext.ASYNC_MAPPING:
// Ignore sets for these reserved names as this class is applied
// we will always override these particular attributes.
break;
default:
super.setAttribute(key, value);
break;
}
}
}

View File

@ -34,7 +34,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
private final Context _context;
private final AsyncContextState _asyncContext;
private final HttpURI _baseURI;
private volatile HttpChannelState _state;
private final HttpChannelState _state;
private ServletContext _dispatchContext;
private String _dispatchPath;
private volatile Scheduler.Task _timeoutTask;
@ -53,33 +53,9 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
_state = state;
_baseURI = baseURI;
// If we haven't been async dispatched before
if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI) == null)
{
// We are setting these attributes during startAsync, when the spec implies that
// they are only available after a call to AsyncContext.dispatch(...);
// have we been forwarded before?
String uri = (String)baseRequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
if (uri != null)
{
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI, uri);
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH, baseRequest.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, baseRequest.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO, baseRequest.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING, baseRequest.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, baseRequest.getAttribute(RequestDispatcher.FORWARD_MAPPING));
}
else
{
baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI, baseRequest.getRequestURI());
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH, baseRequest.getContextPath());
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, baseRequest.getServletPath());
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO, baseRequest.getPathInfo());
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING, baseRequest.getQueryString());
baseRequest.setAttribute(AsyncContext.ASYNC_MAPPING, baseRequest.getHttpServletMapping());
}
}
// We are setting these attributes during startAsync, when the spec implies that
// they are only available after a call to AsyncContext.dispatch(...);
baseRequest.setAsyncAttributes();
}
public HttpURI getBaseURI()

View File

@ -26,7 +26,6 @@ import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -101,14 +100,7 @@ public class Dispatcher implements RequestDispatcher
}
else
{
IncludeAttributes attr = new IncludeAttributes(old_attr);
attr._requestURI = _uri.getPath();
attr._contextPath = _contextHandler.getContextPath();
attr._servletPath = null; // set by ServletHandler
attr._pathInfo = _pathInContext;
attr._query = _uri.getQuery();
attr._mapping = null; //set by ServletHandler
IncludeAttributes attr = new IncludeAttributes(old_attr, _uri.getPath(), _contextHandler.getContextPath(), _pathInContext, _uri.getQuery());
if (attr._query != null)
baseRequest.mergeQueryParameters(baseRequest.getQueryString(), attr._query);
baseRequest.setAttributes(attr);
@ -147,7 +139,7 @@ public class Dispatcher implements RequestDispatcher
final String old_context_path = baseRequest.getContextPath();
final String old_servlet_path = baseRequest.getServletPath();
final String old_path_info = baseRequest.getPathInfo();
final HttpServletMapping old_mapping = baseRequest.getHttpServletMapping();
final ServletPathMapping old_mapping = baseRequest.getServletPathMapping();
final MultiMap<String> old_query_params = baseRequest.getQueryParameters();
final Attributes old_attr = baseRequest.getAttributes();
@ -163,30 +155,26 @@ public class Dispatcher implements RequestDispatcher
}
else
{
ForwardAttributes attr = new ForwardAttributes(old_attr);
//If we have already been forwarded previously, then keep using the established
//original value. Otherwise, this is the first forward and we need to establish the values.
//Note: the established value on the original request for pathInfo and
//for queryString is allowed to be null, but cannot be null for the other values.
if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
{
attr._pathInfo = (String)old_attr.getAttribute(FORWARD_PATH_INFO);
attr._query = (String)old_attr.getAttribute(FORWARD_QUERY_STRING);
attr._requestURI = (String)old_attr.getAttribute(FORWARD_REQUEST_URI);
attr._contextPath = (String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
attr._servletPath = (String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
attr._mapping = (HttpServletMapping)old_attr.getAttribute(FORWARD_MAPPING);
}
else
{
attr._pathInfo = old_path_info;
attr._query = old_uri.getQuery();
attr._requestURI = old_uri.getPath();
attr._contextPath = old_context_path;
attr._servletPath = old_servlet_path;
attr._mapping = old_mapping;
}
// If we have already been forwarded previously, then keep using the established
// original value. Otherwise, this is the first forward and we need to establish the values.
// Note: the established value on the original request for pathInfo and
// for queryString is allowed to be null, but cannot be null for the other values.
// Note: the pathInfo is passed as the pathInContext since it is only used when there is
// no mapping, and when there is no mapping the pathInfo is the pathInContext.
// TODO Ultimately it is intended for the request to carry the pathInContext for easy access
ForwardAttributes attr = old_attr.getAttribute(FORWARD_REQUEST_URI) != null
? new ForwardAttributes(old_attr,
(String)old_attr.getAttribute(FORWARD_REQUEST_URI),
(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH),
(String)old_attr.getAttribute(FORWARD_PATH_INFO),
(ServletPathMapping)old_attr.getAttribute(FORWARD_MAPPING),
(String)old_attr.getAttribute(FORWARD_QUERY_STRING))
: new ForwardAttributes(old_attr,
old_uri.getPath(),
old_context_path,
baseRequest.getPathInfo(), // TODO replace with pathInContext
old_mapping,
old_uri.getQuery());
String query = _uri.getQuery();
if (query == null)
@ -194,6 +182,7 @@ public class Dispatcher implements RequestDispatcher
baseRequest.setHttpURI(HttpURI.build(old_uri, _uri.getPath(), _uri.getParam(), query));
baseRequest.setContextPath(_contextHandler.getContextPath());
baseRequest.setServletPathMapping(null);
baseRequest.setServletPath(null);
baseRequest.setPathInfo(_pathInContext);
@ -257,16 +246,20 @@ public class Dispatcher implements RequestDispatcher
private class ForwardAttributes extends Attributes.Wrapper
{
String _requestURI;
String _contextPath;
String _servletPath;
String _pathInfo;
String _query;
HttpServletMapping _mapping;
private final String _requestURI;
private final String _contextPath;
private final String _pathInContext;
private final ServletPathMapping _servletPathMapping;
private final String _query;
ForwardAttributes(Attributes attributes)
public ForwardAttributes(Attributes attributes, String requestURI, String contextPath, String pathInContext, ServletPathMapping mapping, String query)
{
super(attributes);
_requestURI = requestURI;
_contextPath = contextPath;
_pathInContext = pathInContext;
_servletPathMapping = mapping;
_query = query;
}
@Override
@ -277,22 +270,23 @@ public class Dispatcher implements RequestDispatcher
switch (key)
{
case FORWARD_PATH_INFO:
return _pathInfo;
return _servletPathMapping == null ? _pathInContext : _servletPathMapping.getPathInfo();
case FORWARD_REQUEST_URI:
return _requestURI;
case FORWARD_SERVLET_PATH:
return _servletPath;
return _servletPathMapping == null ? null : _servletPathMapping.getServletPath();
case FORWARD_CONTEXT_PATH:
return _contextPath;
case FORWARD_QUERY_STRING:
return _query;
case FORWARD_MAPPING:
return _mapping;
return _servletPathMapping;
default:
break;
}
}
// If we are forwarded then we hide include attributes
if (key.startsWith(__INCLUDE_PREFIX))
return null;
@ -312,18 +306,12 @@ public class Dispatcher implements RequestDispatcher
if (_named == null)
{
if (_pathInfo != null)
set.add(FORWARD_PATH_INFO);
else
set.remove(FORWARD_PATH_INFO);
set.add(FORWARD_PATH_INFO);
set.add(FORWARD_REQUEST_URI);
set.add(FORWARD_SERVLET_PATH);
set.add(FORWARD_CONTEXT_PATH);
set.add(FORWARD_MAPPING);
if (_query != null)
set.add(FORWARD_QUERY_STRING);
else
set.remove(FORWARD_QUERY_STRING);
set.add(FORWARD_QUERY_STRING);
}
return set;
@ -332,39 +320,11 @@ public class Dispatcher implements RequestDispatcher
@Override
public void setAttribute(String key, Object value)
{
if (_named == null && key.startsWith("javax.servlet."))
{
switch (key)
{
case FORWARD_PATH_INFO:
_pathInfo = (String)value;
return;
case FORWARD_REQUEST_URI:
_requestURI = (String)value;
return;
case FORWARD_SERVLET_PATH:
_servletPath = (String)value;
return;
case FORWARD_CONTEXT_PATH:
_contextPath = (String)value;
return;
case FORWARD_QUERY_STRING:
_query = (String)value;
return;
case FORWARD_MAPPING:
_mapping = (HttpServletMapping)value;
return;
default:
if (value == null)
_attributes.removeAttribute(key);
else
_attributes.setAttribute(key, value);
}
}
else if (value == null)
_attributes.removeAttribute(key);
else
_attributes.setAttribute(key, value);
// Allow any attribute to be set, even if a reserved name. If a reserved
// name is set here, it will be hidden by this class during the forward,
// but revealed after the forward is complete just as if the reserved name
// attribute had be set by the application before the forward.
_attributes.setAttribute(key, value);
}
@Override
@ -388,16 +348,19 @@ public class Dispatcher implements RequestDispatcher
private class IncludeAttributes extends Attributes.Wrapper
{
String _requestURI;
String _contextPath;
String _servletPath;
String _pathInfo;
String _query;
HttpServletMapping _mapping;
private final String _requestURI;
private final String _contextPath;
private final String _pathInContext;
private ServletPathMapping _servletPathMapping; // Set later by ServletHandler
private final String _query;
IncludeAttributes(Attributes attributes)
public IncludeAttributes(Attributes attributes, String requestURI, String contextPath, String pathInContext, String query)
{
super(attributes);
_requestURI = requestURI;
_contextPath = contextPath;
_pathInContext = pathInContext;
_query = query;
}
@Override
@ -408,9 +371,9 @@ public class Dispatcher implements RequestDispatcher
switch (key)
{
case INCLUDE_PATH_INFO:
return _pathInfo;
return _servletPathMapping == null ? _pathInContext : _servletPathMapping.getPathInfo();
case INCLUDE_SERVLET_PATH:
return _servletPath;
return _servletPathMapping == null ? null : _servletPathMapping.getServletPath();
case INCLUDE_CONTEXT_PATH:
return _contextPath;
case INCLUDE_QUERY_STRING:
@ -418,13 +381,11 @@ public class Dispatcher implements RequestDispatcher
case INCLUDE_REQUEST_URI:
return _requestURI;
case INCLUDE_MAPPING:
return _mapping;
return _servletPathMapping;
default:
break;
}
}
else if (key.startsWith(__INCLUDE_PREFIX))
return null;
return _attributes.getAttribute(key);
}
@ -441,18 +402,12 @@ public class Dispatcher implements RequestDispatcher
if (_named == null)
{
if (_pathInfo != null)
set.add(INCLUDE_PATH_INFO);
else
set.remove(INCLUDE_PATH_INFO);
set.add(INCLUDE_PATH_INFO);
set.add(INCLUDE_REQUEST_URI);
set.add(INCLUDE_SERVLET_PATH);
set.add(INCLUDE_CONTEXT_PATH);
set.add(INCLUDE_MAPPING);
if (_query != null)
set.add(INCLUDE_QUERY_STRING);
else
set.remove(INCLUDE_QUERY_STRING);
set.add(INCLUDE_QUERY_STRING);
}
return set;
@ -461,38 +416,11 @@ public class Dispatcher implements RequestDispatcher
@Override
public void setAttribute(String key, Object value)
{
if (_named == null && key.startsWith("javax.servlet."))
{
switch (key)
{
case INCLUDE_PATH_INFO:
_pathInfo = (String)value;
return;
case INCLUDE_REQUEST_URI:
_requestURI = (String)value;
return;
case INCLUDE_SERVLET_PATH:
_servletPath = (String)value;
return;
case INCLUDE_CONTEXT_PATH:
_contextPath = (String)value;
return;
case INCLUDE_QUERY_STRING:
_query = (String)value;
return;
case INCLUDE_MAPPING:
_mapping = (HttpServletMapping)value;
return;
default:
if (value == null)
_attributes.removeAttribute(key);
else
_attributes.setAttribute(key, value);
}
}
else if (value == null)
_attributes.removeAttribute(key);
if (_servletPathMapping == null && _named == null && INCLUDE_MAPPING.equals(key))
_servletPathMapping = (ServletPathMapping)value;
else
// Allow any attribute to be set, even if a reserved name. If a reserved
// name is set here, it will be revealed after the include is complete.
_attributes.setAttribute(key, value);
}

View File

@ -419,8 +419,17 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
abort(x);
else
{
_response.resetContent();
sendResponseAndComplete();
try
{
_response.resetContent();
sendResponseAndComplete();
}
catch (Throwable t)
{
if (x != t)
x.addSuppressed(t);
abort(x);
}
}
}
finally

View File

@ -475,8 +475,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
synchronized (_channelState)
{
_state = State.CLOSED;
releaseBuffer();
}
releaseBuffer();
}
@Override
@ -601,10 +601,13 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public ByteBuffer getBuffer()
{
return _aggregate;
synchronized (_channelState)
{
return acquireBuffer();
}
}
public ByteBuffer acquireBuffer()
private ByteBuffer acquireBuffer()
{
if (_aggregate == null)
_aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers());
@ -1362,10 +1365,13 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public void resetBuffer()
{
_interceptor.resetBuffer();
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
_written = 0;
synchronized (_channelState)
{
_interceptor.resetBuffer();
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
_written = 0;
}
}
@Override

View File

@ -63,7 +63,6 @@ import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.MappingMatch;
import javax.servlet.http.Part;
import javax.servlet.http.PushBuilder;
@ -83,8 +82,6 @@ import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
@ -193,85 +190,6 @@ public class Request implements HttpServletRequest
return null;
}
public static HttpServletMapping getServletMapping(PathSpec pathSpec, String servletPath, String servletName)
{
final MappingMatch match;
final String mapping;
if (pathSpec instanceof ServletPathSpec)
{
switch (pathSpec.getGroup())
{
case ROOT:
match = MappingMatch.CONTEXT_ROOT;
mapping = "";
break;
case DEFAULT:
match = MappingMatch.DEFAULT;
mapping = "/";
break;
case EXACT:
match = MappingMatch.EXACT;
mapping = servletPath.startsWith("/") ? servletPath.substring(1) : servletPath;
break;
case SUFFIX_GLOB:
match = MappingMatch.EXTENSION;
int dot = servletPath.lastIndexOf('.');
mapping = servletPath.substring(0, dot);
break;
case PREFIX_GLOB:
match = MappingMatch.PATH;
mapping = servletPath;
break;
default:
match = null;
mapping = servletPath;
break;
}
}
else
{
match = null;
mapping = servletPath;
}
return new HttpServletMapping()
{
@Override
public String getMatchValue()
{
return (mapping == null ? "" : mapping);
}
@Override
public String getPattern()
{
if (pathSpec != null)
return pathSpec.getDeclaration();
return "";
}
@Override
public String getServletName()
{
return (servletName == null ? "" : servletName);
}
@Override
public MappingMatch getMappingMatch()
{
return match;
}
@Override
public String toString()
{
return "HttpServletMapping{matchValue=" + getMatchValue() +
", pattern=" + getPattern() + ", servletName=" + getServletName() +
", mappingMatch=" + getMappingMatch() + "}";
}
};
}
private final HttpChannel _channel;
private final List<ServletRequestAttributeListener> _requestAttributeListeners = new ArrayList<>();
private final HttpInput _input;
@ -283,7 +201,7 @@ public class Request implements HttpServletRequest
private String _contextPath;
private String _servletPath;
private String _pathInfo;
private PathSpec _pathSpec;
private ServletPathMapping _servletPathMapping;
private boolean _secure;
private String _asyncNotSupportedSource = null;
private boolean _newContext;
@ -739,7 +657,7 @@ public class Request implements HttpServletRequest
public Attributes getAttributes()
{
if (_attributes == null)
_attributes = new AttributesMap();
_attributes = new ServletAttributes();
return _attributes;
}
@ -1752,7 +1670,7 @@ public class Request implements HttpServletRequest
// TODO this is not really right for CONNECT
path = _uri.isAbsolute() ? "/" : null;
else if (encoded.startsWith("/"))
path = (encoded.length() == 1) ? "/" : URIUtil.canonicalPath(URIUtil.decodePath(encoded));
path = (encoded.length() == 1) ? "/" : _uri.getDecodedPath();
else if ("*".equals(encoded) || HttpMethod.CONNECT.is(getMethod()))
path = encoded;
else
@ -1815,7 +1733,7 @@ public class Request implements HttpServletRequest
_attributes = Attributes.unwrap(_attributes);
if (_attributes != null)
{
if (AttributesMap.class.equals(_attributes.getClass()))
if (ServletAttributes.class.equals(_attributes.getClass()))
_attributes.clearAttributes();
else
_attributes = null;
@ -1841,7 +1759,6 @@ public class Request implements HttpServletRequest
_queryParameters = null;
_contentParameters = null;
_parameters = null;
_pathSpec = null;
_contentParamsExtracted = false;
_inputState = INPUT_NONE;
_multiParts = null;
@ -1896,7 +1813,7 @@ public class Request implements HttpServletRequest
LOG.warn("Deprecated: org.eclipse.jetty.server.sendContent");
if (_attributes == null)
_attributes = new AttributesMap();
_attributes = new ServletAttributes();
_attributes.setAttribute(name, value);
if (!_requestAttributeListeners.isEmpty())
@ -1919,6 +1836,76 @@ public class Request implements HttpServletRequest
_attributes = attributes;
}
public void setAsyncAttributes()
{
// Return if we have been async dispatched before.
if (getAttribute(AsyncContext.ASYNC_REQUEST_URI) != null)
return;
// Unwrap the _attributes to get the base attributes instance.
Attributes baseAttributes;
if (_attributes == null)
baseAttributes = _attributes = new ServletAttributes();
else
baseAttributes = Attributes.unwrap(_attributes);
// We cannot use a apply AsyncAttribute via #setAttributes as that
// will wrap over any dispatch specific attribute wrappers (eg.
// Dispatcher#ForwardAttributes). Async attributes must persist
// after the current dispatch, so they must be set under any other
// wrappers.
String fwdRequestURI = (String)getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
if (fwdRequestURI == null)
{
if (baseAttributes instanceof ServletAttributes)
{
// The baseAttributes map is our ServletAttributes, so we can set the async
// attributes there, under any other wrappers.
((ServletAttributes)baseAttributes).setAsyncAttributes(getRequestURI(),
getContextPath(),
getPathInfo(), // TODO change to pathInContext when cheaply available
getServletPathMapping(),
getQueryString());
}
else
{
// We cannot find our ServletAttributes instance, so just set directly and hope
// whatever non jetty wrappers that have been applied will do the right thing.
_attributes.setAttribute(AsyncContext.ASYNC_REQUEST_URI, getRequestURI());
_attributes.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH, getContextPath());
_attributes.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, getServletPath());
_attributes.setAttribute(AsyncContext.ASYNC_PATH_INFO, getPathInfo());
_attributes.setAttribute(AsyncContext.ASYNC_QUERY_STRING, getQueryString());
_attributes.setAttribute(AsyncContext.ASYNC_MAPPING, getHttpServletMapping());
}
}
else
{
if (baseAttributes instanceof ServletAttributes)
{
// The baseAttributes map is our ServletAttributes, so we can set the async
// attributes there, under any other wrappers.
((ServletAttributes)baseAttributes).setAsyncAttributes(fwdRequestURI,
(String)getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH),
(String)getAttribute(RequestDispatcher.FORWARD_PATH_INFO),
(ServletPathMapping)getAttribute(RequestDispatcher.FORWARD_MAPPING),
(String)getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
}
else
{
// We cannot find our ServletAttributes instance, so just set directly and hope
// whatever non jetty wrappers that have been applied will do the right thing.
_attributes.setAttribute(AsyncContext.ASYNC_REQUEST_URI, fwdRequestURI);
_attributes.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH, getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
_attributes.setAttribute(AsyncContext.ASYNC_SERVLET_PATH, getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
_attributes.setAttribute(AsyncContext.ASYNC_PATH_INFO, getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
_attributes.setAttribute(AsyncContext.ASYNC_QUERY_STRING, getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
_attributes.setAttribute(AsyncContext.ASYNC_MAPPING, getAttribute(RequestDispatcher.FORWARD_MAPPING));
}
}
}
/**
* Set the authentication.
*
@ -2358,19 +2345,34 @@ public class Request implements HttpServletRequest
throw new ServletException("HttpServletRequest.upgrade() not supported in Jetty");
}
public void setPathSpec(PathSpec pathSpec)
/**
* Set the servletPathMapping, the servletPath and the pathInfo.
* TODO remove the side effect on servletPath and pathInfo by removing those fields.
* @param servletPathMapping The mapping used to return from {@link #getHttpServletMapping()}
*/
public void setServletPathMapping(ServletPathMapping servletPathMapping)
{
_pathSpec = pathSpec;
_servletPathMapping = servletPathMapping;
if (servletPathMapping == null)
{
// TODO reset the servletPath and pathInfo, but currently cannot do that
// as we don't know the pathInContext.
}
else
{
_servletPath = servletPathMapping.getServletPath();
_pathInfo = servletPathMapping.getPathInfo();
}
}
public PathSpec getPathSpec()
public ServletPathMapping getServletPathMapping()
{
return _pathSpec;
return _servletPathMapping;
}
@Override
public HttpServletMapping getHttpServletMapping()
{
return Request.getServletMapping(_pathSpec, _servletPath, getServletName());
return _servletPathMapping;
}
}

View File

@ -0,0 +1,78 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
import java.util.Set;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
/**
* An implementation of Attributes that supports the standard async attributes.
*
* This implementation delegates to an internal {@link AttributesMap} instance, which
* can optionally be wrapped with a {@link AsyncAttributes} instance. This allows async
* attributes to be applied underneath any other attribute wrappers.
*/
public class ServletAttributes implements Attributes
{
private final Attributes _attributes = new AttributesMap();
private AsyncAttributes _asyncAttributes;
public void setAsyncAttributes(String requestURI, String contextPath, String pathInContext, ServletPathMapping servletPathMapping, String queryString)
{
_asyncAttributes = new AsyncAttributes(_attributes, requestURI, contextPath, pathInContext, servletPathMapping, queryString);
}
private Attributes getAttributes()
{
return (_asyncAttributes == null) ? _attributes : _asyncAttributes;
}
@Override
public void removeAttribute(String name)
{
getAttributes().removeAttribute(name);
}
@Override
public void setAttribute(String name, Object attribute)
{
getAttributes().setAttribute(name, attribute);
}
@Override
public Object getAttribute(String name)
{
return getAttributes().getAttribute(name);
}
@Override
public Set<String> getAttributeNameSet()
{
return getAttributes().getAttributeNameSet();
}
@Override
public void clearAttributes()
{
getAttributes().clearAttributes();
_asyncAttributes = null;
}
}

View File

@ -0,0 +1,159 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.MappingMatch;
import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
/**
* Implementation of HttpServletMapping.
*
* Represents the application of a {@link ServletPathSpec} to a specific path
* that resulted in a mapping to a {@link javax.servlet.Servlet}.
* As well as supporting the standard {@link HttpServletMapping} methods, this
* class also carries fields, which can be precomputed for the implementation
* of {@link HttpServletRequest#getServletPath()} and
* {@link HttpServletRequest#getPathInfo()}
*/
public class ServletPathMapping implements HttpServletMapping
{
private final MappingMatch _mappingMatch;
private final String _matchValue;
private final String _pattern;
private final String _servletName;
private final String _servletPath;
private final String _pathInfo;
public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInContext)
{
_servletName = (servletName == null ? "" : servletName);
_pattern = pathSpec == null ? null : pathSpec.getDeclaration();
if (pathSpec instanceof ServletPathSpec && pathInContext != null)
{
switch (pathSpec.getGroup())
{
case ROOT:
_mappingMatch = MappingMatch.CONTEXT_ROOT;
_matchValue = "";
_servletPath = "";
_pathInfo = "/";
break;
case DEFAULT:
_mappingMatch = MappingMatch.DEFAULT;
_matchValue = "";
_servletPath = pathInContext;
_pathInfo = null;
break;
case EXACT:
_mappingMatch = MappingMatch.EXACT;
_matchValue = _pattern.startsWith("/") ? _pattern.substring(1) : _pattern;
_servletPath = _pattern;
_pathInfo = null;
break;
case PREFIX_GLOB:
_mappingMatch = MappingMatch.PATH;
_servletPath = pathSpec.getPrefix();
// TODO avoid the substring on the known servletPath!
_matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath;
_pathInfo = pathSpec.getPathInfo(pathInContext);
break;
case SUFFIX_GLOB:
_mappingMatch = MappingMatch.EXTENSION;
int dot = pathInContext.lastIndexOf('.');
_matchValue = pathInContext.substring(pathInContext.startsWith("/") ? 1 : 0, dot);
_servletPath = pathInContext;
_pathInfo = null;
break;
case MIDDLE_GLOB:
_mappingMatch = null;
_matchValue = "";
_servletPath = pathInContext;
_pathInfo = null;
break;
default:
throw new IllegalStateException();
}
}
else
{
_mappingMatch = null;
_matchValue = "";
_servletPath = pathInContext;
_pathInfo = null;
}
}
@Override
public String getMatchValue()
{
return _matchValue;
}
@Override
public String getPattern()
{
return _pattern;
}
@Override
public String getServletName()
{
return _servletName;
}
@Override
public MappingMatch getMappingMatch()
{
return _mappingMatch;
}
public String getServletPath()
{
return _servletPath;
}
public String getPathInfo()
{
return _pathInfo;
}
@Override
public String toString()
{
return "ServletPathMapping{" +
"matchValue=" + _matchValue +
", pattern=" + _pattern +
", servletName=" + _servletName +
", mappingMatch=" + _mappingMatch +
", servletPath=" + _servletPath +
", pathInfo=" + _pathInfo +
"}";
}
}

View File

@ -173,11 +173,23 @@ public class ContextHandlerCollection extends HandlerCollection
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
Handlers handlers = _handlers.get();
if (handlers == null)
Mapping mapping = (Mapping)_handlers.get();
// Handle no contexts
if (mapping == null)
return;
Handler[] handlers = mapping.getHandlers();
if (handlers == null || handlers.length == 0)
return;
Mapping mapping = (Mapping)handlers;
// handle only a single context.
if (handlers.length == 1)
{
handlers[0].handle(target, baseRequest, request, response);
return;
}
// handle async dispatch to specific context
HttpChannelState async = baseRequest.getHttpChannelState();
if (async.isAsync())
{
@ -194,6 +206,7 @@ public class ContextHandlerCollection extends HandlerCollection
}
}
// handle many contexts
if (target.startsWith("/"))
{
Trie<Map.Entry<String, Branch[]>> pathBranches = mapping._pathBranches;
@ -226,11 +239,9 @@ public class ContextHandlerCollection extends HandlerCollection
}
else
{
if (mapping.getHandlers() == null)
return;
for (int i = 0; i < mapping.getHandlers().length; i++)
for (Handler handler : handlers)
{
mapping.getHandlers()[i].handle(target, baseRequest, request, response);
handler.handle(target, baseRequest, request, response);
if (baseRequest.isHandled())
return;
}

View File

@ -298,7 +298,7 @@ public class ErrorHandler extends AbstractHandler
// TODO error page may cause a BufferOverflow. In which case we try
// TODO again with stacks disabled. If it still overflows, it is
// TODO written without a body.
ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().acquireBuffer();
ByteBuffer buffer = baseRequest.getResponse().getHttpOutput().getBuffer();
ByteBufferOutputStream out = new ByteBufferOutputStream(buffer);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, charset));

View File

@ -467,7 +467,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
{
//only write the session out at this point if the attributes changed. If only
//the lastAccess/expiry time changed defer the write until the last request exits
if (session.getSessionData().isDirty() && _flushOnResponseCommit)
if (session.isValid() && session.getSessionData().isDirty() && _flushOnResponseCommit)
{
if (LOG.isDebugEnabled())
LOG.debug("Flush session {} on response commit", session);

View File

@ -66,9 +66,11 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public static class SessionTableSchema
{
public static final int MAX_INTERVAL_NOT_SET = -999;
public static final String INFERRED = "INFERRED";
protected DatabaseAdaptor _dbAdaptor;
protected String _schemaName = null;
protected String _catalogName = null;
protected String _tableName = "JettySessions";
protected String _idColumn = "sessionId";
protected String _contextPathColumn = "contextPath";
@ -87,7 +89,20 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
{
_dbAdaptor = dbadaptor;
}
public void setCatalogName(String catalogName)
{
if (catalogName != null && StringUtil.isBlank(catalogName))
_catalogName = null;
else
_catalogName = catalogName;
}
public String getCatalogName()
{
return _catalogName;
}
public String getSchemaName()
{
return _schemaName;
@ -95,8 +110,10 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public void setSchemaName(String schemaName)
{
checkNotNull(schemaName);
_schemaName = schemaName;
if (schemaName != null && StringUtil.isBlank(schemaName))
_schemaName = null;
else
_schemaName = schemaName;
}
public String getTableName()
@ -484,28 +501,48 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
//make the session table if necessary
String tableName = _dbAdaptor.convertIdentifier(getTableName());
String schemaName = _dbAdaptor.convertIdentifier(getSchemaName());
try (ResultSet result = metaData.getTables(null, schemaName, tableName, null))
if (INFERRED.equalsIgnoreCase(schemaName))
{
//use the value from the connection -
//NOTE that this value will also now be prepended to ALL
//table names in queries/updates.
schemaName = connection.getSchema();
setSchemaName(schemaName);
}
String catalogName = _dbAdaptor.convertIdentifier(getCatalogName());
if (INFERRED.equalsIgnoreCase(catalogName))
{
//use the value from the connection
catalogName = connection.getCatalog();
setCatalogName(catalogName);
}
try (ResultSet result = metaData.getTables(catalogName, schemaName, tableName, null))
{
if (!result.next())
{
if (LOG.isDebugEnabled())
LOG.debug("Creating table {} schema={} catalog={}", tableName, schemaName, catalogName);
//table does not exist, so create it
statement.executeUpdate(getCreateStatementAsString());
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Not creating table {} schema={} catalog={}", tableName, schemaName, catalogName);
//session table exists, check it has maxinterval column
ResultSet colResult = null;
try
{
colResult = metaData.getColumns(null, schemaName, tableName,
colResult = metaData.getColumns(catalogName, schemaName, tableName,
_dbAdaptor.convertIdentifier(getMaxIntervalColumn()));
}
catch (SQLException sqlEx)
{
LOG.warn("Problem checking if " + getTableName() +
" table contains " + getMaxIntervalColumn() + " column. Ensure table contains column definition: \"" +
getMaxIntervalColumn() + " long not null default -999\"");
LOG.warn("Problem checking if {} table contains {} column. Ensure table contains column with definition: long not null default -999",
getTableName(), getMaxIntervalColumn());
throw sqlEx;
}
try
@ -519,9 +556,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
}
catch (SQLException sqlEx)
{
LOG.warn("Problem adding " + getMaxIntervalColumn() +
" column. Ensure table contains column definition: \"" + getMaxIntervalColumn() +
" long not null default -999\"");
LOG.warn("Problem adding {} column. Ensure table contains column definition: long not null default -999", getMaxIntervalColumn());
throw sqlEx;
}
}
@ -538,7 +573,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
boolean index1Exists = false;
boolean index2Exists = false;
try (ResultSet result = metaData.getIndexInfo(null, schemaName, tableName, false, true))
try (ResultSet result = metaData.getIndexInfo(catalogName, schemaName, tableName, false, true))
{
while (result.next())
{
@ -559,8 +594,8 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
@Override
public String toString()
{
return String.format("%s[%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s]", super.toString(),
_schemaName, _tableName, _idColumn, _contextPathColumn, _virtualHostColumn, _cookieTimeColumn, _createTimeColumn,
return String.format("%s[%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s]", super.toString(),
_catalogName, _schemaName, _tableName, _idColumn, _contextPathColumn, _virtualHostColumn, _cookieTimeColumn, _createTimeColumn,
_expiryTimeColumn, _accessTimeColumn, _lastAccessTimeColumn, _lastNodeColumn, _lastSavedTimeColumn, _maxIntervalColumn);
}
}

View File

@ -47,6 +47,7 @@ import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.MappingMatch;
import javax.servlet.http.Part;
import javax.servlet.http.PushBuilder;
@ -86,6 +87,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -1427,69 +1429,6 @@ public class RequestTest
assertThat(response, containsString("Hello World"));
}
@Test
public void testHttpServletMapping() throws Exception
{
String request = "GET / HTTP/1.1\n" +
"Host: whatever\n" +
"Connection: close\n" +
"\n";
_server.stop();
PathMappingHandler handler = new PathMappingHandler(null, null, null);
_server.setHandler(handler);
_server.start();
String response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=, pattern=, servletName=, mappingMatch=null}"));
_server.stop();
ServletPathSpec spec = new ServletPathSpec("");
handler = new PathMappingHandler(spec, spec.getPathMatch("foo"), "Something");
_server.setHandler(handler);
_server.start();
response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=, pattern=, servletName=Something, mappingMatch=CONTEXT_ROOT}"));
_server.stop();
spec = new ServletPathSpec("/");
handler = new PathMappingHandler(spec, "", "Default");
_server.setHandler(handler);
_server.start();
response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/, pattern=/, servletName=Default, mappingMatch=DEFAULT}"));
_server.stop();
spec = new ServletPathSpec("/foo/*");
handler = new PathMappingHandler(spec, spec.getPathMatch("/foo/bar"), "BarServlet");
_server.setHandler(handler);
_server.start();
response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/foo, pattern=/foo/*, servletName=BarServlet, mappingMatch=PATH}"));
_server.stop();
spec = new ServletPathSpec("*.jsp");
handler = new PathMappingHandler(spec, spec.getPathMatch("/foo/bar.jsp"), "JspServlet");
_server.setHandler(handler);
_server.start();
response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=/foo/bar, pattern=*.jsp, servletName=JspServlet, mappingMatch=EXTENSION}"));
_server.stop();
spec = new ServletPathSpec("/catalog");
handler = new PathMappingHandler(spec, spec.getPathMatch("/catalog"), "CatalogServlet");
_server.setHandler(handler);
_server.start();
response = _connector.getResponse(request);
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
assertThat("Response body content", response, containsString("HttpServletMapping{matchValue=catalog, pattern=/catalog, servletName=CatalogServlet, mappingMatch=EXACT}"));
_server.stop();
}
@Test
public void testCookies() throws Exception
{
@ -1935,6 +1874,92 @@ public class RequestTest
assertThat(builder.getHeader("Cookie"), not(containsString("bad")));
}
@Test
public void testServletPathMapping() throws Exception
{
ServletPathSpec spec;
String uri;
ServletPathMapping m;
spec = null;
uri = null;
m = new ServletPathMapping(spec, null, uri);
assertThat(m.getMappingMatch(), nullValue());
assertThat(m.getMatchValue(), is(""));
assertThat(m.getPattern(), nullValue());
assertThat(m.getServletName(), is(""));
assertThat(m.getServletPath(), nullValue());
assertThat(m.getPathInfo(), nullValue());
spec = new ServletPathSpec("");
uri = "/";
m = new ServletPathMapping(spec, "Something", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.CONTEXT_ROOT));
assertThat(m.getMatchValue(), is(""));
assertThat(m.getPattern(), is(""));
assertThat(m.getServletName(), is("Something"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
spec = new ServletPathSpec("/");
uri = "/some/path";
m = new ServletPathMapping(spec, "Default", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.DEFAULT));
assertThat(m.getMatchValue(), is(""));
assertThat(m.getPattern(), is("/"));
assertThat(m.getServletName(), is("Default"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
spec = new ServletPathSpec("/foo/*");
uri = "/foo/bar";
m = new ServletPathMapping(spec, "FooServlet", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
assertThat(m.getMatchValue(), is("foo"));
assertThat(m.getPattern(), is("/foo/*"));
assertThat(m.getServletName(), is("FooServlet"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
uri = "/foo/";
m = new ServletPathMapping(spec, "FooServlet", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
assertThat(m.getMatchValue(), is("foo"));
assertThat(m.getPattern(), is("/foo/*"));
assertThat(m.getServletName(), is("FooServlet"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
uri = "/foo";
m = new ServletPathMapping(spec, "FooServlet", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.PATH));
assertThat(m.getMatchValue(), is("foo"));
assertThat(m.getPattern(), is("/foo/*"));
assertThat(m.getServletName(), is("FooServlet"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
spec = new ServletPathSpec("*.jsp");
uri = "/foo/bar.jsp";
m = new ServletPathMapping(spec, "JspServlet", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.EXTENSION));
assertThat(m.getMatchValue(), is("foo/bar"));
assertThat(m.getPattern(), is("*.jsp"));
assertThat(m.getServletName(), is("JspServlet"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
spec = new ServletPathSpec("/catalog");
uri = "/catalog";
m = new ServletPathMapping(spec, "CatalogServlet", uri);
assertThat(m.getMappingMatch(), is(MappingMatch.EXACT));
assertThat(m.getMatchValue(), is("catalog"));
assertThat(m.getPattern(), is("/catalog"));
assertThat(m.getServletName(), is("CatalogServlet"));
assertThat(m.getServletPath(), is(spec.getPathMatch(uri)));
assertThat(m.getPathInfo(), is(spec.getPathInfo(uri)));
}
interface RequestTester
{
boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException;
@ -2169,7 +2194,6 @@ public class RequestTest
{
((Request)request).setHandled(true);
baseRequest.setServletPath(_servletPath);
baseRequest.setPathSpec(_spec);
if (_servletName != null)
baseRequest.setUserIdentityScope(new TestUserIdentityScope(null, null, _servletName));
HttpServletMapping mapping = baseRequest.getHttpServletMapping();

View File

@ -35,7 +35,6 @@ import org.eclipse.jetty.http.HttpContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.server.CachedContentFactory;
import org.eclipse.jetty.server.ResourceContentFactory;
import org.eclipse.jetty.server.ResourceService;
@ -500,9 +499,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
if ((_welcomeServlets || _welcomeExactServlets) && welcomeServlet == null)
{
MappedResource<ServletHolder> entry = _servletHandler.getMappedServlet(welcomeInContext);
ServletHandler.MappedServlet entry = _servletHandler.getMappedServlet(welcomeInContext);
@SuppressWarnings("ReferenceEquality")
boolean isDefaultHolder = (entry.getResource() != _defaultHolder);
boolean isDefaultHolder = (entry.getServletHolder() != _defaultHolder);
if (entry != null && isDefaultHolder &&
(_welcomeServlets || (_welcomeExactServlets && entry.getPathSpec().getDeclaration().equals(welcomeInContext))))
welcomeServlet = welcomeInContext;

View File

@ -31,7 +31,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
@ -71,7 +70,7 @@ public class Invoker extends HttpServlet
private ContextHandler _contextHandler;
private ServletHandler _servletHandler;
private MappedResource<ServletHolder> _invokerEntry;
private ServletHandler.MappedServlet _invokerEntry;
private Map<String, String> _parameters;
private boolean _nonContextServlets;
private boolean _verbose;
@ -171,12 +170,12 @@ public class Invoker extends HttpServlet
// Check for existing mapping (avoid threaded race).
String path = URIUtil.addPaths(servletPath, servlet);
MappedResource<ServletHolder> entry = _servletHandler.getMappedServlet(path);
ServletHandler.MappedServlet entry = _servletHandler.getMappedServlet(path);
if (entry != null && !entry.equals(_invokerEntry))
{
// Use the holder
holder = (ServletHolder)entry.getResource();
holder = (ServletHolder)entry.getServletHolder();
}
else
{

View File

@ -26,7 +26,6 @@ import java.util.EnumSet;
import java.util.EventListener;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@ -57,6 +56,7 @@ import org.eclipse.jetty.http.pathmap.ServletPathSpec;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.ServletPathMapping;
import org.eclipse.jetty.server.ServletRequestHttpWrapper;
import org.eclipse.jetty.server.ServletResponseHttpWrapper;
import org.eclipse.jetty.server.UserIdentity;
@ -92,8 +92,6 @@ public class ServletHandler extends ScopedHandler
{
private static final Logger LOG = LoggerFactory.getLogger(ServletHandler.class);
public static final String __DEFAULT_SERVLET = "default";
private ServletContextHandler _contextHandler;
private ServletContext _servletContext;
private FilterHolder[] _filters = new FilterHolder[0];
@ -113,8 +111,8 @@ public class ServletHandler extends ScopedHandler
private List<FilterMapping> _filterPathMappings;
private MultiMap<FilterMapping> _filterNameMappings;
private final Map<String, ServletHolder> _servletNameMap = new HashMap<>();
private PathMappings<ServletHolder> _servletPathMap;
private final Map<String, MappedServlet> _servletNameMap = new HashMap<>();
private PathMappings<MappedServlet> _servletPathMap;
private ListenerHolder[] _listeners = new ListenerHolder[0];
private boolean _initialized = false;
@ -167,7 +165,7 @@ public class ServletHandler extends ScopedHandler
LOG.debug("Adding Default404Servlet to {}", this);
addServletWithMapping(Default404Servlet.class, "/");
updateMappings();
getServletMapping("/").setDefault(true);
getServletMapping("/").setFromDefaultDescriptor(true);
}
if (isFilterChainsCached())
@ -247,13 +245,7 @@ public class ServletHandler extends ScopedHandler
//remove all of the mappings that were for non-embedded filters
_filterNameMap.remove(filter.getName());
//remove any mappings associated with this filter
ListIterator<FilterMapping> fmitor = filterMappings.listIterator();
while (fmitor.hasNext())
{
FilterMapping fm = fmitor.next();
if (fm.getFilterName().equals(filter.getName()))
fmitor.remove();
}
filterMappings.removeIf(fm -> fm.getFilterName().equals(filter.getName()));
}
else
filterHolders.add(filter); //only retain embedded
@ -261,10 +253,8 @@ public class ServletHandler extends ScopedHandler
}
//Retain only filters and mappings that were added using jetty api (ie Source.EMBEDDED)
FilterHolder[] fhs = (FilterHolder[])LazyList.toArray(filterHolders, FilterHolder.class);
_filters = fhs;
FilterMapping[] fms = (FilterMapping[])LazyList.toArray(filterMappings, FilterMapping.class);
_filterMappings = fms;
_filters = (FilterHolder[])LazyList.toArray(filterHolders, FilterHolder.class);
_filterMappings = (FilterMapping[])LazyList.toArray(filterMappings, FilterMapping.class);
_matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length - 1);
_matchBeforeIndex = -1;
@ -291,13 +281,7 @@ public class ServletHandler extends ScopedHandler
//remove from servlet name map
_servletNameMap.remove(servlet.getName());
//remove any mappings associated with this servlet
ListIterator<ServletMapping> smitor = servletMappings.listIterator();
while (smitor.hasNext())
{
ServletMapping sm = smitor.next();
if (sm.getServletName().equals(servlet.getName()))
smitor.remove();
}
servletMappings.removeIf(sm -> sm.getServletName().equals(servlet.getName()));
}
else
servletHolders.add(servlet); //only retain embedded
@ -305,10 +289,8 @@ public class ServletHandler extends ScopedHandler
}
//Retain only Servlets and mappings added via jetty apis (ie Source.EMBEDDED)
ServletHolder[] shs = (ServletHolder[])LazyList.toArray(servletHolders, ServletHolder.class);
_servlets = shs;
ServletMapping[] sms = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
_servletMappings = sms;
_servlets = (ServletHolder[])LazyList.toArray(servletHolders, ServletHolder.class);
_servletMappings = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
if (_contextHandler != null)
_contextHandler.contextDestroyed();
@ -332,8 +314,7 @@ public class ServletHandler extends ScopedHandler
listenerHolders.add(listener);
}
}
ListenerHolder[] listeners = (ListenerHolder[])LazyList.toArray(listenerHolders, ListenerHolder.class);
_listeners = listeners;
_listeners = (ListenerHolder[])LazyList.toArray(listenerHolders, ListenerHolder.class);
//will be regenerated on next start
_filterPathMappings = null;
@ -425,50 +406,40 @@ public class ServletHandler extends ScopedHandler
public ServletHolder getServlet(String name)
{
return _servletNameMap.get(name);
MappedServlet mapped = _servletNameMap.get(name);
if (mapped != null)
return mapped.getServletHolder();
return null;
}
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
// Get the base requests
final String old_servlet_path = baseRequest.getServletPath();
final String old_path_info = baseRequest.getPathInfo();
final PathSpec old_path_spec = baseRequest.getPathSpec();
final ServletPathMapping old_servlet_path_mapping = baseRequest.getServletPathMapping();
DispatcherType type = baseRequest.getDispatcherType();
ServletHolder servletHolder = null;
UserIdentity.Scope oldScope = null;
MappedResource<ServletHolder> mapping = getMappedServlet(target);
if (mapping != null)
MappedServlet mappedServlet = getMappedServlet(target);
if (mappedServlet != null)
{
servletHolder = mapping.getResource();
if (mapping.getPathSpec() != null)
servletHolder = mappedServlet.getServletHolder();
ServletPathMapping servletPathMapping = mappedServlet.getServletPathMapping(target);
if (servletPathMapping != null)
{
PathSpec pathSpec = mapping.getPathSpec();
String servletPath = pathSpec.getPathMatch(target);
String pathInfo = pathSpec.getPathInfo(target);
// Setting the servletPathMapping also provides the servletPath and pathInfo
if (DispatcherType.INCLUDE.equals(type))
{
baseRequest.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH, servletPath);
baseRequest.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, pathInfo);
baseRequest.setAttribute(RequestDispatcher.INCLUDE_MAPPING, Request.getServletMapping(pathSpec, servletPath, servletHolder.getName()));
}
baseRequest.setAttribute(RequestDispatcher.INCLUDE_MAPPING, servletPathMapping);
else
{
baseRequest.setPathSpec(pathSpec);
baseRequest.setServletPath(servletPath);
baseRequest.setPathInfo(pathInfo);
}
baseRequest.setServletPathMapping(servletPathMapping);
}
}
if (LOG.isDebugEnabled())
LOG.debug("servlet {}|{}|{} -> {}", baseRequest.getContextPath(), baseRequest.getServletPath(), baseRequest.getPathInfo(), servletHolder);
LOG.debug("servlet {}|{}|{}|{} -> {}", baseRequest.getContextPath(), baseRequest.getServletPath(), baseRequest.getPathInfo(), baseRequest.getHttpServletMapping(), servletHolder);
try
{
@ -484,11 +455,7 @@ public class ServletHandler extends ScopedHandler
baseRequest.setUserIdentityScope(oldScope);
if (!(DispatcherType.INCLUDE.equals(type)))
{
baseRequest.setServletPath(old_servlet_path);
baseRequest.setPathInfo(old_path_info);
baseRequest.setPathSpec(old_path_spec);
}
baseRequest.setServletPathMapping(old_servlet_path_mapping);
}
}
@ -550,34 +517,33 @@ public class ServletHandler extends ScopedHandler
}
/**
* ServletHolder matching path.
* Get MappedServlet for target.
*
* @param target Path within _context or servlet name
* @return MappedResource to the ServletHolder. Named servlets have a null PathSpec
* @return MappedServlet matched by path or name. Named servlets have a null PathSpec
*/
public MappedResource<ServletHolder> getMappedServlet(String target)
public MappedServlet getMappedServlet(String target)
{
if (target.startsWith("/"))
{
if (_servletPathMap == null)
return null;
return _servletPathMap.getMatch(target);
MappedResource<MappedServlet> match = _servletPathMap.getMatch(target);
if (match == null)
return null;
return match.getResource();
}
if (_servletNameMap == null)
return null;
ServletHolder holder = _servletNameMap.get(target);
if (holder == null)
return null;
return new MappedResource<>(null, holder);
return _servletNameMap.get(target);
}
protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
{
String key = pathInContext == null ? servletHolder.getName() : pathInContext;
int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
if (_filterChainsCached && _chainCache != null)
if (_filterChainsCached)
{
FilterChain chain = _chainCache[dispatch].get(key);
if (chain != null)
@ -624,8 +590,7 @@ public class ServletHandler extends ScopedHandler
FilterChain chain = null;
if (_filterChainsCached)
{
if (!filters.isEmpty())
chain = newCachedChain(filters, servletHolder);
chain = newCachedChain(filters, servletHolder);
final Map<String, FilterChain> cache = _chainCache[dispatch];
final Queue<String> lru = _chainLRU[dispatch];
@ -648,7 +613,7 @@ public class ServletHandler extends ScopedHandler
cache.put(key, chain);
lru.add(key);
}
else if (!filters.isEmpty())
else
chain = new Chain(baseRequest, filters, servletHolder);
return chain;
@ -1133,7 +1098,7 @@ public class ServletHandler extends ScopedHandler
if (mappings == null || mappings.length == 0)
{
setFilterMappings(insertFilterMapping(mapping, 0, false));
if (source != null && source == Source.JAVAX_API)
if (source == Source.JAVAX_API)
_matchAfterIndex = 0;
}
else
@ -1141,7 +1106,7 @@ public class ServletHandler extends ScopedHandler
//there are existing entries. If this is a programmatic filtermapping, it is added at the end of the list.
//If this is a normal filtermapping, it is inserted after all the other filtermappings (matchBefores and normals),
//but before the first matchAfter filtermapping.
if (source != null && Source.JAVAX_API == source)
if (Source.JAVAX_API == source)
{
setFilterMappings(insertFilterMapping(mapping, mappings.length - 1, false));
if (_matchAfterIndex < 0)
@ -1177,12 +1142,12 @@ public class ServletHandler extends ScopedHandler
if (mappings == null || mappings.length == 0)
{
setFilterMappings(insertFilterMapping(mapping, 0, false));
if (source != null && Source.JAVAX_API == source)
if (Source.JAVAX_API == source)
_matchBeforeIndex = 0;
}
else
{
if (source != null && Source.JAVAX_API == source)
if (Source.JAVAX_API == source)
{
//programmatically defined filter mappings are prepended to mapping list in the order
//in which they were defined. In other words, insert this mapping at the tail of the
@ -1281,7 +1246,7 @@ public class ServletHandler extends ScopedHandler
// update the maps
for (ServletHolder servlet : _servlets)
{
_servletNameMap.put(servlet.getName(), servlet);
_servletNameMap.put(servlet.getName(), new MappedServlet(null, servlet));
servlet.setServletHandler(this);
}
}
@ -1321,13 +1286,13 @@ public class ServletHandler extends ScopedHandler
}
// Map servlet paths to holders
if (_servletMappings == null || _servletNameMap == null)
if (_servletMappings == null)
{
_servletPathMap = null;
}
else
{
PathMappings<ServletHolder> pm = new PathMappings<>();
PathMappings<MappedServlet> pm = new PathMappings<>();
//create a map of paths to set of ServletMappings that define that mapping
HashMap<String, List<ServletMapping>> sms = new HashMap<>();
@ -1338,12 +1303,7 @@ public class ServletHandler extends ScopedHandler
{
for (String pathSpec : pathSpecs)
{
List<ServletMapping> mappings = sms.get(pathSpec);
if (mappings == null)
{
mappings = new ArrayList<>();
sms.put(pathSpec, mappings);
}
List<ServletMapping> mappings = sms.computeIfAbsent(pathSpec, k -> new ArrayList<>());
mappings.add(servletMapping);
}
}
@ -1360,7 +1320,7 @@ public class ServletHandler extends ScopedHandler
for (ServletMapping mapping : mappings)
{
//Get servlet associated with the mapping and check it is enabled
ServletHolder servletHolder = _servletNameMap.get(mapping.getServletName());
ServletHolder servletHolder = getServlet(mapping.getServletName());
if (servletHolder == null)
throw new IllegalStateException("No such servlet: " + mapping.getServletName());
//if the servlet related to the mapping is not enabled, skip it from consideration
@ -1374,7 +1334,7 @@ public class ServletHandler extends ScopedHandler
{
//already have a candidate - only accept another one
//if the candidate is a default, or we're allowing duplicate mappings
if (finalMapping.isDefault())
if (finalMapping.isFromDefaultDescriptor())
finalMapping = mapping;
else if (isAllowDuplicateMappings())
{
@ -1384,9 +1344,9 @@ public class ServletHandler extends ScopedHandler
else
{
//existing candidate isn't a default, if the one we're looking at isn't a default either, then its an error
if (!mapping.isDefault())
if (!mapping.isFromDefaultDescriptor())
{
ServletHolder finalMappedServlet = _servletNameMap.get(finalMapping.getServletName());
ServletHolder finalMappedServlet = getServlet(finalMapping.getServletName());
throw new IllegalStateException("Multiple servlets map to path " +
pathSpec + ": " +
finalMappedServlet.getName() + "[mapped:" + finalMapping.getSource() + "]," +
@ -1403,22 +1363,21 @@ public class ServletHandler extends ScopedHandler
pathSpec,
finalMapping.getSource(),
finalMapping.getServletName(),
_servletNameMap.get(finalMapping.getServletName()).getSource());
getServlet(finalMapping.getServletName()).getSource());
pm.put(new ServletPathSpec(pathSpec), _servletNameMap.get(finalMapping.getServletName()));
ServletPathSpec servletPathSpec = new ServletPathSpec(pathSpec);
MappedServlet mappedServlet = new MappedServlet(servletPathSpec, getServlet(finalMapping.getServletName()));
pm.put(servletPathSpec, mappedServlet);
}
_servletPathMap = pm;
}
// flush filter chain cache
if (_chainCache != null)
for (int i = _chainCache.length; i-- > 0; )
{
for (int i = _chainCache.length; i-- > 0; )
{
if (_chainCache[i] != null)
_chainCache[i].clear();
}
if (_chainCache[i] != null)
_chainCache[i].clear();
}
if (LOG.isDebugEnabled())
@ -1453,28 +1412,26 @@ public class ServletHandler extends ScopedHandler
{
if (_filters == null)
return false;
boolean found = false;
for (FilterHolder f : _filters)
{
if (f == holder)
found = true;
return true;
}
return found;
return false;
}
protected synchronized boolean containsServletHolder(ServletHolder holder)
{
if (_servlets == null)
return false;
boolean found = false;
for (ServletHolder s : _servlets)
{
@SuppressWarnings("ReferenceEquality")
boolean foundServletHolder = (s == holder);
if (foundServletHolder)
found = true;
return true;
}
return found;
return false;
}
/**
@ -1731,6 +1688,68 @@ public class ServletHandler extends ScopedHandler
_contextHandler.destroyListener(listener);
}
/**
* A mapping of a servlet by pathSpec or by name
*/
public static class MappedServlet
{
private final PathSpec _pathSpec;
private final ServletHolder _servletHolder;
private final ServletPathMapping _servletPathMapping;
MappedServlet(PathSpec pathSpec, ServletHolder servletHolder)
{
_pathSpec = pathSpec;
_servletHolder = servletHolder;
// Create the HttpServletMapping only once if possible.
if (pathSpec instanceof ServletPathSpec)
{
switch (pathSpec.getGroup())
{
case EXACT:
case ROOT:
_servletPathMapping = new ServletPathMapping(_pathSpec, _servletHolder.getName(), _pathSpec.getPrefix());
break;
default:
_servletPathMapping = null;
break;
}
}
else
{
_servletPathMapping = null;
}
}
public PathSpec getPathSpec()
{
return _pathSpec;
}
public ServletHolder getServletHolder()
{
return _servletHolder;
}
public ServletPathMapping getServletPathMapping(String pathInContext)
{
if (_servletPathMapping != null)
return _servletPathMapping;
if (_pathSpec != null)
return new ServletPathMapping(_pathSpec, _servletHolder.getName(), pathInContext);
return null;
}
@Override
public String toString()
{
return String.format("MappedServlet%x{%s->%s}",
hashCode(), _pathSpec == null ? null : _pathSpec.getDeclaration(), _servletHolder);
}
}
@SuppressWarnings("serial")
public static class Default404Servlet extends HttpServlet
{

View File

@ -918,7 +918,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
if (mapping != null)
{
//if the servlet mapping was from a default descriptor, then allow it to be overridden
if (!mapping.isDefault())
if (!mapping.isFromDefaultDescriptor())
{
if (clash == null)
clash = new HashSet<>();

View File

@ -104,12 +104,12 @@ public class ServletMapping
}
@ManagedAttribute(value = "default", readonly = true)
public boolean isDefault()
public boolean isFromDefaultDescriptor()
{
return _default;
}
public void setDefault(boolean fromDefault)
public void setFromDefaultDescriptor(boolean fromDefault)
{
_default = fromDefault;
}

View File

@ -257,7 +257,7 @@ public class AsyncContextTest
assertThat("async run attr query string", responseBody, containsString("async:run:attr:queryString:dispatch=true"));
assertThat("async run context path", responseBody, containsString("async:run:attr:contextPath:/ctx"));
assertThat("async run request uri has correct encoding", responseBody, containsString("async:run:attr:requestURI:/ctx/test/hello%2fthere"));
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:/test"));
assertThat("http servlet mapping matchValue is correct", responseBody, containsString("async:run:attr:mapping:matchValue:test"));
assertThat("http servlet mapping pattern is correct", responseBody, containsString("async:run:attr:mapping:pattern:/test/*"));
assertThat("http servlet mapping servletName is correct", responseBody, containsString("async:run:attr:mapping:servletName:"));
assertThat("http servlet mapping mappingMatch is correct", responseBody, containsString("async:run:attr:mapping:mappingMatch:PATH"));

View File

@ -170,6 +170,43 @@ public class DispatcherTest
assertEquals(expected, responses);
}
@Test
public void testNamedForward() throws Exception
{
_contextHandler.addServlet(NamedForwardServlet.class, "/forward/*");
String echo = _contextHandler.addServlet(EchoURIServlet.class, "/echo/*").getName();
String expected =
"HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: 62\r\n" +
"\r\n" +
"/context\r\n" +
"/forward\r\n" +
"/info\r\n" +
"/context/forward/info;param=value\r\n";
String responses = _connector.getResponse("GET /context/forward/info;param=value?name=" + echo + " HTTP/1.0\n\n");
assertEquals(expected, responses);
}
@Test
public void testNamedInclude() throws Exception
{
_contextHandler.addServlet(NamedIncludeServlet.class, "/include/*");
String echo = _contextHandler.addServlet(EchoURIServlet.class, "/echo/*").getName();
String expected =
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 62\r\n" +
"\r\n" +
"/context\r\n" +
"/include\r\n" +
"/info\r\n" +
"/context/include/info;param=value\r\n";
String responses = _connector.getResponse("GET /context/include/info;param=value?name=" + echo + " HTTP/1.0\n\n");
assertEquals(expected, responses);
}
@Test
public void testForwardWithBadParams() throws Exception
{
@ -507,6 +544,24 @@ public class DispatcherTest
}
}
public static class NamedForwardServlet extends HttpServlet implements Servlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
getServletContext().getNamedDispatcher(request.getParameter("name")).forward(request, response);
}
}
public static class NamedIncludeServlet extends HttpServlet implements Servlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
getServletContext().getNamedDispatcher(request.getParameter("name")).include(request, response);
}
}
public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
{
@Override
@ -734,7 +789,7 @@ public class DispatcherTest
assertEquals("do=assertforward&do=more&test=1", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
assertNotNull(fwdMapping);
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
assertEquals("ForwardServlet", fwdMapping.getMatchValue());
List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);
@ -769,7 +824,7 @@ public class DispatcherTest
assertEquals("do=assertforward&foreign=%d2%e5%ec%ef%e5%f0%e0%f2%f3%f0%e0&test=1", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
assertNotNull(fwdMapping);
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
assertEquals("ForwardServlet", fwdMapping.getMatchValue());
List<String> expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH,
Dispatcher.FORWARD_SERVLET_PATH, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);
@ -818,7 +873,7 @@ public class DispatcherTest
assertEquals("do=end&do=the", request.getAttribute(Dispatcher.INCLUDE_QUERY_STRING));
HttpServletMapping incMapping = (HttpServletMapping)request.getAttribute(Dispatcher.INCLUDE_MAPPING);
assertNotNull(incMapping);
assertEquals("/AssertIncludeServlet", incMapping.getMatchValue());
assertEquals("AssertIncludeServlet", incMapping.getMatchValue());
List expectedAttributeNames = Arrays.asList(Dispatcher.INCLUDE_REQUEST_URI, Dispatcher.INCLUDE_CONTEXT_PATH,
Dispatcher.INCLUDE_SERVLET_PATH, Dispatcher.INCLUDE_QUERY_STRING, Dispatcher.INCLUDE_MAPPING);
@ -851,7 +906,7 @@ public class DispatcherTest
assertEquals("do=include", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
assertNotNull(fwdMapping);
assertEquals("/ForwardServlet", fwdMapping.getMatchValue());
assertEquals("ForwardServlet", fwdMapping.getMatchValue());
assertEquals("/context/AssertForwardIncludeServlet/assertpath", request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI));
assertEquals("/context", request.getAttribute(Dispatcher.INCLUDE_CONTEXT_PATH));
@ -860,7 +915,7 @@ public class DispatcherTest
assertEquals("do=end", request.getAttribute(Dispatcher.INCLUDE_QUERY_STRING));
HttpServletMapping incMapping = (HttpServletMapping)request.getAttribute(Dispatcher.INCLUDE_MAPPING);
assertNotNull(incMapping);
assertEquals("/AssertForwardIncludeServlet", incMapping.getMatchValue());
assertEquals("AssertForwardIncludeServlet", incMapping.getMatchValue());
List expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH, Dispatcher.FORWARD_SERVLET_PATH,
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING,
@ -902,7 +957,7 @@ public class DispatcherTest
assertEquals("do=forward", request.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
HttpServletMapping fwdMapping = (HttpServletMapping)request.getAttribute(Dispatcher.FORWARD_MAPPING);
assertNotNull(fwdMapping);
assertEquals("/IncludeServlet", fwdMapping.getMatchValue());
assertEquals("IncludeServlet", fwdMapping.getMatchValue());
List expectedAttributeNames = Arrays.asList(Dispatcher.FORWARD_REQUEST_URI, Dispatcher.FORWARD_CONTEXT_PATH, Dispatcher.FORWARD_SERVLET_PATH,
Dispatcher.FORWARD_PATH_INFO, Dispatcher.FORWARD_QUERY_STRING, Dispatcher.FORWARD_MAPPING);

View File

@ -62,7 +62,6 @@ import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.RoleInfo;
@ -1376,9 +1375,9 @@ public class ServletContextHandlerTest
root.addBean(new MySCIStarter(root.getServletContext(), new JSPAddingSCI()), true);
_server.start();
MappedResource<ServletHolder> mappedServlet = root.getServletHandler().getMappedServlet("/somejsp/xxx");
assertNotNull(mappedServlet.getResource());
assertEquals("some.jsp", mappedServlet.getResource().getName());
ServletHandler.MappedServlet mappedServlet = root.getServletHandler().getMappedServlet("/somejsp/xxx");
assertNotNull(mappedServlet.getServletHolder());
assertEquals("some.jsp", mappedServlet.getServletHolder().getName());
}
@Test
@ -1452,9 +1451,9 @@ public class ServletContextHandlerTest
root.addBean(new MySCIStarter(root.getServletContext(), new JSPAddingSCI()), true);
_server.start();
MappedResource<ServletHolder> mappedServlet = root.getServletHandler().getMappedServlet("/bar/xxx");
assertNotNull(mappedServlet.getResource());
assertEquals("some.jsp", mappedServlet.getResource().getName());
ServletHandler.MappedServlet mappedServlet = root.getServletHandler().getMappedServlet("/bar/xxx");
assertNotNull(mappedServlet.getServletHolder());
assertEquals("some.jsp", mappedServlet.getServletHolder().getName());
}
@Test

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.servlet;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -79,17 +78,17 @@ public class ServletHandlerTest
fm5.setFilterHolder(fh5);
sh1.setName("s1");
sm1.setDefault(false);
sm1.setFromDefaultDescriptor(false);
sm1.setPathSpec("/foo/*");
sm1.setServletName("s1");
sh2.setName("s2");
sm2.setDefault(false);
sm2.setFromDefaultDescriptor(false);
sm2.setPathSpec("/foo/*");
sm2.setServletName("s2");
sh3.setName("s3");
sm3.setDefault(true);
sm3.setFromDefaultDescriptor(true);
sm3.setPathSpec("/foo/*");
sm3.setServletName("s3");
}
@ -251,9 +250,9 @@ public class ServletHandlerTest
handler.updateMappings();
MappedResource<ServletHolder> entry = handler.getMappedServlet("/foo/*");
ServletHandler.MappedServlet entry = handler.getMappedServlet("/foo/*");
assertNotNull(entry);
assertEquals("s1", entry.getResource().getName());
assertEquals("s1", entry.getServletHolder().getName());
}
@Test
@ -292,9 +291,9 @@ public class ServletHandlerTest
handler.addServletMapping(sm2);
handler.updateMappings();
MappedResource<ServletHolder> entry = handler.getMappedServlet("/foo/*");
ServletHandler.MappedServlet entry = handler.getMappedServlet("/foo/*");
assertNotNull(entry);
assertEquals("s2", entry.getResource().getName());
assertEquals("s2", entry.getServletHolder().getName());
}
@Test

View File

@ -4,16 +4,35 @@
<artifactId>jetty-project</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-slf4j-impl</artifactId>
<name>Jetty :: Slf4j Implementation</name>
<description>Slf4j Logging Implementation based on Jetty's older StdErrLog</description>
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.logging</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>--add-modules</arg>
<arg>java.management</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.logging=java.management
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@ -29,15 +48,17 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,114 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
import java.util.Locale;
import org.slf4j.event.Level;
public enum JettyLevel
{
// Intentionally sorted incrementally by level int
ALL(Level.TRACE.toInt() - 10),
TRACE(Level.TRACE),
DEBUG(Level.DEBUG),
INFO(Level.INFO),
WARN(Level.WARN),
ERROR(Level.ERROR),
OFF(Level.ERROR.toInt() + 1);
private final Level level;
private final int levelInt;
JettyLevel(Level level)
{
this.level = level;
this.levelInt = level.toInt();
}
JettyLevel(int i)
{
this.level = null;
this.levelInt = i;
}
public static JettyLevel fromLevel(Level slf4jLevel)
{
for (JettyLevel level : JettyLevel.values())
{
if (slf4jLevel.toInt() == level.levelInt)
return level;
}
return OFF;
}
public int toInt()
{
return levelInt;
}
public Level toLevel()
{
return level;
}
/**
* Tests that a provided level is included by the level value of this level.
*
* @param testLevel the level to test against.
* @return true if includes this includes the test level.
*/
public boolean includes(JettyLevel testLevel)
{
return (this.levelInt <= testLevel.levelInt);
}
@Override
public String toString()
{
return name();
}
public static JettyLevel intToLevel(int levelInt)
{
for (JettyLevel level : JettyLevel.values())
{
if (levelInt <= level.levelInt)
return level;
}
return OFF;
}
public static JettyLevel strToLevel(String levelStr)
{
if (levelStr == null)
{
return null;
}
String levelName = levelStr.trim().toUpperCase(Locale.ENGLISH);
for (JettyLevel level : JettyLevel.values())
{
if (level.name().equals(levelName))
return level;
}
return null;
}
}

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.logging;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.event.Level;
@ -29,37 +27,130 @@ import org.slf4j.spi.LocationAwareLogger;
public class JettyLogger implements LocationAwareLogger, Logger
{
/**
* The Level to set if you want this logger to be "OFF"
*/
public static final int OFF = 999;
/**
* The Level to set if you want this logger to show all events from all levels.
*/
public static final int ALL = -1;
private final JettyLoggerFactory factory;
private final String name;
private final String condensedName;
private final JettyAppender appender;
private int level;
private boolean hideStacks = false;
private JettyLevel level;
private boolean hideStacks;
public JettyLogger(JettyLoggerFactory factory, String name, JettyAppender appender)
{
this(factory, name, appender, Level.INFO.toInt(), false);
this(factory, name, appender, JettyLevel.INFO, false);
}
public JettyLogger(JettyLoggerFactory factory, String name, JettyAppender appender, int level, boolean hideStacks)
public JettyLogger(JettyLoggerFactory factory, String name, JettyAppender appender, JettyLevel level, boolean hideStacks)
{
this.factory = factory;
this.name = name;
this.condensedName = JettyLoggerFactory.condensePackageString(name);
this.condensedName = condensePackageString(name);
this.appender = appender;
this.level = level;
this.hideStacks = hideStacks;
}
/**
* Condenses a classname by stripping down the package name to just the first character of each package name
* segment.Configured
*
* <pre>
* Examples:
* "org.eclipse.jetty.test.FooTest" = "oejt.FooTest"
* "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
* </pre>
*
* @param classname the fully qualified class name
* @return the condensed name
*/
private static String condensePackageString(String classname)
{
if (classname == null || classname.isEmpty())
return "";
int rawLen = classname.length();
StringBuilder dense = new StringBuilder(rawLen);
boolean foundStart = false;
boolean hasPackage = false;
int startIdx = -1;
int endIdx = -1;
for (int i = 0; i < rawLen; i++)
{
char c = classname.charAt(i);
if (!foundStart)
{
foundStart = Character.isJavaIdentifierStart(c);
if (foundStart)
{
if (startIdx >= 0)
{
dense.append(classname.charAt(startIdx));
hasPackage = true;
}
startIdx = i;
}
}
if (foundStart)
{
if (Character.isJavaIdentifierPart(c))
endIdx = i;
else
foundStart = false;
}
}
// append remaining from startIdx
if ((startIdx >= 0) && (endIdx >= startIdx))
{
if (hasPackage)
dense.append('.');
dense.append(classname, startIdx, endIdx + 1);
}
return dense.toString();
}
public JettyAppender getAppender()
{
return appender;
}
String getCondensedName()
{
return condensedName;
}
public JettyLevel getLevel()
{
return level;
}
public void setLevel(JettyLevel level)
{
this.level = level;
// apply setLevel to children too.
factory.walkChildrenLoggers(this.getName(), (logger) -> logger.setLevel(level));
}
@Override
public String getName()
{
return name;
}
public boolean isHideStacks()
{
return hideStacks;
}
public void setHideStacks(boolean hideStacks)
{
this.hideStacks = hideStacks;
// apply setHideStacks to children too.
factory.walkChildrenLoggers(this.getName(), (logger) -> logger.setHideStacks(hideStacks));
}
@Override
public void debug(String msg)
{
@ -105,12 +196,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
}
}
@Override
public boolean isDebugEnabled(Marker marker)
{
return isDebugEnabled();
}
@Override
public void debug(Marker marker, String msg)
{
@ -146,6 +231,18 @@ public class JettyLogger implements LocationAwareLogger, Logger
debug(msg, t);
}
@Override
public boolean isDebugEnabled()
{
return level.includes(JettyLevel.DEBUG);
}
@Override
public boolean isDebugEnabled(Marker marker)
{
return isDebugEnabled();
}
@Override
public void error(String msg)
{
@ -191,12 +288,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
}
}
@Override
public boolean isErrorEnabled(Marker marker)
{
return isErrorEnabled();
}
@Override
public void error(Marker marker, String msg)
{
@ -232,66 +323,16 @@ public class JettyLogger implements LocationAwareLogger, Logger
error(msg, t);
}
public JettyAppender getAppender()
{
return appender;
}
/**
* Entry point for {@link LocationAwareLogger}
*/
@Override
public void log(Marker marker, String fqcn, int levelInt, String message, Object[] argArray, Throwable throwable)
public boolean isErrorEnabled()
{
if (this.level <= levelInt)
{
long timestamp = System.currentTimeMillis();
String threadName = Thread.currentThread().getName();
getAppender().emit(this, intToLevel(levelInt), timestamp, threadName, throwable, message, argArray);
}
}
/**
* Dynamic (via Reflection) entry point for {@link SubstituteLogger} usage.
*
* @param event the logging event
*/
@SuppressWarnings("unused")
public void log(LoggingEvent event)
{
// TODO: do we want to support org.sfl4j.Marker?
// TODO: do we want to support org.sfl4j.even.KeyValuePair?
getAppender().emit(this, event.getLevel(), event.getTimeStamp(), event.getThreadName(), event.getThrowable(), event.getMessage(), event.getArgumentArray());
}
public String getCondensedName()
{
return condensedName;
}
public int getLevel()
{
return level;
}
public void setLevel(Level level)
{
Objects.requireNonNull(level, "Level");
setLevel(level.toInt());
}
public void setLevel(int lvlInt)
{
this.level = lvlInt;
// apply setLevel to children too.
factory.walkChildLoggers(this.getName(), (logger) -> logger.setLevel(lvlInt));
return level.includes(JettyLevel.ERROR);
}
@Override
public String getName()
public boolean isErrorEnabled(Marker marker)
{
return name;
return isErrorEnabled();
}
@Override
@ -339,12 +380,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
}
}
@Override
public boolean isInfoEnabled(Marker marker)
{
return isInfoEnabled();
}
@Override
public void info(Marker marker, String msg)
{
@ -380,47 +415,16 @@ public class JettyLogger implements LocationAwareLogger, Logger
info(msg, t);
}
@Override
public boolean isDebugEnabled()
{
return level <= Level.DEBUG.toInt();
}
@Override
public boolean isErrorEnabled()
{
return level <= Level.ERROR.toInt();
}
public boolean isHideStacks()
{
return hideStacks;
}
public void setHideStacks(boolean hideStacks)
{
this.hideStacks = hideStacks;
// apply setHideStacks to children too.
factory.walkChildLoggers(this.getName(), (logger) -> logger.setHideStacks(hideStacks));
}
@Override
public boolean isInfoEnabled()
{
return level <= Level.INFO.toInt();
return level.includes(JettyLevel.INFO);
}
@Override
public boolean isTraceEnabled()
public boolean isInfoEnabled(Marker marker)
{
return level <= Level.TRACE.toInt();
}
@Override
public boolean isWarnEnabled()
{
return level <= Level.WARN.toInt();
return isInfoEnabled();
}
@Override
@ -468,12 +472,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
}
}
@Override
public boolean isTraceEnabled(Marker marker)
{
return isTraceEnabled();
}
@Override
public void trace(Marker marker, String msg)
{
@ -509,6 +507,18 @@ public class JettyLogger implements LocationAwareLogger, Logger
trace(msg, t);
}
@Override
public boolean isTraceEnabled()
{
return level.includes(JettyLevel.TRACE);
}
@Override
public boolean isTraceEnabled(Marker marker)
{
return isTraceEnabled();
}
@Override
public void warn(String msg)
{
@ -554,12 +564,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
}
}
@Override
public boolean isWarnEnabled(Marker marker)
{
return isWarnEnabled();
}
@Override
public void warn(Marker marker, String msg)
{
@ -595,6 +599,18 @@ public class JettyLogger implements LocationAwareLogger, Logger
warn(msg, t);
}
@Override
public boolean isWarnEnabled()
{
return level.includes(JettyLevel.WARN);
}
@Override
public boolean isWarnEnabled(Marker marker)
{
return isWarnEnabled();
}
private void emit(Level level, String msg)
{
long timestamp = System.currentTimeMillis();
@ -636,43 +652,36 @@ public class JettyLogger implements LocationAwareLogger, Logger
getAppender().emit(this, level, timestamp, threadName, throwable, msg);
}
public static Level intToLevel(int level)
/**
* Entry point for {@link LocationAwareLogger}
*/
@Override
public void log(Marker marker, String fqcn, int levelInt, String message, Object[] argArray, Throwable throwable)
{
if (level >= JettyLogger.OFF)
return Level.ERROR;
if (level >= Level.ERROR.toInt())
return Level.ERROR;
if (level >= Level.WARN.toInt())
return Level.WARN;
if (level >= Level.INFO.toInt())
return Level.INFO;
if (level >= Level.DEBUG.toInt())
return Level.DEBUG;
if (level >= Level.TRACE.toInt())
return Level.TRACE;
return Level.TRACE; // everything else
if (this.level.toInt() <= levelInt)
{
long timestamp = System.currentTimeMillis();
String threadName = Thread.currentThread().getName();
getAppender().emit(this, JettyLevel.intToLevel(levelInt).toLevel(), timestamp, threadName, throwable, message, argArray);
}
}
public static String levelToString(int level)
/**
* Dynamic (via Reflection) entry point for {@link SubstituteLogger} usage.
*
* @param event the logging event
*/
@SuppressWarnings("unused")
public void log(LoggingEvent event)
{
if (level >= JettyLogger.OFF)
return "OFF";
if (level >= Level.ERROR.toInt())
return "ERROR";
if (level >= Level.WARN.toInt())
return "WARN";
if (level >= Level.INFO.toInt())
return "INFO";
if (level >= Level.DEBUG.toInt())
return "DEBUG";
if (level >= Level.TRACE.toInt())
return "TRACE";
return "OFF"; // everything else
// TODO: do we want to support org.sfl4j.Marker?
// TODO: do we want to support org.sfl4j.even.KeyValuePair?
getAppender().emit(this, event.getLevel(), event.getTimeStamp(), event.getThreadName(), event.getThrowable(), event.getMessage(), event.getArgumentArray());
}
@Override
public String toString()
{
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, levelToString(level));
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, level.name());
}
}

View File

@ -23,12 +23,10 @@ import java.io.InputStream;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
import java.util.function.Function;
import org.slf4j.event.Level;
/**
* JettyLogger specific configuration:
@ -39,7 +37,7 @@ import org.slf4j.event.Level;
*/
public class JettyLoggerConfiguration
{
private static final int DEFAULT_LEVEL = Level.INFO.toInt();
private static final JettyLevel DEFAULT_LEVEL = JettyLevel.INFO;
private static final boolean DEFAULT_HIDE_STACKS = false;
private static final String SUFFIX_LEVEL = ".LEVEL";
private static final String SUFFIX_STACKS = ".STACKS";
@ -78,76 +76,77 @@ public class JettyLoggerConfiguration
// strip ".STACKS" suffix (if present)
if (startName.endsWith(SUFFIX_STACKS))
{
startName = startName.substring(0, startName.length() - SUFFIX_STACKS.length());
}
Boolean hideStacks = walkParentLoggerNames(startName, (key) ->
Boolean hideStacks = JettyLoggerFactory.walkParentLoggerNames(startName, key ->
{
String stacksBool = properties.getProperty(key + SUFFIX_STACKS);
if (stacksBool != null)
{
return Boolean.parseBoolean(stacksBool);
}
return null;
});
if (hideStacks != null)
return hideStacks;
return DEFAULT_HIDE_STACKS;
return hideStacks != null ? hideStacks : DEFAULT_HIDE_STACKS;
}
/**
* Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
* shortest.
* <p>Returns the Logging Level for the provided log name.</p>
* <p>Uses the FQCN first, then each package segment from longest to shortest.</p>
*
* @param name the name to get log for
* @return the logging level int
*/
public int getLevel(String name)
public JettyLevel getLevel(String name)
{
if (properties.isEmpty())
return DEFAULT_LEVEL;
String startName = name != null ? name : "";
// strip trailing dot
// Strip trailing dot.
while (startName.endsWith("."))
{
startName = startName.substring(0, startName.length() - 1);
}
// strip ".LEVEL" suffix (if present)
// Strip ".LEVEL" suffix (if present).
if (startName.endsWith(SUFFIX_LEVEL))
{
startName = startName.substring(0, startName.length() - SUFFIX_LEVEL.length());
}
Integer level = walkParentLoggerNames(startName, (key) ->
JettyLevel level = JettyLoggerFactory.walkParentLoggerNames(startName, key ->
{
String levelStr = properties.getProperty(key + SUFFIX_LEVEL);
if (levelStr != null)
{
return getLevelInt(key, levelStr);
}
return null;
return toJettyLevel(key, levelStr);
});
if (level == null)
{
// try legacy root logging config
String levelStr = properties.getProperty("log" + SUFFIX_LEVEL);
if (levelStr != null)
{
level = getLevelInt("log", levelStr);
}
// Try slf4j root logging config.
String levelStr = properties.getProperty(JettyLogger.ROOT_LOGGER_NAME + SUFFIX_LEVEL);
level = toJettyLevel(JettyLogger.ROOT_LOGGER_NAME, levelStr);
}
if (level != null)
return level;
if (level == null)
{
// Try legacy root logging config.
String levelStr = properties.getProperty("log" + SUFFIX_LEVEL);
level = toJettyLevel("log", levelStr);
}
return DEFAULT_LEVEL;
return level != null ? level : DEFAULT_LEVEL;
}
static JettyLevel toJettyLevel(String loggerName, String levelStr)
{
if (levelStr == null)
return null;
JettyLevel level = JettyLevel.strToLevel(levelStr);
if (level == null)
{
System.err.printf("Unknown JettyLogger/SLF4J Level [%s]=[%s], expecting only %s as values.%n",
loggerName, levelStr, Arrays.toString(JettyLevel.values()));
}
return level;
}
public TimeZone getTimeZone(String key)
@ -155,7 +154,6 @@ public class JettyLoggerConfiguration
String zoneIdStr = properties.getProperty(key);
if (zoneIdStr == null)
return null;
return TimeZone.getTimeZone(zoneIdStr);
}
@ -193,6 +191,11 @@ public class JettyLoggerConfiguration
});
}
public String getString(String key, String defValue)
{
return properties.getProperty(key, defValue);
}
public boolean getBoolean(String key, boolean defValue)
{
String val = properties.getProperty(key, Boolean.toString(defValue));
@ -203,9 +206,7 @@ public class JettyLoggerConfiguration
{
String val = properties.getProperty(key, Integer.toString(defValue));
if (val == null)
{
return defValue;
}
try
{
return Integer.parseInt(val);
@ -216,46 +217,12 @@ public class JettyLoggerConfiguration
}
}
private Integer getLevelInt(String levelSegment, String levelStr)
{
if (levelStr == null)
{
return null;
}
String levelName = levelStr.trim().toUpperCase(Locale.ENGLISH);
switch (levelName)
{
case "ALL":
return JettyLogger.ALL;
case "TRACE":
return Level.TRACE.toInt();
case "DEBUG":
return Level.DEBUG.toInt();
case "INFO":
return Level.INFO.toInt();
case "WARN":
return Level.WARN.toInt();
case "ERROR":
return Level.ERROR.toInt();
case "OFF":
return JettyLogger.OFF;
default:
System.err.println("Unknown JettyLogger/Slf4J Level [" + levelSegment + "]=[" + levelName + "], expecting only [ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF] as values.");
return null;
}
}
private URL getResource(ClassLoader loader, String resourceName)
{
if (loader == null)
{
return ClassLoader.getSystemResource(resourceName);
}
else
{
return loader.getResource(resourceName);
}
}
/**
@ -286,9 +253,7 @@ public class JettyLoggerConfiguration
{
URL propsUrl = getResource(loader, resourceName);
if (propsUrl == null)
{
return null;
}
try (InputStream in = propsUrl.openStream())
{
@ -303,30 +268,4 @@ public class JettyLoggerConfiguration
}
return null;
}
private <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
String nameSegment = startName;
// Checking with FQCN first, then each package segment from longest to shortest.
while ((nameSegment != null) && (nameSegment.length() > 0))
{
T ret = nameFunction.apply(nameSegment);
if (ret != null)
return ret;
// Trim and try again.
int idx = nameSegment.lastIndexOf('.');
if (idx >= 0)
{
nameSegment = nameSegment.substring(0, idx);
}
else
{
nameSegment = null;
}
}
return null;
}
}

View File

@ -19,19 +19,20 @@
package org.eclipse.jetty.logging;
import java.util.Objects;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
import java.util.function.Function;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
public class JettyLoggerFactory implements ILoggerFactory
public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBean
{
private static final String ROOT_LOGGER_NAME = "";
private final JettyLoggerConfiguration configuration;
private final JettyLogger rootLogger;
private ConcurrentMap<String, JettyLogger> loggerMap;
private final ConcurrentMap<String, JettyLogger> loggerMap;
public JettyLoggerFactory(JettyLoggerConfiguration config)
{
@ -41,9 +42,16 @@ public class JettyLoggerFactory implements ILoggerFactory
StdErrAppender appender = new StdErrAppender(configuration);
rootLogger = new JettyLogger(this, ROOT_LOGGER_NAME, appender);
loggerMap.put(ROOT_LOGGER_NAME, rootLogger);
rootLogger.setLevel(configuration.getLevel(ROOT_LOGGER_NAME));
rootLogger = new JettyLogger(this, Logger.ROOT_LOGGER_NAME, appender);
loggerMap.put(Logger.ROOT_LOGGER_NAME, rootLogger);
rootLogger.setLevel(configuration.getLevel(Logger.ROOT_LOGGER_NAME));
}
@SuppressWarnings("unused")
public String jmxContext()
{
// Used to build the ObjectName.
return configuration.getString("org.eclipse.jetty.logging.jmx.context", null);
}
/**
@ -54,11 +62,8 @@ public class JettyLoggerFactory implements ILoggerFactory
*/
public JettyLogger getJettyLogger(String name)
{
if (name.equals(ROOT_LOGGER_NAME))
{
if (name.equals(Logger.ROOT_LOGGER_NAME))
return getRootLogger();
}
return loggerMap.computeIfAbsent(name, this::createLogger);
}
@ -74,109 +79,96 @@ public class JettyLoggerFactory implements ILoggerFactory
return getJettyLogger(name);
}
protected void walkChildLoggers(String parentName, Consumer<JettyLogger> childConsumer)
void walkChildrenLoggers(String parentName, Consumer<JettyLogger> childConsumer)
{
String prefix = parentName;
if (parentName.length() > 0 && !prefix.endsWith("."))
{
prefix += ".";
}
for (JettyLogger logger : loggerMap.values())
{
// Skip self.
if (logger.getName().equals(parentName))
{
// skip self
continue;
}
// is child, and is not itself
// It is a child, and is not itself.
if (logger.getName().startsWith(prefix))
{
childConsumer.accept(logger);
}
}
}
public JettyLogger getRootLogger()
JettyLogger getRootLogger()
{
return rootLogger;
}
private JettyLogger createLogger(String name)
{
// or is that handled by slf4j itself?
JettyAppender appender = rootLogger.getAppender();
int level = this.configuration.getLevel(name);
JettyLevel level = this.configuration.getLevel(name);
boolean hideStacks = this.configuration.getHideStacks(name);
return new JettyLogger(this, name, appender, level, hideStacks);
}
/**
* Condenses a classname by stripping down the package name to just the first character of each package name
* segment.Configured
*
* <pre>
* Examples:
* "org.eclipse.jetty.test.FooTest" = "oejt.FooTest"
* "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
* </pre>
*
* @param classname the fully qualified class name
* @return the condensed name
*/
protected static String condensePackageString(String classname)
static <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
if (classname == null || classname.isEmpty())
if (startName == null)
return null;
// Checking with FQCN first, then each package segment from longest to shortest.
String nameSegment = startName;
while (nameSegment.length() > 0)
{
return "";
T ret = nameFunction.apply(nameSegment);
if (ret != null)
return ret;
// Trim and try again.
int idx = nameSegment.lastIndexOf('.');
if (idx >= 0)
nameSegment = nameSegment.substring(0, idx);
else
break;
}
int rawLen = classname.length();
StringBuilder dense = new StringBuilder(rawLen);
boolean foundStart = false;
boolean hasPackage = false;
int startIdx = -1;
int endIdx = -1;
for (int i = 0; i < rawLen; i++)
{
char c = classname.charAt(i);
if (!foundStart)
{
foundStart = Character.isJavaIdentifierStart(c);
if (foundStart)
{
if (startIdx >= 0)
{
dense.append(classname.charAt(startIdx));
hasPackage = true;
}
startIdx = i;
}
}
return nameFunction.apply(Logger.ROOT_LOGGER_NAME);
}
if (foundStart)
{
if (!Character.isJavaIdentifierPart(c))
{
foundStart = false;
}
else
{
endIdx = i;
}
}
}
// append remaining from startIdx
if ((startIdx >= 0) && (endIdx >= startIdx))
{
if (hasPackage)
{
dense.append('.');
}
dense.append(classname, startIdx, endIdx + 1);
}
@Override
public String[] getLoggerNames()
{
TreeSet<String> names = new TreeSet<>(loggerMap.keySet());
return names.toArray(new String[0]);
}
return dense.toString();
@Override
public int getLoggerCount()
{
return loggerMap.size();
}
@Override
public String getLoggerLevel(String loggerName)
{
return walkParentLoggerNames(loggerName, key ->
{
JettyLogger logger = loggerMap.get(key);
if (logger != null)
return logger.getLevel().name();
return null;
});
}
@Override
public boolean setLoggerLevel(String loggerName, String levelName)
{
JettyLevel level = JettyLoggerConfiguration.toJettyLevel(loggerName, levelName);
if (level == null)
{
return false;
}
JettyLogger jettyLogger = getJettyLogger(loggerName);
jettyLogger.setLevel(level);
return true;
}
}

View File

@ -0,0 +1,30 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
public interface JettyLoggerFactoryMBean
{
int getLoggerCount();
String[] getLoggerNames();
boolean setLoggerLevel(String loggerName, String levelName);
String getLoggerLevel(String loggerName);
}

View File

@ -0,0 +1,73 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JMXTest
{
@Test
public void testJMX() throws Exception
{
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Properties props = new Properties();
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
JettyLoggerFactory loggerFactory = new JettyLoggerFactory(config);
ObjectName objectName = ObjectName.getInstance("org.eclipse.jetty.logging", "type", JettyLoggerFactory.class.getSimpleName().toLowerCase(Locale.ENGLISH));
mbeanServer.registerMBean(loggerFactory, objectName);
JettyLoggerFactoryMBean mbean = JMX.newMBeanProxy(mbeanServer, objectName, JettyLoggerFactoryMBean.class);
// Only the root logger.
assertEquals(1, mbean.getLoggerCount());
JettyLogger child = loggerFactory.getJettyLogger("org.eclipse.jetty.logging");
JettyLogger parent = loggerFactory.getJettyLogger("org.eclipse.jetty");
assertEquals(3, mbean.getLoggerCount());
// Names are sorted.
List<String> expected = new ArrayList<>(Arrays.asList(JettyLogger.ROOT_LOGGER_NAME, parent.getName(), child.getName()));
expected.sort(String::compareTo);
String[] loggerNames = mbean.getLoggerNames();
assertEquals(expected, Arrays.asList(loggerNames));
// Setting the parent level should propagate to the children.
parent.setLevel(JettyLevel.DEBUG);
assertEquals(parent.getLevel().toString(), mbean.getLoggerLevel(child.getName()));
// Setting the level via JMX affects the logger.
assertTrue(mbean.setLoggerLevel(child.getName(), "INFO"));
assertEquals(JettyLevel.INFO, child.getLevel());
}
}

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.logging;
import java.util.Properties;
import org.junit.jupiter.api.Test;
import org.slf4j.event.Level;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -46,8 +45,8 @@ public class JettyLoggerConfigurationTest
assertFalse(appender.isCondensedNames());
assertEquals(appender.getThreadPadding(), 10);
int level = config.getLevel("com.mortbay");
assertEquals(Level.WARN.toInt(), level);
JettyLevel level = config.getLevel("com.mortbay");
assertEquals(JettyLevel.WARN, level);
boolean stacks = config.getHideStacks("com.mortbay.Foo");
assertFalse(stacks);
@ -59,8 +58,8 @@ public class JettyLoggerConfigurationTest
Properties props = new Properties();
props.setProperty("com.mortbay.LEVEL", "WARN");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
int level = config.getLevel("com.mortbay");
assertEquals(Level.WARN.toInt(), level);
JettyLevel level = config.getLevel("com.mortbay");
assertEquals(JettyLevel.WARN, level);
}
@Test
@ -70,8 +69,8 @@ public class JettyLoggerConfigurationTest
props.setProperty("com.mortbay.LEVEL", "WARN");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// extra trailing dot "."
int level = config.getLevel("com.mortbay.");
assertEquals(Level.WARN.toInt(), level);
JettyLevel level = config.getLevel("com.mortbay.");
assertEquals(JettyLevel.WARN, level);
}
@Test
@ -81,8 +80,8 @@ public class JettyLoggerConfigurationTest
props.setProperty("com.mortbay.LEVEL", "WARN");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// asking for name with ".LEVEL"
int level = config.getLevel("com.mortbay.Bar.LEVEL");
assertEquals(Level.WARN.toInt(), level);
JettyLevel level = config.getLevel("com.mortbay.Bar.LEVEL");
assertEquals(JettyLevel.WARN, level);
}
@Test
@ -91,8 +90,8 @@ public class JettyLoggerConfigurationTest
Properties props = new Properties();
props.setProperty("com.mortbay.LEVEL", "WARN");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
int level = config.getLevel("com.mortbay.Foo");
assertEquals(Level.WARN.toInt(), level);
JettyLevel level = config.getLevel("com.mortbay.Foo");
assertEquals(JettyLevel.WARN, level);
}
@Test
@ -102,8 +101,8 @@ public class JettyLoggerConfigurationTest
props.setProperty("com.mortbay.LEVEL", "WARN");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// asking for name that isn't configured, returns default value
int level = config.getLevel("org.eclipse.jetty");
assertEquals(Level.INFO.toInt(), level);
JettyLevel level = config.getLevel("org.eclipse.jetty");
assertEquals(JettyLevel.INFO, level);
}
@Test
@ -163,41 +162,41 @@ public class JettyLoggerConfigurationTest
public void testGetLoggingLevelBad()
{
Properties props = new Properties();
props.setProperty("log.LEVEL", "WARN");
props.setProperty("ROOT.LEVEL", "WARN");
props.setProperty("org.eclipse.jetty.bad.LEVEL", "EXPECTED_BAD_LEVEL");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Level (because of bad level value)
assertEquals(Level.WARN.toInt(), config.getLevel("org.eclipse.jetty.bad"));
assertEquals(JettyLevel.WARN, config.getLevel("org.eclipse.jetty.bad"));
}
@Test
public void testGetLoggingLevelLowercase()
{
Properties props = new Properties();
props.setProperty("log.LEVEL", "warn");
props.setProperty("ROOT.LEVEL", "warn");
props.setProperty("org.eclipse.jetty.util.LEVEL", "info");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Level
assertEquals(Level.WARN.toInt(), config.getLevel("org.eclipse.jetty"));
assertEquals(JettyLevel.WARN, config.getLevel("org.eclipse.jetty"));
// Specific Level
assertEquals(Level.INFO.toInt(), config.getLevel("org.eclipse.jetty.util"));
assertEquals(JettyLevel.INFO, config.getLevel("org.eclipse.jetty.util"));
}
@Test
public void testGetLoggingLevelRoot()
{
Properties props = new Properties();
props.setProperty("log.LEVEL", "DEBUG");
props.setProperty("ROOT.LEVEL", "DEBUG");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Levels
assertEquals(Level.DEBUG.toInt(), config.getLevel(null));
assertEquals(Level.DEBUG.toInt(), config.getLevel(""));
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty"));
assertEquals(JettyLevel.DEBUG, config.getLevel(null));
assertEquals(JettyLevel.DEBUG, config.getLevel(""));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty"));
String name = JettyLoggerConfigurationTest.class.getName();
assertEquals(Level.DEBUG.toInt(), config.getLevel(name), "Default Logging Level - " + name + " name");
assertEquals(JettyLevel.DEBUG, config.getLevel(name), "Default Logging Level - " + name + " name");
}
@Test
@ -209,12 +208,12 @@ public class JettyLoggerConfigurationTest
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Levels
assertEquals(Level.INFO.toInt(), config.getLevel(null));
assertEquals(Level.INFO.toInt(), config.getLevel(""));
assertEquals(Level.INFO.toInt(), config.getLevel("org.eclipse.jetty"));
assertEquals(JettyLevel.INFO, config.getLevel(null));
assertEquals(JettyLevel.INFO, config.getLevel(""));
assertEquals(JettyLevel.INFO, config.getLevel("org.eclipse.jetty"));
// Specified Level
assertEquals(JettyLogger.ALL, config.getLevel(name));
assertEquals(JettyLevel.ALL, config.getLevel(name));
}
@Test
@ -225,40 +224,40 @@ public class JettyLoggerConfigurationTest
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Levels
assertEquals(Level.INFO.toInt(), config.getLevel(null));
assertEquals(Level.INFO.toInt(), config.getLevel(""));
assertEquals(Level.INFO.toInt(), config.getLevel("org.eclipse.jetty"));
assertEquals(Level.INFO.toInt(), config.getLevel("org.eclipse.jetty.server.BogusObject"));
assertEquals(Level.INFO.toInt(), config.getLevel(JettyLoggerConfigurationTest.class.getName()));
assertEquals(JettyLevel.INFO, config.getLevel(null));
assertEquals(JettyLevel.INFO, config.getLevel(""));
assertEquals(JettyLevel.INFO, config.getLevel("org.eclipse.jetty"));
assertEquals(JettyLevel.INFO, config.getLevel("org.eclipse.jetty.server.BogusObject"));
assertEquals(JettyLevel.INFO, config.getLevel(JettyLoggerConfigurationTest.class.getName()));
// Configured Level
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty.util.Bogus"));
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty.util"));
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty.util.resource.PathResource"));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty.util.Bogus"));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty.util"));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty.util.resource.PathResource"));
}
@Test
public void testGetLoggingLevelMixedLevels()
{
Properties props = new Properties();
props.setProperty("log.LEVEL", "DEBUG");
props.setProperty("ROOT.LEVEL", "DEBUG");
props.setProperty("org.eclipse.jetty.util.LEVEL", "WARN");
props.setProperty("org.eclipse.jetty.util.ConcurrentHashMap.LEVEL", "ALL");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
// Default Levels
assertEquals(Level.DEBUG.toInt(), config.getLevel(null));
assertEquals(Level.DEBUG.toInt(), config.getLevel(""));
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty"));
assertEquals(Level.DEBUG.toInt(), config.getLevel("org.eclipse.jetty.server.BogusObject"));
assertEquals(Level.DEBUG.toInt(), config.getLevel(JettyLoggerConfigurationTest.class.getName()));
assertEquals(JettyLevel.DEBUG, config.getLevel(null));
assertEquals(JettyLevel.DEBUG, config.getLevel(""));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty"));
assertEquals(JettyLevel.DEBUG, config.getLevel("org.eclipse.jetty.server.BogusObject"));
assertEquals(JettyLevel.DEBUG, config.getLevel(JettyLoggerConfigurationTest.class.getName()));
// Configured Level
assertEquals(Level.WARN.toInt(), config.getLevel("org.eclipse.jetty.util.MagicUtil"));
assertEquals(Level.WARN.toInt(), config.getLevel("org.eclipse.jetty.util"));
assertEquals(Level.WARN.toInt(), config.getLevel("org.eclipse.jetty.util.resource.PathResource"));
assertEquals(JettyLevel.WARN, config.getLevel("org.eclipse.jetty.util.MagicUtil"));
assertEquals(JettyLevel.WARN, config.getLevel("org.eclipse.jetty.util"));
assertEquals(JettyLevel.WARN, config.getLevel("org.eclipse.jetty.util.resource.PathResource"));
assertEquals(JettyLogger.ALL, config.getLevel("org.eclipse.jetty.util.ConcurrentHashMap"));
assertEquals(JettyLevel.ALL, config.getLevel("org.eclipse.jetty.util.ConcurrentHashMap"));
}
}

View File

@ -91,11 +91,11 @@ public class JettyLoggerTest
JettyLogger log = factory.getJettyLogger("xxx");
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
log.debug("testing {} {}", "test", "debug");
log.info("testing {} {}", "test", "info");
log.warn("testing {} {}", "test", "warn");
log.setLevel(Level.INFO);
log.setLevel(JettyLevel.INFO);
log.debug("YOU SHOULD NOT SEE THIS!");
output.assertContains("DEBUG:xxx:tname: testing test debug");
@ -177,7 +177,7 @@ public class JettyLoggerTest
appender.setStream(output);
JettyLogger log = factory.getJettyLogger(JettyLoggerTest.class.getName());
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
String nullMsg = null;
try (StacklessLogging ignored = new StacklessLogging(log))
@ -234,11 +234,11 @@ public class JettyLoggerTest
log.warn("See Me");
// Set to debug level
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
log.warn("Hear Me");
// Set to warn level
log.setLevel(Level.WARN);
log.setLevel(JettyLevel.WARN);
log.warn("Cheer Me");
log.warn("<zoom>", new Throwable("out of focus"));
@ -279,18 +279,18 @@ public class JettyLoggerTest
log.info("I will not buy");
// Level Debug
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
log.info("this record");
// Level All
log.setLevel(Level.TRACE);
log.setLevel(JettyLevel.TRACE);
log.info("it is scratched.");
log.info("<zoom>", new Throwable("out of focus"));
log.info("shot issue", new Throwable("scene lost"));
// Level Warn
log.setLevel(Level.WARN);
log.setLevel(JettyLevel.WARN);
log.info("sorry?");
log.info("<spoken line>", new Throwable("on editing room floor"));
@ -326,7 +326,7 @@ public class JettyLoggerTest
JettyLogger log = factory.getJettyLogger(JettyLoggerTest.class.getName());
try (StacklessLogging ignored = new StacklessLogging(log))
{
log.setLevel(Level.ERROR);
log.setLevel(JettyLevel.ERROR);
// Various logging events
log.debug("Squelch");
@ -359,7 +359,7 @@ public class JettyLoggerTest
try (StacklessLogging ignored = new StacklessLogging(log))
{
log.setLevel(JettyLogger.OFF);
log.setLevel(JettyLevel.OFF);
// Various logging events
log.debug("Squelch");
@ -399,18 +399,18 @@ public class JettyLoggerTest
log.debug("<spoken line>", new Throwable("on editing room floor"));
// Level Debug
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
log.debug("my hovercraft is");
log.debug("<zoom>", new Throwable("out of focus"));
log.debug("shot issue", new Throwable("scene lost"));
// Level All
log.setLevel(Level.TRACE);
log.setLevel(JettyLevel.TRACE);
log.debug("full of eels.");
// Level Warn
log.setLevel(Level.WARN);
log.setLevel(JettyLevel.WARN);
log.debug("what?");
// Validate Output
@ -444,22 +444,22 @@ public class JettyLoggerTest
try (StacklessLogging ignored = new StacklessLogging(log))
{
log.setLevel(Level.TRACE);
log.setLevel(JettyLevel.TRACE);
assertThat("log.level(trace).isDebugEnabled", log.isDebugEnabled(), is(true));
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
assertThat("log.level(debug).isDebugEnabled", log.isDebugEnabled(), is(true));
log.setLevel(Level.INFO);
log.setLevel(JettyLevel.INFO);
assertThat("log.level(info).isDebugEnabled", log.isDebugEnabled(), is(false));
log.setLevel(Level.WARN);
log.setLevel(JettyLevel.WARN);
assertThat("log.level(warn).isDebugEnabled", log.isDebugEnabled(), is(false));
log.setLevel(Level.ERROR);
log.setLevel(JettyLevel.ERROR);
assertThat("log.level(error).isDebugEnabled", log.isDebugEnabled(), is(false));
log.setLevel(JettyLogger.OFF);
log.setLevel(JettyLevel.OFF);
assertThat("log.level(null).isDebugEnabled", log.isDebugEnabled(), is(false));
}
}
@ -478,23 +478,23 @@ public class JettyLoggerTest
try (StacklessLogging ignored = new StacklessLogging(log))
{
log.setLevel(Level.TRACE);
assertThat("log.level(trace).getLevel()", log.getLevel(), is(Level.TRACE.toInt()));
log.setLevel(JettyLevel.TRACE);
assertThat("log.level(trace).getLevel()", log.getLevel(), is(JettyLevel.TRACE));
log.setLevel(Level.DEBUG);
assertThat("log.level(debug).getLevel()", log.getLevel(), is(Level.DEBUG.toInt()));
log.setLevel(JettyLevel.DEBUG);
assertThat("log.level(debug).getLevel()", log.getLevel(), is(JettyLevel.DEBUG));
log.setLevel(Level.INFO);
assertThat("log.level(info).getLevel()", log.getLevel(), is(Level.INFO.toInt()));
log.setLevel(JettyLevel.INFO);
assertThat("log.level(info).getLevel()", log.getLevel(), is(JettyLevel.INFO));
log.setLevel(Level.WARN);
assertThat("log.level(warn).getLevel()", log.getLevel(), is(Level.WARN.toInt()));
log.setLevel(JettyLevel.WARN);
assertThat("log.level(warn).getLevel()", log.getLevel(), is(JettyLevel.WARN));
log.setLevel(Level.ERROR);
assertThat("log.level(error).getLevel()", log.getLevel(), is(Level.ERROR.toInt()));
log.setLevel(JettyLevel.ERROR);
assertThat("log.level(error).getLevel()", log.getLevel(), is(JettyLevel.ERROR));
log.setLevel(888);
assertThat("log.level(888).getLevel()", log.getLevel(), is(888));
log.setLevel(JettyLevel.OFF);
assertThat("log.level(off).getLevel()", log.getLevel(), is(JettyLevel.OFF));
}
}
@ -510,22 +510,22 @@ public class JettyLoggerTest
JettyLogger log = factory.getJettyLogger("xxx");
log.setLevel(Level.TRACE);
log.setLevel(JettyLevel.TRACE);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=TRACE"));
log.setLevel(Level.DEBUG);
log.setLevel(JettyLevel.DEBUG);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=DEBUG"));
log.setLevel(Level.INFO);
log.setLevel(JettyLevel.INFO);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=INFO"));
log.setLevel(Level.WARN);
log.setLevel(JettyLevel.WARN);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=WARN"));
log.setLevel(Level.ERROR);
log.setLevel(JettyLevel.ERROR);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=ERROR"));
log.setLevel(JettyLogger.OFF);
log.setLevel(JettyLevel.OFF);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=OFF"));
}
@ -544,25 +544,25 @@ public class JettyLoggerTest
appender.setStream(output);
JettyLogger root = factory.getJettyLogger("");
assertLevel(root, Level.INFO); // default
assertLevel(root, JettyLevel.INFO); // default
JettyLogger log = factory.getJettyLogger("org.eclipse.jetty.util.Foo");
assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(false));
assertLevel(log, Level.WARN); // as configured
assertLevel(log, JettyLevel.WARN); // as configured
// Boot stomp it all to debug
root.setLevel(Level.DEBUG);
root.setLevel(JettyLevel.DEBUG);
assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(true));
assertLevel(log, Level.DEBUG); // as stomped
assertLevel(log, JettyLevel.DEBUG); // as stomped
// Restore configured
factory.walkChildLoggers(root.getName(), (logger) ->
factory.walkChildrenLoggers(root.getName(), (logger) ->
{
int configuredLevel = config.getLevel(logger.getName());
JettyLevel configuredLevel = config.getLevel(logger.getName());
logger.setLevel(configuredLevel);
});
assertThat("Log.isDebugEnabled()", log.isDebugEnabled(), is(false));
assertLevel(log, Level.WARN); // as configured
assertLevel(log, JettyLevel.WARN); // as configured
}
@Test
@ -606,9 +606,9 @@ public class JettyLoggerTest
output.assertContains("\t|\t|java.lang.Exception: branch0");
}
private void assertLevel(JettyLogger log, Level expectedLevel)
private void assertLevel(JettyLogger log, JettyLevel expectedLevel)
{
assertThat("Log[" + log.getName() + "].level",
JettyLogger.levelToString(log.getLevel()), is(expectedLevel.toString()));
log.getLevel(), is(expectedLevel));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,528 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util.ajax;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class AsyncJSONTest
{
private static AsyncJSON newAsyncJSON()
{
AsyncJSON.Factory factory = new AsyncJSON.Factory();
factory.setDetailedParseException(true);
return factory.newAsyncJSON();
}
@ParameterizedTest
@ValueSource(strings = {"|", "}", "]", "{]", "[}", "+", ".", "{} []"})
public void testParseInvalidJSON(String json)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertThrows(IllegalArgumentException.class, () -> parser.parse(bytes));
assertTrue(parser.isEmpty());
// Parse byte by byte.
assertThrows(IllegalArgumentException.class, () ->
{
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
});
assertTrue(parser.isEmpty());
}
@ParameterizedTest(name = "[{index}] ''{0}'' -> ''{1}''")
@MethodSource("validStrings")
public void testParseString(String string, String expected)
{
String json = "\"${value}\"".replace("${value}", string);
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertTrue(parser.parse(bytes));
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
// Parse byte by byte.
for (int i = 0; i < bytes.length; ++i)
{
byte b = bytes[i];
if (i == bytes.length - 1)
assertTrue(parser.parse(new byte[]{b}));
else
assertFalse(parser.parse(new byte[]{b}));
}
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
}
public static List<Object[]> validStrings()
{
List<Object[]> result = new ArrayList<>();
result.add(new Object[]{"", ""});
result.add(new Object[]{" \t\r\n", " \t\r\n"});
result.add(new Object[]{"\u20AC", "\u20AC"}); // euro symbol
result.add(new Object[]{"\\u20AC", "\u20AC"}); // euro symbol
result.add(new Object[]{"/foo", "/foo"});
result.add(new Object[]{"123E+01", "123E+01"});
result.add(new Object[]{"A\\u20AC/foo\\t\\n", "A\u20AC/foo\t\n"}); // euro symbol
result.add(new Object[]{" ABC ", " ABC "});
return result;
}
@ParameterizedTest
@ValueSource(strings = {"\\u", "\\u0", "\\x"})
public void testParseInvalidString(String value)
{
String json = "\"${value}\"".replace("${value}", value);
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertThrows(IllegalArgumentException.class, () -> parser.parse(bytes));
assertTrue(parser.isEmpty());
// Parse byte by byte.
assertThrows(IllegalArgumentException.class, () ->
{
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
});
assertTrue(parser.isEmpty());
}
@ParameterizedTest(name = "[{index}] {0} -> {1}")
@MethodSource("validArrays")
public void testParseArray(String json, List<?> expected)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertTrue(parser.parse(bytes));
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
ByteBuffer buffer = ByteBuffer.wrap(bytes);
assertTrue(parser.parse(buffer));
assertFalse(buffer.hasRemaining());
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
// Parse byte by byte.
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
}
public static List<Object[]> validArrays()
{
List<Object[]> result = new ArrayList<>();
List<Object> expected = Collections.emptyList();
result.add(new Object[]{"[]", expected});
result.add(new Object[]{"[] \n", expected});
expected = new ArrayList<>();
expected.add(Collections.emptyList());
result.add(new Object[]{"[[]]", expected});
expected = new ArrayList<>();
expected.add("first");
expected.add(5D);
expected.add(null);
expected.add(true);
expected.add(false);
expected.add(new HashMap<>());
HashMap<String, Object> last = new HashMap<>();
last.put("a", new ArrayList<>());
expected.add(last);
result.add(new Object[]{"[\"first\", 5E+0, null, true, false, {}, {\"a\":[]}]", expected});
return result;
}
@ParameterizedTest
@ValueSource(strings = {"[", "]", "[[,]", " [ 1,2,[ "})
public void testParseInvalidArray(String json)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertThrows(IllegalArgumentException.class, () ->
{
parser.parse(bytes);
parser.complete();
});
assertTrue(parser.isEmpty());
// Parse byte by byte.
assertThrows(IllegalArgumentException.class, () ->
{
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
parser.complete();
});
assertTrue(parser.isEmpty());
}
@ParameterizedTest(name = "[{index}] {0} -> {1}")
@MethodSource("validObjects")
public void testParseObject(String json, Object expected)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertTrue(parser.parse(bytes));
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
// Parse byte by byte.
for (int i = 0; i < bytes.length; ++i)
{
byte b = bytes[i];
if (i == bytes.length - 1)
{
assertTrue(parser.parse(new byte[]{b}));
}
else
{
assertFalse(parser.parse(new byte[]{b}));
}
}
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
}
public static List<Object[]> validObjects()
{
List<Object[]> result = new ArrayList<>();
HashMap<String, Object> expected = new HashMap<>();
result.add(new Object[]{"{}", expected});
expected = new HashMap<>();
expected.put("", 0L);
result.add(new Object[]{"{\"\":0}", expected});
expected = new HashMap<>();
expected.put("name", "value");
result.add(new Object[]{"{ \"name\": \"value\" }", expected});
expected = new HashMap<>();
expected.put("name", null);
expected.put("valid", true);
expected.put("secure", false);
expected.put("value", 42L);
result.add(new Object[]{
"{, \"name\": null, \"valid\": true\n , \"secure\": false\r\n,\n \"value\":42, }", expected
});
return result;
}
@ParameterizedTest
@ValueSource(strings = {"{", "}", "{{,}", "{:\"s\"}", "{[]:0}", "{1:0}", " {\": 0} ", "{\"a: \"b\"}"})
public void testParseInvalidObject(String json)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertThrows(IllegalArgumentException.class, () ->
{
parser.parse(bytes);
parser.complete();
});
assertTrue(parser.isEmpty());
// Parse byte by byte.
assertThrows(IllegalArgumentException.class, () ->
{
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
parser.complete();
});
assertTrue(parser.isEmpty());
}
@ParameterizedTest(name = "[{index}] {0} -> {1}")
@MethodSource("validNumbers")
public void testParseNumber(String json, Number expected)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
parser.parse(bytes);
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
ByteBuffer buffer = ByteBuffer.wrap(bytes);
parser.parse(buffer);
assertEquals(expected, parser.complete());
assertFalse(buffer.hasRemaining());
assertTrue(parser.isEmpty());
// Parse byte by byte.
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
assertEquals(expected, parser.complete());
assertTrue(parser.isEmpty());
}
public static List<Object[]> validNumbers()
{
List<Object[]> result = new ArrayList<>();
result.add(new Object[]{"0", 0L});
result.add(new Object[]{"-0", -0L});
result.add(new Object[]{"13\n", 13L});
result.add(new Object[]{"-42", -42L});
result.add(new Object[]{"123.456", 123.456D});
result.add(new Object[]{"-234.567", -234.567D});
result.add(new Object[]{"9e0", 9D});
result.add(new Object[]{"8E+1\t", 80D});
result.add(new Object[]{"-7E-2 ", -0.07D});
result.add(new Object[]{"70.5E-1", 7.05D});
return result;
}
@ParameterizedTest
@ValueSource(strings = {"--", "--1", ".5", "e0", "1a1", "3-7", "1+2", "1e0e1", "1.2.3"})
public void testParseInvalidNumber(String json)
{
byte[] bytes = json.getBytes(UTF_8);
AsyncJSON parser = newAsyncJSON();
// Parse the whole input.
assertThrows(IllegalArgumentException.class, () ->
{
parser.parse(bytes);
parser.complete();
});
assertTrue(parser.isEmpty());
// Parse byte by byte.
assertThrows(IllegalArgumentException.class, () ->
{
for (byte b : bytes)
{
parser.parse(new byte[]{b});
}
parser.complete();
});
assertTrue(parser.isEmpty());
}
@Test
public void testParseObjectWithConvertor()
{
AsyncJSON.Factory factory = new AsyncJSON.Factory();
CustomConvertor convertor = new CustomConvertor();
factory.putConvertor(CustomConvertor.class.getName(), convertor);
String json = "{" +
"\"f1\": {\"class\":\"" + CustomConvertible.class.getName() + "\", \"field\": \"value\"}," +
"\"f2\": {\"class\":\"" + CustomConvertor.class.getName() + "\"}" +
"}";
AsyncJSON parser = factory.newAsyncJSON();
assertTrue(parser.parse(UTF_8.encode(json)));
Map<String, Object> result = parser.complete();
Object value1 = result.get("f1");
assertTrue(value1 instanceof CustomConvertible);
assertEquals("value", ((CustomConvertible)value1).field);
Object value2 = result.get("f2");
assertTrue(value2 instanceof CustomConvertor.Custom);
assertSame(convertor, factory.removeConvertor(CustomConvertor.class.getName()));
assertTrue(parser.parse(UTF_8.encode(json)));
result = parser.complete();
value1 = result.get("f1");
assertTrue(value1 instanceof CustomConvertible);
assertEquals("value", ((CustomConvertible)value1).field);
value2 = result.get("f2");
assertTrue(value2 instanceof Map);
@SuppressWarnings("unchecked")
Map<String, Object> map2 = (Map<String, Object>)value2;
assertEquals(CustomConvertor.class.getName(), map2.get("class"));
}
public static class CustomConvertible implements JSON.Convertible
{
private Object field;
@Override
public void toJSON(JSON.Output out)
{
}
@Override
public void fromJSON(Map<String, Object> map)
{
this.field = map.get("field");
}
}
public static class CustomConvertor implements JSON.Convertor
{
@Override
public void toJSON(Object obj, JSON.Output out)
{
}
@Override
public Object fromJSON(Map<String, Object> map)
{
return new Custom();
}
public static class Custom
{
}
}
@Test
public void testContext()
{
AsyncJSON.Factory factory = new AsyncJSON.Factory()
{
@Override
public AsyncJSON newAsyncJSON()
{
return new AsyncJSON(this)
{
@Override
protected Map<String, Object> newObject(Context context)
{
if (context.depth() == 1)
{
return new CustomMap();
}
return super.newObject(context);
}
};
}
};
AsyncJSON parser = factory.newAsyncJSON();
String json = "[{" +
"\"channel\": \"/meta/handshake\"," +
"\"version\": \"1.0\"," +
"\"supportedConnectionTypes\": [\"long-polling\"]," +
"\"advice\": {\"timeout\": 0}" +
"}]";
assertTrue(parser.parse(UTF_8.encode(json)));
List<CustomMap> messages = parser.complete();
for (CustomMap message : messages)
{
@SuppressWarnings("unchecked")
Map<String, Object> advice = (Map<String, Object>)message.get("advice");
assertFalse(advice instanceof CustomMap);
}
}
public static class CustomMap extends HashMap<String, Object>
{
}
@Test
public void testCaching()
{
AsyncJSON.Factory factory = new AsyncJSON.Factory();
String foo = "foo";
factory.cache(foo);
AsyncJSON parser = factory.newAsyncJSON();
String json = "{\"foo\": [\"foo\", \"foo\"]}";
parser.parse(UTF_8.encode(json));
Map<String, Object> object = parser.complete();
Map.Entry<String, Object> entry = object.entrySet().iterator().next();
assertSame(foo, entry.getKey());
@SuppressWarnings("unchecked")
List<String> array = (List<String>)entry.getValue();
for (String item : array)
{
assertSame(foo, item);
}
}
@Test
public void testEncodedCaching()
{
AsyncJSON.Factory factory = new AsyncJSON.Factory();
assertFalse(factory.cache("yèck"));
String foo = "foo\\yuck";
assertTrue(factory.cache(foo));
AsyncJSON parser = factory.newAsyncJSON();
String json = "{\"foo\\\\yuck\": [\"foo\\\\yuck\", \"foo\\\\yuck\"]}";
parser.parse(UTF_8.encode(json));
Map<String, Object> object = parser.complete();
Map.Entry<String, Object> entry = object.entrySet().iterator().next();
assertSame(foo, entry.getKey());
@SuppressWarnings("unchecked")
List<String> array = (List<String>)entry.getValue();
for (String item : array)
{
assertSame(foo, item);
}
}
}

View File

@ -614,7 +614,7 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
boolean added = _trie.put(s, v);
while (!added && _growby > 0)
{
ArrayTernaryTrie<V> bigger = new ArrayTernaryTrie<>(_trie._key.length + _growby);
ArrayTernaryTrie<V> bigger = new ArrayTernaryTrie<>(_trie.isCaseInsensitive(), _trie._key.length + _growby);
for (Map.Entry<String, V> entry : _trie.entrySet())
{
bigger.put(entry.getKey(), entry.getValue());

View File

@ -111,8 +111,7 @@ public class AttributesMap implements Attributes, Dumpable
if (attrs instanceof AttributesMap)
return Collections.enumeration(((AttributesMap)attrs).keySet());
List<String> names = new ArrayList<>();
names.addAll(Collections.list(attrs.getAttributeNames()));
List<String> names = new ArrayList<>(Collections.list(attrs.getAttributeNames()));
return Collections.enumeration(names);
}

View File

@ -19,11 +19,13 @@
package org.eclipse.jetty.util.thread;
import java.io.Closeable;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@ -36,6 +38,7 @@ import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class QueuedThreadPoolTest extends AbstractThreadPoolTest
@ -753,6 +756,24 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
assertThrows(IllegalArgumentException.class, () -> new QueuedThreadPool(4, 8));
}
@Test
public void testJoinWithStopTimeout() throws Exception
{
final long stopTimeout = 100;
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setStopTimeout(100);
threadPool.start();
// Verify that join does not timeout after waiting twice the stopTimeout.
assertThrows(Throwable.class, () ->
assertTimeoutPreemptively(Duration.ofMillis(stopTimeout * 2), threadPool::join)
);
// After stopping the ThreadPool join should unblock.
LifeCycle.stop(threadPool);
assertTimeoutPreemptively(Duration.ofMillis(stopTimeout), threadPool::join);
}
@Test
public void testDump() throws Exception
{

View File

@ -1200,7 +1200,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
{
ServletMapping mapping = new ServletMapping(new Source(Source.Origin.DESCRIPTOR, descriptor.getResource().toString()));
mapping.setServletName(servletName);
mapping.setDefault(descriptor instanceof DefaultsDescriptor);
mapping.setFromDefaultDescriptor(descriptor instanceof DefaultsDescriptor);
List<String> paths = new ArrayList<String>();
Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
@ -1221,9 +1221,9 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
{
//The same path has been mapped multiple times, either to a different servlet or the same servlet.
//If its a different servlet, this is only valid to do if the old mapping was from a default descriptor.
if (p.equals(ps) && (sm.isDefault() || servletName.equals(sm.getServletName())))
if (p.equals(ps) && (sm.isFromDefaultDescriptor() || servletName.equals(sm.getServletName())))
{
if (sm.isDefault())
if (sm.isFromDefaultDescriptor())
{
if (LOG.isDebugEnabled())
LOG.debug("{} in mapping {} from defaults descriptor is overridden by ", ps, sm, servletName);

View File

@ -14,9 +14,11 @@
<url>http://www.eclipse.org/jetty</url>
<modules>
<module>websocket-core</module>
<module>websocket-util</module>
<module>websocket-util-server</module>
<!-- WebSocket Core Implementation -->
<module>websocket-core-common</module>
<module>websocket-core-client</module>
<module>websocket-core-server</module>
<module>websocket-core-tests</module>
<!-- Jetty WebSocket Implementation -->
<module>websocket-jetty-api</module>
<module>websocket-jetty-common</module>
@ -28,6 +30,9 @@
<module>websocket-javax-client</module>
<module>websocket-javax-server</module>
<module>websocket-javax-tests</module>
<!-- Common WebSocket Implementation -->
<module>websocket-util</module>
<module>websocket-util-server</module>
</modules>
<build>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>websocket-core-client</artifactId>
<name>Jetty :: Websocket :: Core :: Client</name>
<properties>
<bundle-symbolic-name>${project.groupId}.core.client</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-core-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>ban-ws-apis</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<includes>
<include>org.eclipse.jetty.websocket:websocket-jetty-api</include>
<include>javax.websocket</include>
</includes>
</bannedDependencies>
</rules>
</configuration>
</execution>
<execution>
<id>ban-java-servlet-api</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<includes>
<include>javax.servlet</include>
<include>servletapi</include>
<include>org.eclipse.jetty.orbit:javax.servlet</include>
<include>org.mortbay.jetty:servlet-api</include>
<include>jetty:servlet-api</include>
<include>jetty-servlet-api</include>
</includes>
</bannedDependencies>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,29 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
module org.eclipse.jetty.websocket.core.client
{
exports org.eclipse.jetty.websocket.core.client;
requires org.slf4j;
requires transitive org.eclipse.jetty.client;
requires transitive org.eclipse.jetty.websocket.core.common;
// Only required if using XmlHttpClientProvider.
requires static org.eclipse.jetty.xml;
}

View File

@ -80,8 +80,8 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon
protected final CompletableFuture<CoreSession> futureCoreSession;
private final WebSocketCoreClient wsClient;
private FrameHandler frameHandler;
private Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer();
private List<UpgradeListener> upgradeListeners = new ArrayList<>();
private final Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer();
private final List<UpgradeListener> upgradeListeners = new ArrayList<>();
private List<ExtensionConfig> requestedExtensions = new ArrayList<>();
public ClientUpgradeRequest(WebSocketCoreClient webSocketClient, URI requestURI)

View File

@ -40,7 +40,7 @@ public class WebSocketCoreClient extends ContainerLifeCycle
private static final Logger LOG = LoggerFactory.getLogger(WebSocketCoreClient.class);
private final HttpClient httpClient;
private WebSocketComponents components;
private final WebSocketComponents components;
// TODO: Things to consider for inclusion in this class (or removal if they can be set elsewhere, like HttpClient)
// - AsyncWrite Idle Timeout

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>websocket-core-common</artifactId>
<name>Jetty :: Websocket :: Core :: Common</name>
<properties>
<bundle-symbolic-name>${project.groupId}.core.common</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>ban-ws-apis</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<includes>
<include>org.eclipse.jetty.websocket:websocket-jetty-api</include>
<include>javax.websocket</include>
</includes>
</bannedDependencies>
</rules>
</configuration>
</execution>
<execution>
<id>ban-java-servlet-api</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<includes>
<include>javax.servlet</include>
<include>servletapi</include>
<include>org.eclipse.jetty.orbit:javax.servlet</include>
<include>org.mortbay.jetty:servlet-api</include>
<include>jetty:servlet-api</include>
<include>jetty-servlet-api</include>
</includes>
</bannedDependencies>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>generate-manifest</id>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Export-Package>*,org.eclipse.jetty.websocket.core.common.internal.*</Export-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,11 @@
DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
[description]
Enable both jetty and javax websocket modules for deployed web applications.
[tags]
websocket
[depend]
websocket-jetty
websocket-javax

View File

@ -22,22 +22,16 @@ import org.eclipse.jetty.websocket.core.internal.IdentityExtension;
import org.eclipse.jetty.websocket.core.internal.PerMessageDeflateExtension;
import org.eclipse.jetty.websocket.core.internal.ValidationExtension;
module org.eclipse.jetty.websocket.core
module org.eclipse.jetty.websocket.core.common
{
exports org.eclipse.jetty.websocket.core;
exports org.eclipse.jetty.websocket.core.client;
exports org.eclipse.jetty.websocket.core.server;
exports org.eclipse.jetty.websocket.core.exception;
exports org.eclipse.jetty.websocket.core.internal to org.eclipse.jetty.util;
exports org.eclipse.jetty.websocket.core.internal to org.eclipse.jetty.websocket.core.client, org.eclipse.jetty.websocket.core.server, org.eclipse.jetty.util;
requires jetty.servlet.api;
requires transitive org.eclipse.jetty.client;
requires transitive org.eclipse.jetty.server;
requires org.eclipse.jetty.http;
requires org.eclipse.jetty.io;
requires org.slf4j;
// Only required if using XmlHttpClientProvider.
requires static org.eclipse.jetty.xml;
uses Extension;
provides Extension with

View File

@ -306,15 +306,12 @@ public class CloseStatus
}
// Reserved / not yet allocated
if ((statusCode == 1004) || // Reserved in RFC6455 (might be defined in the future)
((statusCode >= 1016) && (statusCode <= 2999)) || // Reserved in RFC6455 (for future revisions, and extensions)
(statusCode >= 5000)) // RFC6455 Not allowed to be used for any purpose
{
return false;
}
// RFC6455 Not allowed to be used for any purpose
return (statusCode != 1004) && // Reserved in RFC6455 (might be defined in the future)
((statusCode < 1016) || (statusCode > 2999)) && // Reserved in RFC6455 (for future revisions, and extensions)
(statusCode < 5000);
// All others are allowed
return true;
}
public Frame toFrame()

View File

@ -38,7 +38,7 @@ import org.eclipse.jetty.util.Trie;
*/
public class ExtensionConfig
{
private static Trie<ExtensionConfig> CACHE = new ArrayTrie<>(512);
private static final Trie<ExtensionConfig> CACHE = new ArrayTrie<>(512);
static
{
@ -315,7 +315,7 @@ public class ExtensionConfig
{
private final String parameterizedName;
private String name;
private Map<String, String> params = new HashMap<>();
private final Map<String, String> params = new HashMap<>();
public ParamParser(String parameterizedName)
{

View File

@ -193,11 +193,7 @@ public class Frame
{
return false;
}
if (!Arrays.equals(mask, other.mask))
{
return false;
}
return true;
return Arrays.equals(mask, other.mask);
}
public byte[] getMask()

View File

@ -19,8 +19,6 @@
package org.eclipse.jetty.websocket.core;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.core.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.core.server.Negotiation;
/**
* Interface for local WebSocket Endpoint Frame handling.
@ -31,11 +29,11 @@ import org.eclipse.jetty.websocket.core.server.Negotiation;
* is instantiated by the application, either:
* </p>
* <ul>
* <li>On the server, the application layer must provide a {@link org.eclipse.jetty.websocket.core.server.WebSocketNegotiator} instance
* <li>On the server, the application layer must provide a {@code org.eclipse.jetty.websocket.core.server.WebSocketNegotiator} instance
* to negotiate and accept websocket connections, which will return the FrameHandler instance to use from
* {@link org.eclipse.jetty.websocket.core.server.WebSocketNegotiator#negotiate(Negotiation)}.</li>
* <li>On the client, the application returns the FrameHandler instance to user from the {@link ClientUpgradeRequest}
* instance that it passes to the {@link org.eclipse.jetty.websocket.core.client.WebSocketCoreClient#connect(ClientUpgradeRequest)} method/</li>
* {@code org.eclipse.jetty.websocket.core.server.WebSocketNegotiator#negotiate(Negotiation)}.</li>
* <li>On the client, the application returns the FrameHandler instance to user from the {@code ClientUpgradeRequest}
* instance that it passes to the {@code org.eclipse.jetty.websocket.core.client.WebSocketCoreClient#connect(ClientUpgradeRequest)} method/</li>
* </ul>
* <p>
* Once instantiated the FrameHandler follows is used as follows:

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.websocket.core;
import java.util.zip.Deflater;
import javax.servlet.ServletContext;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
@ -31,26 +30,14 @@ import org.eclipse.jetty.util.compression.InflaterPool;
/**
* A collection of components which are the resources needed for websockets such as
* {@link ByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}.
*
* These components should be accessed through {@link WebSocketComponents#ensureWebSocketComponents} so that
* the instance can be shared by being stored as a bean on the ContextHandler.
*/
public class WebSocketComponents
{
public static final String WEBSOCKET_COMPONENTS_ATTRIBUTE = WebSocketComponents.class.getName();
public static WebSocketComponents ensureWebSocketComponents(ServletContext servletContext)
{
// Ensure a mapping exists
WebSocketComponents components = (WebSocketComponents)servletContext.getAttribute(WEBSOCKET_COMPONENTS_ATTRIBUTE);
if (components == null)
{
components = new WebSocketComponents();
servletContext.setAttribute(WEBSOCKET_COMPONENTS_ATTRIBUTE, components);
}
return components;
}
private final DecoratedObjectFactory objectFactory;
private final WebSocketExtensionRegistry extensionRegistry;
private final ByteBufferPool bufferPool;
private final InflaterPool inflaterPool;
private final DeflaterPool deflaterPool;
public WebSocketComponents()
{
@ -69,12 +56,6 @@ public class WebSocketComponents
this.inflaterPool = inflaterPool;
}
private DecoratedObjectFactory objectFactory;
private WebSocketExtensionRegistry extensionRegistry;
private ByteBufferPool bufferPool;
private InflaterPool inflaterPool;
private DeflaterPool deflaterPool;
public ByteBufferPool getBufferPool()
{
return bufferPool;

View File

@ -30,7 +30,7 @@ import org.eclipse.jetty.util.TypeUtil;
public class WebSocketExtensionRegistry implements Iterable<Class<? extends Extension>>
{
private Map<String, Class<? extends Extension>> availableExtensions = new HashMap<>();
private final Map<String, Class<? extends Extension>> availableExtensions = new HashMap<>();
public WebSocketExtensionRegistry()
{

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