/******************************************************************************* * 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; import org.everrest.core.ApplicationContext; import org.everrest.core.DependencySupplier; import org.everrest.core.Parameter; import org.everrest.core.impl.method.ParameterResolver; import org.everrest.core.impl.method.ParameterResolverFactory; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentMatcher; import javax.ws.rs.CookieParam; import javax.ws.rs.DefaultValue; import javax.ws.rs.Encoded; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.Response; import java.lang.reflect.Constructor; import java.util.List; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ConstructorDescriptorImplTest { @Rule public ExpectedException thrown = ExpectedException.none(); private ParameterResolverFactory parameterResolverFactory; private ParameterResolver<PathParam> pathParameterResolver; private ParameterResolver<QueryParam> queryParameterResolver; private ParameterResolver<MatrixParam> matrixParameterResolver; private ParameterResolver<CookieParam> cookieParameterResolver; private ParameterResolver<HeaderParam> headerParameterResolver; private ApplicationContext applicationContext; private DependencySupplier dependencySupplier; @Before public void setUp() throws Exception { mockParameterResolverFactory(); mockApplicationContext(); } @After public void tearDown() throws Exception { Resource.thrownByDefaultConstructorIfSet = null; } private void mockApplicationContext() { dependencySupplier = mock(DependencySupplier.class); applicationContext = mock(ApplicationContext.class); when(applicationContext.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); when(applicationContext.getDependencySupplier()).thenReturn(dependencySupplier); ApplicationContext.setCurrent(applicationContext); } @SuppressWarnings("unchecked") private void mockParameterResolverFactory() { parameterResolverFactory = mock(ParameterResolverFactory.class); pathParameterResolver = mock(ParameterResolver.class); queryParameterResolver = mock(ParameterResolver.class); matrixParameterResolver = mock(ParameterResolver.class); cookieParameterResolver = mock(ParameterResolver.class); headerParameterResolver = mock(ParameterResolver.class); when(parameterResolverFactory.createParameterResolver(isA(PathParam.class))).thenReturn(pathParameterResolver); when(parameterResolverFactory.createParameterResolver(isA(QueryParam.class))).thenReturn(queryParameterResolver); when(parameterResolverFactory.createParameterResolver(isA(MatrixParam.class))).thenReturn(matrixParameterResolver); when(parameterResolverFactory.createParameterResolver(isA(CookieParam.class))).thenReturn(cookieParameterResolver); when(parameterResolverFactory.createParameterResolver(isA(HeaderParam.class))).thenReturn(headerParameterResolver); } @Test public void createsConstructorDescriptorForSimpleConstructor() throws Exception { Constructor<Resource> constructor = Resource.class.getConstructor(); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); assertSame(constructor, constructorDescriptor.getConstructor()); assertTrue(constructorDescriptor.getParameters().isEmpty()); } @Test public void createsInstanceOfClassFromConstructorDescriptorForSimpleConstructor() throws Exception { Constructor<Resource> constructor = Resource.class.getConstructor(); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); assertTrue(constructorDescriptor.createInstance(applicationContext) instanceof Resource); } @Test public void createsConstructorDescriptorForConstructorWithRequestParameters() throws Exception { Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); assertSame(constructor, constructorDescriptor.getConstructor()); List<Parameter> parameters = constructorDescriptor.getParameters(); assertEquals(5, parameters.size()); Parameter pathParameter = parameters.get(0); assertEquals(PathParam.class, pathParameter.getAnnotation().annotationType()); assertEquals(String.class, pathParameter.getParameterClass()); assertEquals(1, pathParameter.getAnnotations().length); assertNull(pathParameter.getDefaultValue()); assertFalse(pathParameter.isEncoded()); Parameter queryParameter = parameters.get(1); assertEquals(QueryParam.class, queryParameter.getAnnotation().annotationType()); assertEquals(String.class, queryParameter.getParameterClass()); assertEquals(2, queryParameter.getAnnotations().length); assertNull(queryParameter.getDefaultValue()); assertTrue(queryParameter.isEncoded()); Parameter matrixParameter = parameters.get(2); assertEquals(MatrixParam.class, matrixParameter.getAnnotation().annotationType()); assertEquals(String.class, matrixParameter.getParameterClass()); assertEquals(1, matrixParameter.getAnnotations().length); assertNull(matrixParameter.getDefaultValue()); assertFalse(matrixParameter.isEncoded()); Parameter cookieParameter = parameters.get(3); assertEquals(CookieParam.class, cookieParameter.getAnnotation().annotationType()); assertEquals(String.class, cookieParameter.getParameterClass()); assertEquals(1, cookieParameter.getAnnotations().length); assertNull(cookieParameter.getDefaultValue()); assertFalse(cookieParameter.isEncoded()); Parameter headerParameter = parameters.get(4); assertEquals(HeaderParam.class, headerParameter.getAnnotation().annotationType()); assertEquals(String.class, headerParameter.getParameterClass()); assertEquals(2, headerParameter.getAnnotations().length); assertEquals("default", headerParameter.getDefaultValue()); assertFalse(headerParameter.isEncoded()); } @Test public void createsInstanceOfClassFromConstructorDescriptorForConstructorWithRequestParameters() throws Exception { when(pathParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenReturn("path parameter"); when(queryParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenReturn("query parameter"); when(matrixParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenReturn("matrix parameter"); when(cookieParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenReturn("cookie parameter"); when(headerParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenReturn("header parameter"); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); Object instance = constructorDescriptor.createInstance(applicationContext); assertTrue(instance instanceof Resource); Resource resource = (Resource)instance; assertEquals("path parameter", resource.pathParam); assertEquals("query parameter", resource.queryParam); assertEquals("matrix parameter", resource.matrixParam); assertEquals("cookie parameter", resource.cookieParam); assertEquals("header parameter", resource.headerParam); } @Test public void createsConstructorDescriptorForConstructorWithExternalDependency() throws Exception { Constructor<Resource> constructor = Resource.class.getConstructor(Dependency.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); assertSame(constructor, constructorDescriptor.getConstructor()); assertEquals(1, constructorDescriptor.getParameters().size()); Parameter parameter = constructorDescriptor.getParameters().get(0); assertEquals(Dependency.class, parameter.getParameterClass()); assertNull(parameter.getAnnotation()); assertEquals(0, parameter.getAnnotations().length); assertNull(parameter.getDefaultValue()); } @Test public void createsInstanceOfClassFromConstructorDescriptorForConstructorWithExternalDependency() throws Exception { Dependency dependency = new Dependency(); when(dependencySupplier.getInstance(argThat(new ArgumentMatcher<ConstructorParameter>() { @Override public boolean matches(Object argument) { return ((ConstructorParameter)argument).getParameterClass() == Dependency.class; } }))).thenReturn(dependency); Constructor<Resource> constructor = Resource.class.getConstructor(Dependency.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); Object instance = constructorDescriptor.createInstance(applicationContext); assertTrue(instance instanceof Resource); Resource resource = (Resource)instance; assertSame(dependency, resource.dependency); } @Test public void failsCreateInstanceOfClassFromConstructorDescriptorForConstructorWithExternalDependencyWhenDependencySupplierIsNotSetInApplicationContext() throws Exception { when(applicationContext.getDependencySupplier()).thenReturn(null); Constructor<Resource> constructor = Resource.class.getConstructor(Dependency.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(RuntimeException.class); constructorDescriptor.createInstance(applicationContext); } @Test public void failsCreateInstanceOfClassFromConstructorDescriptorForConstructorWithExternalDependencyWhenDependencySupplierCanNotResolveRequiredDependency() throws Exception { when(dependencySupplier.getInstance(argThat(new ArgumentMatcher<ConstructorParameter>() { @Override public boolean matches(Object argument) { return ((ConstructorParameter)argument).getParameterClass() == Dependency.class; } }))).thenReturn(null); Constructor<Resource> constructor = Resource.class.getConstructor(Dependency.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(RuntimeException.class); constructorDescriptor.createInstance(applicationContext); } @Test public void wrapsNonWebApplicationExceptionThrownByConstructorWithInternalException() throws Exception { Exception thrownByConstructor = new Exception(); Resource.thrownByDefaultConstructorIfSet = thrownByConstructor; Constructor<Resource> constructor = Resource.class.getConstructor(); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(InternalException.class); thrown.expectCause(exceptionSameInstanceMatcher(thrownByConstructor)); constructorDescriptor.createInstance(applicationContext); } @Test public void rethrowsInternalExceptionThrownByConstructor() throws Exception { InternalException thrownByConstructor = new InternalException(new Exception()); Resource.thrownByDefaultConstructorIfSet = thrownByConstructor; Constructor<Resource> constructor = Resource.class.getConstructor(); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(exceptionSameInstanceMatcher(thrownByConstructor)); constructorDescriptor.createInstance(applicationContext); } @Test public void rethrowsWebApplicationExceptionThrownByConstructor() throws Exception { WebApplicationException thrownByConstructor = new WebApplicationException(); Resource.thrownByDefaultConstructorIfSet = thrownByConstructor; Constructor<Resource> constructor = Resource.class.getConstructor(); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(exceptionSameInstanceMatcher(thrownByConstructor)); constructorDescriptor.createInstance(applicationContext); } @Test public void throwsWebApplicationExceptionWithStatus_NOT_FOUND_WhenParameterAnnotatedWithPathParamAnnotationCanNotBeResolved() throws Exception { when(pathParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenThrow(new Exception()); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(webApplicationExceptionWithStatusMatcher(NOT_FOUND)); constructorDescriptor.createInstance(applicationContext); } @Test public void throwsWebApplicationExceptionWithStatus_NOT_FOUND_WhenParameterAnnotatedWithQueryParamAnnotationCanNotBeResolved() throws Exception { when(queryParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenThrow(new Exception()); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(webApplicationExceptionWithStatusMatcher(NOT_FOUND)); constructorDescriptor.createInstance(applicationContext); } @Test public void throwsWebApplicationExceptionWithStatus_NOT_FOUND_WhenParameterAnnotatedWithMatrixParamAnnotationCanNotBeResolved() throws Exception { when(matrixParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenThrow(new Exception()); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(webApplicationExceptionWithStatusMatcher(NOT_FOUND)); constructorDescriptor.createInstance(applicationContext); } @Test public void throwsWebApplicationExceptionWithStatus_BAD_REQUEST_WhenParameterAnnotatedWithHeaderParamAnnotationCanNotBeResolved() throws Exception { when(headerParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenThrow(new Exception()); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(webApplicationExceptionWithStatusMatcher(BAD_REQUEST)); constructorDescriptor.createInstance(applicationContext); } @Test public void throwsWebApplicationExceptionWithStatus_BAD_REQUEST_WhenParameterAnnotatedWithCookieParamAnnotationCanNotBeResolved() throws Exception { when(cookieParameterResolver.resolve(isA(Parameter.class), eq(applicationContext))).thenThrow(new Exception()); Constructor<Resource> constructor = Resource.class.getConstructor(String.class, String.class, String.class, String.class, String.class); ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(constructor, parameterResolverFactory); thrown.expect(webApplicationExceptionWithStatusMatcher(BAD_REQUEST)); constructorDescriptor.createInstance(applicationContext); } private BaseMatcher<Throwable> exceptionSameInstanceMatcher(Exception expectedException) { return new BaseMatcher<Throwable>() { @Override public boolean matches(Object item) { return item == expectedException; } @Override public void describeTo(Description description) { description.appendText(String.format("Expected exception: %s", expectedException)); } }; } private BaseMatcher<Throwable> webApplicationExceptionWithStatusMatcher(Response.Status status) { return new BaseMatcher<Throwable>() { @Override public boolean matches(Object item) { return item instanceof WebApplicationException && status.equals(((WebApplicationException)item).getResponse().getStatusInfo()); } @Override public void describeTo(Description description) { description.appendText(String.format("WebApplicationException with status %d \"%s\"", status.getStatusCode(), status.getReasonPhrase())); } }; } @Path("/a/{x}") public static class Resource { private static Exception thrownByDefaultConstructorIfSet; private String pathParam; private String queryParam; private String matrixParam; private String cookieParam; private String headerParam; private Dependency dependency; public Resource() throws Exception { if (thrownByDefaultConstructorIfSet != null) { throw thrownByDefaultConstructorIfSet; } } public Resource(@PathParam("x") String pathParam, @Encoded @QueryParam("q") String queryParam, @MatrixParam("m") String matrixParam, @CookieParam("c") String cookieParam, @DefaultValue("default") @HeaderParam("h") String headerParam) { this.pathParam = pathParam; this.queryParam = queryParam; this.matrixParam = matrixParam; this.cookieParam = cookieParam; this.headerParam = headerParam; } public Resource(Dependency dependency) { this.dependency = dependency; } } public static class Dependency { } }