Resource methods in interfaces (default implementations) are now picked up when registering providers

This commit is contained in:
ttn 2020-01-30 16:24:18 +01:00 committed by James Agnew
parent da54d3443e
commit 785997aade
2 changed files with 144 additions and 0 deletions

View File

@ -360,11 +360,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Class<?> supertype = clazz.getSuperclass();
while (!Object.class.equals(supertype)) {
count += findResourceMethods(theProvider, supertype);
count += findResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces());
supertype = supertype.getSuperclass();
}
try {
count += findResourceMethods(theProvider, clazz);
count += findResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces());
} catch (ConfigurationException e) {
throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage(), e);
}
@ -373,6 +375,15 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
private int findResourceMethodsOnInterfaces(Object theProvider, Class<?>[] interfaces) {
int count = 0;
for (Class<?> anInterface : interfaces) {
count += findResourceMethods(theProvider, anInterface);
count += findResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces());
}
return count;
}
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
int count = 0;
@ -1604,14 +1615,23 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Collection<String> resourceNames = new ArrayList<>();
while (!Object.class.equals(supertype)) {
removeResourceMethods(theProvider, supertype, resourceNames);
removeResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces(), resourceNames);
supertype = supertype.getSuperclass();
}
removeResourceMethods(theProvider, clazz, resourceNames);
removeResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces(), resourceNames);
for (String resourceName : resourceNames) {
myResourceNameToBinding.remove(resourceName);
}
}
private void removeResourceMethodsOnInterfaces(Object theProvider, Class<?>[] interfaces, Collection<String> resourceNames) {
for (Class<?> anInterface : interfaces) {
removeResourceMethods(theProvider, anInterface, resourceNames);
removeResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces(), resourceNames);
}
}
/*
* Collect the set of RESTful methods for a single class when it is being unregistered
*/

View File

@ -0,0 +1,124 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.IFhirVersionServer;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class RestfulServerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private FhirContext myCtx;
private RestfulServer restfulServer;
@Before
public void setUp() throws ServletException {
when(myCtx.getVersion().getVersion()).thenReturn(FhirVersionEnum.DSTU3);
when(myCtx.getVersion().getServerVersion()).thenReturn(new MyFhirVersionServer());
restfulServer = new RestfulServer(myCtx);
restfulServer.init();
}
@Test
public void testRegisterProviders() {
//test register Plain Provider
restfulServer.registerProvider(new MyClassWithRestInterface());
assertEquals(1, restfulServer.getPlainProviders().size());
Object plainProvider = restfulServer.getPlainProviders().iterator().next();
assertTrue(plainProvider instanceof MyClassWithRestInterface);
//test register Resource Provider
restfulServer.registerProvider(new MyResourceProvider());
assertEquals(1, restfulServer.getResourceProviders().size());
IResourceProvider resourceProvider = restfulServer.getResourceProviders().iterator().next();
assertTrue(resourceProvider instanceof MyResourceProvider);
//test unregister providers
restfulServer.unregisterProvider(plainProvider);
assertTrue(restfulServer.getPlainProviders().isEmpty());
restfulServer.unregisterProvider(resourceProvider);
assertTrue(restfulServer.getResourceProviders().isEmpty());
}
@Test(expected = ConfigurationException.class)
public void testFailRegisterInterfaceProviderWithoutRestfulMethod() {
restfulServer.registerProvider(new MyClassWithoutRestInterface());
}
//--------- Scaffolding ---------//
private static class MyClassWithoutRestInterface implements Serializable {
}
private static class MyClassWithRestInterface implements MyRestInterface {
}
@SuppressWarnings("unused")
private interface MyRestInterface {
@Create
default MethodOutcome create(@ResourceParam IBaseResource theResource) {
return mock(MethodOutcome.class);
}
}
private static class MyFhirVersionServer implements IFhirVersionServer {
@Override
public IServerConformanceProvider<? extends IBaseResource> createServerConformanceProvider(RestfulServer theRestfulServer) {
return new IServerConformanceProvider<IBaseResource>() {
@Override
@Metadata
public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
return mock(IBaseConformance.class);
}
@Override
public void setRestfulServer(RestfulServer theRestfulServer) {
}
};
}
@Override
public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) {
return new MyResourceProvider();
}
}
@SuppressWarnings("unused")
private static class MyResourceProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam IBaseResource theResource) {
return mock(MethodOutcome.class);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return IBaseResource.class;
}
}
}