/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core.impl.async;
import org.everrest.core.ApplicationContext;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.GenericContainerResponse;
import org.everrest.core.impl.EverrestConfiguration;
import org.everrest.core.impl.MultivaluedMapImpl;
import org.everrest.core.resource.ResourceMethodDescriptor;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class AsynchronousJobPoolTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private final String asyncServicePath = "/async";
private ApplicationContext applicationContext;
private AsynchronousJobPool asynchronousJobPool;
private Resource resource;
private ResourceMethodDescriptor methodDescriptor;
@Before
public void setUp() throws Exception {
applicationContext = mockApplicationContext();
ApplicationContext.setCurrent(applicationContext);
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousQueueSize(1);
configuration.setAsynchronousPoolSize(1);
configuration.setAsynchronousServicePath(asyncServicePath);
asynchronousJobPool = new AsynchronousJobPool(configuration);
AsynchronousFuture job = mock(AsynchronousFuture.class);
when(job.getJobId()).thenReturn(1L);
Map<String, Object> jobContext = new HashMap<>();
when(job.getContext()).thenReturn(jobContext);
when(job.getExpirationDate()).thenReturn(System.currentTimeMillis() + 10000);
doAnswer(sleep(500)).when(job).run();
final AsynchronousFutureFactory asynchronousFutureFactory = mock(AsynchronousFutureFactory.class);
when(asynchronousFutureFactory.createAsynchronousFuture(isA(Callable.class),
anyLong(),
isA(ResourceMethodDescriptor.class),
anyListOf(AsynchronousJobListener.class)))
.thenReturn(job);
asynchronousJobPool.setAsynchronousFutureFactory(asynchronousFutureFactory);
resource = new Resource();
methodDescriptor = mock(ResourceMethodDescriptor.class);
when(methodDescriptor.getMethod()).thenReturn(Resource.class.getMethod("m"));
}
@After
public void tearDown() throws Exception {
asynchronousJobPool.stop();
}
@Test
public void readsServicePathFromConfiguration() throws Exception {
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousServicePath("/async/bla/bla");
asynchronousJobPool = new AsynchronousJobPool(configuration);
assertEquals("/async/bla/bla", asynchronousJobPool.getAsynchronousServicePath());
}
@Test
public void readsMaxCacheFromConfiguration() throws Exception {
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousCacheSize(123);
asynchronousJobPool = new AsynchronousJobPool(configuration);
assertEquals(123, asynchronousJobPool.getMaxCacheSize());
}
@Test
public void readsTimeoutFromConfiguration() throws Exception {
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousJobTimeout(777);
asynchronousJobPool = new AsynchronousJobPool(configuration);
assertEquals(777, asynchronousJobPool.getJobTimeout());
}
@Test
public void readsQueueSizeFromConfiguration() throws Exception {
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousQueueSize(555);
asynchronousJobPool = new AsynchronousJobPool(configuration);
assertEquals(555, asynchronousJobPool.getMaxQueueSize());
}
@Test
public void readsPoolSizeFromConfiguration() throws Exception {
final EverrestConfiguration configuration = new EverrestConfiguration();
configuration.setAsynchronousPoolSize(111);
asynchronousJobPool = new AsynchronousJobPool(configuration);
assertEquals(111, asynchronousJobPool.getThreadPoolSize());
}
@Test
public void addsJobInPool() throws Exception {
AsynchronousFuture job = (AsynchronousFuture)asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
ArgumentCaptor<String> jobUriCaptor = ArgumentCaptor.forClass(String.class);
verify(job).setJobURI(jobUriCaptor.capture());
assertEquals(String.format("%s/%d", asyncServicePath, job.getJobId()), jobUriCaptor.getValue());
assertSame(applicationContext.getProviders(), job.getContext().get("org.everrest.async.providers"));
GenericContainerRequest originRequest = applicationContext.getContainerRequest();
GenericContainerRequest copiedRequest = (GenericContainerRequest)job.getContext().get("org.everrest.async.request");
assertEquals(originRequest.getMethod(), copiedRequest.getMethod());
assertEquals(originRequest.getRequestUri(), copiedRequest.getRequestUri());
assertEquals(originRequest.getBaseUri(), copiedRequest.getBaseUri());
assertEquals(originRequest.getRequestHeaders(), copiedRequest.getRequestHeaders());
Thread.sleep(100);
verify(job).run();
}
@Test
public void failsAddNewJobIfTooManyJobsInProgress() throws Exception {
asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
thrown.expect(AsynchronousJobRejectedException.class);
thrown.expectMessage("Can't accept new asynchronous request. Too many asynchronous jobs in progress");
asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
}
@Test
public void removesJobFromPool() throws Exception {
AsynchronousFuture job = (AsynchronousFuture)asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
Thread.sleep(100);
asynchronousJobPool.removeJob(job.getJobId());
verify(job).cancel();
}
@Test
public void getsJobById() throws Exception {
AsynchronousFuture job = (AsynchronousFuture)asynchronousJobPool.addJob(resource, methodDescriptor, new Object[]{});
assertSame(job, asynchronousJobPool.getJob(job.getJobId()));
}
private Answer sleep(long millis) {
return invocation -> {
Thread.sleep(millis);
return null;
};
}
private ApplicationContext mockApplicationContext() {
GenericContainerRequest containerRequest = mockContainerRequest();
GenericContainerResponse containerResponse = mock(GenericContainerResponse.class);
ApplicationContext applicationContext = mock(ApplicationContext.class);
when(applicationContext.getContainerRequest()).thenReturn(containerRequest);
when(applicationContext.getContainerResponse()).thenReturn(containerResponse);
return applicationContext;
}
private GenericContainerRequest mockContainerRequest() {
GenericContainerRequest containerRequest = mock(GenericContainerRequest.class);
when(containerRequest.getMethod()).thenReturn("POST");
when(containerRequest.getRequestUri()).thenReturn(URI.create("/a"));
when(containerRequest.getBaseUri()).thenReturn(URI.create(""));
when(containerRequest.getRequestHeaders()).thenReturn(new MultivaluedMapImpl());
return containerRequest;
}
public static class Resource {
public void m() {
}
}
}