Add some tests and some cleanup of unused code

This commit is contained in:
jamesagnew 2015-09-09 22:41:10 -04:00
parent f7667d2565
commit 437625505c
34 changed files with 1498 additions and 151 deletions

View File

@ -806,8 +806,6 @@ class ModelScanner {
try {
// Datatypes
ourLog.warn("NEXT: {}", nextValue);
@SuppressWarnings("unchecked")
Class<? extends IBase> dtType = (Class<? extends IBase>) Class.forName(nextValue);
retVal.add(dtType);

View File

@ -75,4 +75,26 @@ public class ProvidedResourceScanner {
}
}
}
/**
* Remove any metadata that was added by any {@code ProvidesResources} annotation
* present in {@code theProvider}. This method is callled from {@code RestfulService}
* when it is unregistering a Resource Provider.
*
* @param theProvider
* - Normally a {@link ca.uhn.fhir.rest.server.IResourceProvider} that might
* be annotated with {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
*/
public void removeProvidedResources(Object theProvider) {
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
if (annotation == null)
return;
for (Class<?> clazz : annotation.resources()) {
if (IBaseResource.class.isAssignableFrom(clazz)) {
// TODO -- not currently used but should be finished for completeness
} else {
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
}
}
}
}

View File

@ -421,17 +421,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
public static class QualifierDetails {
private String myColonQualifier;
private String myDotQualifier;
public String getColonQualifier() {
return myColonQualifier;
}
public String getDotQualifier() {
return myDotQualifier;
}
public boolean passes(Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
if (theQualifierWhitelist != null) {
if (!theQualifierWhitelist.contains(".*")) {
@ -445,7 +436,9 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
}
}
if (!theQualifierWhitelist.contains(":*")) {
/*
* This was removed Sep 9 2015, as I don't see any way it could possibly be triggered.
if (!theQualifierWhitelist.contains(SearchParameter.QUALIFIER_ANY_TYPE)) {
if (myColonQualifier != null) {
if (!theQualifierWhitelist.contains(myColonQualifier)) {
return false;
@ -456,6 +449,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
}
}
*/
}
if (theQualifierBlacklist != null) {
if (myDotQualifier != null) {

View File

@ -136,7 +136,7 @@ public class SearchParameter extends BaseQueryParameter {
private List<Class<? extends IResource>> myDeclaredTypes;
private String myDescription;
private String myName;
private IParamBinder myParamBinder;
private IParamBinder<?> myParamBinder;
private RestSearchParameterTypeEnum myParamType;
private Set<String> myQualifierBlacklist;
private Set<String> myQualifierWhitelist;
@ -271,7 +271,7 @@ public class SearchParameter extends BaseQueryParameter {
this.myRequired = required;
}
@SuppressWarnings({ "unchecked" })
@SuppressWarnings({ "unchecked", "unused" })
public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
this.myType = type;
if (IQueryParameterType.class.isAssignableFrom(type)) {

View File

@ -50,8 +50,6 @@ public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implement
}
}
public abstract RestSearchParameterTypeEnum getSearchParamType();
abstract T newInstance();
@Override

View File

@ -1,7 +1,6 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -41,11 +40,6 @@ public class CompositeAndListParam<A extends IQueryParameterType, B extends IQue
return new CompositeOrListParam<A,B>(myLeftType, myRightType);
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.COMPOSITE;
}
@CoverageIgnore
@Override
public CompositeAndListParam<A, B> addAnd(CompositeOrListParam<A, B> theValue) {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -31,11 +30,6 @@ public class DateAndListParam extends BaseAndListParam<DateOrListParam> {
return new DateOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.DATE;
}
@CoverageIgnore
@Override
public DateAndListParam addAnd(DateOrListParam theValue) {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -31,11 +30,6 @@ public class NumberAndListParam extends BaseAndListParam<NumberOrListParam> {
return new NumberOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.NUMBER;
}
@CoverageIgnore
@Override
public NumberAndListParam addAnd(NumberOrListParam theValue) {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -30,11 +29,6 @@ public class QuantityAndListParam extends BaseAndListParam<QuantityOrListParam>
QuantityOrListParam newInstance() {
return new QuantityOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.QUANTITY;
}
@CoverageIgnore
@Override

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -31,11 +30,6 @@ public class ReferenceAndListParam extends BaseAndListParam<ReferenceOrListParam
return new ReferenceOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.REFERENCE;
}
@CoverageIgnore
@Override
public ReferenceAndListParam addAnd(ReferenceOrListParam theValue) {

View File

@ -30,11 +30,6 @@ public class StringAndListParam extends BaseAndListParam<StringOrListParam> {
StringOrListParam newInstance() {
return new StringOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.STRING;
}
@CoverageIgnore
@Override

View File

@ -31,11 +31,6 @@ public class TokenAndListParam extends BaseAndListParam<TokenOrListParam> {
return new TokenOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.TOKEN;
}
@CoverageIgnore
@Override
public TokenAndListParam addAnd(TokenOrListParam theValue) {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/*
@ -31,11 +30,6 @@ public class UriAndListParam extends BaseAndListParam<UriOrListParam> {
return new UriOrListParam();
}
@Override
public RestSearchParameterTypeEnum getSearchParamType() {
return RestSearchParameterTypeEnum.URI;
}
@CoverageIgnore
@Override
public UriAndListParam addAnd(UriOrListParam theValue) {

View File

@ -33,4 +33,12 @@ public interface IServerConformanceProvider<T extends IBaseResource> {
*/
public abstract T getServerConformance(HttpServletRequest theRequest);
/**
* This setter is needed in implementation classes (along with
* a no-arg constructor) to avoid reference cycles in the
* Spring wiring of a RestfulServer instance.
*
* @param theRestfulServer
*/
public void setRestfulServer (RestfulServer theRestfulServer);
}

View File

@ -37,6 +37,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
@ -92,9 +94,10 @@ public class RestfulServer extends HttpServlet {
private String myImplementationDescription;
private final List<IServerInterceptor> myInterceptors = new ArrayList<IServerInterceptor>();
private IPagingProvider myPagingProvider;
private Collection<Object> myPlainProviders;
private Collection<Object> myPlainProviders = new ArrayList<Object>();
private Map<String, ResourceBinding> myResourceNameToBinding = new HashMap<String, ResourceBinding>();
private Collection<IResourceProvider> myResourceProviders;
private Collection<IResourceProvider> myResourceProviders = new ArrayList<IResourceProvider>();
private Map<String,IResourceProvider> myTypeToProvider = new HashMap<String, IResourceProvider>();
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
private ResourceBinding myServerBinding = new ResourceBinding();
private BaseMethodBinding<?> myServerConformanceMethod;
@ -104,6 +107,7 @@ public class RestfulServer extends HttpServlet {
private String myServerVersion = VersionUtil.getVersion();
private boolean myStarted;
private boolean myUseBrowserFriendlyContentTypes;
private Lock myProviderRegistrationMutex = new ReentrantLock();
/**
* Constructor. Note that if no {@link FhirContext} is passed in to the server (either through the constructor, or through {@link #setFhirContext(FhirContext)}) the server will determine which
@ -192,6 +196,46 @@ public class RestfulServer extends HttpServlet {
return theServletPath.length() + delta;
}
/*
* Remove registered RESTful methods for a Provider
* (and all superclasses) when it is being unregistered
*/
private void removeResourceMethods (Object theProvider) throws Exception {
ourLog.info("Removing RESTful methods for: {}", theProvider.getClass());
Class<?> clazz = theProvider.getClass();
Class<?> supertype = clazz.getSuperclass();
Collection<String> resourceNames = new ArrayList<String>();
while (!Object.class.equals(supertype)) {
removeResourceMethods(theProvider, supertype, resourceNames);
supertype = supertype.getSuperclass();
}
removeResourceMethods(theProvider, clazz, resourceNames);
for (String resourceName : resourceNames) {
myResourceNameToBinding.remove(resourceName);
}
}
/*
* Collect the set of RESTful methods for a single class
* when it is being unregistered
*/
private void removeResourceMethods(Object theProvider, Class<?> clazz, Collection<String> resourceNames) throws ConfigurationException {
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) {
continue; // not a bound method
}
if (foundMethodBinding instanceof ConformanceMethodBinding) {
myServerConformanceMethod = null;
continue;
}
String resourceName = foundMethodBinding.getResourceName();
if (!resourceNames.contains(resourceName)) {
resourceNames.add(resourceName);
}
}
}
private void findResourceMethods(Object theProvider) throws Exception {
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
@ -768,79 +812,61 @@ public class RestfulServer extends HttpServlet {
*/
@Override
public final void init() throws ServletException {
initialize();
Object confProvider;
myProviderRegistrationMutex.lock();
try {
ourLog.info("Initializing HAPI FHIR restful server running in " + getFhirContext().getVersion().getVersion().name() + " mode");
initialize();
Object confProvider;
try {
ourLog.info("Initializing HAPI FHIR restful server running in " + getFhirContext().getVersion().getVersion().name() + " mode");
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
providedResourceScanner.scanForProvidedResources(this);
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
providedResourceScanner.scanForProvidedResources(this);
Collection<IResourceProvider> resourceProvider = getResourceProviders();
if (resourceProvider != null) {
Map<String, IResourceProvider> typeToProvider = new HashMap<String, IResourceProvider>();
for (IResourceProvider nextProvider : resourceProvider) {
Collection<IResourceProvider> resourceProvider = getResourceProviders();
// 'true' tells registerProviders() that
// this call is part of initialization
registerProviders(resourceProvider, true);
Class<? extends IBaseResource> resourceType = nextProvider.getResourceType();
if (resourceType == null) {
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
}
Collection<Object> providers = getPlainProviders();
// 'true' tells registerProviders() that
// this call is part of initialization
registerProviders(providers, true);
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
if (typeToProvider.containsKey(resourceName)) {
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + typeToProvider.get(resourceName).getClass().getCanonicalName()
+ "] and Second[" + nextProvider.getClass().getCanonicalName() + "]");
}
typeToProvider.put(resourceName, nextProvider);
providedResourceScanner.scanForProvidedResources(nextProvider);
findResourceMethods(getServerProfilesProvider());
confProvider = getServerConformanceProvider();
if (confProvider == null) {
confProvider = getFhirContext().getVersion().createServerConformanceProvider(this);
}
ourLog.info("Got {} resource providers", typeToProvider.size());
for (IResourceProvider provider : typeToProvider.values()) {
assertProviderIsValid(provider);
findResourceMethods(provider);
// findSystemMethods(confProvider);
findResourceMethods(confProvider);
} catch (Exception ex) {
ourLog.error("An error occurred while loading request handlers!", ex);
throw new ServletException("Failed to initialize FHIR Restful server", ex);
}
ourLog.trace("Invoking provider initialize methods");
if (getResourceProviders() != null) {
for (IResourceProvider iResourceProvider : getResourceProviders()) {
invokeInitialize(iResourceProvider);
}
}
Collection<Object> providers = getPlainProviders();
if (providers != null) {
for (Object next : providers) {
assertProviderIsValid(next);
findResourceMethods(next);
if (confProvider != null) {
invokeInitialize(confProvider);
}
if (getPlainProviders() != null) {
for (Object next : getPlainProviders()) {
invokeInitialize(next);
}
}
findResourceMethods(getServerProfilesProvider());
confProvider = getServerConformanceProvider();
if (confProvider == null) {
confProvider = getFhirContext().getVersion().createServerConformanceProvider(this);
}
// findSystemMethods(confProvider);
findResourceMethods(confProvider);
} catch (Exception ex) {
ourLog.error("An error occurred while loading request handlers!", ex);
throw new ServletException("Failed to initialize FHIR Restful server", ex);
myStarted = true;
ourLog.info("A FHIR has been lit on this server");
} finally {
myProviderRegistrationMutex.unlock();
}
ourLog.trace("Invoking provider initialize methods");
if (getResourceProviders() != null) {
for (IResourceProvider iResourceProvider : getResourceProviders()) {
invokeInitialize(iResourceProvider);
}
}
if (confProvider != null) {
invokeInitialize(confProvider);
}
if (getPlainProviders() != null) {
for (Object next : getPlainProviders()) {
invokeInitialize(next);
}
}
myStarted = true;
ourLog.info("A FHIR has been lit on this server");
}
/**
@ -854,6 +880,153 @@ public class RestfulServer extends HttpServlet {
// nothing by default
}
/**
* Register a single provider. This could be a Resource Provider
* or a "plain" provider not associated with any resource.
*
* @param provider
* @throws Exception
*/
public void registerProvider (Object provider) throws Exception {
if (provider != null) {
Collection<Object> providerList = new ArrayList<Object>(1);
providerList.add(provider);
registerProviders(providerList);
}
}
/**
* Register a group of providers. These could be Resource Providers,
* "plain" providers or a mixture of the two.
*
* @param providers a {@code Collection} of providers. The parameter
* could be null or an empty {@code Collection}
* @throws Exception
*/
public void registerProviders (Collection<? extends Object> providers) throws Exception {
myProviderRegistrationMutex.lock();
try {
if (!myStarted) {
for (Object provider : providers) {
ourLog.info("Registration of provider ["+provider.getClass().getName()+"] will be delayed until FHIR server startup");
if (provider instanceof IResourceProvider) {
myResourceProviders.add((IResourceProvider)provider);
} else {
myPlainProviders.add(provider);
}
}
return;
}
} finally {
myProviderRegistrationMutex.unlock();
}
registerProviders(providers, false);
}
/*
* Inner method to actually register providers
*/
protected void registerProviders (Collection<? extends Object> providers, boolean inInit) throws Exception {
List<IResourceProvider> newResourceProviders = new ArrayList<IResourceProvider>();
List<Object> newPlainProviders = new ArrayList<Object>();
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
if (providers != null) {
for (Object provider : providers) {
if (provider instanceof IResourceProvider) {
IResourceProvider rsrcProvider = (IResourceProvider)provider;
Class<? extends IBaseResource> resourceType = rsrcProvider.getResourceType();
if (resourceType == null) {
throw new NullPointerException("getResourceType() on class '" + rsrcProvider.getClass().getCanonicalName() + "' returned null");
}
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
if (myTypeToProvider.containsKey(resourceName)) {
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + myTypeToProvider.get(resourceName).getClass().getCanonicalName() + "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
}
if (!inInit) {
myResourceProviders.add(rsrcProvider);
}
myTypeToProvider.put(resourceName, rsrcProvider);
providedResourceScanner.scanForProvidedResources(rsrcProvider);
newResourceProviders.add(rsrcProvider);
} else {
if (!inInit) {
myPlainProviders.add(provider);
}
newPlainProviders.add(provider);
}
}
if (!newResourceProviders.isEmpty()) {
ourLog.info("Added {} resource provider(s). Total {}", newResourceProviders.size(), myTypeToProvider.size());
for (IResourceProvider provider : newResourceProviders) {
assertProviderIsValid(provider);
findResourceMethods(provider);
}
}
if (!newPlainProviders.isEmpty()) {
ourLog.info("Added {} plain provider(s). Total {}", newPlainProviders.size());
for (Object provider : newPlainProviders) {
assertProviderIsValid(provider);
findResourceMethods(provider);
}
}
if (!inInit) {
ourLog.trace("Invoking provider initialize methods");
if (!newResourceProviders.isEmpty()) {
for (IResourceProvider provider : newResourceProviders) {
invokeInitialize(provider);
}
}
if (!newPlainProviders.isEmpty()) {
for (Object provider : newPlainProviders) {
invokeInitialize(provider);
}
}
}
}
}
/**
* Unregister one provider (either a Resource provider or a plain provider)
*
* @param provider
* @throws Exception
*/
public void unregisterProvider (Object provider) throws Exception {
if (provider != null) {
Collection<Object> providerList = new ArrayList<Object>(1);
providerList.add(provider);
unregisterProviders(providerList);
}
}
/**
* Unregister a {@code Collection} of providers
*
* @param providers
* @throws Exception
*/
public void unregisterProviders (Collection<? extends Object> providers) throws Exception {
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
if (providers != null) {
for (Object provider : providers) {
removeResourceMethods(provider);
if (provider instanceof IResourceProvider) {
myResourceProviders.remove(provider);
IResourceProvider rsrcProvider = (IResourceProvider)provider;
Class<? extends IBaseResource> resourceType = rsrcProvider.getResourceType();
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
myTypeToProvider.remove(resourceName);
providedResourceScanner.removeProvidedResources(rsrcProvider);
} else {
myPlainProviders.remove(provider);
}
invokeDestroy(provider);
}
}
}
private void invokeDestroy(Object theProvider) {
invokeDestroy(theProvider, theProvider.getClass());
}
@ -1093,6 +1266,19 @@ public class RestfulServer extends HttpServlet {
if (myStarted) {
throw new IllegalStateException("Server is already started");
}
// call the setRestfulServer() method to point the Conformance
// Provider to this server instance. This is done to avoid
// passing the server into the constructor. Having that sort
// of cross linkage causes reference cycles in Spring wiring
try {
Method setRestfulServer = theServerConformanceProvider.getClass().getMethod("setRestfulServer", new Class[]{RestfulServer.class});
if (setRestfulServer != null) {
setRestfulServer.invoke(theServerConformanceProvider, new Object[]{this});
}
} catch (Exception e) {
ourLog.warn("Error calling IServerConformanceProvider.setRestfulServer", e);
}
myServerConformanceProvider = theServerConformanceProvider;
}

View File

@ -0,0 +1,52 @@
<project name="HAPI FHIR Core OSGi Bundle" default="all">
<property file="project.properties"/>
<property name="spring.dir" value="${resources.dir}/META-INF/spring" />
<property name="bundle.jar" value="${bundle.file.name}_${major.version}.${minor.version}.${micro.version}.jar" />
<property name="bundle.file" value="${target.dir}/${bundle.jar}" />
<property name="hapi.fhir.base.jar" value="${hapi.fhir.base.name}-${hapi.fhir.version}.jar" />
<property name="hapi.fhir.base.file" value="../${hapi.fhir.base.name}/target/${hapi.fhir.base.jar}" />
<property name="hapi.fhir.dstu.jar" value="${hapi.fhir.dstu.name}-${hapi.fhir.version}.jar" />
<property name="hapi.fhir.dstu.file" value="../${hapi.fhir.dstu.name}/target/${hapi.fhir.dstu.jar}" />
<property name="hapi.fhir.dstu2.jar" value="${hapi.fhir.dstu2.name}-${hapi.fhir.version}.jar" />
<property name="hapi.fhir.dstu2.file" value="../${hapi.fhir.dstu2.name}/target/${hapi.fhir.dstu2.jar}" />
<property name="hapi.fhir.hl7dstu2.jar" value="${hapi.fhir.hl7dstu2.name}-${hapi.fhir.version}.jar" />
<property name="hapi.fhir.hl7dstu2.file" value="../${hapi.fhir.hl7dstu2.name}/target/${hapi.fhir.hl7dstu2.jar}" />
<target name="all" depends="bundle" />
<target name="init">
<delete dir="${obr.target.dir}" failonerror="false"/>
<mkdir dir="${obr.target.dir}" />
</target>
<target name="collect.jars" depends="init">
<delete dir="${temp.target.dir}" failonerror="false"/>
<mkdir dir="${temp.target.dir}" />
<copy todir="${temp.target.dir}">
<fileset file="${hapi.fhir.base.file}"/>
<fileset file="${hapi.fhir.dstu.file}"/>
<fileset file="${hapi.fhir.dstu2.file}"/>
<fileset file="${hapi.fhir.hl7dstu2.file}"/>
</copy>
</target>
<target name="bundle" depends="collect.jars">
<echo>creating HAPI FHIR Core OSGi Bundle</echo>
<concat destfile="${temp.target.dir}/MANIFEST.MF">
<fileset dir="${resources.dir}/META-INF" includes="MANIFEST.MF" />
<footer>
Bundle-Classpath: .,
lib/${hapi.fhir.base.jar},
lib/${hapi.fhir.dstu.jar},
lib/${hapi.fhir.dstu2.jar},
lib/${hapi.fhir.hl7dstu2.jar}
</footer>
</concat>
<jar destfile="${bundle.file}" manifest="${temp.target.dir}/MANIFEST.MF">
<fileset dir="${classes.dir}" includes="**/*" />
<zipfileset dir="${temp.target.dir}" includes="*.jar" prefix="lib"/>
<zipfileset dir="${spring.dir}" prefix="META-INF/spring"/>
</jar>
</target>
</project>

View File

@ -0,0 +1,37 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-osgi-core</artifactId>
<packaging>jar</packaging>
<url>http://jamesagnew.github.io/hapi-fhir/</url>
<name>HAPI FHIR - OSGi Bundle</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,18 @@
#Fri, 31 Jul 2015 17:06:47 -0700
bundle.file.name=hapi-fhir-osgi-core
major.version=1
minor.version=2
micro.version=0.SNAPSHOT
src.dir=./src/main/java
resources.dir=./src/main/resources
classes.dir=./target/classes
target.dir=./target
obr.target.dir=./target/build-obr
temp.target.dir=./target/build-temp
hapi.fhir.version=1.2-SNAPSHOT
hapi.fhir.base.name=hapi-fhir-base
hapi.fhir.dstu.name=hapi-fhir-structures-dstu
hapi.fhir.dstu2.name=hapi-fhir-structures-dstu2
hapi.fhir.hl7dstu2.name=hapi-fhir-structures-hl7org-dstu2

View File

@ -0,0 +1,50 @@
package ca.uhn.fhir.osgi;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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%
*/
/**
* Exception thrown from the Spring-DM/OSGi wiring. These
* exceptions are thrown when an error was encountered
* that was caused by incorrect wiring.
*
* @author Akana, Inc. Professional Services
*
*/
public class FhirConfigurationException extends Exception {
public FhirConfigurationException() {
super();
}
public FhirConfigurationException(String message) {
super(message);
}
public FhirConfigurationException(Throwable cause) {
super(cause);
}
public FhirConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,56 @@
package ca.uhn.fhir.osgi;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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.util.Collection;
/**
* This is an abstraction for adding one or more Providers
* ("plain" providers as well as Resource Providers)
* to the configuration of a Fhir Server. This approach
* is needed versus direct publication of providers as
* OSGi services because references to OSGi services are
* really proxies that only implement the methods of the
* service's interfaces. This means that the introspection
* and annotation processing needed for HAPI FHIR provider
* processing is not possible on those proxy references..
*
* To get around this restriction, instances of this interface
* will be published as OSGi services and the real providers
* will typically be Spring wired into the underlying bean.
*
* Beans that are decorated with this interface can be
* published as OSGi services and will be registered in
* the specified FHIR Server. The OSGi service definition
* should have the following <service-property> entry:
*
* <entry key="fhir.server.name" value="a-name"/>
*
* where the value matches the same <service-property>
* assigned to a FhirServer OSGi service.
*
* @author Akana, Inc. Professional Services
*
*/
public interface FhirProviderBundle {
public Collection<Object> getProviders();
}

View File

@ -0,0 +1,87 @@
package ca.uhn.fhir.osgi;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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.util.Collection;
/**
* Instances of the FHIR Server must implement this interface
* in order to be registered as OSGi services capable of dynamic
* provider registration. It expected that implementations of this
* interface will also extend RestfulService.
*
* The OSGi service definition for instances of the FHIR SERver
* should have the following <service-property> entry:
*
* <entry key="fhir.server.name" value="a-name"/>
*
* where the value matches the same <service-property> specified
* on the published "provider" OSGi services that are to be
* dynamically registered in the FHIR Server instance.
*
* @author Akana, Inc. Professional Services
*
*/
public interface FhirServer {
public static final String SVCPROP_SERVICE_NAME = "fhir.server.name";
/**
* Dynamically registers a single provider with the RestfulServer
*
* @param provider the provider to be registered
* @throws FhirConfigurationException
*/
public void registerOsgiProvider(Object provider) throws FhirConfigurationException;
/**
* Dynamically unregisters a single provider with the RestfulServer
*
* @param provider the provider to be unregistered
* @throws FhirConfigurationException
*/
public void unregisterOsgiProvider(Object provider) throws FhirConfigurationException;
/**
* Dynamically registers a list of providers with the RestfulServer
*
* @param provider the providers to be registered
* @throws FhirConfigurationException
*/
public void registerOsgiProviders(Collection<Object> provider) throws FhirConfigurationException;
/**
* Dynamically unregisters a list of providers with the RestfulServer
*
* @param provider the providers to be unregistered
* @throws FhirConfigurationException
*/
public void unregisterOsgiProviders(Collection<Object> provider) throws FhirConfigurationException;
/**
* Dynamically unregisters all of providers currently registered
*
* @throws FhirConfigurationException
*/
public void unregisterOsgiProviders() throws FhirConfigurationException;
}

View File

@ -0,0 +1,157 @@
package ca.uhn.fhir.osgi.impl;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.osgi.FhirConfigurationException;
import ca.uhn.fhir.osgi.FhirServer;
import ca.uhn.fhir.rest.server.RestfulServer;
/**
*
* @author Akana, Inc. Professional Services
*
*/
public class FhirServerImpl extends RestfulServer implements FhirServer {
private static Logger log = LoggerFactory.getLogger(FhirServerImpl.class);
private Collection<Object> serverProviders = Collections.synchronizedCollection(new ArrayList<Object>());
public FhirServerImpl() {
super();
}
public FhirServerImpl(FhirContext theCtx) {
super(theCtx);
}
/**
* Dynamically registers a single provider with the RestfulServer
*
* @param provider the provider to be registered
* @throws FhirConfigurationException
*/
@Override
public void registerOsgiProvider (Object provider) throws FhirConfigurationException {
if (null == provider) {
throw new NullPointerException("FHIR Provider cannot be null");
}
try {
super.registerProvider(provider);
log.trace("registered provider. class ["+provider.getClass().getName()+"]");
this.serverProviders.add(provider);
} catch (Exception e) {
log.error("Error registering FHIR Provider", e);
throw new FhirConfigurationException("Error registering FHIR Provider", e);
}
}
/**
* Dynamically unregisters a single provider with the RestfulServer
*
* @param provider the provider to be unregistered
* @throws FhirConfigurationException
*/
@Override
public void unregisterOsgiProvider (Object provider) throws FhirConfigurationException {
if (null == provider) {
throw new NullPointerException("FHIR Provider cannot be null");
}
try {
this.serverProviders.remove(provider);
log.trace("unregistered provider. class ["+provider.getClass().getName()+"]");
super.unregisterProvider(provider);
} catch (Exception e) {
log.error("Error unregistering FHIR Provider", e);
throw new FhirConfigurationException("Error unregistering FHIR Provider", e);
}
}
/**
* Dynamically registers a list of providers with the RestfulServer
*
* @param provider the providers to be registered
* @throws FhirConfigurationException
*/
@Override
public void registerOsgiProviders (Collection<Object> providers) throws FhirConfigurationException {
if (null == providers) {
throw new NullPointerException("FHIR Provider list cannot be null");
}
try {
super.registerProviders(providers);
for (Object provider : providers) {
log.trace("registered provider. class ["+provider.getClass().getName()+"]");
this.serverProviders.add(provider);
}
} catch (Exception e) {
log.error("Error registering FHIR Providers", e);
throw new FhirConfigurationException("Error registering FHIR Providers", e);
}
}
/**
* Dynamically unregisters a list of providers with the RestfulServer
*
* @param provider the providers to be unregistered
* @throws FhirConfigurationException
*/
@Override
public void unregisterOsgiProviders (Collection<Object> providers) throws FhirConfigurationException {
if (null == providers) {
throw new NullPointerException("FHIR Provider list cannot be null");
}
try {
for (Object provider : providers) {
log.trace("unregistered provider. class ["+provider.getClass().getName()+"]");
this.serverProviders.remove(provider);
}
super.unregisterProvider(providers);
} catch (Exception e) {
log.error("Error unregistering FHIR Providers", e);
throw new FhirConfigurationException("Error unregistering FHIR Providers", e);
}
}
/**
* Dynamically unregisters all of providers currently registered
*
* @throws FhirConfigurationException
*/
@Override
public void unregisterOsgiProviders () throws FhirConfigurationException {
// need to make a copy to be able to remove items
Collection<Object> providers = new ArrayList<Object>();
providers.addAll(this.serverProviders);
this.unregisterOsgiProviders(providers);
}
}

View File

@ -0,0 +1,301 @@
package ca.uhn.fhir.osgi.impl;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.osgi.FhirConfigurationException;
import ca.uhn.fhir.osgi.FhirProviderBundle;
import ca.uhn.fhir.osgi.FhirServer;
/**
* Manage the dynamic registration of FHIR Servers and FHIR Providers.
* Methods on this Spring Bean will be invoked from OSGi Reference
* Listeners when OSGi services are published for these interfaces.
*
* @author Akana, Inc. Professional Services
*
*/
public class FhirServerManager {
private static Logger log = LoggerFactory.getLogger(FhirServerManager.class);
private static final String FIRST_SERVER = "#first";
private Map<String,FhirServer> registeredServers = new ConcurrentHashMap<String,FhirServer>();
private Map<String,Collection<Collection<Object>>> serverProviders = new ConcurrentHashMap<String,Collection<Collection<Object>>>();
private Collection<Collection<Object>> registeredProviders = Collections.synchronizedList(new ArrayList<Collection<Object>>());
private Map<String,Collection<Collection<Object>>> pendingProviders = new ConcurrentHashMap<String,Collection<Collection<Object>>>();
private boolean haveDefaultProviders = false;
/**
* Register a new FHIR Server OSGi service.
* We need to track these services so we can find the correct
* server to use when registering/unregistering providers.
* <p>
* The OSGi service definition of a FHIR Server should look like:
* <code><pre>
* &lt;osgi:service ref="<b><i>some.bean</i></b>" interface="ca.uhn.fhir.osgi.FhirServer">
* &lt;osgi:service-properties>
* &lt;entry key="name" value="<b><i>osgi-service-name</i></b>"/>
* &lt;entry key="fhir.server.name" value="<b><i>fhir-server-name</i></b>"/>
* &lt;/osgi:service-properties>
* &lt;/osgi:service>
* </pre></code>
* The <b><i>fhir-server-name</i></b> parameter is also specified for all
* of the FHIR Providers that are to be dynamically registered with the
* named FHIR Server.
*
* @param server OSGi service implementing the FhirService interface
* @param props the <service-properties> for that service
*
* @throws FhirConfigurationException
*/
public void registerFhirServer (FhirServer server, Map<String,Object> props) throws FhirConfigurationException {
if (server != null) {
String serviceName = (String)props.get("name");
if (null == serviceName) {
serviceName = "<default>";
}
String serverName = (String)props.get(FhirServer.SVCPROP_SERVICE_NAME);
if (serverName != null) {
if (registeredServers.containsKey(serverName)) {
throw new FhirConfigurationException("FHIR Server named ["+serverName+"] is already registered. These names must be unique.");
}
log.trace("Registering FHIR Server ["+serverName+"]. (OSGi service named ["+serviceName+"])");
registeredServers.put(serverName, server);
if (haveDefaultProviders && registeredServers.size() > 1) {
throw new FhirConfigurationException("FHIR Providers are registered without a server name. Only one FHIR Server is allowed.");
}
Collection<Collection<Object>> providers = pendingProviders.get(serverName);
if (providers != null) {
log.trace("Registering FHIR providers waiting for this server to be registered.");
pendingProviders.remove(serverName);
for (Collection<Object> list : providers) {
this.registerProviders(list, server, serverName);
}
}
if (registeredServers.size() == 1) {
providers = pendingProviders.get(FIRST_SERVER);
if (providers != null) {
log.trace("Registering FHIR providers waiting for the first/only server to be registered.");
pendingProviders.remove(FIRST_SERVER);
for (Collection<Object> list : providers) {
this.registerProviders(list, server, serverName);
}
}
}
} else {
throw new FhirConfigurationException("FHIR Server registered in OSGi is missing the required ["+FhirServer.SVCPROP_SERVICE_NAME+"] service-property");
}
}
}
/**
* This method will be called when a FHIR Server OSGi service
* is being removed from the container. This normally will only
* occur when its bundle is stopped because it is being removed
* or updated.
*
* @param server OSGi service implementing the FhirService interface
* @param props the <service-properties> for that service
*
* @throws FhirConfigurationException
*/
public void unregisterFhirServer (FhirServer server, Map<String,Object> props) throws FhirConfigurationException {
if (server != null) {
String serverName = (String)props.get(FhirServer.SVCPROP_SERVICE_NAME);
if (serverName != null) {
FhirServer service = registeredServers.get(serverName);
if (service != null) {
log.trace("Unregistering FHIR Server ["+serverName+"]");
service.unregisterOsgiProviders();
registeredServers.remove(serverName);
log.trace("Dequeue any FHIR providers waiting for this server");
pendingProviders.remove(serverName);
if (registeredServers.size() == 0) {
log.trace("Dequeue any FHIR providers waiting for the first/only server");
pendingProviders.remove(FIRST_SERVER);
}
Collection<Collection<Object>> providers = serverProviders.get(serverName);
if (providers != null) {
serverProviders.remove(serverName);
registeredProviders.removeAll(providers);
}
}
} else {
throw new FhirConfigurationException("FHIR Server registered in OSGi is missing the required ["+FhirServer.SVCPROP_SERVICE_NAME+"] service-property");
}
}
}
/**
* Register a new FHIR Provider-Bundle OSGi service.
*
* This could be a "plain" provider that is published with the
* FhirProvider interface or it could be a resource provider that
* is published with either that same interface or the IResourceProvider
* interface.
*
* (That check is not made here but is included as usage documentation)
*
* <p>
* The OSGi service definition of a FHIR Provider would look like:
* <code><pre>
* &lt;osgi:service ref="<b><i>some.bean</i></b>" interface="ca.uhn.fhir.osgi.IResourceProvider">
* &lt;osgi:service-properties>
* &lt;entry key="name" value="<b><i>osgi-service-name</i></b>"/>
* &lt;entry key="fhir.server.name" value="<b><i>fhir-server-name</i></b>"/>
* &lt;/osgi:service-properties>
* &lt;/osgi:service>
* </pre></code>
* The <b><i>fhir-server-name</i></b> parameter is the value assigned to the
* <code>fhir.server.name</code> service-property of one of the OSGi-published
* FHIR Servers.
*
* @param server OSGi service implementing a FHIR provider interface
* @param props the <service-properties> for that service
*
* @throws FhirConfigurationException
*/
public void registerFhirProviders (FhirProviderBundle bundle, Map<String,Object> props) throws FhirConfigurationException {
if (bundle != null) {
Collection<Object> providers = bundle.getProviders();
if (providers != null && !providers.isEmpty()) {
try {
String serverName = (String)props.get(FhirServer.SVCPROP_SERVICE_NAME);
String ourServerName = getServerName(serverName);
String bundleName = (String)props.get("name");
if (null == bundleName) {
bundleName = "<default>";
}
log.trace("Register FHIR Provider Bundle ["+bundleName+"] on FHIR Server ["+ourServerName+"]");
FhirServer server = registeredServers.get(ourServerName);
if (server != null) {
registerProviders(providers, server, serverName);
} else {
log.trace("Queue the Provider Bundle waiting for FHIR Server to be registered");
Collection<Collection<Object>> pending;
synchronized(pendingProviders) {
pending = pendingProviders.get(serverName);
if (null == pending) {
pending = Collections.synchronizedCollection(new ArrayList<Collection<Object>>());
pendingProviders.put(serverName, pending);
}
}
pending.add(providers);
}
} catch (BadServerException e) {
throw new FhirConfigurationException("Unable to register the OSGi FHIR Provider. Multiple Restful Servers exist. Specify the ["+FhirServer.SVCPROP_SERVICE_NAME+"] service-property");
}
}
}
}
protected void registerProviders (Collection<Object> providers, FhirServer server, String serverName) throws FhirConfigurationException {
server.registerOsgiProviders(providers);
Collection<Collection<Object>> active;
synchronized(serverProviders) {
active = serverProviders.get(serverName);
if (null == active) {
active = Collections.synchronizedCollection(new ArrayList<Collection<Object>>());
serverProviders.put(serverName, active);
}
}
active.add(providers);
registeredProviders.add(providers);
}
/**
* This method will be called when a FHIR Provider OSGi service
* is being removed from the container. This normally will only
* occur when its bundle is stopped because it is being removed
* or updated.
*
* @param server OSGi service implementing one of the provider
* interfaces
* @param props the <service-properties> for that service
*
* @throws FhirConfigurationException
*/
public void unregisterFhirProviders (FhirProviderBundle bundle, Map<String,Object> props) throws FhirConfigurationException {
if (bundle != null) {
Collection<Object> providers = bundle.getProviders();
if (providers != null && !providers.isEmpty()) {
try {
registeredProviders.remove(providers);
String serverName = (String)props.get(FhirServer.SVCPROP_SERVICE_NAME);
String ourServerName = getServerName(serverName);
FhirServer server = registeredServers.get(ourServerName);
if (server != null) {
server.unregisterOsgiProviders(providers);
Collection<Collection<Object>> active = serverProviders.get(serverName);
if (active != null) {
active.remove(providers);
}
}
} catch (BadServerException e) {
throw new FhirConfigurationException("Unable to register the OSGi FHIR Provider. Multiple Restful Servers exist. Specify the ["+FhirServer.SVCPROP_SERVICE_NAME+"] service-property");
}
}
}
}
/*
* Adjust the FHIR Server name allowing for null which would
* indicate that the Provider should be registered with the
* only FHIR Server defined.
*/
private String getServerName (String osgiName) throws BadServerException {
String result = osgiName;
if (null == result) {
if (registeredServers.isEmpty()) { // wait for the first one
haveDefaultProviders = true; // only allow one server
result = FIRST_SERVER;
} else
if (registeredServers.size() == 1) { // use the only one
haveDefaultProviders = true; // only allow one server
result = registeredServers.keySet().iterator().next();
} else {
throw new BadServerException();
}
}
return result;
}
class BadServerException extends Exception {
BadServerException() {
super();
}
}
}

View File

@ -0,0 +1,58 @@
package ca.uhn.fhir.osgi.impl;
/*
* #%L
* HAPI FHIR - OSGi Bundle
* %%
* Copyright (C) 2014 - 2015 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.util.Collection;
import ca.uhn.fhir.osgi.FhirProviderBundle;
/**
*
* @author Akana, Inc. Professional Services
*
*/
public class SimpleFhirProviderBundle implements FhirProviderBundle {
// /////////////////////////////////////
// //////// Spring Wiring ////////
// /////////////////////////////////////
private Collection<Object> providers;
public void setProviders (Collection<Object> providers) {
this.providers = providers;
}
// /////////////////////////////////////
// /////////////////////////////////////
// /////////////////////////////////////
public SimpleFhirProviderBundle () {
super();
}
@Override
public Collection<Object> getProviders () {
return this.providers;
}
}

View File

@ -0,0 +1,134 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HAPI FHIR - Core Library and DSTU/DSTU2 Structures
Bundle-SymbolicName: hapi-fhir-osgi-core
Bundle-Version: 1.2.0.SNAPSHOT
Spring-Context: *;publish-context:=false
Export-Package: ca.uhn.fhir;version="1.2.0",
ca.uhn.fhir.context;version="1.2.0",
ca.uhn.fhir.i18n;version="1.2.0",
ca.uhn.fhir.model.api;version="1.2.0",
ca.uhn.fhir.model.api.annotation;version="1.2.0",
ca.uhn.fhir.model.base.composite;version="1.2.0",
ca.uhn.fhir.model.base.resource;version="1.2.0",
ca.uhn.fhir.model.dstu;version="1.2.0",
ca.uhn.fhir.model.dstu.api;version="1.2.0",
ca.uhn.fhir.model.dstu.composite;version="1.2.0",
ca.uhn.fhir.model.dstu.resource;version="1.2.0",
ca.uhn.fhir.model.dstu.valueset;version="1.2.0",
ca.uhn.fhir.model.dstu2;version="1.2.0",
ca.uhn.fhir.model.dstu2.composite;version="1.2.0",
ca.uhn.fhir.model.dstu2.resource;version="1.2.0",
ca.uhn.fhir.model.dstu2.valueset;version="1.2.0",
ca.uhn.fhir.model.primitive;version="1.2.0",
ca.uhn.fhir.model.valueset;version="1.2.0",
ca.uhn.fhir.model.view;version="1.2.0",
ca.uhn.fhir.narrative;version="1.2.0",
ca.uhn.fhir.narrative.datatype;version="1.2.0",
ca.uhn.fhir.narrative.title;version="1.2.0",
ca.uhn.fhir.osgi;version="1.2.0",
ca.uhn.fhir.osgi.impl;version="1.2.0",
ca.uhn.fhir.parser;version="1.2.0",
ca.uhn.fhir.rest.annotation;version="1.2.0",
ca.uhn.fhir.rest.api;version="1.2.0",
ca.uhn.fhir.rest.client;version="1.2.0",
ca.uhn.fhir.rest.client.api;version="1.2.0",
ca.uhn.fhir.rest.client.exceptions;version="1.2.0",
ca.uhn.fhir.rest.client.interceptor;version="1.2.0",
ca.uhn.fhir.rest.gclient;version="1.2.0",
ca.uhn.fhir.rest.method;version="1.2.0",
ca.uhn.fhir.rest.param;version="1.2.0",
ca.uhn.fhir.rest.server;version="1.2.0",
ca.uhn.fhir.rest.server.audit;version="1.2.0",
ca.uhn.fhir.rest.server.exceptions;version="1.2.0",
ca.uhn.fhir.rest.server.interceptor;version="1.2.0",
ca.uhn.fhir.rest.server.provider;version="1.2.0",
ca.uhn.fhir.rest.server.provider.dstu2;version="1.2.0",
ca.uhn.fhir.rest.server.provider.dstu2hl7org,
ca.uhn.fhir.store;version="1.2.0",
ca.uhn.fhir.util;version="1.2.0",
ca.uhn.fhir.validation;version="1.2.0",
org.hl7.fhir.instance;version="1.2.0",
org.hl7.fhir.instance.client;version="1.2.0",
org.hl7.fhir.instance.conf;version="1.2.0",
org.hl7.fhir.instance.formats;version="1.2.0",
org.hl7.fhir.instance.model;version="1.2.0",
org.hl7.fhir.instance.model.annotations;version="1.2.0",
org.hl7.fhir.instance.model.api;version="1.2.0",
org.hl7.fhir.instance.model.valuesets;version="1.2.0",
org.hl7.fhir.instance.terminologies;version="1.2.0",
org.hl7.fhir.instance.utils;version="1.2.0",
org.hl7.fhir.instance.validation;version="1.2.0",
org.hl7.fhir.utilities;version="1.2.0",
org.hl7.fhir.utilities.xhtml;version="1.2.0",
org.hl7.fhir.utilities.xml;version="1.2.0"
Import-Package: com.ctc.wstx.api;version="4.4.0",
com.ctc.wstx.stax;version="4.4.0",
com.google.gson;resolution:=optional,
com.phloc.commons;resolution:=optional,
com.phloc.commons.error;resolution:=optional,
com.phloc.schematron;resolution:=optional,
com.phloc.schematron.xslt;resolution:=optional,
javax.json,
javax.json.stream,
javax.servlet,
javax.servlet.http,
javax.xml.parsers,
javax.xml.stream,
javax.xml.stream,events,
net.sf.saxon;resolution:=optional,
net.sourceforge.cobertura;resolution:=optional,
org.apache.commons.codec.binary,
org.apache.commons.io,
org.apache.commons.io.input,
org.apache.commons.lang3,
org.apache.commons.lang3.builder,
org.apache.commons.lang3.exception,
org.apache.commons.lang3.text,
org.apache.commons.lang3.time,
org.apache.http;version="4.4.0",
org.apache.http.auth;version="4.4.0",
org.apache.http.client;version="4.4.0",
org.apache.http.client.config;version="4.4.0",
org.apache.http.client.entity;version="4.4.0",
org.apache.http.client.methods;version="4.4.0",
org.apache.http.client.protocol;version="4.4.0",
org.apache.http.client.utils;version="4.4.0",
org.apache.http.entity;version="4.4.0",
org.apache.http.impl.auth;version="4.4.0",
org.apache.http.impl.client;version="4.4.0",
org.apache.http.impl.conn;version="4.4.0",
org.apache.http.message;version="4.4.0",
org.apache.http.protocol;version="4.4.0",
org.apache.xerces.parsers,
org.apache.xerces.xni.parser,
org.codehaus.stax2,
org.codehaus.stax2.io,
org.oclc.purl.dsdl.svrl,
org.slf4j,
org.thymeleaf,
org.thymeleaf.context,
org.thymeleaf.dialect,
org.thymeleaf.dom,
org.thymeleaf.exceptions,
org.thymeleaf.messageresolver,
org.thymeleaf.processor,
org.thymeleaf.processor.attr,
org.thymeleaf.resourceresolver,
org.thymeleaf.standard,
org.thymeleaf.standard.expression,
org.thymeleaf.templatemode,
org.thymeleaf.templateparser.xmlsax,
org.thymeleaf.templateresolver,
org.thymeleaf.util,
org.unbescape.html,
org.unbescape.java,
org.unbescape.javascript,
org.unbescape.uri,
org.w3c.dom,
org.w3c.dom.ls,
org.xmlpull.v1;resolution:=optional,
org.xml.sax,
org.xml.sax.ext,
org.xml.sax.helpers
Bundle-Vendor: University Health Network

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ctx="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:osgi="http://www.eclipse.org/gemini/blueprint/schema/blueprint"
xmlns:osgix="http://www.eclipse.org/gemini/blueprint/schema/blueprint-compendium"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.eclipse.org/gemini/blueprint/schema/blueprint-compendium
http://www.eclipse.org/gemini/blueprint/schema/blueprint-compendium/gemini-blueprint-compendium.xsd
http://www.eclipse.org/gemini/blueprint/schema/blueprint
http://www.eclipse.org/gemini/blueprint/schema/blueprint/gemini-blueprint.xsd">
<!-- ++====================================++
|| S E R V E R M A N A G E R ||
++====================================++
-->
<bean id="fhir.server.manager" class="ca.uhn.fhir.osgi.impl.FhirServerManager">
</bean>
<!-- ++=====================++
|| S E R V E R S ||
++=====================++
-->
<osgi:list id="fhir.servers" interface="ca.uhn.fhir.osgi.FhirServer" cardinality="0..N">
<osgi:listener ref="fhir.server.manager"
bind-method="registerFhirServer"
unbind-method="unregisterFhirServer" />
</osgi:list>
<!-- ++========================================++
|| P R O V I D E R B U N D L E S ||
++========================================++
-->
<osgi:list id="fhir.osgi.providers" interface="ca.uhn.fhir.osgi.FhirProviderBundle" cardinality="0..N" >
<osgi:listener ref="fhir.server.manager"
bind-method="registerFhirProviders"
unbind-method="unregisterFhirProviders" />
</osgi:list>
</beans>

View File

@ -77,11 +77,25 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private boolean myCache = true;
private volatile Conformance myConformance;
private String myPublisher = "Not provided";
private final RestfulServer myRestfulServer;
private RestfulServer myRestfulServer;
public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
/*
* Add a no-arg constructor and seetter so that the
* ServerConfirmanceProvider can be Spring-wired with
* the RestfulService avoiding the potential reference
* cycle that would happen.
*/
public ServerConformanceProvider () {
super();
}
public void setRestfulServer (RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
/**
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this

View File

@ -15,6 +15,36 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParamTest {
@Test
public void testAndList() {
assertNotNull(new DateAndListParam().newInstance());
assertNotNull(new NumberAndListParam().newInstance());
assertNotNull(new ReferenceAndListParam().newInstance());
assertNotNull(new QuantityAndListParam().newInstance());
assertNotNull(new UriAndListParam().newInstance());
assertNotNull(new StringAndListParam().newInstance());
}
@Test
public void testAddAnd() {
assertEquals(1, new DateAndListParam().addAnd(new DateOrListParam()).getValuesAsQueryTokens().size());
assertEquals(1, new NumberAndListParam().addAnd(new NumberOrListParam()).getValuesAsQueryTokens().size());
assertEquals(1, new ReferenceAndListParam().addAnd(new ReferenceOrListParam()).getValuesAsQueryTokens().size());
assertEquals(1, new QuantityAndListParam().addAnd(new QuantityOrListParam()).getValuesAsQueryTokens().size());
assertEquals(1, new UriAndListParam().addAnd(new UriOrListParam()).getValuesAsQueryTokens().size());
assertEquals(1, new StringAndListParam().addAnd(new StringOrListParam()).getValuesAsQueryTokens().size());
}
@Test
public void testOrList() {
assertNotNull(new DateOrListParam().newInstance());
assertNotNull(new NumberOrListParam().newInstance());
assertNotNull(new ReferenceOrListParam().newInstance());
assertNotNull(new QuantityOrListParam().newInstance());
assertNotNull(new UriOrListParam().newInstance());
assertNotNull(new StringOrListParam().newInstance());
}
private static DateRangeParam create(String theLower, String theUpper) throws InvalidRequestException {
DateRangeParam p = new DateRangeParam();
List<QualifiedParamList> tokens = new ArrayList<QualifiedParamList>();

View File

@ -123,6 +123,7 @@ public class DateRangeParamSearchTest {
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.Collections;
@ -76,7 +75,6 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
/**
@ -94,11 +92,25 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
private String myPublisher = "Not provided";
private final RestfulServer myRestfulServer;
private RestfulServer myRestfulServer;
public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
/*
* Add a no-arg constructor and seetter so that the
* ServerConfirmanceProvider can be Spring-wired with
* the RestfulService avoiding the potential reference
* cycle that would happen.
*/
public ServerConformanceProvider () {
super();
}
public void setRestfulServer (RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) {

View File

@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -21,6 +22,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -29,7 +31,10 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.util.PatternMatcher;
import ca.uhn.fhir.util.PortUtil;
@ -42,10 +47,72 @@ public class SearchDstu2Test {
private static FhirContext ourCtx = FhirContext.forDstu2();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2Test.class);
private static int ourPort;
private static InstantDt ourReturnPublished;
private static Server ourServer;
private static String ourLastMethod;
private static DateAndListParam ourLastDateAndList;
@Before
public void before() {
ourLastMethod = null;
ourLastDateAndList = null;
}
@Test
public void testSearchWhitelist01Failing() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref=value");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
}
@Test
public void testSearchDateAndList() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals("searchDateAndList", ourLastMethod);
assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().size());
assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size());
assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(1).getValuesAsQueryTokens().size());
assertEquals("2001", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValueAsString());
assertEquals("2002", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(1).getValueAsString());
}
@Test
public void testSearchBlacklist01Failing() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.black1=value");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
}
@Test
public void testSearchBlacklist01Passing() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.white1=value");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchBlacklist01", ourLastMethod);
}
@Test
public void testSearchWhitelist01Passing() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref.white1=value");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchWhitelist01", ourLastMethod);
}
@Test
public void testEncodeConvertsReferencesToRelative() throws Exception {
@ -145,6 +212,34 @@ public class SearchDstu2Test {
return Patient.class;
}
//@formatter:off
@Search(queryName="searchWhitelist01")
public List<Patient> searchWhitelist01(
@RequiredParam(chainWhitelist="white1", name = "ref") ReferenceParam theParam) {
ourLastMethod = "searchWhitelist01";
return Collections.emptyList();
}
//@formatter:on
//@formatter:off
@Search()
public List<Patient> searchDateAndList(
@RequiredParam(name = "searchDateAndList") DateAndListParam theParam) {
ourLastMethod = "searchDateAndList";
ourLastDateAndList = theParam;
return Collections.emptyList();
}
//@formatter:on
//@formatter:off
@Search(queryName="searchBlacklist01")
public List<Patient> searchBlacklist01(
@RequiredParam(chainBlacklist="black1", name = "ref") ReferenceParam theParam) {
ourLastMethod = "searchBlacklist01";
return Collections.emptyList();
}
//@formatter:on
@Search(queryName="searchWithBundleProvider")
public IBundleProvider searchWithBundleProvider() {
return new IBundleProvider() {

View File

@ -75,16 +75,19 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
/**
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
* Server FHIR Provider which serves the conformance statement for a RESTful
* server implementation
*
* <p>
* Note: This class is safe to extend, but it is important to note that the same instance of {@link Conformance} is always returned unless
* {@link #setCache(boolean)} is called with a value of <code>false</code>. This means that if you are adding anything to the returned conformance instance on
* each call you should call <code>setCache(false)</code> in your provider constructor.
* Note: This class is safe to extend, but it is important to note that the same
* instance of {@link Conformance} is always returned unless
* {@link #setCache(boolean)} is called with a value of <code>false</code>. This
* means that if you are adding anything to the returned conformance instance on
* each call you should call <code>setCache(false)</code> in your provider
* constructor.
* </p>
*/
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
@ -94,12 +97,25 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
private String myPublisher = "Not provided";
private final RestfulServer myRestfulServer;
private RestfulServer myRestfulServer;
public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
/*
* Add a no-arg constructor and seetter so that the ServerConfirmanceProvider
* can be Spring-wired with the RestfulService avoiding the potential
* reference cycle that would happen.
*/
public ServerConformanceProvider() {
super();
}
public void setRestfulServer(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer;
}
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps,
BaseMethodBinding<?> nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) {
@ -148,8 +164,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
}
/**
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null
* (although this is not enforced). The value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
* Gets the value of the "publisher" that will be placed in the generated
* conformance statement. As this is a mandatory element, the value should not
* be null (although this is not enforced). The value defaults to
* "Not provided" but may be set to null, which will cause this element to be
* omitted.
*/
public String getPublisher() {
return myPublisher;
@ -167,7 +186,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher);
retVal.setDate(new Date());
retVal.setFhirVersion("1.0.0"); // TODO: pull from model
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this
// configurable -
// this is a fairly
// big effort since
// the parser
// needs to be modified to actually allow it
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
@ -197,7 +220,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
TreeSet<String> includes = new TreeSet<String>();
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam =
// new HashMap<String,
// Conformance.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) {
@ -325,7 +349,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
* If the parameter has no description, default to the one from the
* resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
@ -368,9 +393,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
// query = new OperationDefinition();
// operation.setDefinition(new ResourceReferenceDt(query));
// query.getDescriptionElement().setValue(searchMethodBinding.getDescription());
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
// query.addUndeclaredExtension(false,
// ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
// for (String nextInclude : searchMethodBinding.getIncludes()) {
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
// query.addUndeclaredExtension(false,
// ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
// }
// }
@ -388,7 +415,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
* If the parameter has no description, default to the one from the
* resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
@ -521,9 +549,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
}
/**
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
* Sets the cache property (default is true). If set to true, the same
* response will be returned for each invocation.
* <p>
* See the class documentation for an important note if you are extending this class
* See the class documentation for an important note if you are extending this
* class
* </p>
*/
public void setCache(boolean theCache) {
@ -531,8 +561,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
}
/**
* Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null
* (although this is not enforced). The value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
* Sets the value of the "publisher" that will be placed in the generated
* conformance statement. As this is a mandatory element, the value should not
* be null (although this is not enforced). The value defaults to
* "Not provided" but may be set to null, which will cause this element to be
* omitted.
*/
public void setPublisher(String thePublisher) {
myPublisher = thePublisher;

View File

@ -1275,6 +1275,7 @@
<module>hapi-fhir-android</module>
<module>hapi-fhir-dist</module>
<module>examples</module>
<module>hapi-fhir-osgi-core</module>
</modules>
</profile>
<profile>

View File

@ -178,6 +178,10 @@
words, if this parameter is found, the response won't be returned as
HTML even if the request is detected as coming from a browser.
</action>
<action type="add">
RestfulServer now supports dynamically adding and removing resource providers
at runtime. Thanks to Bill Denton for adding this.
</action>
</release>
<release version="1.1" date="2015-07-13">
<action type="add">