Initial implementation (#2381)

This commit is contained in:
Nick Goupinets 2021-02-12 05:47:49 -05:00 committed by GitHub
parent fdd0b67188
commit 9c3f9f84d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 166 additions and 17 deletions

View File

@ -91,10 +91,13 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Manifest;
@ -106,7 +109,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This class is the central class for the HAPI FHIR Plain Server framework.
*
* <p>
* See <a href="https://hapifhir.io/hapi-fhir/docs/server_plain/">HAPI FHIR Plain Server</a>
* for information on how to use this framework.
*/
@ -184,7 +187,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
this(theCtx, new InterceptorService("RestfulServer"));
}
public RestfulServer(FhirContext theCtx, IInterceptorService theInterceptorService) {
public RestfulServer(FhirContext theCtx, IInterceptorService theInterceptorService) {
myFhirContext = theCtx;
setInterceptorService(theInterceptorService);
}
@ -769,6 +772,19 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return myResourceNameToBinding.values();
}
public Collection<BaseMethodBinding<?>> getProviderMethodBindings(Object theProvider) {
Set<BaseMethodBinding<?>> retVal = new HashSet<>();
for (ResourceBinding resourceBinding : getResourceBindings()) {
for (BaseMethodBinding<?> methodBinding : resourceBinding.getMethodBindings()) {
if (theProvider.equals(methodBinding.getProvider())) {
retVal.add(methodBinding);
}
}
}
return retVal;
}
/**
* Provides the resource providers for this server
*/
@ -1001,8 +1017,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
preProcessedParams.add(HttpServletRequest.class, theRequest);
preProcessedParams.add(HttpServletResponse.class, theResponse);
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED, preProcessedParams)) {
return;
}
return;
}
String requestPath = getRequestPath(requestFullPath, servletContextPath, servletPath);
@ -1054,8 +1070,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
postProcessedParams.add(HttpServletRequest.class, theRequest);
postProcessedParams.add(HttpServletResponse.class, theResponse);
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED, postProcessedParams)) {
return;
}
return;
}
/*
* Actually invoke the server method. This call is to a HAPI method binding, which
@ -1074,7 +1090,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myInterceptorService.callHooks(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY, hookParams);
ourLog.trace("Done writing to stream: {}", outputStreamOrWriter);
}
}
} catch (NotModifiedException | AuthenticationException e) {
@ -1085,8 +1101,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
handleExceptionParams.add(HttpServletResponse.class, theResponse);
handleExceptionParams.add(BaseServerResponseException.class, e);
if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
return;
}
return;
}
writeExceptionToResponse(theResponse, e);
@ -1141,8 +1157,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
handleExceptionParams.add(HttpServletResponse.class, theResponse);
handleExceptionParams.add(BaseServerResponseException.class, exception);
if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
return;
}
return;
}
/*
* If we're handling an exception, no summary mode should be applied
@ -1684,8 +1700,27 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
removeResourceMethods(theProvider, clazz, resourceNames);
removeResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces(), resourceNames);
removeResourceNameBindings(resourceNames, theProvider);
}
private void removeResourceNameBindings(Collection<String> resourceNames, Object theProvider) {
for (String resourceName : resourceNames) {
myResourceNameToBinding.remove(resourceName);
ResourceBinding resourceBinding = myResourceNameToBinding.get(resourceName);
if (resourceBinding == null) {
continue;
}
for (Iterator<BaseMethodBinding<?>> it = resourceBinding.getMethodBindings().iterator(); it.hasNext(); ) {
BaseMethodBinding<?> binding = it.next();
if (theProvider.equals(binding.getProvider())) {
it.remove();
ourLog.info("{} binding of {} was removed", resourceName, binding);
}
}
if (resourceBinding.getMethodBindings().isEmpty()) {
myResourceNameToBinding.remove(resourceName);
}
}
}

View File

@ -3,14 +3,19 @@ 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.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Operation;
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.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseConformance;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -21,12 +26,10 @@ import org.mockito.junit.jupiter.MockitoExtension;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class RestfulServerTest {
@ -43,6 +46,32 @@ public class RestfulServerTest {
restfulServer.init();
}
private void mockResource(Class theClass) {
RuntimeResourceDefinition resourceDefinitionMock = mock(RuntimeResourceDefinition.class);
String className = theClass.getSimpleName();
lenient().when(resourceDefinitionMock.getName()).thenReturn(className);
lenient().when(myCtx.getResourceDefinition(className)).thenReturn(resourceDefinitionMock);
lenient().when(myCtx.getResourceType(theClass)).thenReturn(className);
}
@Test
public void testRegisterProvidersWithMethodBindings() {
mockResource(MyResource.class);
mockResource(MyResource2.class);
MyProvider provider = new MyProvider();
restfulServer.registerProvider(provider);
MyProvider2 provider2 = new MyProvider2();
restfulServer.registerProvider(provider2);
assertFalse(restfulServer.getProviderMethodBindings(provider).isEmpty());
assertFalse(restfulServer.getProviderMethodBindings(provider2).isEmpty());
restfulServer.unregisterProvider(provider);
assertTrue(restfulServer.getProviderMethodBindings(provider).isEmpty());
assertFalse(restfulServer.getProviderMethodBindings(provider2).isEmpty());
}
@Test
public void testRegisterProviders() {
//test register Plain Provider
@ -123,4 +152,89 @@ public class RestfulServerTest {
}
}
private static class MyProvider implements IResourceProvider {
@Operation(name = "SHOW_ME_THE_MONEY", typeName = "MyResource")
public IBaseBundle match() {
return mock(IBaseBundle.class);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return MyResource.class;
}
}
private static class MyProvider2 implements IResourceProvider {
@Operation(name = "SHOW_ME_MORE_MONEY", typeName = "MyResource2")
public IBaseBundle match() {
return mock(IBaseBundle.class);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return MyResource2.class;
}
}
private static class MyResource implements IBaseResource {
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public List<String> getFormatCommentsPre() {
return null;
}
@Override
public List<String> getFormatCommentsPost() {
return null;
}
@Override
public Object getUserData(String theName) {
return null;
}
@Override
public void setUserData(String theName, Object theValue) {
}
@Override
public IBaseMetaType getMeta() {
return null;
}
@Override
public IIdType getIdElement() {
return null;
}
@Override
public IBaseResource setId(String theId) {
return null;
}
@Override
public IBaseResource setId(IIdType theId) {
return null;
}
@Override
public FhirVersionEnum getStructureFhirVersionEnum() {
return null;
}
}
private static class MyResource2 extends MyResource {
}
}