Add double-check for #610

This commit is contained in:
James 2017-04-17 18:10:57 -04:00
parent e9a1069c9d
commit 6a5d0f95ae
2 changed files with 50 additions and 41 deletions

View File

@ -12,7 +12,7 @@ import java.lang.reflect.Method;
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -95,7 +95,7 @@ public class FhirContext {
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport; private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport;
private final IFhirVersion myVersion; private final IFhirVersion myVersion;
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap(); private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
/** /**
@ -106,7 +106,7 @@ public class FhirContext {
public FhirContext() { public FhirContext() {
this(EMPTY_LIST); this(EMPTY_LIST);
} }
/** /**
* @deprecated It is recommended that you use one of the static initializer methods instead * @deprecated It is recommended that you use one of the static initializer methods instead
* of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()}
@ -164,13 +164,14 @@ public class FhirContext {
} }
if (theVersion == null) { if (theVersion == null) {
ourLog.info("Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()", myVersion.getVersion().name()); ourLog.info("Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()",
myVersion.getVersion().name());
} else { } else {
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name()); ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
} }
myResourceTypesToScan = theResourceTypes; myResourceTypesToScan = theResourceTypes;
/* /*
* Check if we're running in Android mode and configure the context appropriately if so * Check if we're running in Android mode and configure the context appropriately if so
*/ */
@ -186,8 +187,7 @@ public class FhirContext {
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
ourLog.trace("Android mode not detected"); ourLog.trace("Android mode not detected");
} }
} }
private String createUnknownResourceNameError(String theResourceName, FhirVersionEnum theVersion) { private String createUnknownResourceNameError(String theResourceName, FhirVersionEnum theVersion) {
@ -266,7 +266,7 @@ public class FhirContext {
validateInitialized(); validateInitialized();
return Collections.unmodifiableCollection(myClassToElementDefinition.values()); return Collections.unmodifiableCollection(myClassToElementDefinition.values());
} }
/** /**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution * caution
@ -393,19 +393,19 @@ public class FhirContext {
return myIdToResourceDefinition.get(theId); return myIdToResourceDefinition.get(theId);
} }
// /** // /**
// * Return an unmodifiable collection containing all known resource definitions // * Return an unmodifiable collection containing all known resource definitions
// */ // */
// public Collection<RuntimeResourceDefinition> getResourceDefinitions() { // public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
// //
// Set<Class<? extends IBase>> datatypes = Collections.emptySet(); // Set<Class<? extends IBase>> datatypes = Collections.emptySet();
// Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap(); // Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap();
// HashMap<String, Class<? extends IBaseResource>> types = new HashMap<String, Class<? extends IBaseResource>>(); // HashMap<String, Class<? extends IBaseResource>> types = new HashMap<String, Class<? extends IBaseResource>>();
// ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing);
// for (int next : types.) // for (int next : types.)
// //
// return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); // return Collections.unmodifiableCollection(myIdToResourceDefinition.values());
// } // }
/** /**
* Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the
@ -438,6 +438,7 @@ public class FhirContext {
* Returns the validation support module configured for this context, creating a default * Returns the validation support module configured for this context, creating a default
* implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)} * implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)}
* method * method
*
* @see #setValidationSupport(IContextValidationSupport) * @see #setValidationSupport(IContextValidationSupport)
*/ */
public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() { public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() {
@ -474,7 +475,7 @@ public class FhirContext {
/** /**
* Creates a new FluentPath engine which can be used to exvaluate * Creates a new FluentPath engine which can be used to exvaluate
* path expressions over FHIR resources. Note that this engine will use the * path expressions over FHIR resources. Note that this engine will use the
* {@link IContextValidationSupport context validation support} module which is * {@link IContextValidationSupport context validation support} module which is
* configured on the context at the time this method is called. * configured on the context at the time this method is called.
* <p> * <p>
* In other words, call {@link #setValidationSupport(IContextValidationSupport)} before * In other words, call {@link #setValidationSupport(IContextValidationSupport)} before
@ -523,12 +524,12 @@ public class FhirContext {
* </p> * </p>
* *
* @param theClientType * @param theClientType
* The client type, which is an interface type to be instantiated * The client type, which is an interface type to be instantiated
* @param theServerBase * @param theServerBase
* The URL of the base for the restful FHIR server to connect to * The URL of the base for the restful FHIR server to connect to
* @return A newly created client * @return A newly created client
* @throws ConfigurationException * @throws ConfigurationException
* If the interface type is not an interface * If the interface type is not an interface
*/ */
public <T extends IRestfulClient> T newRestfulClient(Class<T> theClientType, String theServerBase) { public <T extends IRestfulClient> T newRestfulClient(Class<T> theClientType, String theServerBase) {
return getRestfulClientFactory().newClient(theClientType, theServerBase); return getRestfulClientFactory().newClient(theClientType, theServerBase);
@ -545,7 +546,7 @@ public class FhirContext {
* </p> * </p>
* *
* @param theServerBase * @param theServerBase
* The URL of the base for the restful FHIR server to connect to * The URL of the base for the restful FHIR server to connect to
*/ */
public IGenericClient newRestfulGenericClient(String theServerBase) { public IGenericClient newRestfulGenericClient(String theServerBase) {
return getRestfulClientFactory().newGenericClient(theServerBase); return getRestfulClientFactory().newGenericClient(theServerBase);
@ -597,7 +598,7 @@ public class FhirContext {
* </p> * </p>
* *
* @param theType * @param theType
* The custom type to add (must not be <code>null</code>) * The custom type to add (must not be <code>null</code>)
*/ */
public void registerCustomType(Class<? extends IBase> theType) { public void registerCustomType(Class<? extends IBase> theType) {
Validate.notNull(theType, "theType must not be null"); Validate.notNull(theType, "theType must not be null");
@ -617,7 +618,7 @@ public class FhirContext {
* </p> * </p>
* *
* @param theTypes * @param theTypes
* The custom types to add (must not be <code>null</code> or contain null elements in the collection) * The custom types to add (must not be <code>null</code> or contain null elements in the collection)
*/ */
public void registerCustomTypes(Collection<Class<? extends IBase>> theTypes) { public void registerCustomTypes(Collection<Class<? extends IBase>> theTypes) {
Validate.notNull(theTypes, "theTypes must not be null"); Validate.notNull(theTypes, "theTypes must not be null");
@ -719,7 +720,7 @@ public class FhirContext {
* </p> * </p>
* *
* @param theAddProfileTagWhenEncoding * @param theAddProfileTagWhenEncoding
* The add profile mode (must not be <code>null</code>) * The add profile mode (must not be <code>null</code>)
*/ */
public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) { public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) {
Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null"); Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null");
@ -737,10 +738,10 @@ public class FhirContext {
* </p> * </p>
* *
* @param theProfile * @param theProfile
* The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be * The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be
* <code>null</code> or empty. * <code>null</code> or empty.
* @param theClass * @param theClass
* The resource type, or <code>null</code> to clear any existing type * The resource type, or <code>null</code> to clear any existing type
*/ */
public void setDefaultTypeForProfile(String theProfile, Class<? extends IBaseResource> theClass) { public void setDefaultTypeForProfile(String theProfile, Class<? extends IBaseResource> theClass) {
Validate.notBlank(theProfile, "theProfile must not be null or empty"); Validate.notBlank(theProfile, "theProfile must not be null or empty");
@ -767,7 +768,7 @@ public class FhirContext {
* Sets a parser error handler to use by default on all parsers * Sets a parser error handler to use by default on all parsers
* *
* @param theParserErrorHandler * @param theParserErrorHandler
* The error handler * The error handler
*/ */
public void setParserErrorHandler(IParserErrorHandler theParserErrorHandler) { public void setParserErrorHandler(IParserErrorHandler theParserErrorHandler) {
Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null"); Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null");
@ -777,8 +778,9 @@ public class FhirContext {
/** /**
* Sets the parser options object which will be used to supply default * Sets the parser options object which will be used to supply default
* options to newly created parsers * options to newly created parsers
* *
* @param theParserOptions The parser options object - Must not be <code>null</code> * @param theParserOptions
* The parser options object - Must not be <code>null</code>
*/ */
public void setParserOptions(ParserOptions theParserOptions) { public void setParserOptions(ParserOptions theParserOptions) {
Validate.notNull(theParserOptions, "theParserOptions must not be null"); Validate.notNull(theParserOptions, "theParserOptions must not be null");
@ -823,7 +825,7 @@ public class FhirContext {
/** /**
* Sets the validation support module to use for this context. The validation support module * Sets the validation support module to use for this context. The validation support module
* is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc)
* as well as to provide terminology services to modules such as the validator and FluentPath executor * as well as to provide terminology services to modules such as the validator and FluentPath executor
*/ */
public void setValidationSupport(IContextValidationSupport<?, ?, ?, ?, ?, ?> theValidationSupport) { public void setValidationSupport(IContextValidationSupport<?, ?, ?, ?, ?, ?> theValidationSupport) {
myValidationSupport = theValidationSupport; myValidationSupport = theValidationSupport;
@ -841,9 +843,14 @@ public class FhirContext {
return resTypes; return resTypes;
} }
private synchronized void validateInitialized() { private void validateInitialized() {
// See #610
if (!myInitialized) { if (!myInitialized) {
scanResourceTypes(toElementList(myResourceTypesToScan)); synchronized (this) {
if (!myInitialized) {
scanResourceTypes(toElementList(myResourceTypesToScan));
}
}
} }
} }

View File

@ -91,7 +91,7 @@ public class FhirContextDstu3Test {
public void testInitialisationThreadSafety() { public void testInitialisationThreadSafety() {
final FhirContext ctx = FhirContext.forDstu3(); final FhirContext ctx = FhirContext.forDstu3();
final int numThreads = 4; final int numThreads = 40;
final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>()); final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads); final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
try { try {
@ -105,7 +105,9 @@ public class FhirContextDstu3Test {
threadsReady.countDown(); threadsReady.countDown();
try { try {
threadsReady.await(); threadsReady.await();
ctx.getResourceDefinition("patient"); RuntimeResourceDefinition def = ctx.getResourceDefinition("patient");
ourLog.info(def.toString());
assertNotNull(def);
} catch(final Exception e) { } catch(final Exception e) {
exceptions.add(e); exceptions.add(e);
} }