Merge pull request #49 from wdebeau1/master
Added @Destroy annotation, which allows ResourceProviders to do cleanup ...
This commit is contained in:
commit
374d95ea76
|
@ -0,0 +1,36 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResourceProvider methods tagged with @Destroy will be invoked when the RestfulServer is shut down.
|
||||||
|
* This is your chance to do any cleanup.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface Destroy {
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
@ -44,18 +45,6 @@ import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.annotation.AddTags;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Delete;
|
|
||||||
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
|
||||||
import ca.uhn.fhir.rest.annotation.GetTags;
|
|
||||||
import ca.uhn.fhir.rest.annotation.History;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Validate;
|
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||||
|
@ -257,6 +246,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
AddTags addTags = theMethod.getAnnotation(AddTags.class);
|
AddTags addTags = theMethod.getAnnotation(AddTags.class);
|
||||||
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
||||||
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
||||||
|
|
||||||
// ** if you add another annotation above, also add it to the next line:
|
// ** if you add another annotation above, also add it to the next line:
|
||||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction)) {
|
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -20,48 +20,10 @@ package ca.uhn.fhir.rest.server;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletOutputStream;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.*;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
|
||||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
|
@ -69,13 +31,9 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Destroy;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.method.*;
|
||||||
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
|
||||||
import ca.uhn.fhir.rest.method.OtherOperationTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.method.Request;
|
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
@ -83,6 +41,28 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.util.VersionUtil;
|
import ca.uhn.fhir.util.VersionUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class RestfulServer extends HttpServlet {
|
public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
|
@ -280,6 +260,32 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void invokeDestroy(Object theProvider) {
|
||||||
|
Class<?> clazz = theProvider.getClass();
|
||||||
|
invokeDestroy(theProvider, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeDestroy(Object theProvider, Class<?> clazz) {
|
||||||
|
for (Method m : clazz.getDeclaredMethods()) {
|
||||||
|
Destroy destroy = m.getAnnotation(Destroy.class);
|
||||||
|
if (destroy != null) {
|
||||||
|
try {
|
||||||
|
m.invoke(theProvider);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
ourLog.error("Exception occurred in destroy ", e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
ourLog.error("Exception occurred in destroy ", e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> supertype = clazz.getSuperclass();
|
||||||
|
if (!Object.class.equals(supertype)) {
|
||||||
|
invokeDestroy(theProvider, supertype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the setting for automatically adding profile tags
|
* Returns the setting for automatically adding profile tags
|
||||||
*
|
*
|
||||||
|
@ -761,7 +767,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUseBrowserFriendlyContentTypes() {
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
if (getResourceProviders() != null) {
|
||||||
|
for (IResourceProvider iResourceProvider : getResourceProviders()) {
|
||||||
|
invokeDestroy(iResourceProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
return myUseBrowserFriendlyContentTypes;
|
return myUseBrowserFriendlyContentTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.junit.Test;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.rest.server.ResfulServerSelfReferenceTest.DummyPatientResourceProvider;
|
import ca.uhn.fhir.rest.server.RestfulServerSelfReferenceTest.DummyPatientResourceProvider;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Destroy;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Bill de Beaubien on 11/10/2014.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class DestroyTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDestroyCallsAnnotatedMethodsOnProviders() throws ServletException {
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
DiagnosticReportProvider provider = spy(new DiagnosticReportProvider());
|
||||||
|
servlet.setResourceProviders(Arrays.asList((IResourceProvider) provider));
|
||||||
|
servlet.init();
|
||||||
|
servlet.destroy();
|
||||||
|
verify(provider).destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainsUpThroughSuperclasses() throws ServletException {
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
DerivedDiagnosticReportProvider provider = spy(new DerivedDiagnosticReportProvider());
|
||||||
|
servlet.setResourceProviders(Arrays.asList((IResourceProvider) provider));
|
||||||
|
servlet.init();
|
||||||
|
servlet.destroy();
|
||||||
|
verify(provider).destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoDestroyDoesNotCauseInfiniteRecursion() throws ServletException {
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
DiagnosticReportProviderSansDestroy provider = new DiagnosticReportProviderSansDestroy();
|
||||||
|
servlet.setResourceProviders(Arrays.asList((IResourceProvider) provider));
|
||||||
|
servlet.init();
|
||||||
|
servlet.destroy();
|
||||||
|
// nothing to verify other than the test didn't hang forever
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DiagnosticReportProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return DiagnosticReport.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome createResource(@ResourceParam DiagnosticReport theDiagnosticReport) {
|
||||||
|
// do nothing
|
||||||
|
return new MethodOutcome();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Destroy
|
||||||
|
public void destroy() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DerivedDiagnosticReportProvider extends DiagnosticReportProvider {
|
||||||
|
// move along, nothing to see here
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DiagnosticReportProviderSansDestroy implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return DiagnosticReport.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome createResource(@ResourceParam DiagnosticReport theDiagnosticReport) {
|
||||||
|
// do nothing
|
||||||
|
return new MethodOutcome();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,11 +75,11 @@ import ca.uhn.fhir.util.PortUtil;
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
public class ResfulServerMethodTest {
|
public class RestfulServerMethodTest {
|
||||||
|
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
private static FhirContext ourCtx;
|
private static FhirContext ourCtx;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResfulServerMethodTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerMethodTest.class);
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
private static DummyDiagnosticReportResourceProvider ourReportProvider;
|
private static DummyDiagnosticReportResourceProvider ourReportProvider;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
|
@ -41,9 +41,9 @@ import ca.uhn.fhir.util.PortUtil;
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
public class ResfulServerSelfReferenceTest {
|
public class RestfulServerSelfReferenceTest {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResfulServerSelfReferenceTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerSelfReferenceTest.class);
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
private static FhirContext ourCtx;
|
private static FhirContext ourCtx;
|
||||||
|
|
Loading…
Reference in New Issue