diff --git a/.gitignore b/.gitignore index ec67fe6..80a9a29 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /2.22.1.tar.gz /2.22.2.tar.gz /jersey-2.23.1.tar.gz +/jersey-2.23.2.tar.gz diff --git a/jersey-2.23.1-port-to-simple6.patch b/jersey-2.23.1-port-to-simple6.patch deleted file mode 100644 index e7f685e..0000000 --- a/jersey-2.23.1-port-to-simple6.patch +++ /dev/null @@ -1,910 +0,0 @@ -diff -Nru jersey-2.23.1/containers/simple-http/pom.xml jersey-2.23.1.simple/containers/simple-http/pom.xml ---- jersey-2.23.1/containers/simple-http/pom.xml 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/containers/simple-http/pom.xml 2016-07-27 11:00:51.199536729 +0200 -@@ -61,7 +61,15 @@ - - - org.simpleframework -- simple -+ simple-common -+ -+ -+ org.simpleframework -+ simple-http -+ -+ -+ org.simpleframework -+ simple-transport - - - -diff -Nru jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java ---- jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java 2016-07-27 11:07:53.941555233 +0200 -@@ -57,8 +57,8 @@ - import org.glassfish.hk2.api.ServiceLocator; - - import org.simpleframework.http.core.Container; --import org.simpleframework.http.core.ContainerServer; --import org.simpleframework.transport.Server; -+import org.simpleframework.http.core.ContainerSocketProcessor; -+import org.simpleframework.transport.SocketProcessor; - import org.simpleframework.transport.connect.Connection; - import org.simpleframework.transport.connect.SocketConnection; - -@@ -180,10 +180,10 @@ - public static SimpleServer create(final URI address, - final SSLContext context, - final SimpleContainer container) { -- return _create(address, context, container, new UnsafeValue() { -+ return _create(address, context, container, new UnsafeValue() { - @Override -- public Server get() throws IOException { -- return new ContainerServer(container); -+ public SocketProcessor get() throws IOException { -+ return new ContainerSocketProcessor(container); - } - }); - } -@@ -241,10 +241,10 @@ - final int count, - final int select) throws ProcessingException { - -- return _create(address, context, container, new UnsafeValue() { -+ return _create(address, context, container, new UnsafeValue() { - @Override -- public Server get() throws IOException { -- return new ContainerServer(container, count, select); -+ public SocketProcessor get() throws IOException { -+ return new ContainerSocketProcessor(container, count, select); - } - }); - } -@@ -252,7 +252,7 @@ - private static SimpleServer _create(final URI address, - final SSLContext context, - final SimpleContainer container, -- final UnsafeValue serverProvider) throws ProcessingException { -+ final UnsafeValue serverProvider) throws ProcessingException { - if (address == null) { - throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL()); - } -@@ -277,8 +277,9 @@ - final InetSocketAddress listen = new InetSocketAddress(port); - final Connection connection; - try { -- final Server server = serverProvider.get(); -- connection = new SocketConnection(server); -+ final SimpleTraceAnalyzer analyzer = new SimpleTraceAnalyzer(); -+ final SocketProcessor server = serverProvider.get(); -+ connection = new SocketConnection(server, analyzer); - - final SocketAddress socketAddr = connection.connect(listen, context); - container.onServerStart(); -@@ -288,6 +289,7 @@ - @Override - public void close() throws IOException { - container.onServerStop(); -+ analyzer.stop(); - connection.close(); - } - -@@ -295,6 +297,21 @@ - public int getPort() { - return ((InetSocketAddress) socketAddr).getPort(); - } -+ -+ @Override -+ public boolean isDebug() { -+ return analyzer.isActive(); -+ } -+ -+ @Override -+ public void setDebug(boolean enable) { -+ if(enable) { -+ analyzer.start(); -+ } else { -+ analyzer.stop(); -+ } -+ } -+ - }; - } catch (final IOException ex) { - throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), ex); -diff -Nru jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java ---- jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java 2016-07-27 11:18:55.837316112 +0200 -@@ -47,7 +47,11 @@ - import java.security.Principal; - import java.util.List; - import java.util.Map; -+import java.util.concurrent.ScheduledExecutorService; -+import java.util.concurrent.ScheduledFuture; -+import java.util.concurrent.ScheduledThreadPoolExecutor; - import java.util.concurrent.TimeUnit; -+import java.util.concurrent.atomic.AtomicReference; - import java.util.logging.Level; - import java.util.logging.Logger; - -@@ -70,16 +74,20 @@ - import org.glassfish.jersey.server.internal.ContainerUtils; - import org.glassfish.jersey.server.spi.Container; - import org.glassfish.jersey.server.spi.ContainerResponseWriter; -+import org.glassfish.jersey.server.spi.ContainerResponseWriter.TimeoutHandler; - import org.glassfish.jersey.server.spi.RequestScopedInitializer; - - import org.glassfish.hk2.api.ServiceLocator; - import org.glassfish.hk2.api.TypeLiteral; - import org.glassfish.hk2.utilities.binding.AbstractBinder; - -+import org.simpleframework.common.thread.DaemonFactory; - import org.simpleframework.http.Address; -+import org.simpleframework.http.Protocol; - import org.simpleframework.http.Request; - import org.simpleframework.http.Response; - import org.simpleframework.http.Status; -+import org.simpleframework.http.parse.PrincipalParser; - - /** - * Jersey {@code Container} implementation based on Simple framework {@link org.simpleframework.http.core.Container}. -@@ -141,14 +149,19 @@ - } - } - -+ private volatile ScheduledExecutorService scheduler; - private volatile ApplicationHandler appHandler; - -- private static final class Writer implements ContainerResponseWriter { -+ private static final class ResponseWriter implements ContainerResponseWriter { - -+ private final AtomicReference reference; -+ private final ScheduledExecutorService scheduler; - private final Response response; - -- Writer(final Response response) { -+ ResponseWriter(final Response response, final ScheduledExecutorService scheduler) { -+ this.reference = new AtomicReference(); - this.response = response; -+ this.scheduler = scheduler; - } - - @Override -@@ -177,12 +190,36 @@ - - @Override - public boolean suspend(final long timeOut, final TimeUnit timeUnit, final TimeoutHandler timeoutHandler) { -- throw new UnsupportedOperationException("Method suspend is not supported by the container."); -+ try { -+ TimeoutTimer timer = reference.get(); -+ -+ if(timer == null) { -+ TimeoutDispatcher task = new TimeoutDispatcher(this, timeoutHandler); -+ ScheduledFuture future = scheduler.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); -+ timer = new TimeoutTimer(scheduler, future, task); -+ reference.set(timer); -+ return true; -+ } -+ return false; -+ } catch (final IllegalStateException ex) { -+ return false; -+ } finally { -+ logger.debugLog("suspend(...) called"); -+ } - } - - @Override - public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throws IllegalStateException { -- throw new UnsupportedOperationException("Method suspend is not supported by the container."); -+ try { -+ TimeoutTimer timer = reference.get(); -+ -+ if(timer == null) { -+ throw new IllegalStateException("Response has not been suspended"); -+ } -+ timer.reschedule(timeOut, timeUnit); -+ } finally { -+ logger.debugLog("setTimeout(...) called"); -+ } - } - - @Override -@@ -196,6 +233,10 @@ - } - } - -+ public boolean isSuspended() { -+ return reference.get() != null; -+ } -+ - @Override - public void failure(final Throwable error) { - try { -@@ -231,9 +272,55 @@ - - } - -+ private static final class TimeoutTimer { -+ -+ private final AtomicReference> reference; -+ private final ScheduledExecutorService service; -+ private final TimeoutDispatcher task; -+ -+ public TimeoutTimer(ScheduledExecutorService service, ScheduledFuture future, TimeoutDispatcher task) { -+ this.reference = new AtomicReference>(); -+ this.service = service; -+ this.task = task; -+ } -+ -+ public void reschedule(long timeOut, TimeUnit timeUnit) { -+ ScheduledFuture future = reference.getAndSet(null); -+ -+ if(future != null) { -+ if(future.cancel(false)) { -+ future = service.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); -+ reference.set(future); -+ } -+ } else { -+ future = service.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); -+ reference.set(future); -+ } -+ } -+ } -+ -+ private static final class TimeoutDispatcher implements Runnable { -+ -+ private final ResponseWriter writer; -+ private final TimeoutHandler handler; -+ -+ public TimeoutDispatcher(ResponseWriter writer, TimeoutHandler handler) { -+ this.writer = writer; -+ this.handler = handler; -+ } -+ -+ public void run() { -+ try { -+ handler.onTimeout(writer); -+ } catch(Exception e) { -+ logger.log(Level.INFO, "Failed to call timeout handler", e); -+ } -+ } -+ } -+ - @Override - public void handle(final Request request, final Response response) { -- final Writer responseWriter = new Writer(response); -+ final ResponseWriter responseWriter = new ResponseWriter(response, scheduler); - final URI baseUri = getBaseUri(request); - final URI requestUri = getRequestUri(request, baseUri); - -@@ -261,7 +348,9 @@ - } catch (final Exception ex) { - throw new RuntimeException(ex); - } finally { -- close(response); -+ if(!responseWriter.isSuspended()) { -+ close(response); -+ } - } - } - -@@ -316,7 +405,7 @@ - - @Override - public Principal getUserPrincipal() { -- return request.getSecuritySession().getLocalPrincipal(); -+ return null; - } - - @Override -@@ -348,7 +437,8 @@ - public void reload(final ResourceConfig configuration) { - appHandler.onShutdown(this); - -- appHandler = new ApplicationHandler(configuration.register(new SimpleBinder())); -+ appHandler = new ApplicationHandler(configuration.register(new SimpleBinder())); -+ scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); - appHandler.onReload(this); - appHandler.onStartup(this); - } -@@ -374,6 +464,7 @@ - */ - void onServerStop() { - appHandler.onShutdown(this); -+ scheduler.shutdown(); - } - - /** -@@ -384,6 +475,7 @@ - */ - SimpleContainer(final Application application, final ServiceLocator parentLocator) { - this.appHandler = new ApplicationHandler(application, new SimpleBinder(), parentLocator); -+ this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); - } - - /** -@@ -393,5 +485,6 @@ - */ - SimpleContainer(final Application application) { - this.appHandler = new ApplicationHandler(application, new SimpleBinder()); -+ this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); - } - } -diff -Nru jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java ---- jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java 2016-07-27 11:20:16.619503481 +0200 -@@ -50,5 +50,34 @@ - */ - public interface SimpleServer extends Closeable { - -+ /** -+ * The port the server is listening to for incomming HTTP connections. If the -+ * port is not specified the {@linke org.glassfish.jersey.server.spi.Container.DEFAULT_PORT} -+ * is used. -+ * -+ * @return the port the server is listening on -+ */ - public int getPort(); -+ -+ /** -+ * If this is true then very low level I/O operations are logged. Typically this is used -+ * to debug I/O issues such as HTTPS handshakes or performance issues by analysing the -+ * various latencies involved in the HTTP conversation. -+ *

-+ * There is a minimal performance penalty if this is enabled and it is perfectly suited -+ * to being enabled in a production environment, at the cost of logging overhead. -+ * -+ * @return true if debug is enabled, false otherwise -+ */ -+ public boolean isDebug(); -+ -+ /** -+ * To enable very low level logging this can be enabled. This goes far beyond logging -+ * issues such as connection establishment of request dispatch, it can trace the TCP -+ * operations latencies involved. -+ * -+ * @param enable if true debug tracing will be enabled -+ */ -+ public void setDebug(boolean enable); -+ - } -diff -Nru jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java ---- jersey-2.23.1/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java 1970-01-01 01:00:00.000000000 +0100 -+++ jersey-2.23.1.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java 2016-07-27 09:54:22.000000000 +0200 -@@ -0,0 +1,177 @@ -+package org.glassfish.jersey.simple; -+ -+import java.io.PrintWriter; -+import java.io.StringWriter; -+import java.nio.channels.SelectableChannel; -+import java.util.Queue; -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.ThreadFactory; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicLong; -+import java.util.logging.Level; -+import java.util.logging.Logger; -+ -+import org.glassfish.jersey.internal.util.ExtendedLogger; -+import org.simpleframework.common.thread.DaemonFactory; -+import org.simpleframework.transport.trace.Trace; -+import org.simpleframework.transport.trace.TraceAnalyzer; -+ -+/** -+ * Tracing at a very low level can be performed with a {@link TraceAnalyzer}. This provides much -+ * more useful information than the conventional {@link LoggingFilter} in that it provides -+ * details at a very low level. This is very useful when monitoring performance interactions -+ * at the TCP level between clients and servers. -+ *

-+ * Performance overhead for the server is minimal as events are pumped out in batches. The -+ * amount of logging information will increase quite significantly though. -+ * -+ * @author Niall Gallagher -+ */ -+public class SimpleTraceAnalyzer implements TraceAnalyzer { -+ -+ private static final ExtendedLogger logger = new ExtendedLogger(Logger.getLogger(SimpleTraceAnalyzer.class.getName()), Level.FINEST); -+ -+ private final TraceConsumer consumer; -+ private final ThreadFactory factory; -+ private final AtomicBoolean active; -+ private final AtomicLong count; -+ -+ public SimpleTraceAnalyzer() { -+ this.factory = new DaemonFactory(TraceConsumer.class); -+ this.consumer = new TraceConsumer(); -+ this.active = new AtomicBoolean(); -+ this.count = new AtomicLong(); -+ } -+ -+ public boolean isActive() { -+ return active.get(); -+ } -+ -+ @Override -+ public Trace attach(SelectableChannel channel) { -+ long sequence = count.getAndIncrement(); -+ return new TraceFeeder(channel, sequence); -+ } -+ -+ public void start() { -+ if(active.compareAndSet(false, true)) { -+ Thread thread = factory.newThread(consumer); -+ thread.start(); -+ } -+ } -+ -+ @Override -+ public void stop() { -+ active.set(false); -+ } -+ -+ private class TraceConsumer implements Runnable { -+ -+ private final Queue queue; -+ -+ public TraceConsumer() { -+ this.queue = new ConcurrentLinkedQueue(); -+ } -+ -+ public void consume(TraceRecord record) { -+ queue.offer(record); -+ } -+ -+ public void run() { -+ try { -+ while(active.get()) { -+ Thread.sleep(1000); -+ drain(); -+ } -+ } catch(Exception e) { -+ logger.info("Trace analyzer error"); -+ } finally { -+ try { -+ drain(); -+ } catch(Exception e) { -+ logger.info("Trace analyzer could not drain queue"); -+ } -+ active.set(false); -+ } -+ -+ } -+ -+ private void drain() { -+ while(!queue.isEmpty()) { -+ TraceRecord record = queue.poll(); -+ -+ if(record != null) { -+ String message = record.toString(); -+ logger.info(message); -+ } -+ } -+ } -+ } -+ -+ private class TraceFeeder implements Trace { -+ -+ private final SelectableChannel channel; -+ private final long sequence; -+ -+ public TraceFeeder(SelectableChannel channel, long sequence) { -+ this.sequence = sequence; -+ this.channel = channel; -+ } -+ -+ @Override -+ public void trace(Object event) { -+ trace(event, null); -+ } -+ -+ @Override -+ public void trace(Object event, Object value) { -+ if(active.get()) { -+ TraceRecord record = new TraceRecord(channel, event, value, sequence); -+ consumer.consume(record); -+ } -+ } -+ -+ } -+ -+ private class TraceRecord { -+ -+ private final SelectableChannel channel; -+ private final String thread; -+ private final Object event; -+ private final Object value; -+ private final long sequence; -+ -+ public TraceRecord(SelectableChannel channel, Object event, Object value, long sequence) { -+ this.thread = Thread.currentThread().getName(); -+ this.sequence = sequence; -+ this.channel = channel; -+ this.event = event; -+ this.value = value; -+ } -+ -+ public String toString() { -+ StringWriter builder = new StringWriter(); -+ PrintWriter writer = new PrintWriter(builder); -+ -+ writer.print(sequence); -+ writer.print(" "); -+ writer.print(channel); -+ writer.print(" ("); -+ writer.print(thread); -+ writer.print("): "); -+ writer.print(event); -+ -+ if(value != null) { -+ if(value instanceof Throwable) { -+ writer.print(" -> "); -+ ((Throwable)value).printStackTrace(writer); -+ } else { -+ writer.print(" -> "); -+ writer.print(value); -+ } -+ } -+ writer.close(); -+ return builder.toString(); -+ } -+ } -+} -diff -Nru jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java ---- jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java 2016-07-27 11:28:02.254502511 +0200 -@@ -94,7 +94,7 @@ - return DEFAULT_PORT; - } - -- private volatile Closeable server; -+ private volatile SimpleServer server; - - public UriBuilder getUri() { - return UriBuilder.fromUri("http://localhost").port(getPort()).path(CONTEXT); -@@ -108,6 +108,13 @@ - LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); - } - -+ public void startServerNoLoggingFilter(Class... resources) { -+ ResourceConfig config = new ResourceConfig(resources); -+ final URI baseUri = getBaseUri(); -+ server = SimpleContainerFactory.create(baseUri, config); -+ LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); -+ } -+ - public void startServer(ResourceConfig config) { - final URI baseUri = getBaseUri(); - config.register(LoggingFeature.class); -@@ -126,6 +133,12 @@ - return UriBuilder.fromUri("http://localhost/").port(getPort()).build(); - } - -+ public void setDebug(boolean enable) { -+ if(server != null) { -+ server.setDebug(enable); -+ } -+ } -+ - public void stopServer() { - try { - server.close(); -diff -Nru jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java ---- jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java 1970-01-01 01:00:00.000000000 +0100 -+++ jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java 2016-07-27 09:54:22.000000000 +0200 -@@ -0,0 +1,193 @@ -+/* -+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. -+ * -+ * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. -+ * -+ * The contents of this file are subject to the terms of either the GNU -+ * General Public License Version 2 only ("GPL") or the Common Development -+ * and Distribution License("CDDL") (collectively, the "License"). You -+ * may not use this file except in compliance with the License. You can -+ * obtain a copy of the License at -+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html -+ * or packager/legal/LICENSE.txt. See the License for the specific -+ * language governing permissions and limitations under the License. -+ * -+ * When distributing the software, include this License Header Notice in each -+ * file and include the License file at packager/legal/LICENSE.txt. -+ * -+ * GPL Classpath Exception: -+ * Oracle designates this particular file as subject to the "Classpath" -+ * exception as provided by Oracle in the GPL Version 2 section of the License -+ * file that accompanied this code. -+ * -+ * Modifications: -+ * If applicable, add the following below the License Header, with the fields -+ * enclosed by brackets [] replaced by your own identifying information: -+ * "Portions Copyright [year] [name of copyright owner]" -+ * -+ * Contributor(s): -+ * If you wish your version of this file to be governed by only the CDDL or -+ * only the GPL Version 2, indicate your decision by adding "[Contributor] -+ * elects to include this software in this distribution under the [CDDL or GPL -+ * Version 2] license." If you don't indicate a single choice of license, a -+ * recipient has the option to distribute your version of this file under -+ * either the CDDL, the GPL Version 2 or to extend the choice of license to -+ * its licensees as provided above. However, if you add GPL Version 2 code -+ * and therefore, elected the GPL Version 2 license, then the option applies -+ * only if the new code is made subject to such option by the copyright -+ * holder. -+ */ -+package org.glassfish.jersey.simple; -+ -+import static org.hamcrest.CoreMatchers.is; -+import static org.junit.Assert.assertEquals; -+import static org.junit.Assert.assertThat; -+ -+import java.util.concurrent.ExecutionException; -+import java.util.concurrent.Future; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.TimeoutException; -+import java.util.concurrent.atomic.AtomicInteger; -+ -+import javax.ws.rs.GET; -+import javax.ws.rs.Path; -+import javax.ws.rs.client.Client; -+import javax.ws.rs.client.ClientBuilder; -+import javax.ws.rs.container.AsyncResponse; -+import javax.ws.rs.container.Suspended; -+import javax.ws.rs.container.TimeoutHandler; -+import javax.ws.rs.core.Response; -+ -+import org.junit.After; -+import org.junit.Before; -+import org.junit.Test; -+ -+/** -+ * @author Arul Dhesiaseelan (aruld at acm.org) -+ * @author Michal Gajdos -+ */ -+public class AsyncTest extends AbstractSimpleServerTester { -+ -+ @Path("/async") -+ @SuppressWarnings("VoidMethodAnnotatedWithGET") -+ public static class AsyncResource { -+ -+ public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0); -+ -+ @GET -+ public void asyncGet(@Suspended final AsyncResponse asyncResponse) { -+ new Thread(new Runnable() { -+ -+ @Override -+ public void run() { -+ final String result = veryExpensiveOperation(); -+ asyncResponse.resume(result); -+ } -+ -+ private String veryExpensiveOperation() { -+ // ... very expensive operation that typically finishes within 5 seconds, simulated using sleep() -+ try { -+ Thread.sleep(5000); -+ } catch (final InterruptedException e) { -+ // ignore -+ } -+ return "DONE"; -+ } -+ }).start(); -+ } -+ -+ @GET -+ @Path("timeout") -+ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) { -+ asyncResponse.setTimeoutHandler(new TimeoutHandler() { -+ -+ @Override -+ public void handleTimeout(final AsyncResponse asyncResponse) { -+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.") -+ .build()); -+ } -+ }); -+ asyncResponse.setTimeout(3, TimeUnit.SECONDS); -+ -+ new Thread(new Runnable() { -+ -+ @Override -+ public void run() { -+ final String result = veryExpensiveOperation(); -+ asyncResponse.resume(result); -+ } -+ -+ private String veryExpensiveOperation() { -+ // ... very expensive operation that typically finishes within 10 seconds, simulated using sleep() -+ try { -+ Thread.sleep(7000); -+ } catch (final InterruptedException e) { -+ // ignore -+ } -+ return "DONE"; -+ } -+ }).start(); -+ } -+ -+ @GET -+ @Path("multiple-invocations") -+ public void asyncMultipleInvocations(@Suspended final AsyncResponse asyncResponse) { -+ INVOCATION_COUNT.incrementAndGet(); -+ -+ new Thread(new Runnable() { -+ @Override -+ public void run() { -+ asyncResponse.resume("OK"); -+ } -+ }).start(); -+ } -+ } -+ -+ private Client client; -+ -+ @Before -+ public void setUp() throws Exception { -+ startServer(AsyncResource.class); -+ client = ClientBuilder.newClient(); -+ } -+ -+ @Override -+ @After -+ public void tearDown() { -+ super.tearDown(); -+ client = null; -+ } -+ -+ @Test -+ public void testAsyncGet() throws ExecutionException, InterruptedException { -+ final Future responseFuture = client.target(getUri().path("/async")).request().async().get(); -+ // Request is being processed asynchronously. -+ final Response response = responseFuture.get(); -+ // get() waits for the response -+ assertEquals("DONE", response.readEntity(String.class)); -+ } -+ -+ @Test -+ public void testAsyncGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException { -+ final Future responseFuture = client.target(getUri().path("/async/timeout")).request().async().get(); -+ // Request is being processed asynchronously. -+ final Response response = responseFuture.get(); -+ -+ // get() waits for the response -+ assertEquals(503, response.getStatus()); -+ assertEquals("Operation time out.", response.readEntity(String.class)); -+ } -+ -+ /** -+ * JERSEY-2616 reproducer. Make sure resource method is only invoked once per one request. -+ */ -+ @Test -+ public void testAsyncMultipleInvocations() throws Exception { -+ final Response response = client.target(getUri().path("/async/multiple-invocations")).request().get(); -+ -+ assertThat(AsyncResource.INVOCATION_COUNT.get(), is(1)); -+ -+ assertThat(response.getStatus(), is(200)); -+ assertThat(response.readEntity(String.class), is("OK")); -+ } -+} -diff -Nru jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java ---- jersey-2.23.1/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java 1970-01-01 01:00:00.000000000 +0100 -+++ jersey-2.23.1.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java 2016-07-27 09:54:22.000000000 +0200 -@@ -0,0 +1,89 @@ -+package org.glassfish.jersey.simple; -+ -+import static org.hamcrest.CoreMatchers.is; -+import static org.hamcrest.MatcherAssert.assertThat; -+import static org.junit.Assert.assertEquals; -+import static org.junit.Assert.assertTrue; -+ -+import javax.ws.rs.GET; -+import javax.ws.rs.Path; -+import javax.ws.rs.Produces; -+import javax.ws.rs.client.Client; -+import javax.ws.rs.client.ClientBuilder; -+import javax.ws.rs.core.Response; -+ -+import org.junit.After; -+import org.junit.Before; -+import org.junit.Test; -+ -+public class TraceTest extends AbstractSimpleServerTester { -+ -+ @Path("helloworld") -+ public static class HelloWorldResource { -+ public static final String CLICHED_MESSAGE = "Hello World!"; -+ -+ @GET -+ @Produces("text/plain") -+ public String getHello() { -+ return CLICHED_MESSAGE; -+ } -+ } -+ -+ @Path("/users") -+ public class UserResource { -+ -+ @Path("/current") -+ @GET -+ @Produces("text/plain") -+ public String getCurrentUser() { -+ return "current user"; -+ } -+ } -+ -+ private Client client; -+ -+ @Before -+ public void setUp() throws Exception { -+ startServerNoLoggingFilter(HelloWorldResource.class, UserResource.class); // disable crude LoggingFilter -+ setDebug(true); -+ client = ClientBuilder.newClient(); -+ } -+ -+ @Override -+ @After -+ public void tearDown() { -+ super.tearDown(); -+ client = null; -+ } -+ -+ -+ @Test -+ public void testFooBarOptions() { -+ for(int i = 0; i < 100; i++) { -+ Response response = client.target(getUri()).path("helloworld").request().header("Accept", "foo/bar").options(); -+ assertEquals(200, response.getStatus()); -+ final String allowHeader = response.getHeaderString("Allow"); -+ _checkAllowContent(allowHeader); -+ assertEquals(0, response.getLength()); -+ assertEquals("foo/bar", response.getMediaType().toString()); -+ -+ try { -+ Thread.sleep(50); -+ } catch(Exception e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ private void _checkAllowContent(final String content) { -+ assertTrue(content.contains("GET")); -+ assertTrue(content.contains("HEAD")); -+ assertTrue(content.contains("OPTIONS")); -+ } -+ -+ @Test -+ public void testNoDefaultMethod() { -+ Response response = client.target(getUri()).path("/users").request().options(); -+ assertThat(response.getStatus(), is(404)); -+ } -+} -diff -Nru jersey-2.23.1/pom.xml jersey-2.23.1.simple/pom.xml ---- jersey-2.23.1/pom.xml 2016-06-09 20:03:52.000000000 +0200 -+++ jersey-2.23.1.simple/pom.xml 2016-07-27 11:29:36.308056659 +0200 -@@ -1483,7 +1483,17 @@ - - - org.simpleframework -- simple -+ simple-common -+ ${simple.version} -+ -+ -+ org.simpleframework -+ simple-http -+ ${simple.version} -+ -+ -+ org.simpleframework -+ simple-transport - ${simple.version} - - -@@ -1927,7 +1937,7 @@ - 1.0.12 - 2.4 - 3.0.1 -- 5.1.4 -+ 6.0.1 - 1.7.12 - 3.2.3.RELEASE - 1.1.0.Final diff --git a/jersey-2.23.2-port-to-simple6.patch b/jersey-2.23.2-port-to-simple6.patch new file mode 100644 index 0000000..16579f6 --- /dev/null +++ b/jersey-2.23.2-port-to-simple6.patch @@ -0,0 +1,910 @@ +diff -Nru jersey-2.23.2/containers/simple-http/pom.xml jersey-2.23.2.simple/containers/simple-http/pom.xml +--- jersey-2.23.2/containers/simple-http/pom.xml 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/containers/simple-http/pom.xml 2016-09-28 13:59:48.888238051 +0200 +@@ -61,7 +61,15 @@ + + + org.simpleframework +- simple ++ simple-common ++ ++ ++ org.simpleframework ++ simple-http ++ ++ ++ org.simpleframework ++ simple-transport + + + +diff -Nru jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java +--- jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java 2016-09-28 13:59:48.889237999 +0200 +@@ -57,8 +57,8 @@ + import org.glassfish.hk2.api.ServiceLocator; + + import org.simpleframework.http.core.Container; +-import org.simpleframework.http.core.ContainerServer; +-import org.simpleframework.transport.Server; ++import org.simpleframework.http.core.ContainerSocketProcessor; ++import org.simpleframework.transport.SocketProcessor; + import org.simpleframework.transport.connect.Connection; + import org.simpleframework.transport.connect.SocketConnection; + +@@ -180,10 +180,10 @@ + public static SimpleServer create(final URI address, + final SSLContext context, + final SimpleContainer container) { +- return _create(address, context, container, new UnsafeValue() { ++ return _create(address, context, container, new UnsafeValue() { + @Override +- public Server get() throws IOException { +- return new ContainerServer(container); ++ public SocketProcessor get() throws IOException { ++ return new ContainerSocketProcessor(container); + } + }); + } +@@ -241,10 +241,10 @@ + final int count, + final int select) throws ProcessingException { + +- return _create(address, context, container, new UnsafeValue() { ++ return _create(address, context, container, new UnsafeValue() { + @Override +- public Server get() throws IOException { +- return new ContainerServer(container, count, select); ++ public SocketProcessor get() throws IOException { ++ return new ContainerSocketProcessor(container, count, select); + } + }); + } +@@ -252,7 +252,7 @@ + private static SimpleServer _create(final URI address, + final SSLContext context, + final SimpleContainer container, +- final UnsafeValue serverProvider) throws ProcessingException { ++ final UnsafeValue serverProvider) throws ProcessingException { + if (address == null) { + throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL()); + } +@@ -277,8 +277,9 @@ + final InetSocketAddress listen = new InetSocketAddress(port); + final Connection connection; + try { +- final Server server = serverProvider.get(); +- connection = new SocketConnection(server); ++ final SimpleTraceAnalyzer analyzer = new SimpleTraceAnalyzer(); ++ final SocketProcessor server = serverProvider.get(); ++ connection = new SocketConnection(server, analyzer); + + final SocketAddress socketAddr = connection.connect(listen, context); + container.onServerStart(); +@@ -288,6 +289,7 @@ + @Override + public void close() throws IOException { + container.onServerStop(); ++ analyzer.stop(); + connection.close(); + } + +@@ -295,6 +297,21 @@ + public int getPort() { + return ((InetSocketAddress) socketAddr).getPort(); + } ++ ++ @Override ++ public boolean isDebug() { ++ return analyzer.isActive(); ++ } ++ ++ @Override ++ public void setDebug(boolean enable) { ++ if(enable) { ++ analyzer.start(); ++ } else { ++ analyzer.stop(); ++ } ++ } ++ + }; + } catch (final IOException ex) { + throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), ex); +diff -Nru jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java +--- jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java 2016-09-28 13:59:48.889237999 +0200 +@@ -47,7 +47,11 @@ + import java.security.Principal; + import java.util.List; + import java.util.Map; ++import java.util.concurrent.ScheduledExecutorService; ++import java.util.concurrent.ScheduledFuture; ++import java.util.concurrent.ScheduledThreadPoolExecutor; + import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicReference; + import java.util.logging.Level; + import java.util.logging.Logger; + +@@ -70,16 +74,20 @@ + import org.glassfish.jersey.server.internal.ContainerUtils; + import org.glassfish.jersey.server.spi.Container; + import org.glassfish.jersey.server.spi.ContainerResponseWriter; ++import org.glassfish.jersey.server.spi.ContainerResponseWriter.TimeoutHandler; + import org.glassfish.jersey.server.spi.RequestScopedInitializer; + + import org.glassfish.hk2.api.ServiceLocator; + import org.glassfish.hk2.api.TypeLiteral; + import org.glassfish.hk2.utilities.binding.AbstractBinder; + ++import org.simpleframework.common.thread.DaemonFactory; + import org.simpleframework.http.Address; ++import org.simpleframework.http.Protocol; + import org.simpleframework.http.Request; + import org.simpleframework.http.Response; + import org.simpleframework.http.Status; ++import org.simpleframework.http.parse.PrincipalParser; + + /** + * Jersey {@code Container} implementation based on Simple framework {@link org.simpleframework.http.core.Container}. +@@ -141,14 +149,19 @@ + } + } + ++ private volatile ScheduledExecutorService scheduler; + private volatile ApplicationHandler appHandler; + +- private static final class Writer implements ContainerResponseWriter { ++ private static final class ResponseWriter implements ContainerResponseWriter { + ++ private final AtomicReference reference; ++ private final ScheduledExecutorService scheduler; + private final Response response; + +- Writer(final Response response) { ++ ResponseWriter(final Response response, final ScheduledExecutorService scheduler) { ++ this.reference = new AtomicReference(); + this.response = response; ++ this.scheduler = scheduler; + } + + @Override +@@ -177,12 +190,36 @@ + + @Override + public boolean suspend(final long timeOut, final TimeUnit timeUnit, final TimeoutHandler timeoutHandler) { +- throw new UnsupportedOperationException("Method suspend is not supported by the container."); ++ try { ++ TimeoutTimer timer = reference.get(); ++ ++ if(timer == null) { ++ TimeoutDispatcher task = new TimeoutDispatcher(this, timeoutHandler); ++ ScheduledFuture future = scheduler.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); ++ timer = new TimeoutTimer(scheduler, future, task); ++ reference.set(timer); ++ return true; ++ } ++ return false; ++ } catch (final IllegalStateException ex) { ++ return false; ++ } finally { ++ logger.debugLog("suspend(...) called"); ++ } + } + + @Override + public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throws IllegalStateException { +- throw new UnsupportedOperationException("Method suspend is not supported by the container."); ++ try { ++ TimeoutTimer timer = reference.get(); ++ ++ if(timer == null) { ++ throw new IllegalStateException("Response has not been suspended"); ++ } ++ timer.reschedule(timeOut, timeUnit); ++ } finally { ++ logger.debugLog("setTimeout(...) called"); ++ } + } + + @Override +@@ -196,6 +233,10 @@ + } + } + ++ public boolean isSuspended() { ++ return reference.get() != null; ++ } ++ + @Override + public void failure(final Throwable error) { + try { +@@ -231,9 +272,55 @@ + + } + ++ private static final class TimeoutTimer { ++ ++ private final AtomicReference> reference; ++ private final ScheduledExecutorService service; ++ private final TimeoutDispatcher task; ++ ++ public TimeoutTimer(ScheduledExecutorService service, ScheduledFuture future, TimeoutDispatcher task) { ++ this.reference = new AtomicReference>(); ++ this.service = service; ++ this.task = task; ++ } ++ ++ public void reschedule(long timeOut, TimeUnit timeUnit) { ++ ScheduledFuture future = reference.getAndSet(null); ++ ++ if(future != null) { ++ if(future.cancel(false)) { ++ future = service.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); ++ reference.set(future); ++ } ++ } else { ++ future = service.schedule(task, timeOut == 0 ? Integer.MAX_VALUE : timeOut, timeOut == 0 ? TimeUnit.SECONDS : timeUnit); ++ reference.set(future); ++ } ++ } ++ } ++ ++ private static final class TimeoutDispatcher implements Runnable { ++ ++ private final ResponseWriter writer; ++ private final TimeoutHandler handler; ++ ++ public TimeoutDispatcher(ResponseWriter writer, TimeoutHandler handler) { ++ this.writer = writer; ++ this.handler = handler; ++ } ++ ++ public void run() { ++ try { ++ handler.onTimeout(writer); ++ } catch(Exception e) { ++ logger.log(Level.INFO, "Failed to call timeout handler", e); ++ } ++ } ++ } ++ + @Override + public void handle(final Request request, final Response response) { +- final Writer responseWriter = new Writer(response); ++ final ResponseWriter responseWriter = new ResponseWriter(response, scheduler); + final URI baseUri = getBaseUri(request); + final URI requestUri = getRequestUri(request, baseUri); + +@@ -261,7 +348,9 @@ + } catch (final Exception ex) { + throw new RuntimeException(ex); + } finally { +- close(response); ++ if(!responseWriter.isSuspended()) { ++ close(response); ++ } + } + } + +@@ -316,7 +405,7 @@ + + @Override + public Principal getUserPrincipal() { +- return request.getSecuritySession().getLocalPrincipal(); ++ return null; + } + + @Override +@@ -348,7 +437,8 @@ + public void reload(final ResourceConfig configuration) { + appHandler.onShutdown(this); + +- appHandler = new ApplicationHandler(configuration.register(new SimpleBinder())); ++ appHandler = new ApplicationHandler(configuration.register(new SimpleBinder())); ++ scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); + appHandler.onReload(this); + appHandler.onStartup(this); + } +@@ -374,6 +464,7 @@ + */ + void onServerStop() { + appHandler.onShutdown(this); ++ scheduler.shutdown(); + } + + /** +@@ -384,6 +475,7 @@ + */ + SimpleContainer(final Application application, final ServiceLocator parentLocator) { + this.appHandler = new ApplicationHandler(application, new SimpleBinder(), parentLocator); ++ this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); + } + + /** +@@ -393,5 +485,6 @@ + */ + SimpleContainer(final Application application) { + this.appHandler = new ApplicationHandler(application, new SimpleBinder()); ++ this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); + } + } +diff -Nru jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java +--- jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java 2016-09-28 13:59:48.889237999 +0200 +@@ -50,5 +50,34 @@ + */ + public interface SimpleServer extends Closeable { + ++ /** ++ * The port the server is listening to for incomming HTTP connections. If the ++ * port is not specified the {@linke org.glassfish.jersey.server.spi.Container.DEFAULT_PORT} ++ * is used. ++ * ++ * @return the port the server is listening on ++ */ + public int getPort(); ++ ++ /** ++ * If this is true then very low level I/O operations are logged. Typically this is used ++ * to debug I/O issues such as HTTPS handshakes or performance issues by analysing the ++ * various latencies involved in the HTTP conversation. ++ *

++ * There is a minimal performance penalty if this is enabled and it is perfectly suited ++ * to being enabled in a production environment, at the cost of logging overhead. ++ * ++ * @return true if debug is enabled, false otherwise ++ */ ++ public boolean isDebug(); ++ ++ /** ++ * To enable very low level logging this can be enabled. This goes far beyond logging ++ * issues such as connection establishment of request dispatch, it can trace the TCP ++ * operations latencies involved. ++ * ++ * @param enable if true debug tracing will be enabled ++ */ ++ public void setDebug(boolean enable); ++ + } +diff -Nru jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java +--- jersey-2.23.2/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java 1970-01-01 01:00:00.000000000 +0100 ++++ jersey-2.23.2.simple/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleTraceAnalyzer.java 2016-09-28 13:59:48.890237947 +0200 +@@ -0,0 +1,177 @@ ++package org.glassfish.jersey.simple; ++ ++import java.io.PrintWriter; ++import java.io.StringWriter; ++import java.nio.channels.SelectableChannel; ++import java.util.Queue; ++import java.util.concurrent.ConcurrentLinkedQueue; ++import java.util.concurrent.ThreadFactory; ++import java.util.concurrent.atomic.AtomicBoolean; ++import java.util.concurrent.atomic.AtomicLong; ++import java.util.logging.Level; ++import java.util.logging.Logger; ++ ++import org.glassfish.jersey.internal.util.ExtendedLogger; ++import org.simpleframework.common.thread.DaemonFactory; ++import org.simpleframework.transport.trace.Trace; ++import org.simpleframework.transport.trace.TraceAnalyzer; ++ ++/** ++ * Tracing at a very low level can be performed with a {@link TraceAnalyzer}. This provides much ++ * more useful information than the conventional {@link LoggingFilter} in that it provides ++ * details at a very low level. This is very useful when monitoring performance interactions ++ * at the TCP level between clients and servers. ++ *

++ * Performance overhead for the server is minimal as events are pumped out in batches. The ++ * amount of logging information will increase quite significantly though. ++ * ++ * @author Niall Gallagher ++ */ ++public class SimpleTraceAnalyzer implements TraceAnalyzer { ++ ++ private static final ExtendedLogger logger = new ExtendedLogger(Logger.getLogger(SimpleTraceAnalyzer.class.getName()), Level.FINEST); ++ ++ private final TraceConsumer consumer; ++ private final ThreadFactory factory; ++ private final AtomicBoolean active; ++ private final AtomicLong count; ++ ++ public SimpleTraceAnalyzer() { ++ this.factory = new DaemonFactory(TraceConsumer.class); ++ this.consumer = new TraceConsumer(); ++ this.active = new AtomicBoolean(); ++ this.count = new AtomicLong(); ++ } ++ ++ public boolean isActive() { ++ return active.get(); ++ } ++ ++ @Override ++ public Trace attach(SelectableChannel channel) { ++ long sequence = count.getAndIncrement(); ++ return new TraceFeeder(channel, sequence); ++ } ++ ++ public void start() { ++ if(active.compareAndSet(false, true)) { ++ Thread thread = factory.newThread(consumer); ++ thread.start(); ++ } ++ } ++ ++ @Override ++ public void stop() { ++ active.set(false); ++ } ++ ++ private class TraceConsumer implements Runnable { ++ ++ private final Queue queue; ++ ++ public TraceConsumer() { ++ this.queue = new ConcurrentLinkedQueue(); ++ } ++ ++ public void consume(TraceRecord record) { ++ queue.offer(record); ++ } ++ ++ public void run() { ++ try { ++ while(active.get()) { ++ Thread.sleep(1000); ++ drain(); ++ } ++ } catch(Exception e) { ++ logger.info("Trace analyzer error"); ++ } finally { ++ try { ++ drain(); ++ } catch(Exception e) { ++ logger.info("Trace analyzer could not drain queue"); ++ } ++ active.set(false); ++ } ++ ++ } ++ ++ private void drain() { ++ while(!queue.isEmpty()) { ++ TraceRecord record = queue.poll(); ++ ++ if(record != null) { ++ String message = record.toString(); ++ logger.info(message); ++ } ++ } ++ } ++ } ++ ++ private class TraceFeeder implements Trace { ++ ++ private final SelectableChannel channel; ++ private final long sequence; ++ ++ public TraceFeeder(SelectableChannel channel, long sequence) { ++ this.sequence = sequence; ++ this.channel = channel; ++ } ++ ++ @Override ++ public void trace(Object event) { ++ trace(event, null); ++ } ++ ++ @Override ++ public void trace(Object event, Object value) { ++ if(active.get()) { ++ TraceRecord record = new TraceRecord(channel, event, value, sequence); ++ consumer.consume(record); ++ } ++ } ++ ++ } ++ ++ private class TraceRecord { ++ ++ private final SelectableChannel channel; ++ private final String thread; ++ private final Object event; ++ private final Object value; ++ private final long sequence; ++ ++ public TraceRecord(SelectableChannel channel, Object event, Object value, long sequence) { ++ this.thread = Thread.currentThread().getName(); ++ this.sequence = sequence; ++ this.channel = channel; ++ this.event = event; ++ this.value = value; ++ } ++ ++ public String toString() { ++ StringWriter builder = new StringWriter(); ++ PrintWriter writer = new PrintWriter(builder); ++ ++ writer.print(sequence); ++ writer.print(" "); ++ writer.print(channel); ++ writer.print(" ("); ++ writer.print(thread); ++ writer.print("): "); ++ writer.print(event); ++ ++ if(value != null) { ++ if(value instanceof Throwable) { ++ writer.print(" -> "); ++ ((Throwable)value).printStackTrace(writer); ++ } else { ++ writer.print(" -> "); ++ writer.print(value); ++ } ++ } ++ writer.close(); ++ return builder.toString(); ++ } ++ } ++} +diff -Nru jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java +--- jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java 2016-09-28 13:59:48.890237947 +0200 +@@ -94,7 +94,7 @@ + return DEFAULT_PORT; + } + +- private volatile Closeable server; ++ private volatile SimpleServer server; + + public UriBuilder getUri() { + return UriBuilder.fromUri("http://localhost").port(getPort()).path(CONTEXT); +@@ -108,6 +108,13 @@ + LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); + } + ++ public void startServerNoLoggingFilter(Class... resources) { ++ ResourceConfig config = new ResourceConfig(resources); ++ final URI baseUri = getBaseUri(); ++ server = SimpleContainerFactory.create(baseUri, config); ++ LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); ++ } ++ + public void startServer(ResourceConfig config) { + final URI baseUri = getBaseUri(); + config.register(LoggingFeature.class); +@@ -126,6 +133,12 @@ + return UriBuilder.fromUri("http://localhost/").port(getPort()).build(); + } + ++ public void setDebug(boolean enable) { ++ if(server != null) { ++ server.setDebug(enable); ++ } ++ } ++ + public void stopServer() { + try { + server.close(); +diff -Nru jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java +--- jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java 1970-01-01 01:00:00.000000000 +0100 ++++ jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AsyncTest.java 2016-09-28 13:59:48.890237947 +0200 +@@ -0,0 +1,193 @@ ++/* ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. ++ * ++ * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. ++ * ++ * The contents of this file are subject to the terms of either the GNU ++ * General Public License Version 2 only ("GPL") or the Common Development ++ * and Distribution License("CDDL") (collectively, the "License"). You ++ * may not use this file except in compliance with the License. You can ++ * obtain a copy of the License at ++ * http://glassfish.java.net/public/CDDL+GPL_1_1.html ++ * or packager/legal/LICENSE.txt. See the License for the specific ++ * language governing permissions and limitations under the License. ++ * ++ * When distributing the software, include this License Header Notice in each ++ * file and include the License file at packager/legal/LICENSE.txt. ++ * ++ * GPL Classpath Exception: ++ * Oracle designates this particular file as subject to the "Classpath" ++ * exception as provided by Oracle in the GPL Version 2 section of the License ++ * file that accompanied this code. ++ * ++ * Modifications: ++ * If applicable, add the following below the License Header, with the fields ++ * enclosed by brackets [] replaced by your own identifying information: ++ * "Portions Copyright [year] [name of copyright owner]" ++ * ++ * Contributor(s): ++ * If you wish your version of this file to be governed by only the CDDL or ++ * only the GPL Version 2, indicate your decision by adding "[Contributor] ++ * elects to include this software in this distribution under the [CDDL or GPL ++ * Version 2] license." If you don't indicate a single choice of license, a ++ * recipient has the option to distribute your version of this file under ++ * either the CDDL, the GPL Version 2 or to extend the choice of license to ++ * its licensees as provided above. However, if you add GPL Version 2 code ++ * and therefore, elected the GPL Version 2 license, then the option applies ++ * only if the new code is made subject to such option by the copyright ++ * holder. ++ */ ++package org.glassfish.jersey.simple; ++ ++import static org.hamcrest.CoreMatchers.is; ++import static org.junit.Assert.assertEquals; ++import static org.junit.Assert.assertThat; ++ ++import java.util.concurrent.ExecutionException; ++import java.util.concurrent.Future; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.TimeoutException; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++import javax.ws.rs.GET; ++import javax.ws.rs.Path; ++import javax.ws.rs.client.Client; ++import javax.ws.rs.client.ClientBuilder; ++import javax.ws.rs.container.AsyncResponse; ++import javax.ws.rs.container.Suspended; ++import javax.ws.rs.container.TimeoutHandler; ++import javax.ws.rs.core.Response; ++ ++import org.junit.After; ++import org.junit.Before; ++import org.junit.Test; ++ ++/** ++ * @author Arul Dhesiaseelan (aruld at acm.org) ++ * @author Michal Gajdos ++ */ ++public class AsyncTest extends AbstractSimpleServerTester { ++ ++ @Path("/async") ++ @SuppressWarnings("VoidMethodAnnotatedWithGET") ++ public static class AsyncResource { ++ ++ public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0); ++ ++ @GET ++ public void asyncGet(@Suspended final AsyncResponse asyncResponse) { ++ new Thread(new Runnable() { ++ ++ @Override ++ public void run() { ++ final String result = veryExpensiveOperation(); ++ asyncResponse.resume(result); ++ } ++ ++ private String veryExpensiveOperation() { ++ // ... very expensive operation that typically finishes within 5 seconds, simulated using sleep() ++ try { ++ Thread.sleep(5000); ++ } catch (final InterruptedException e) { ++ // ignore ++ } ++ return "DONE"; ++ } ++ }).start(); ++ } ++ ++ @GET ++ @Path("timeout") ++ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) { ++ asyncResponse.setTimeoutHandler(new TimeoutHandler() { ++ ++ @Override ++ public void handleTimeout(final AsyncResponse asyncResponse) { ++ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.") ++ .build()); ++ } ++ }); ++ asyncResponse.setTimeout(3, TimeUnit.SECONDS); ++ ++ new Thread(new Runnable() { ++ ++ @Override ++ public void run() { ++ final String result = veryExpensiveOperation(); ++ asyncResponse.resume(result); ++ } ++ ++ private String veryExpensiveOperation() { ++ // ... very expensive operation that typically finishes within 10 seconds, simulated using sleep() ++ try { ++ Thread.sleep(7000); ++ } catch (final InterruptedException e) { ++ // ignore ++ } ++ return "DONE"; ++ } ++ }).start(); ++ } ++ ++ @GET ++ @Path("multiple-invocations") ++ public void asyncMultipleInvocations(@Suspended final AsyncResponse asyncResponse) { ++ INVOCATION_COUNT.incrementAndGet(); ++ ++ new Thread(new Runnable() { ++ @Override ++ public void run() { ++ asyncResponse.resume("OK"); ++ } ++ }).start(); ++ } ++ } ++ ++ private Client client; ++ ++ @Before ++ public void setUp() throws Exception { ++ startServer(AsyncResource.class); ++ client = ClientBuilder.newClient(); ++ } ++ ++ @Override ++ @After ++ public void tearDown() { ++ super.tearDown(); ++ client = null; ++ } ++ ++ @Test ++ public void testAsyncGet() throws ExecutionException, InterruptedException { ++ final Future responseFuture = client.target(getUri().path("/async")).request().async().get(); ++ // Request is being processed asynchronously. ++ final Response response = responseFuture.get(); ++ // get() waits for the response ++ assertEquals("DONE", response.readEntity(String.class)); ++ } ++ ++ @Test ++ public void testAsyncGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException { ++ final Future responseFuture = client.target(getUri().path("/async/timeout")).request().async().get(); ++ // Request is being processed asynchronously. ++ final Response response = responseFuture.get(); ++ ++ // get() waits for the response ++ assertEquals(503, response.getStatus()); ++ assertEquals("Operation time out.", response.readEntity(String.class)); ++ } ++ ++ /** ++ * JERSEY-2616 reproducer. Make sure resource method is only invoked once per one request. ++ */ ++ @Test ++ public void testAsyncMultipleInvocations() throws Exception { ++ final Response response = client.target(getUri().path("/async/multiple-invocations")).request().get(); ++ ++ assertThat(AsyncResource.INVOCATION_COUNT.get(), is(1)); ++ ++ assertThat(response.getStatus(), is(200)); ++ assertThat(response.readEntity(String.class), is("OK")); ++ } ++} +diff -Nru jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java +--- jersey-2.23.2/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java 1970-01-01 01:00:00.000000000 +0100 ++++ jersey-2.23.2.simple/containers/simple-http/src/test/java/org/glassfish/jersey/simple/TraceTest.java 2016-09-28 13:59:48.890237947 +0200 +@@ -0,0 +1,89 @@ ++package org.glassfish.jersey.simple; ++ ++import static org.hamcrest.CoreMatchers.is; ++import static org.hamcrest.MatcherAssert.assertThat; ++import static org.junit.Assert.assertEquals; ++import static org.junit.Assert.assertTrue; ++ ++import javax.ws.rs.GET; ++import javax.ws.rs.Path; ++import javax.ws.rs.Produces; ++import javax.ws.rs.client.Client; ++import javax.ws.rs.client.ClientBuilder; ++import javax.ws.rs.core.Response; ++ ++import org.junit.After; ++import org.junit.Before; ++import org.junit.Test; ++ ++public class TraceTest extends AbstractSimpleServerTester { ++ ++ @Path("helloworld") ++ public static class HelloWorldResource { ++ public static final String CLICHED_MESSAGE = "Hello World!"; ++ ++ @GET ++ @Produces("text/plain") ++ public String getHello() { ++ return CLICHED_MESSAGE; ++ } ++ } ++ ++ @Path("/users") ++ public class UserResource { ++ ++ @Path("/current") ++ @GET ++ @Produces("text/plain") ++ public String getCurrentUser() { ++ return "current user"; ++ } ++ } ++ ++ private Client client; ++ ++ @Before ++ public void setUp() throws Exception { ++ startServerNoLoggingFilter(HelloWorldResource.class, UserResource.class); // disable crude LoggingFilter ++ setDebug(true); ++ client = ClientBuilder.newClient(); ++ } ++ ++ @Override ++ @After ++ public void tearDown() { ++ super.tearDown(); ++ client = null; ++ } ++ ++ ++ @Test ++ public void testFooBarOptions() { ++ for(int i = 0; i < 100; i++) { ++ Response response = client.target(getUri()).path("helloworld").request().header("Accept", "foo/bar").options(); ++ assertEquals(200, response.getStatus()); ++ final String allowHeader = response.getHeaderString("Allow"); ++ _checkAllowContent(allowHeader); ++ assertEquals(0, response.getLength()); ++ assertEquals("foo/bar", response.getMediaType().toString()); ++ ++ try { ++ Thread.sleep(50); ++ } catch(Exception e) { ++ e.printStackTrace(); ++ } ++ } ++ } ++ ++ private void _checkAllowContent(final String content) { ++ assertTrue(content.contains("GET")); ++ assertTrue(content.contains("HEAD")); ++ assertTrue(content.contains("OPTIONS")); ++ } ++ ++ @Test ++ public void testNoDefaultMethod() { ++ Response response = client.target(getUri()).path("/users").request().options(); ++ assertThat(response.getStatus(), is(404)); ++ } ++} +diff -Nru jersey-2.23.2/pom.xml jersey-2.23.2.simple/pom.xml +--- jersey-2.23.2/pom.xml 2016-08-08 19:11:27.000000000 +0200 ++++ jersey-2.23.2.simple/pom.xml 2016-09-28 13:59:48.891237895 +0200 +@@ -1489,7 +1489,17 @@ + + + org.simpleframework +- simple ++ simple-common ++ ${simple.version} ++ ++ ++ org.simpleframework ++ simple-http ++ ${simple.version} ++ ++ ++ org.simpleframework ++ simple-transport + ${simple.version} + + +@@ -1941,7 +1951,7 @@ + 1.0.12 + 2.4 + 3.0.1 +- 5.1.4 ++ 6.0.1 + 1.7.12 + 3.2.3.RELEASE + 1.1.0.Final diff --git a/jersey.spec b/jersey.spec index b0958a2..376404b 100644 --- a/jersey.spec +++ b/jersey.spec @@ -1,8 +1,8 @@ # Use jetty 9.2.14.v20151106 %bcond_with jetty Name: jersey -Version: 2.23.1 -Release: 4%{?dist} +Version: 2.23.2 +Release: 1%{?dist} Summary: JAX-RS (JSR 311) production quality Reference Implementation # One file in jersey-core/ is under ASL 2.0 license License: (CDDL or GPLv2 with exceptions) and ASL 2.0 @@ -13,7 +13,7 @@ Source1: http://www.apache.org/licenses/LICENSE-2.0.txt # Support fo servlet 3.1 apis Patch0: jersey-2.17-mvc-jsp-servlet31.patch -Patch1: jersey-2.23.1-port-to-simple6.patch +Patch1: jersey-2.23.2-port-to-simple6.patch Patch2: jersey-2.23.1-port-json-jackson-to-jackson-2.7.patch @@ -64,6 +64,7 @@ BuildRequires: mvn(org.freemarker:freemarker) BuildRequires: mvn(org.glassfish:javax.json) BuildRequires: mvn(org.glassfish:jsonp-jaxrs) BuildRequires: mvn(org.glassfish.grizzly:grizzly-http-servlet) +# Use hk2:2.5.0-b05 update when is available "Final" release BuildRequires: mvn(org.glassfish.hk2:hk2) BuildRequires: mvn(org.glassfish.hk2:hk2-api) BuildRequires: mvn(org.glassfish.hk2:hk2-bom:pom:) @@ -72,8 +73,6 @@ BuildRequires: mvn(org.glassfish.hk2:osgi-resource-locator) BuildRequires: mvn(org.glassfish.hk2:spring-bridge) BuildRequires: mvn(org.glassfish.web:javax.el) BuildRequires: mvn(org.hamcrest:hamcrest-library) -# org.hibernate:hibernate-validator:5.1.3.Final -# https://bugzilla.redhat.com/show_bug.cgi?id=1182918 BuildRequires: mvn(org.hibernate:hibernate-validator) BuildRequires: mvn(org.hibernate:hibernate-validator-cdi) BuildRequires: mvn(org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec) @@ -122,7 +121,8 @@ find . -name "*.class" -print -delete %patch0 -p1 %patch1 -p1 -%patch2 -p1 +#%% patch2 -p1 + # Remove repackaged dependencies: guava, atinject sed -i '/jersey.repackaged/d' \ @@ -179,7 +179,12 @@ sed -i 's/\r//' LICENSE-2.0.txt %pom_disable_module examples/java8-webapp %pom_disable_module examples/rx-client-java8-webapp %pom_disable_module gae-integration incubator -%pom_disable_module html-json incubator + +# org.netbeans.api:org-openide-util-lookup:RELEASE80 +# org.netbeans.html:ko-ws-tyrus:1.0 +# org.netbeans.html:net.java.html.json:1.0 +#%% pom_disable_module html-json incubator + # org.codehaus.groovy:groovy-eclipse-compiler:2.9.2-01 %pom_disable_module container-runner-maven-plugin test-framework/maven @@ -214,6 +219,13 @@ sed -i 's/\r//' LICENSE-2.0.txt %pom_disable_module moxy media %pom_remove_dep :jersey-media-moxy bom +# io.netty:netty-all:4.1.4.Final +%pom_disable_module netty-connector connectors +%pom_disable_module netty-http containers +%pom_remove_dep :jersey-container-netty-http bom +%pom_disable_module netty test-framework/providers +%pom_remove_dep :jersey-test-framework-provider-netty test-framework/providers/bundle + # Fix asm aId (asm-debug-all) %pom_xpath_set "pom:dependency[pom:groupId = 'org.ow2.asm']/pom:artifactId" asm-all %pom_xpath_set "pom:dependency[pom:groupId = 'org.ow2.asm']/pom:artifactId" asm-all core-server @@ -379,6 +391,9 @@ sed -i "s|Xmx1024m|Xmx512m|" pom.xml %license LICENSE.html LICENSE.txt LICENSE-2.0.txt etc/config/copyright.txt %changelog +* Wed Sep 28 2016 gil cattaneo 2.23.2-1 +- update to 2.23.2 + * Wed Aug 24 2016 gil cattaneo 2.23.1-4 - jackson 2.7 build fix diff --git a/sources b/sources index 418d6b9..4649a7b 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -dc4a417e1291385c0adaac17587fee61 jersey-2.23.1.tar.gz +a06ff3ffbdd8488f3eb076ee6a0e44e1 jersey-2.23.2.tar.gz