Added the ProvidesResources annotation and supporting classes, which allows server authors to specify resources that should be added to the initial resource list (and thus show up in the list of profiles returned from a search)
This commit is contained in:
parent
3d320a8fe3
commit
257156b014
|
@ -0,0 +1,70 @@
|
||||||
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ProvidesResources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans a class tagged with {@code ProvidesResources} and adds any resources listed to its FhirContext's resource
|
||||||
|
* definition list. This makes the profile generator find the classes.
|
||||||
|
*
|
||||||
|
* @see ca.uhn.fhir.model.api.annotation.ProvidesResources
|
||||||
|
*/
|
||||||
|
public class ProvidedResourceScanner {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
|
||||||
|
private FhirContext myContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param theContext - context whose resource definition list is to be updated by the scanner
|
||||||
|
*/
|
||||||
|
public ProvidedResourceScanner(FhirContext theContext) {
|
||||||
|
myContext = theContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@code theProvider} is tagged with the {@code ProvidesResources} annotation, this method will add every resource listed
|
||||||
|
* by the {@code resources} method.
|
||||||
|
* <p/>
|
||||||
|
* notes:
|
||||||
|
* <ul>
|
||||||
|
* <li>if {@code theProvider} isn't annotated with {@code resources} nothing is done; it's expected that most RestfulServers and
|
||||||
|
* ResourceProviders won't be annotated.</li>
|
||||||
|
* <li>any object listed in {@code resources} that doesn't implement {@code IResource} will generate a warning in the log.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param theProvider - Normally, either a {@link ca.uhn.fhir.rest.server.RestfulServer} or a {@link ca.uhn.fhir.rest.server.IResourceProvider}
|
||||||
|
* that might be annotated with {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
|
||||||
|
*/
|
||||||
|
public void scanForProvidedResources(Object theProvider) {
|
||||||
|
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
|
||||||
|
if (annotation == null)
|
||||||
|
return;
|
||||||
|
for (Class clazz : annotation.resources()) {
|
||||||
|
if (IResource.class.isAssignableFrom(clazz)) {
|
||||||
|
myContext.getResourceDefinition(clazz);
|
||||||
|
} else {
|
||||||
|
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ca.uhn.fhir.model.api.annotation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IResourceProvider and RestfulServer subclasses can use this annotation to designate which custom resources they can provide.
|
||||||
|
* These resources will automatically be added to the resource list used for profile generation.
|
||||||
|
* <pre>
|
||||||
|
* Examples:
|
||||||
|
* {@code
|
||||||
|
* @literal@ProvidesResources(resource=CustomObservation.class)
|
||||||
|
* class CustomObservationResourceProvider implements IResourceProvider{...}
|
||||||
|
*
|
||||||
|
* @literal@ProvidesResources(resource={CustomPatient.class,CustomObservation.class}){...}
|
||||||
|
* class FhirServer extends RestfulServer
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* Note that you needn't annotate both the IResourceProvider and the RestfulServer for a given resource; either one will suffice.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ProvidesResources {
|
||||||
|
Class[] resources();
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
@ -132,7 +133,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
||||||
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
||||||
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Class ust be public");
|
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Class must be public");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,6 +696,9 @@ public class RestfulServer extends HttpServlet {
|
||||||
ourLog.trace("No security manager has been provided");
|
ourLog.trace("No security manager has been provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
|
||||||
|
providedResourceScanner.scanForProvidedResources(this);
|
||||||
|
|
||||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||||
if (resourceProvider != null) {
|
if (resourceProvider != null) {
|
||||||
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||||
|
@ -707,6 +711,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||||
}
|
}
|
||||||
typeToProvider.put(resourceType, nextProvider);
|
typeToProvider.put(resourceType, nextProvider);
|
||||||
|
providedResourceScanner.scanForProvidedResources(nextProvider);
|
||||||
}
|
}
|
||||||
ourLog.info("Got {} resource providers", typeToProvider.size());
|
ourLog.info("Got {} resource providers", typeToProvider.size());
|
||||||
for (IResourceProvider provider : typeToProvider.values()) {
|
for (IResourceProvider provider : typeToProvider.values()) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
* Created by Bill de Beaubien on 10/31/2014.
|
* Created by Bill de Beaubien on 10/31/2014.
|
||||||
*/
|
*/
|
||||||
@ResourceDef(name="Observation", id="customobservation")
|
@ResourceDef(name="Observation", id="customobservation")
|
||||||
public class CustomObservation extends Observation {
|
class CustomObservation extends Observation {
|
||||||
@Child(name = "valueUnits", order = 3)
|
@Child(name = "valueUnits", order = 3)
|
||||||
@Extension(url = "http://hapi.test.com/profile/customobservation#valueUnits", definedLocally = true, isModifier = false)
|
@Extension(url = "http://hapi.test.com/profile/customobservation#valueUnits", definedLocally = true, isModifier = false)
|
||||||
@Description(shortDefinition = "Units on an observation whose type is of valueString")
|
@Description(shortDefinition = "Units on an observation whose type is of valueString")
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ProvidesResources;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
public class ProvidedResourceScannerTest extends TestCase {
|
||||||
|
@Test
|
||||||
|
public void testScannerShouldAddProvidedResources() {
|
||||||
|
FhirContext ctx = new FhirContext();
|
||||||
|
assertNull(ctx.getElementDefinition(CustomPatient.class));
|
||||||
|
|
||||||
|
ProvidedResourceScanner scanner = new ProvidedResourceScanner(ctx);
|
||||||
|
scanner.scanForProvidedResources(new TestResourceProviderB());
|
||||||
|
|
||||||
|
assertNotNull(ctx.getElementDefinition(CustomPatient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProvidesResources(resources=CustomObservation.class)
|
||||||
|
class TestResourceProviderA {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProvidesResources(resources={CustomPatient.class,ResourceWithExtensionsA.class})
|
||||||
|
class TestResourceProviderB {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResourceDef(name = "Patient", id="CustomPatient")
|
||||||
|
class CustomPatient extends Patient {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ProvidesResources;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Bill de Beaubien on 11/1/2014.
|
||||||
|
*/
|
||||||
|
public class ServerProvidedResourceScannerTest extends TestCase {
|
||||||
|
@Test
|
||||||
|
public void testWhenRestfulServerInitialized_annotatedResources_shouldBeAddedToContext() throws ServletException {
|
||||||
|
// Given
|
||||||
|
MyServer server = new MyServer();
|
||||||
|
|
||||||
|
// When
|
||||||
|
server.init();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertNotNull(server.getFhirContext().getElementDefinition(CustomObservation.class));
|
||||||
|
assertNotNull(server.getFhirContext().getElementDefinition(CustomPatient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenUnannotatedServerInitialized_annotatedResources_shouldNotBeAddedToContext() throws ServletException {
|
||||||
|
// Given
|
||||||
|
RestfulServer server = new RestfulServer();
|
||||||
|
|
||||||
|
// When
|
||||||
|
server.init();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertNull(server.getFhirContext().getElementDefinition(CustomObservation.class));
|
||||||
|
assertNull(server.getFhirContext().getElementDefinition(CustomPatient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenServletWithAnnotatedProviderInitialized_annotatedResource_shouldBeAddedToContext() throws ServletException {
|
||||||
|
// Given
|
||||||
|
MyServerWithProvider server = new MyServerWithProvider();
|
||||||
|
|
||||||
|
// When
|
||||||
|
server.init();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertNotNull(server.getFhirContext().getElementDefinition(CustomObservation.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResourceDef(name = "Patient", id="CustomPatient")
|
||||||
|
class CustomPatient extends Patient {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResourceDef(name = "Observation", id="CustomObservation")
|
||||||
|
class CustomObservation extends Observation {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProvidesResources(resources={CustomPatient.class,CustomObservation.class})
|
||||||
|
class MyServer extends RestfulServer {
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyServerWithProvider extends RestfulServer {
|
||||||
|
@Override
|
||||||
|
protected void initialize() throws ServletException {
|
||||||
|
List<IResourceProvider> providers = new ArrayList<IResourceProvider>();
|
||||||
|
providers.add(new MyObservationProvider());
|
||||||
|
setResourceProviders(providers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProvidesResources(resources=CustomObservation.class)
|
||||||
|
public static class MyObservationProvider implements IResourceProvider {
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return CustomObservation.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read(version = false)
|
||||||
|
public CustomObservation readObservation(@IdParam IdDt theId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue