blob: 822cb0822a20e8c6901f0011e700aa2dbeefb357 [file] [log] [blame]
/*
* Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://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: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.tests.e2e.server;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.inject.Inject;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
import org.glassfish.jersey.server.ClientBinding;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.Uri;
import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.Assert;
import org.junit.Test;
/**
* Test, that managed client uses the custom executor service.
*
* @author Adam Lindenthal
*/
public class ManagedClientExecutorTest extends JerseyTest {
@Override
protected ResourceConfig configure() {
enable(TestProperties.LOG_TRAFFIC);
return new ResourceConfig(TestResource.class);
}
/**
* Test JAX-RS resource.
*/
@Path(value = "/test")
public static class TestResource {
/**
* Invoke an asynchronous request using the injected managed client.
* <p>
* The client is configured to use custom message body reader, that checks its worker thread and replaces the original
* response with the transfer object containing the thread name. The name is than used to identify if the correct
* (custom) executor service is used.
*
* @return name of the thread, that executed the asynchronous request
*/
@GET
@Path("executor")
public String managedClientWithExecutor(@ClientA @Uri("http://localhost:9998/") WebTarget target)
throws ExecutionException, InterruptedException {
Future<ThreadName> nameFuture = target.path("test/dummy").request().async().get(ThreadName.class);
return nameFuture.get().getThreadName();
}
/**
* Invoke an asynchronous request using the injected managed client.
* <p>
* The client is configured to use custom message body reader, that uses injected scheduled executor service and
* schedules a task, that checks its worker thread name, then it does the same with the injected scheduled executor
* service provider (to check that both injection approaches produce same result) and returns both thread names in a
* transfer objects. The names are than used to identify if the correct (custom) scheduled executor service(s) is (are)
* used.
*
* @return names of the threads, that executed the scheduled tasks
*/
@GET
@Path("scheduledExecutor")
public String managedClientWithScheduledExecutor(@ClientA @Uri("http://localhost:9998/") WebTarget target)
throws ExecutionException, InterruptedException {
Future<SchedulerThreadName> namesFuture = target.path("test/dummy").request().async().get(SchedulerThreadName.class);
return namesFuture.get().getThreadNameFromService() + " " + namesFuture.get().getThreadNameFromProvider();
}
/**
* Dummy method, that is invoked by the managed client.
*
* @return dummy response, does not really matter
*/
@GET
@Path("dummy")
public String dummyMethod() {
return "nothing";
}
}
/**
* Managed client binding.
*/
@ClientBinding(configClass = MyClientAConfig.class)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface ClientA {
}
/**
* Managed client config.
*/
public static class MyClientAConfig extends ClientConfig {
public MyClientAConfig() {
final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
.setNameFormat("foo-executor-service-%d")
.build());
final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder()
.setNameFormat("bar-executor-service")
.build());
this.register(new ThreadNameReader())
.register(SchedulerThreadNameReader.class)
.executorService(executor)
.scheduledExecutorService(scheduler);
}
}
/**
* Transfer object for executor service name.
*/
static class ThreadName {
private String threadName;
ThreadName(String threadName) {
this.threadName = threadName;
}
String getThreadName() {
return threadName;
}
}
/**
* Transfer object for scheduled executor service(s) name(s), both resolved from directly injected service and via provider.
*/
static class SchedulerThreadName {
private String threadNameFromService;
private String threadNameFromProvider;
SchedulerThreadName(String threadNameFromService, String threadNameFromProvider) {
this.threadNameFromService = threadNameFromService;
this.threadNameFromProvider = threadNameFromProvider;
}
String getThreadNameFromService() {
return threadNameFromService;
}
String getThreadNameFromProvider() {
return threadNameFromProvider;
}
}
/**
* Custom reader, that checks its own thread's name and returns it as an entity.
*/
public static class ThreadNameReader implements MessageBodyReader<ThreadName> {
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public ThreadName readFrom(Class<ThreadName> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
return new ThreadName(Thread.currentThread().getName());
}
}
/**
* Custom reader, that schedules one task on each injected scheduler (directly and via provider) and returns the names of
* the threads that executed those tasks as an entity.
*/
public static class SchedulerThreadNameReader implements MessageBodyReader<SchedulerThreadName> {
@Inject
ScheduledExecutorServiceProvider injectedProvider;
@Inject
ScheduledExecutorService injectedService;
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public SchedulerThreadName readFrom(Class<SchedulerThreadName> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
AtomicReference<String> nameFromService = new AtomicReference<>();
AtomicReference<String> nameFromProvider = new AtomicReference<>();
CountDownLatch cdl = new CountDownLatch(2);
injectedService.schedule(() -> {
nameFromService.set(Thread.currentThread().getName());
cdl.countDown();
}, 1, TimeUnit.MILLISECONDS);
injectedProvider.getExecutorService().schedule(() -> {
nameFromProvider.set(Thread.currentThread().getName());
cdl.countDown();
}, 1, TimeUnit.MILLISECONDS);
try {
cdl.await(200, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
nameFromService.set("failed");
nameFromProvider.set("failed");
}
return new SchedulerThreadName(nameFromService.get(), nameFromProvider.get());
}
}
@Test
public void testManagedClientExecutor() {
final String response = target().path("test/executor").request().get(String.class);
Assert.assertEquals("foo-executor-service-0", response);
}
@Test
public void testManagedClientScheduledExecutor() {
final String response = target().path("test/scheduledExecutor").request().get(String.class);
Assert.assertEquals("bar-executor-service bar-executor-service", response);
System.out.println(response);
}
}