Merge branch 'master' into hapi3_refactor
This commit is contained in:
commit
11cab97504
|
@ -632,6 +632,9 @@
|
|||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkCount>1</forkCount>
|
||||
<excludes>
|
||||
<exclude>**/stresstest/*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -647,6 +650,9 @@
|
|||
<configuration>
|
||||
<forkCount>2</forkCount>
|
||||
<reuseForks>true</reuseForks>
|
||||
<excludes>
|
||||
<exclude>**/stresstest/*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import javax.transaction.Transactional.TxType;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -31,6 +34,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
@Transactional(value=TxType.REQUIRED)
|
||||
public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportDstu2.class);
|
||||
|
@ -52,11 +56,13 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
|
|||
private FhirContext myDstu2Ctx;
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public ValueSet fetchCodeSystem(FhirContext theCtx, String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
@ -95,11 +101,13 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,10 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.transaction.Transactional.TxType;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
|
@ -43,6 +42,7 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
@Transactional(value=TxType.REQUIRED)
|
||||
public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportDstu3.class);
|
||||
|
@ -72,6 +72,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
|
|||
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
@ -81,6 +82,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
|
|||
return fetchResource(theCtx, CodeSystem.class, theSystem);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
IdType id = new IdType(theUri);
|
||||
|
@ -143,11 +145,13 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
@ -160,6 +164,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
|
|||
|
||||
|
||||
@Override
|
||||
@Transactional(value=TxType.SUPPORTS)
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
|
||||
retVal.setUsername("");
|
||||
retVal.setPassword("");
|
||||
retVal.setMaxTotal(4);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config;
|
|||
import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3DispatcherConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3;
|
||||
import ca.uhn.fhir.jpa.interceptor.RestHookSubscriptionDstu3Interceptor;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -47,7 +48,7 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
|
||||
public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||
|
||||
private static JpaValidationSupportChainDstu3 myValidationSupport;
|
||||
protected static JpaValidationSupportChainDstu3 myValidationSupport;
|
||||
protected static IGenericClient ourClient;
|
||||
protected static CloseableHttpClient ourHttpClient;
|
||||
protected static int ourPort;
|
||||
|
@ -56,6 +57,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
protected static String ourServerBase;
|
||||
private static GenericWebApplicationContext ourWebApplicationContext;
|
||||
private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider;
|
||||
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
||||
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
||||
protected static RestHookSubscriptionDstu3Interceptor ourRestHookSubscriptionInterceptor;
|
||||
protected static ISearchDao mySearchEntityDao;
|
||||
|
@ -150,7 +152,9 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
||||
ourRestHookSubscriptionInterceptor = wac.getBean(RestHookSubscriptionDstu3Interceptor.class);
|
||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||
if (shouldLogClient()) {
|
||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||
|
@ -159,6 +163,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
builder.setMaxConnPerRoute(99);
|
||||
ourHttpClient = builder.build();
|
||||
|
||||
ourServer = server;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Note: Tests in this package are not executed by Travis, because travis
|
||||
just gets mad that the build is taking too long. So make sure to run them
|
||||
locally!
|
||||
|
||||
A normal build (mvn install) will run them.
|
|
@ -0,0 +1,171 @@
|
|||
package ca.uhn.fhir.jpa.stresstest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.junit.*;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class StressTestDstu3Test extends BaseResourceProviderDstu3Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StressTestDstu3Test.class);
|
||||
private RequestValidatingInterceptor myRequestValidatingInterceptor;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
|
||||
myRequestValidatingInterceptor = new RequestValidatingInterceptor();
|
||||
FhirInstanceValidator module = new FhirInstanceValidator();
|
||||
module.setValidationSupport(myValidationSupport);
|
||||
myRequestValidatingInterceptor.addValidatorModule(module);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
ourRestServer.unregisterInterceptor(myRequestValidatingInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test prevents a deadlock that was detected with a large number of
|
||||
* threads creating resources and blocking on the searchparamcache refreshing
|
||||
* (since this is a synchronized method) while the instance that was actually
|
||||
* executing was waiting on a DB connection. This was solved by making
|
||||
* JpaValidationSupportDstuXX be transactional, which it should have been
|
||||
* anyhow.
|
||||
*/
|
||||
@Test
|
||||
public void testMultithreadedSearchWithValidation() throws Exception {
|
||||
ourRestServer.registerInterceptor(myRequestValidatingInterceptor);
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.TRANSACTION);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("http://test").setValue("BAR");
|
||||
input.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
|
||||
}
|
||||
ourClient.transaction().withBundle(input).execute();
|
||||
|
||||
CloseableHttpResponse getMeta = ourHttpClient.execute(new HttpGet(ourServerBase + "/metadata"));
|
||||
try {
|
||||
assertEquals(200, getMeta.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(getMeta);
|
||||
}
|
||||
|
||||
List<BaseTask> tasks = Lists.newArrayList();
|
||||
try {
|
||||
for (int threadIndex = 0; threadIndex < 8; threadIndex++) {
|
||||
SearchTask task = new SearchTask();
|
||||
tasks.add(task);
|
||||
task.start();
|
||||
}
|
||||
for (int threadIndex = 0; threadIndex < 8; threadIndex++) {
|
||||
CreateTask task = new CreateTask();
|
||||
tasks.add(task);
|
||||
task.start();
|
||||
}
|
||||
} finally {
|
||||
for (BaseTask next : tasks) {
|
||||
next.join();
|
||||
}
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
for (BaseTask next : tasks) {
|
||||
if (next.getError() != null) {
|
||||
fail(next.getError().toString());
|
||||
}
|
||||
total += next.getTaskCount();
|
||||
}
|
||||
|
||||
ourLog.info("Loaded {} searches", total);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
public class BaseTask extends Thread {
|
||||
protected Throwable myError;
|
||||
protected int myTaskCount = 0;
|
||||
|
||||
public BaseTask() {
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
public int getTaskCount() {
|
||||
return myTaskCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class SearchTask extends BaseTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
CloseableHttpResponse get = null;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
try {
|
||||
get = ourHttpClient.execute(new HttpGet(ourServerBase + "/Patient?identifier=http%3A%2F%2Ftest%7CBAR," + UUID.randomUUID().toString()));
|
||||
try {
|
||||
assertEquals(200, get.getStatusLine().getStatusCode());
|
||||
myTaskCount++;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(get);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
ourLog.error("Failure during search", e);
|
||||
myError = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CreateTask extends BaseTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
try {
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("http://test").setValue("BAR").setType(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("bar")));
|
||||
p.setGender(org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender.MALE);
|
||||
ourClient.create().resource(p).execute();
|
||||
|
||||
ourSearchParamRegistry.forceRefresh();
|
||||
|
||||
} catch (Throwable e) {
|
||||
ourLog.error("Failure during search", e);
|
||||
myError = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
<body>
|
||||
<form action="" method="get" id="outerForm">
|
||||
<input type="hidden" id="serverId" name="serverId" th:value="${serverId}"></input>
|
||||
<input th:if="${_csrf} != null" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
|
||||
|
||||
<div th:replace="tmpl-navbar-top :: top" ></div>
|
||||
|
||||
|
|
|
@ -315,8 +315,8 @@
|
|||
<a th:if="${entry.resource} != null" th:href="${entry.resource.id}" th:text="${entry.resource.idElement.toUnqualified()}" style="font-size: 0.8em"/>
|
||||
</td>
|
||||
<td th:if="${entry.resource} == null or ${entry.resource.meta.lastUpdatedElement.value} == null"></td>
|
||||
<td th:if="${entry.resource.meta.lastUpdatedElement.value} != null and ${entry.resource.meta.lastUpdatedElement.today} == true" th:text="${#dates.format(entry.resource.meta.lastUpdated, 'HH:mm:ss')}"></td>
|
||||
<td th:if="${entry.resource.meta.lastUpdatedElement.value} != null and ${entry.resource.meta.lastUpdatedElement.today} == false" th:text="${#dates.format(entry.resource.meta.lastUpdated, 'yyyy-MM-dd HH:mm:ss')}"></td>
|
||||
<td th:if="${entry.resource} != null and ${entry.resource.meta.lastUpdatedElement.value} != null and ${entry.resource.meta.lastUpdatedElement.today} == true" th:text="${#dates.format(entry.resource.meta.lastUpdated, 'HH:mm:ss')}"></td>
|
||||
<td th:if="${entry.resource} != null and ${entry.resource.meta.lastUpdatedElement.value} != null and ${entry.resource.meta.lastUpdatedElement.today} == false" th:text="${#dates.format(entry.resource.meta.lastUpdated, 'yyyy-MM-dd HH:mm:ss')}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -134,6 +134,17 @@
|
|||
JpaConformanceProvider now has a configuration setting to enable and
|
||||
disable adding resource counts to the server metadata.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Avoid a deadlock in JPA server when the RequestValidatingInterceptor is being
|
||||
used and a large number of resources are being created by clients at
|
||||
the same time.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Testpage Overlay's transaction method did not work if the response
|
||||
Bundle contained any entries that did not contain a resource (which
|
||||
is often the case in more recent versions of HAPI). Thanks to Sujay R
|
||||
for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue