commit
e9497c019a
|
@ -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