Start working on JPA subscriptions
This commit is contained in:
parent
8fee057de3
commit
04c2cce13f
|
@ -6,6 +6,9 @@ import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -159,4 +162,91 @@ public class UrlUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
/**
|
||||||
|
* Parse a URL in one of the following forms:
|
||||||
|
* <ul>
|
||||||
|
* <li>[Resource Type]?[Search Params]
|
||||||
|
* <li>[Resource Type]/[Resource ID]
|
||||||
|
* <li>[Resource Type]/[Resource ID]/_history/[Version ID]
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
//@formatter:on
|
||||||
|
public static UrlParts parseUrl(String theUrl) {
|
||||||
|
UrlParts retVal = new UrlParts();
|
||||||
|
|
||||||
|
int nextStart = 0;
|
||||||
|
boolean nextIsHistory = false;
|
||||||
|
|
||||||
|
for (int idx = 0; idx < theUrl.length(); idx++) {
|
||||||
|
char nextChar = theUrl.charAt(idx);
|
||||||
|
boolean atEnd = (idx + 1) == theUrl.length();
|
||||||
|
if (nextChar == '?' || nextChar == '/' || atEnd) {
|
||||||
|
int endIdx = atEnd ? idx + 1 : idx;
|
||||||
|
String nextSubstring = theUrl.substring(nextStart, endIdx);
|
||||||
|
if (retVal.getResourceType() == null) {
|
||||||
|
retVal.setResourceType(nextSubstring);
|
||||||
|
} else if (retVal.getResourceId() == null) {
|
||||||
|
retVal.setResourceId(nextSubstring);
|
||||||
|
} else if (nextIsHistory) {
|
||||||
|
retVal.setVersionId(nextSubstring);
|
||||||
|
} else {
|
||||||
|
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
|
||||||
|
nextIsHistory = true;
|
||||||
|
} else {
|
||||||
|
throw new InvalidRequestException("Invalid FHIR resource URL: " + theUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextChar == '?') {
|
||||||
|
if (theUrl.length() > idx + 1) {
|
||||||
|
retVal.setParams(theUrl.substring(idx + 1, theUrl.length()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextStart = idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UrlParts {
|
||||||
|
private String myParams;
|
||||||
|
private String myResourceId;
|
||||||
|
private String myResourceType;
|
||||||
|
private String myVersionId;
|
||||||
|
|
||||||
|
public String getParams() {
|
||||||
|
return myParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResourceId() {
|
||||||
|
return myResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResourceType() {
|
||||||
|
return myResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionId() {
|
||||||
|
return myVersionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParams(String theParams) {
|
||||||
|
myParams = theParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceId(String theResourceId) {
|
||||||
|
myResourceId = theResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceType(String theResourceType) {
|
||||||
|
myResourceType = theResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersionId(String theVersionId) {
|
||||||
|
myVersionId = theVersionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,72 +284,6 @@
|
||||||
<runOrder>alphabetical</runOrder>
|
<runOrder>alphabetical</runOrder>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>de.juplo</groupId>
|
|
||||||
<artifactId>hibernate4-maven-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<force>true</force>
|
|
||||||
<target>SCRIPT</target>
|
|
||||||
<skip>${skip-hib4}</skip>
|
|
||||||
</configuration>
|
|
||||||
<!--
|
|
||||||
This needs to be uncommented in order for this plugin to work with
|
|
||||||
Hibernate 4.3+ (as of hibernate4-maven-plugin version 1.0.5)
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.hibernate</groupId>
|
|
||||||
<artifactId>hibernate-core</artifactId>
|
|
||||||
<version>${hibernate_version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
-->
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>o10g</id>
|
|
||||||
<goals>
|
|
||||||
<goal>export</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<hibernateDialect>org.hibernate.dialect.Oracle10gDialect</hibernateDialect>
|
|
||||||
<outputFile>${project.build.directory}/schema_oracle_10g.sql</outputFile>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>derby</id>
|
|
||||||
<goals>
|
|
||||||
<goal>export</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<hibernateDialect>org.hibernate.dialect.DerbyTenSevenDialect</hibernateDialect>
|
|
||||||
<outputFile>${project.build.directory}/schema_derby.sql</outputFile>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>hsql</id>
|
|
||||||
<goals>
|
|
||||||
<goal>export</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<hibernateDialect>org.hibernate.dialect.HSQLDialect</hibernateDialect>
|
|
||||||
<outputFile>${project.build.directory}/schema_hsql.sql</outputFile>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>mysql5</id>
|
|
||||||
<goals>
|
|
||||||
<goal>export</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
|
|
||||||
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-tinder-plugin</artifactId>
|
<artifactId>hapi-tinder-plugin</artifactId>
|
||||||
|
@ -427,6 +361,79 @@
|
||||||
<skip-hib4>true</skip-hib4>
|
<skip-hib4>true</skip-hib4>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>DIST</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>de.juplo</groupId>
|
||||||
|
<artifactId>hibernate4-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<force>true</force>
|
||||||
|
<target>SCRIPT</target>
|
||||||
|
<skip>${skip-hib4}</skip>
|
||||||
|
</configuration>
|
||||||
|
<!--
|
||||||
|
This needs to be uncommented in order for this plugin to work with
|
||||||
|
Hibernate 4.3+ (as of hibernate4-maven-plugin version 1.0.5)
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate</groupId>
|
||||||
|
<artifactId>hibernate-core</artifactId>
|
||||||
|
<version>${hibernate_version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
-->
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>o10g</id>
|
||||||
|
<goals>
|
||||||
|
<goal>export</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<hibernateDialect>org.hibernate.dialect.Oracle10gDialect</hibernateDialect>
|
||||||
|
<outputFile>${project.build.directory}/schema_oracle_10g.sql</outputFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>derby</id>
|
||||||
|
<goals>
|
||||||
|
<goal>export</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<hibernateDialect>org.hibernate.dialect.DerbyTenSevenDialect</hibernateDialect>
|
||||||
|
<outputFile>${project.build.directory}/schema_derby.sql</outputFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>hsql</id>
|
||||||
|
<goals>
|
||||||
|
<goal>export</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<hibernateDialect>org.hibernate.dialect.HSQLDialect</hibernateDialect>
|
||||||
|
<outputFile>${project.build.directory}/schema_hsql.sql</outputFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>mysql5</id>
|
||||||
|
<goals>
|
||||||
|
<goal>export</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
|
||||||
|
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -87,6 +87,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource;
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
|
@ -520,7 +521,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
IResource resource = (IResource) toResource(type.getImplementingClass(), next);
|
IResource resource = (IResource) toResource(type.getImplementingClass(), next, true);
|
||||||
retVal.add(resource);
|
retVal.add(resource);
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -558,12 +559,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
||||||
|
|
||||||
if (theEntity.getPublished().isEmpty()) {
|
|
||||||
theEntity.setPublished(new Date());
|
|
||||||
}
|
|
||||||
theEntity.setUpdated(new Date());
|
|
||||||
|
|
||||||
theEntity.setResourceType(toResourceName(theResource));
|
theEntity.setResourceType(toResourceName(theResource));
|
||||||
|
|
||||||
List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class);
|
List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class);
|
||||||
|
@ -936,13 +931,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IBaseResource toResource(BaseHasResource theEntity) {
|
protected IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||||
return toResource(type.getImplementingClass(), theEntity);
|
return toResource(type.getImplementingClass(), theEntity, theForHistoryOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity) {
|
protected <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||||
String resourceText = null;
|
String resourceText = null;
|
||||||
switch (theEntity.getEncoding()) {
|
switch (theEntity.getEncoding()) {
|
||||||
case JSON:
|
case JSON:
|
||||||
|
@ -983,7 +978,21 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
||||||
retVal = (R) res;
|
retVal = (R) res;
|
||||||
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
|
if (theForHistoryOperation) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
|
||||||
|
}
|
||||||
|
} else if (theForHistoryOperation) {
|
||||||
|
/*
|
||||||
|
* If the create and update times match, this was when the resource was created
|
||||||
|
* so we should mark it as a POST. Otherwise, it's a PUT.
|
||||||
|
*/
|
||||||
|
Date published = theEntity.getPublished().getValue();
|
||||||
|
Date updated = theEntity.getUpdated().getValue();
|
||||||
|
if (published.equals(updated)) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
|
||||||
|
} else {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setId(theEntity.getIdDt());
|
res.setId(theEntity.getIdDt());
|
||||||
|
@ -1063,25 +1072,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull) {
|
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, Date theUpdateTime) {
|
||||||
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true);
|
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true, theUpdateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion) {
|
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
|
||||||
|
|
||||||
if (theEntity.getPublished() == null) {
|
|
||||||
theEntity.setPublished(new Date());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This should be the very first thing..
|
||||||
|
*/
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
validateResourceForStorage((T) theResource);
|
validateResourceForStorage((T) theResource, theEntity);
|
||||||
String resourceType = myContext.getResourceDefinition(theResource).getName();
|
String resourceType = myContext.getResourceDefinition(theResource).getName();
|
||||||
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
|
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
|
||||||
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theEntity.getPublished() == null) {
|
||||||
|
theEntity.setPublished(theUpdateTime);
|
||||||
|
}
|
||||||
|
|
||||||
if (theUpdateHistory) {
|
if (theUpdateHistory) {
|
||||||
final ResourceHistoryTable historyEntry = theEntity.toHistory();
|
final ResourceHistoryTable historyEntry = theEntity.toHistory();
|
||||||
myEntityManager.persist(historyEntry);
|
myEntityManager.persist(historyEntry);
|
||||||
|
@ -1159,7 +1171,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
links = extractResourceLinks(theEntity, theResource);
|
links = extractResourceLinks(theEntity, theResource);
|
||||||
populateResourceIntoEntity(theResource, theEntity);
|
populateResourceIntoEntity(theResource, theEntity);
|
||||||
|
|
||||||
theEntity.setUpdated(new Date());
|
theEntity.setUpdated(theUpdateTime);
|
||||||
theEntity.setLanguage(theResource.getLanguage().getValue());
|
theEntity.setLanguage(theResource.getLanguage().getValue());
|
||||||
theEntity.setParamsString(stringParams);
|
theEntity.setParamsString(stringParams);
|
||||||
theEntity.setParamsStringPopulated(stringParams.isEmpty() == false);
|
theEntity.setParamsStringPopulated(stringParams.isEmpty() == false);
|
||||||
|
@ -1182,7 +1194,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
populateResourceIntoEntity(theResource, theEntity);
|
populateResourceIntoEntity(theResource, theEntity);
|
||||||
theEntity.setUpdated(new Date());
|
theEntity.setUpdated(theUpdateTime);
|
||||||
theEntity.setLanguage(theResource.getLanguage().getValue());
|
theEntity.setLanguage(theResource.getLanguage().getValue());
|
||||||
theEntity.setIndexStatus(null);
|
theEntity.setIndexStatus(null);
|
||||||
|
|
||||||
|
@ -1197,8 +1209,24 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
myEntityManager.persist(theEntity.getForcedId());
|
myEntityManager.persist(theEntity.getForcedId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postPersist(theEntity, (T) theResource);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
theEntity = myEntityManager.merge(theEntity);
|
theEntity = myEntityManager.merge(theEntity);
|
||||||
|
|
||||||
|
postUpdate(theEntity, (T) theResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When subscription is enabled, for each resource we store we also
|
||||||
|
* store a subscription candidate. These are examined by the subscription
|
||||||
|
* module and then deleted.
|
||||||
|
*/
|
||||||
|
if (myConfig.isSubscriptionEnabled() && thePerformIndexing) {
|
||||||
|
SubscriptionCandidateResource candidate = new SubscriptionCandidateResource();
|
||||||
|
candidate.setResource(theEntity);
|
||||||
|
candidate.setResourceVersion(theEntity.getVersion());
|
||||||
|
myEntityManager.persist(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thePerformIndexing) {
|
if (thePerformIndexing) {
|
||||||
|
@ -1289,6 +1317,30 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return theEntity;
|
return theEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||||
|
* first time.
|
||||||
|
*
|
||||||
|
* @param theEntity
|
||||||
|
* The resource
|
||||||
|
* @param theResource The resource being persisted
|
||||||
|
*/
|
||||||
|
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||||
|
* first time.
|
||||||
|
*
|
||||||
|
* @param theEntity
|
||||||
|
* The resource
|
||||||
|
* @param theResource The resource being persisted
|
||||||
|
*/
|
||||||
|
protected void postPersist(ResourceTable theEntity, T theResource) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow
|
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow
|
||||||
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects
|
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects
|
||||||
|
@ -1296,8 +1348,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
*
|
*
|
||||||
* @param theResource
|
* @param theResource
|
||||||
* The resource that is about to be persisted
|
* The resource that is about to be persisted
|
||||||
|
* @param theEntityToSave TODO
|
||||||
*/
|
*/
|
||||||
protected void validateResourceForStorage(T theResource) {
|
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||||
IResource res = (IResource) theResource;
|
IResource res = (IResource) theResource;
|
||||||
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
|
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
|
||||||
if (tagList != null) {
|
if (tagList != null) {
|
||||||
|
|
|
@ -142,7 +142,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
|
||||||
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
private EntityManager myEntityManager;
|
protected EntityManager myEntityManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformTransactionManager myPlatformTransactionManager;
|
private PlatformTransactionManager myPlatformTransactionManager;
|
||||||
|
@ -1004,7 +1004,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return doCreate(theResource, theIfNoneExist, thePerformIndexing);
|
return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
|
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
|
||||||
|
@ -1268,7 +1268,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
|
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
|
||||||
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
||||||
|
|
||||||
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
|
Date updateTime = new Date();
|
||||||
|
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime);
|
||||||
|
|
||||||
notifyWriteCompleted();
|
notifyWriteCompleted();
|
||||||
|
|
||||||
|
@ -1298,14 +1299,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
||||||
|
|
||||||
// Perform delete
|
// Perform delete
|
||||||
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
|
Date updateTime = new Date();
|
||||||
|
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime);
|
||||||
notifyWriteCompleted();
|
notifyWriteCompleted();
|
||||||
|
|
||||||
ourLog.info("Processed delete on {} in {}ms", theUrl, w.getMillisAndRestart());
|
ourLog.info("Processed delete on {} in {}ms", theUrl, w.getMillisAndRestart());
|
||||||
return toMethodOutcome(savedEntity, null);
|
return toMethodOutcome(savedEntity, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) {
|
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime) {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
|
||||||
preProcessResourceForStorage(theResource);
|
preProcessResourceForStorage(theResource);
|
||||||
|
@ -1346,7 +1348,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource), theResource);
|
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource), theResource);
|
||||||
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
|
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
|
||||||
|
|
||||||
updateEntity(theResource, entity, false, null, thePerformIndexing, true);
|
updateEntity(theResource, entity, false, null, thePerformIndexing, true, theUpdateTime);
|
||||||
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
||||||
|
|
||||||
|
@ -1419,7 +1421,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
try {
|
try {
|
||||||
BaseHasResource entity = readEntity(theId.toVersionless(), false);
|
BaseHasResource entity = readEntity(theId.toVersionless(), false);
|
||||||
validateResourceType(entity);
|
validateResourceType(entity);
|
||||||
currentTmp = toResource(myResourceType, entity);
|
currentTmp = toResource(myResourceType, entity, true);
|
||||||
if (ResourceMetadataKeyEnum.UPDATED.get(currentTmp).after(end.getValue())) {
|
if (ResourceMetadataKeyEnum.UPDATED.get(currentTmp).after(end.getValue())) {
|
||||||
currentTmp = null;
|
currentTmp = null;
|
||||||
}
|
}
|
||||||
|
@ -1496,7 +1498,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
if (retVal.size() == maxResults) {
|
if (retVal.size() == maxResults) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
retVal.add(toResource(myResourceType, next));
|
retVal.add(toResource(myResourceType, next, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -1527,7 +1529,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids) {
|
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation) {
|
||||||
if (theIncludePids.isEmpty()) {
|
if (theIncludePids.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1546,7 +1548,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
|
|
||||||
for (ResourceTable next : q.getResultList()) {
|
for (ResourceTable next : q.getResultList()) {
|
||||||
Class<? extends IBaseResource> resourceType = getContext().getResourceDefinition(next.getResourceType()).getImplementingClass();
|
Class<? extends IBaseResource> resourceType = getContext().getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||||
IResource resource = (IResource) toResource(resourceType, next);
|
IResource resource = (IResource) toResource(resourceType, next, theForHistoryOperation);
|
||||||
Integer index = position.get(next.getId());
|
Integer index = position.get(next.getId());
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
||||||
|
@ -1827,7 +1829,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
BaseHasResource entity = readEntity(theId);
|
BaseHasResource entity = readEntity(theId);
|
||||||
validateResourceType(entity);
|
validateResourceType(entity);
|
||||||
|
|
||||||
T retVal = toResource(myResourceType, entity);
|
T retVal = toResource(myResourceType, entity, false);
|
||||||
|
|
||||||
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
|
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
|
||||||
if (deleted != null && !deleted.isEmpty()) {
|
if (deleted != null && !deleted.isEmpty()) {
|
||||||
|
@ -2065,7 +2067,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
|
|
||||||
// Execute the query and make sure we return distinct results
|
// Execute the query and make sure we return distinct results
|
||||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||||
loadResourcesByPid(pidsSubList, retVal, revIncludedPids);
|
loadResourcesByPid(pidsSubList, retVal, revIncludedPids, false);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -2381,7 +2383,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
if (resourceId.isIdPartValidLong()) {
|
if (resourceId.isIdPartValidLong()) {
|
||||||
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
|
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
|
||||||
}
|
}
|
||||||
return doCreate(theResource, null, thePerformIndexing);
|
return doCreate(theResource, null, thePerformIndexing, new Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2398,7 +2400,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
||||||
notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
|
notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
|
||||||
|
|
||||||
// Perform update
|
// Perform update
|
||||||
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true);
|
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true, new Date());
|
||||||
|
|
||||||
notifyWriteCompleted();
|
notifyWriteCompleted();
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao<IBaseReso
|
||||||
for (ResourceTable resourceTable : resources) {
|
for (ResourceTable resourceTable : resources) {
|
||||||
final IBaseResource resource;
|
final IBaseResource resource;
|
||||||
try {
|
try {
|
||||||
resource = toResource(resourceTable);
|
resource = toResource(resourceTable, false);
|
||||||
} catch (DataFormatException e) {
|
} catch (DataFormatException e) {
|
||||||
ourLog.warn("Failure parsing resource: {}", e.toString());
|
ourLog.warn("Failure parsing resource: {}", e.toString());
|
||||||
throw new UnprocessableEntityException(Long.toString(resourceTable.getId()));
|
throw new UnprocessableEntityException(Long.toString(resourceTable.getId()));
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class DaoConfig {
|
||||||
private int myIncludeLimit = 2000;
|
private int myIncludeLimit = 2000;
|
||||||
private List<IServerInterceptor> myInterceptors;
|
private List<IServerInterceptor> myInterceptors;
|
||||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||||
|
private boolean mySubscriptionEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link #setIncludeLimit(int)}
|
* See {@link #setIncludeLimit(int)}
|
||||||
|
@ -64,6 +65,13 @@ public class DaoConfig {
|
||||||
return myResourceEncoding;
|
return myResourceEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See {@link #setSubscriptionEnabled(boolean)}
|
||||||
|
*/
|
||||||
|
public boolean isSubscriptionEnabled() {
|
||||||
|
return mySubscriptionEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHardSearchLimit(int theHardSearchLimit) {
|
public void setHardSearchLimit(int theHardSearchLimit) {
|
||||||
myHardSearchLimit = theHardSearchLimit;
|
myHardSearchLimit = theHardSearchLimit;
|
||||||
}
|
}
|
||||||
|
@ -90,12 +98,12 @@ public class DaoConfig {
|
||||||
* ID).
|
* ID).
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
||||||
myInterceptors = theInterceptors;
|
if (theInterceptor == null || theInterceptor.length==0){
|
||||||
}
|
setInterceptors(new ArrayList<IServerInterceptor>());
|
||||||
|
} else {
|
||||||
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
|
setInterceptors(Arrays.asList(theInterceptor));
|
||||||
myResourceEncoding = theResourceEncoding;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,12 +115,23 @@ public class DaoConfig {
|
||||||
* ID).
|
* ID).
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
||||||
if (theInterceptor == null || theInterceptor.length==0){
|
myInterceptors = theInterceptors;
|
||||||
setInterceptors(new ArrayList<IServerInterceptor>());
|
}
|
||||||
} else {
|
|
||||||
setInterceptors(Arrays.asList(theInterceptor));
|
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
|
||||||
}
|
myResourceEncoding = theResourceEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this server support subscription? If set to true, the server
|
||||||
|
* will enable the subscription monitoring mode, which adds a bit of
|
||||||
|
* overhead. Note that if this is enabled, you must also include
|
||||||
|
* Spring task scanning to your XML config for the scheduled tasks
|
||||||
|
* used by the subscription module.
|
||||||
|
*/
|
||||||
|
public void setSubscriptionEnabled(boolean theSubscriptionEnabled) {
|
||||||
|
mySubscriptionEnabled = theSubscriptionEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
|
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
|
||||||
|
@ -58,8 +59,8 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void validateResourceForStorage(QuestionnaireResponse theResource) {
|
protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
if (!myValidateResponses) {
|
if (!myValidateResponses) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Subscription;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||||
|
|
||||||
|
public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subscription>implements IFhirResourceDaoSubscription<Subscription> {
|
||||||
|
|
||||||
|
private static final ResourceMetadataKeyEnum<Object> ALLOW_STATUS_CHANGE = new ResourceMetadataKeyEnum<Object>(FhirResourceDaoSubscriptionDstu2.class.getName() + "_ALLOW_STATUS_CHANGE") {
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(IResource theResource) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(IResource theResource, Object theObject) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSubscriptionDstu2.class);
|
||||||
|
|
||||||
|
private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
|
||||||
|
SubscriptionTable subscriptionEntity = new SubscriptionTable();
|
||||||
|
subscriptionEntity.setSubscriptionResource(theEntity);
|
||||||
|
subscriptionEntity.setNextCheck(theEntity.getPublished().getValue());
|
||||||
|
subscriptionEntity.setNextCheckSince(theEntity.getPublished().getValue());
|
||||||
|
subscriptionEntity.setStatus(theSubscription.getStatusElement().getValueAsEnum());
|
||||||
|
myEntityManager.persist(subscriptionEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SubscriptionTable getSubscriptionByResourceId(long theSubscriptionResourceId) {
|
||||||
|
TypedQuery<SubscriptionTable> q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_GET_BY_RES", SubscriptionTable.class);
|
||||||
|
q.setParameter("res_id", theSubscriptionResourceId);
|
||||||
|
return q.getSingleResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
|
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||||
|
@Override
|
||||||
|
public void pollForNewUndeliveredResources() {
|
||||||
|
if (getConfig().isSubscriptionEnabled() == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ourLog.trace("Beginning pollForNewUndeliveredResources()");
|
||||||
|
|
||||||
|
TypedQuery<SubscriptionTable> q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_NEXT_CHECK", SubscriptionTable.class);
|
||||||
|
q.setParameter("next_check", new Date());
|
||||||
|
q.setParameter("status", SubscriptionStatusEnum.ACTIVE);
|
||||||
|
List<SubscriptionTable> subscriptions = q.getResultList();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
|
||||||
|
super.postPersist(theEntity, theSubscription);
|
||||||
|
|
||||||
|
createSubscriptionTable(theEntity, theSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSubscriptionStatus(Long theResourceId, SubscriptionStatusEnum theStatus) {
|
||||||
|
Validate.notNull(theResourceId);
|
||||||
|
Validate.notNull(theStatus);
|
||||||
|
|
||||||
|
ResourceTable existing = readEntityLatestVersion(new IdDt("Subscription", theResourceId));
|
||||||
|
Subscription existingRes = toResource(Subscription.class, existing, false);
|
||||||
|
|
||||||
|
existingRes.getResourceMetadata().put(ALLOW_STATUS_CHANGE, new Object());
|
||||||
|
existingRes.setStatus(theStatus);
|
||||||
|
|
||||||
|
update(existingRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
|
||||||
|
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
|
||||||
|
|
||||||
|
Subscription resource = (Subscription) theResource;
|
||||||
|
Long resourceId = theEntity.getId();
|
||||||
|
if (theDeletedTimestampOrNull != null) {
|
||||||
|
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_DELETE");
|
||||||
|
q.setParameter("res_id", resourceId);
|
||||||
|
q.executeUpdate();
|
||||||
|
} else {
|
||||||
|
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_SET_STATUS");
|
||||||
|
q.setParameter("res_id", resourceId);
|
||||||
|
q.setParameter("status", resource.getStatusElement().getValueAsEnum());
|
||||||
|
if (q.executeUpdate() > 0) {
|
||||||
|
ourLog.info("Updated subscription status for subscription {} to {}", resourceId, resource.getStatusElement().getValueAsEnum());
|
||||||
|
} else {
|
||||||
|
createSubscriptionTable(retVal, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
|
||||||
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
|
String query = theResource.getCriteria();
|
||||||
|
if (isBlank(query)) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.criteria must be populated");
|
||||||
|
}
|
||||||
|
|
||||||
|
int sep = query.indexOf('?');
|
||||||
|
if (sep <= 1) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
String resType = query.substring(0, sep);
|
||||||
|
if (resType.contains("/")) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeResourceDefinition resDef;
|
||||||
|
try {
|
||||||
|
resDef = getContext().getResourceDefinition(resType);
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
|
||||||
|
if (dao == null) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchParameterMap parsedUrl = translateMatchUrl(query, resDef);
|
||||||
|
|
||||||
|
if (theResource.getChannel().getType() == null) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionStatusEnum status = theResource.getStatusElement().getValueAsEnum();
|
||||||
|
Subscription existing = theEntityToSave.getEncoding() != null ? toResource(Subscription.class, theEntityToSave, false) : null;
|
||||||
|
if (status == null) {
|
||||||
|
// if (existing != null) {
|
||||||
|
// status = existing.getStatusElement().getValueAsEnum();
|
||||||
|
// theResource.setStatus(status);
|
||||||
|
// } else {
|
||||||
|
status = SubscriptionStatusEnum.REQUESTED;
|
||||||
|
theResource.setStatus(status);
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
SubscriptionStatusEnum existingStatus = existing.getStatusElement().getValueAsEnum();
|
||||||
|
if (existingStatus != status) {
|
||||||
|
if (!theResource.getResourceMetadata().containsKey(ALLOW_STATUS_CHANGE)) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.status can not be changed from " + existingStatus + " to " + status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theEntityToSave.getId() == null) {
|
||||||
|
if (status != SubscriptionStatusEnum.REQUESTED) {
|
||||||
|
throw new UnprocessableEntityException("Subscription.status must be " + SubscriptionStatusEnum.REQUESTED.getCode() + " on a newly created subscription");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
|
||||||
if (sourceEntity == null) {
|
if (sourceEntity == null) {
|
||||||
throw new ResourceNotFoundException(theId);
|
throw new ResourceNotFoundException(theId);
|
||||||
}
|
}
|
||||||
ValueSet source = (ValueSet) toResource(sourceEntity);
|
ValueSet source = (ValueSet) toResource(sourceEntity, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add composed concepts
|
* Add composed concepts
|
||||||
|
|
|
@ -104,6 +104,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
|
||||||
OperationOutcome oo = new OperationOutcome();
|
OperationOutcome oo = new OperationOutcome();
|
||||||
retVal.add(oo);
|
retVal.add(oo);
|
||||||
|
|
||||||
|
Date updateTime = new Date();
|
||||||
for (int resourceIdx = 0; resourceIdx < theResources.size(); resourceIdx++) {
|
for (int resourceIdx = 0; resourceIdx < theResources.size(); resourceIdx++) {
|
||||||
IResource nextResource = theResources.get(resourceIdx);
|
IResource nextResource = theResources.get(resourceIdx);
|
||||||
|
|
||||||
|
@ -160,6 +161,8 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
nextResouceOperationOut = BundleEntryTransactionMethodEnum.POST;
|
nextResouceOperationOut = BundleEntryTransactionMethodEnum.POST;
|
||||||
entity = toEntity(nextResource);
|
entity = toEntity(nextResource);
|
||||||
|
entity.setUpdated(updateTime);
|
||||||
|
entity.setPublished(updateTime);
|
||||||
if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) {
|
if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) {
|
||||||
ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
|
ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
|
||||||
} else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) {
|
} else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) {
|
||||||
|
@ -170,7 +173,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
|
||||||
if (candidateMatches.size() == 1) {
|
if (candidateMatches.size() == 1) {
|
||||||
ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
|
ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
|
||||||
BaseHasResource existingEntity = loadFirstEntityFromCandidateMatches(candidateMatches);
|
BaseHasResource existingEntity = loadFirstEntityFromCandidateMatches(candidateMatches);
|
||||||
IResource existing = (IResource) toResource(existingEntity);
|
IResource existing = (IResource) toResource(existingEntity, false);
|
||||||
persistedResources.add(null);
|
persistedResources.add(null);
|
||||||
retVal.add(existing);
|
retVal.add(existing);
|
||||||
continue;
|
continue;
|
||||||
|
@ -262,11 +265,11 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
|
||||||
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource);
|
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource);
|
||||||
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
|
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
|
||||||
if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(resource) == BundleEntryTransactionMethodEnum.DELETE) {
|
if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(resource) == BundleEntryTransactionMethodEnum.DELETE) {
|
||||||
deletedTimestampOrNull = new Date();
|
deletedTimestampOrNull = updateTime;
|
||||||
ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull));
|
ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull);
|
updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull, updateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
long delay = System.currentTimeMillis() - start;
|
long delay = System.currentTimeMillis() - start;
|
||||||
|
|
|
@ -19,7 +19,9 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -64,6 +66,8 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||||
|
|
||||||
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class);
|
||||||
|
@ -92,8 +96,8 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
resp.addEntry().setResource(ooResp);
|
resp.addEntry().setResource(ooResp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For batch, we handle each entry as a mini-transaction in its own
|
* For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it
|
||||||
* database transaction so that if one fails, it doesn't prevent others
|
* doesn't prevent others
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (final Entry nextRequestEntry : theRequest.getEntry()) {
|
for (final Entry nextRequestEntry : theRequest.getEntry()) {
|
||||||
|
@ -118,8 +122,8 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
Entry subResponseEntry = nextResponseBundle.getEntry().get(0);
|
Entry subResponseEntry = nextResponseBundle.getEntry().get(0);
|
||||||
resp.addEntry(subResponseEntry);
|
resp.addEntry(subResponseEntry);
|
||||||
/*
|
/*
|
||||||
* If the individual entry didn't have a resource in its response, bring the
|
* If the individual entry didn't have a resource in its response, bring the sub-transaction's
|
||||||
* sub-transaction's OperationOutcome across so the client can see it
|
* OperationOutcome across so the client can see it
|
||||||
*/
|
*/
|
||||||
if (subResponseEntry.getResource() == null) {
|
if (subResponseEntry.getResource() == null) {
|
||||||
subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
|
subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
|
||||||
|
@ -167,75 +171,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UrlParts parseUrl(String theAction, String theUrl) {
|
|
||||||
UrlParts retVal = new UrlParts();
|
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
/*
|
|
||||||
* We assume that the URL passed in is in one of the following forms:
|
|
||||||
* [Resource Type]?[Search Params]
|
|
||||||
* [Resource Type]/[Resource ID]
|
|
||||||
* [Resource Type]/[Resource ID]/_history/[Version ID]
|
|
||||||
*/
|
|
||||||
//@formatter:on
|
|
||||||
int nextStart = 0;
|
|
||||||
boolean nextIsHistory = false;
|
|
||||||
|
|
||||||
for (int idx = 0; idx < theUrl.length(); idx++) {
|
|
||||||
char nextChar = theUrl.charAt(idx);
|
|
||||||
boolean atEnd = (idx + 1) == theUrl.length();
|
|
||||||
if (nextChar == '?' || nextChar == '/' || atEnd) {
|
|
||||||
int endIdx = atEnd ? idx + 1 : idx;
|
|
||||||
String nextSubstring = theUrl.substring(nextStart, endIdx);
|
|
||||||
if (retVal.getResourceType() == null) {
|
|
||||||
retVal.setResourceType(nextSubstring);
|
|
||||||
} else if (retVal.getResourceId() == null) {
|
|
||||||
retVal.setResourceId(nextSubstring);
|
|
||||||
} else if (nextIsHistory) {
|
|
||||||
retVal.setVersionId(nextSubstring);
|
|
||||||
} else {
|
|
||||||
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
|
|
||||||
nextIsHistory = true;
|
|
||||||
} else {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nextChar == '?') {
|
|
||||||
if (theUrl.length() > idx + 1) {
|
|
||||||
retVal.setParams(theUrl.substring(idx + 1, theUrl.length()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nextStart = idx + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeResourceDefinition resType;
|
|
||||||
try {
|
|
||||||
resType = getContext().getResourceDefinition(retVal.getResourceType());
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
IFhirResourceDao<? extends IBaseResource> dao = null;
|
|
||||||
if (resType != null) {
|
|
||||||
dao = getDao(resType.getImplementingClass());
|
|
||||||
}
|
|
||||||
if (dao == null) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
retVal.setDao(dao);
|
|
||||||
|
|
||||||
if (retVal.getResourceId() == null && retVal.getParams() == null) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
@Override
|
@Override
|
||||||
public Bundle transaction(Bundle theRequest) {
|
public Bundle transaction(Bundle theRequest) {
|
||||||
|
@ -265,6 +200,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
|
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
Date updateTime = new Date();
|
||||||
|
|
||||||
Set<IdDt> allIds = new LinkedHashSet<IdDt>();
|
Set<IdDt> allIds = new LinkedHashSet<IdDt>();
|
||||||
Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>();
|
Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>();
|
||||||
|
@ -330,11 +266,12 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
// DELETE
|
// DELETE
|
||||||
Entry newEntry = response.addEntry();
|
Entry newEntry = response.addEntry();
|
||||||
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
||||||
UrlParts parts = parseUrl(verb.getCode(), url);
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
|
ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> dao = toDao(parts, verb.getCode(), url);
|
||||||
if (parts.getResourceId() != null) {
|
if (parts.getResourceId() != null) {
|
||||||
parts.getDao().delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
||||||
} else {
|
} else {
|
||||||
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
|
dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_204_NO_CONTENT));
|
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_204_NO_CONTENT));
|
||||||
|
@ -350,7 +287,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
|
|
||||||
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
||||||
|
|
||||||
UrlParts parts = parseUrl(verb.getCode(), url);
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
if (isNotBlank(parts.getResourceId())) {
|
if (isNotBlank(parts.getResourceId())) {
|
||||||
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
||||||
outcome = resourceDao.update(res, null, false);
|
outcome = resourceDao.update(res, null, false);
|
||||||
|
@ -365,10 +302,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
case GET: {
|
case GET: {
|
||||||
// SEARCH/READ/VREAD
|
// SEARCH/READ/VREAD
|
||||||
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
||||||
UrlParts parts = parseUrl(verb.getCode(), url);
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
IFhirResourceDao resourceDao = parts.getDao();
|
IFhirResourceDao dao = toDao(parts, verb.getCode(), url);
|
||||||
|
|
||||||
String ifNoneMatch = nextEntry.getRequest().getIfNoneMatch();
|
String ifNoneMatch = nextEntry.getRequest().getIfNoneMatch();
|
||||||
if (isNotBlank(ifNoneMatch)) {
|
if (isNotBlank(ifNoneMatch)) {
|
||||||
|
@ -382,9 +319,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
if (isNotBlank(ifNoneMatch)) {
|
if (isNotBlank(ifNoneMatch)) {
|
||||||
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
|
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
|
||||||
}
|
}
|
||||||
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
|
found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
|
||||||
} else {
|
} else {
|
||||||
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
||||||
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
|
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
|
||||||
notChanged = true;
|
notChanged = true;
|
||||||
}
|
}
|
||||||
|
@ -402,9 +339,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
||||||
}
|
}
|
||||||
} else if (parts.getParams() != null) {
|
} else if (parts.getParams() != null) {
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType());
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(dao.getResourceType());
|
||||||
SearchParameterMap params = translateMatchUrl(url, def);
|
SearchParameterMap params = translateMatchUrl(url, def);
|
||||||
IBundleProvider bundle = parts.getDao().search(params);
|
IBundleProvider bundle = dao.search(params);
|
||||||
|
|
||||||
Bundle searchBundle = new Bundle();
|
Bundle searchBundle = new Bundle();
|
||||||
searchBundle.setTotal(bundle.size());
|
searchBundle.setTotal(bundle.size());
|
||||||
|
@ -453,7 +390,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
|
|
||||||
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
|
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
|
||||||
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
|
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
|
||||||
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false);
|
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false, updateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
myEntityManager.flush();
|
myEntityManager.flush();
|
||||||
|
@ -468,8 +405,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass());
|
IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass());
|
||||||
Set<Long> val = resourceDao.processMatchUrl(matchUrl);
|
Set<Long> val = resourceDao.processMatchUrl(matchUrl);
|
||||||
if (val.size() > 1) {
|
if (val.size() > 1) {
|
||||||
throw new InvalidRequestException(
|
throw new InvalidRequestException("Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
||||||
"Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,6 +431,31 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
|
||||||
|
RuntimeResourceDefinition resType;
|
||||||
|
try {
|
||||||
|
resType = getContext().getResourceDefinition(theParts.getResourceType());
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
IFhirResourceDao<? extends IBaseResource> dao = null;
|
||||||
|
if (resType != null) {
|
||||||
|
dao = getDao(resType.getImplementingClass());
|
||||||
|
}
|
||||||
|
if (dao == null) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theParts.getResourceId() == null && theParts.getParams() == null) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dao;
|
||||||
|
}
|
||||||
|
|
||||||
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IResource> theClass) {
|
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IResource> theClass) {
|
||||||
IFhirResourceDao<? extends IResource> retVal = getDao(theClass);
|
IFhirResourceDao<? extends IResource> retVal = getDao(theClass);
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
|
@ -503,15 +464,15 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome,
|
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, Entry newEntry, String theResourceType, IResource theRes) {
|
||||||
Entry newEntry, String theResourceType, IResource theRes) {
|
|
||||||
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
||||||
IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
|
IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
|
||||||
if (newId.equals(resourceId) == false) {
|
if (newId.equals(resourceId) == false) {
|
||||||
idSubstitutions.put(resourceId, newId);
|
idSubstitutions.put(resourceId, newId);
|
||||||
if (isPlaceholder(resourceId)) {
|
if (isPlaceholder(resourceId)) {
|
||||||
/*
|
/*
|
||||||
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified kind too just to be lenient.
|
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified
|
||||||
|
* kind too just to be lenient.
|
||||||
*/
|
*/
|
||||||
idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId);
|
idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId);
|
||||||
}
|
}
|
||||||
|
@ -538,52 +499,4 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||||
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
|
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class UrlParts {
|
|
||||||
private IFhirResourceDao<? extends IBaseResource> myDao;
|
|
||||||
private String myParams;
|
|
||||||
private String myResourceId;
|
|
||||||
private String myResourceType;
|
|
||||||
private String myVersionId;
|
|
||||||
|
|
||||||
public IFhirResourceDao<? extends IBaseResource> getDao() {
|
|
||||||
return myDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParams() {
|
|
||||||
return myParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getResourceId() {
|
|
||||||
return myResourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getResourceType() {
|
|
||||||
return myResourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVersionId() {
|
|
||||||
return myVersionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDao(IFhirResourceDao<? extends IBaseResource> theDao) {
|
|
||||||
myDao = theDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParams(String theParams) {
|
|
||||||
myParams = theParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceId(String theResourceId) {
|
|
||||||
myResourceId = theResourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceType(String theResourceType) {
|
|
||||||
myResourceType = theResourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersionId(String theVersionId) {
|
|
||||||
myVersionId = theVersionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* 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 org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
|
||||||
|
|
||||||
|
public interface IFhirResourceDaoSubscription<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||||
|
|
||||||
|
void pollForNewUndeliveredResources();
|
||||||
|
|
||||||
|
void setSubscriptionStatus(Long theResourceId, SubscriptionStatusEnum theStatus);
|
||||||
|
|
||||||
|
SubscriptionTable getSubscriptionByResourceId(long theSubscriptionResourceId);
|
||||||
|
|
||||||
|
}
|
|
@ -98,7 +98,11 @@ public abstract class BaseHasResource {
|
||||||
public abstract IdDt getIdDt();
|
public abstract IdDt getIdDt();
|
||||||
|
|
||||||
public InstantDt getPublished() {
|
public InstantDt getPublished() {
|
||||||
return new InstantDt(myPublished);
|
if (myPublished != null) {
|
||||||
|
return new InstantDt(myPublished);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getResource() {
|
public byte[] getResource() {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "HFJ_SUBSCRIPTION_CAND_RES")
|
||||||
|
public class SubscriptionCandidateResource {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||||
|
@SequenceGenerator(name = "SEQ_SUBSCRIPTION_CAND_ID", sequenceName = "SEQ_SUBSCRIPTION_CAND_ID")
|
||||||
|
@Column(name = "PID", insertable = false, updatable = false)
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID")
|
||||||
|
private ResourceTable myResource;
|
||||||
|
|
||||||
|
@Column(name = "RES_VERSION", nullable = false)
|
||||||
|
private long myResourceVersion;
|
||||||
|
|
||||||
|
public ResourceTable getResource() {
|
||||||
|
return myResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getResourceVersion() {
|
||||||
|
return myResourceVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(ResourceTable theResource) {
|
||||||
|
myResource = theResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceVersion(long theResourceVersion) {
|
||||||
|
myResourceVersion = theResourceVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "HFJ_SUBSCRIPTION_FLAG_RES")
|
||||||
|
public class SubscriptionFlaggedResource {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||||
|
@SequenceGenerator(name = "SEQ_SUBSCRIPTION_FLAG_ID", sequenceName = "SEQ_SUBSCRIPTION_FLAG_ID")
|
||||||
|
@Column(name = "PID", insertable = false, updatable = false)
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "CREATED", nullable = false)
|
||||||
|
private Date myCreated;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.ForeignKey;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Entity
|
||||||
|
@Table(name = "HFJ_SUBSCRIPTION", uniqueConstraints= {
|
||||||
|
@UniqueConstraint(name="IDX_SUBS_RESID", columnNames= { "RES_ID" }),
|
||||||
|
@UniqueConstraint(name="IDX_SUBS_NEXTCHECK", columnNames= { "SUBSCRIPTION_STATUS", "NEXT_CHECK" })
|
||||||
|
})
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_SET_STATUS", query="UPDATE SubscriptionTable t SET t.myStatus = :status WHERE t.myResId = :res_id"),
|
||||||
|
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_NEXT_CHECK", query="SELECT t FROM SubscriptionTable t WHERE t.myStatus = :status AND t.myNextCheck <= :next_check"),
|
||||||
|
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_GET_BY_RES", query="SELECT t FROM SubscriptionTable t WHERE t.myResId = :res_id"),
|
||||||
|
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_DELETE", query="DELETE FROM SubscriptionTable t WHERE t.myResId = :res_id"),
|
||||||
|
})
|
||||||
|
//@formatter:on
|
||||||
|
public class SubscriptionTable {
|
||||||
|
|
||||||
|
@Column(name = "CHECK_INTERVAL", nullable = false)
|
||||||
|
private long myCheckInterval;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||||
|
@SequenceGenerator(name = "SEQ_SUBSCRIPTION_ID", sequenceName = "SEQ_SUBSCRIPTION_ID")
|
||||||
|
@Column(name = "PID", insertable = false, updatable = false)
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "NEXT_CHECK", nullable = false)
|
||||||
|
private Date myNextCheck;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "NEXT_CHECK_SINCE", nullable = false)
|
||||||
|
private Date myNextCheckSince;
|
||||||
|
|
||||||
|
@Column(name = "RES_ID", insertable = false, updatable = false)
|
||||||
|
private Long myResId;
|
||||||
|
|
||||||
|
@Column(name = "SUBSCRIPTION_STATUS", nullable = false, length = 20)
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private SubscriptionStatusEnum myStatus;
|
||||||
|
|
||||||
|
@OneToOne()
|
||||||
|
@JoinColumn(name = "RES_ID", insertable = true, updatable = false, referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_SUBSCRIPTION_RESOURCE_ID") )
|
||||||
|
private ResourceTable mySubscriptionResource;
|
||||||
|
|
||||||
|
public long getCheckInterval() {
|
||||||
|
return myCheckInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return myId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getNextCheck() {
|
||||||
|
return myNextCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getNextCheckSince() {
|
||||||
|
return myNextCheckSince;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubscriptionStatusEnum getStatus() {
|
||||||
|
return myStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceTable getSubscriptionResource() {
|
||||||
|
return mySubscriptionResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckInterval(long theCheckInterval) {
|
||||||
|
myCheckInterval = theCheckInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextCheck(Date theNextCheck) {
|
||||||
|
myNextCheck = theNextCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextCheckSince(Date theNextCheckSince) {
|
||||||
|
myNextCheckSince = theNextCheckSince;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(SubscriptionStatusEnum theStatus) {
|
||||||
|
myStatus = theStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubscriptionResource(ResourceTable theSubscriptionResource) {
|
||||||
|
mySubscriptionResource = theSubscriptionResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.transaction.TransactionConfiguration;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -38,6 +37,9 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
@ -55,6 +57,7 @@ import ca.uhn.fhir.model.dstu2.resource.Practitioner;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
|
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
|
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Subscription;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Substance;
|
import ca.uhn.fhir.model.dstu2.resource.Substance;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
|
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
@ -66,7 +69,6 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
@ContextConfiguration(locations={
|
@ContextConfiguration(locations={
|
||||||
"classpath:hapi-fhir-server-resourceproviders-dstu2.xml",
|
"classpath:hapi-fhir-server-resourceproviders-dstu2.xml",
|
||||||
"classpath:fhir-jpabase-spring-test-config.xml"})
|
"classpath:fhir-jpabase-spring-test-config.xml"})
|
||||||
@TransactionConfiguration(defaultRollback=false)
|
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
|
|
||||||
|
@ -74,9 +76,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
@Qualifier("myConceptMapDaoDstu2")
|
@Qualifier("myConceptMapDaoDstu2")
|
||||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("mySubstanceDaoDstu2")
|
|
||||||
protected IFhirResourceDao<Substance> mySubstanceDao;
|
|
||||||
@Autowired
|
|
||||||
protected DaoConfig myDaoConfig;
|
protected DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myDeviceDaoDstu2")
|
@Qualifier("myDeviceDaoDstu2")
|
||||||
|
@ -126,6 +125,12 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
@Qualifier("myStructureDefinitionDaoDstu2")
|
@Qualifier("myStructureDefinitionDaoDstu2")
|
||||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("mySubscriptionDaoDstu2")
|
||||||
|
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("mySubstanceDaoDstu2")
|
||||||
|
protected IFhirResourceDao<Substance> mySubstanceDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("mySystemDaoDstu2")
|
@Qualifier("mySystemDaoDstu2")
|
||||||
protected IFhirSystemDao<Bundle> mySystemDao;
|
protected IFhirSystemDao<Bundle> mySystemDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -160,6 +165,13 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
return newJsonParser.parseResource(type, string);
|
return newJsonParser.parseResource(type, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionTemplate newTxTemplate() {
|
||||||
|
TransactionTemplate retVal = new TransactionTemplate(myTxManager);
|
||||||
|
retVal.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
|
||||||
|
retVal.afterPropertiesSet();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
|
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
|
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
|
||||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||||
|
@ -174,6 +186,8 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("DELETE from " + SubscriptionCandidateResource.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
@ -189,6 +203,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("DELETE from " + SubscriptionTable.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Subscription;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
|
||||||
|
public class FhirResourceDaoDstu2SubscriptionTest extends BaseJpaDstu2Test {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SubscriptionTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateSubscriptionInvalidCriteria() {
|
||||||
|
Subscription subs = new Subscription();
|
||||||
|
subs.setCriteria("Observation");
|
||||||
|
try {
|
||||||
|
mySubscriptionDao.create(subs);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
subs = new Subscription();
|
||||||
|
subs.setCriteria("http://foo.com/Observation?AAA=BBB");
|
||||||
|
try {
|
||||||
|
mySubscriptionDao.create(subs);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
subs = new Subscription();
|
||||||
|
subs.setCriteria("ObservationZZZZ?a=b");
|
||||||
|
try {
|
||||||
|
mySubscriptionDao.create(subs);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Subscription.criteria contains invalid/unsupported resource type: ObservationZZZZ"));
|
||||||
|
}
|
||||||
|
|
||||||
|
subs = new Subscription();
|
||||||
|
subs.setCriteria("Observation?identifier=123");
|
||||||
|
try {
|
||||||
|
mySubscriptionDao.create(subs);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Subscription.channel.type must be populated on this server"));
|
||||||
|
}
|
||||||
|
|
||||||
|
subs = new Subscription();
|
||||||
|
subs.setCriteria("Observation?identifier=123");
|
||||||
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||||
|
assertTrue(mySubscriptionDao.create(subs).getId().hasIdPart());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void beforeEnableSubscription() {
|
||||||
|
myDaoConfig.setSubscriptionEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionResourcesAppear() {
|
||||||
|
String methodName = "testSubscriptionResourcesAppear";
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.addName().addFamily(methodName);
|
||||||
|
IIdType pId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getSubject().setReference(pId);
|
||||||
|
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||||
|
IIdType beforeId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Subscription subs = new Subscription();
|
||||||
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||||
|
subs.setCriteria("Observation?subject=Patient/123");
|
||||||
|
IIdType id = mySubscriptionDao.create(subs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
obs = new Observation();
|
||||||
|
obs.getSubject().setReference(pId);
|
||||||
|
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||||
|
IIdType afterId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
obs = new Observation();
|
||||||
|
obs.getSubject().setReference(pId);
|
||||||
|
obs.setStatus(ObservationStatusEnum.FINAL);
|
||||||
|
IIdType afterId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
mySubscriptionDao.pollForNewUndeliveredResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateSubscription() {
|
||||||
|
Subscription subs = new Subscription();
|
||||||
|
subs.setCriteria("Observation?subject=Patient/123");
|
||||||
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||||
|
|
||||||
|
IIdType id = mySubscriptionDao.create(subs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
TypedQuery<SubscriptionTable> q = myEntityManager.createQuery("SELECT t from SubscriptionTable t WHERE t.mySubscriptionResource.myId = :id", SubscriptionTable.class);
|
||||||
|
q.setParameter("id", id.getIdPartAsLong());
|
||||||
|
final SubscriptionTable table = q.getSingleResult();
|
||||||
|
|
||||||
|
assertNotNull(table);
|
||||||
|
assertNotNull(table.getNextCheck());
|
||||||
|
assertEquals(table.getNextCheck(), table.getSubscriptionResource().getPublished().getValue());
|
||||||
|
assertEquals(SubscriptionStatusEnum.REQUESTED, myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
|
||||||
|
assertEquals(SubscriptionStatusEnum.REQUESTED, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
|
||||||
|
|
||||||
|
mySubscriptionDao.setSubscriptionStatus(id.getIdPartAsLong(), SubscriptionStatusEnum.ACTIVE);
|
||||||
|
|
||||||
|
assertEquals(SubscriptionStatusEnum.ACTIVE, myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
|
||||||
|
assertEquals(SubscriptionStatusEnum.ACTIVE, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
|
||||||
|
|
||||||
|
mySubscriptionDao.delete(id);
|
||||||
|
|
||||||
|
assertNull(myEntityManager.find(SubscriptionTable.class, table.getId()));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Re-create again
|
||||||
|
*/
|
||||||
|
|
||||||
|
subs = new Subscription();
|
||||||
|
subs.setCriteria("Observation?subject=Patient/123");
|
||||||
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||||
|
subs.setId(id);
|
||||||
|
mySubscriptionDao.update(subs);
|
||||||
|
|
||||||
|
assertEquals(SubscriptionStatusEnum.REQUESTED, myEntityManager.createQuery("SELECT t FROM SubscriptionTable t WHERE t.myResId = " + id.getIdPart(), SubscriptionTable.class).getSingleResult().getStatus());
|
||||||
|
assertEquals(SubscriptionStatusEnum.REQUESTED, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -77,6 +77,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.model.primitive.UriDt;
|
import ca.uhn.fhir.model.primitive.UriDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
|
@ -966,8 +967,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
||||||
assertEquals(id.withVersion("1"), entries.get(2).getIdElement());
|
assertEquals(id.withVersion("1"), entries.get(2).getIdElement());
|
||||||
|
|
||||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(0)));
|
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(0)));
|
||||||
|
assertEquals(BundleEntryTransactionMethodEnum.PUT, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(0)));
|
||||||
|
|
||||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(1)));
|
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(1)));
|
||||||
|
assertEquals(BundleEntryTransactionMethodEnum.DELETE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(1)));
|
||||||
|
|
||||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(2)));
|
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(2)));
|
||||||
|
assertEquals(BundleEntryTransactionMethodEnum.POST, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||||
|
|
||||||
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||||
|
|
||||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
and other properties supported by BasicDataSource.
|
and other properties supported by BasicDataSource.
|
||||||
-->
|
-->
|
||||||
<bean id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
<bean id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||||
<property name="url" value="jdbc:derby:directory:jpaserver_derby_files;create=true" />
|
<property name="url" value="jdbc:derby:directory:target/jpaserver_derby_files;create=true" />
|
||||||
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"></property>
|
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"></property>
|
||||||
<property name="username" value=""/>
|
<property name="username" value=""/>
|
||||||
<property name="password" value=""/>
|
<property name="password" value=""/>
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||||
|
|
||||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
|
||||||
|
|
||||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -104,12 +104,10 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-webmvc</artifactId>
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-context</artifactId>
|
<artifactId>spring-context</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<artifactId>xml-apis</artifactId>
|
<artifactId>xml-apis</artifactId>
|
||||||
|
@ -120,53 +118,44 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-beans</artifactId>
|
<artifactId>spring-beans</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-tx</artifactId>
|
<artifactId>spring-tx</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-context-support</artifactId>
|
<artifactId>spring-context-support</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>${spring_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-servlets</artifactId>
|
<artifactId>jetty-servlets</artifactId>
|
||||||
<version>${jetty_version}</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-webapp</artifactId>
|
<artifactId>jetty-webapp</artifactId>
|
||||||
<version>${jetty_version}</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-server</artifactId>
|
<artifactId>jetty-server</artifactId>
|
||||||
<version>${jetty_version}</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-servlet</artifactId>
|
<artifactId>jetty-servlet</artifactId>
|
||||||
<version>${jetty_version}</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-util</artifactId>
|
<artifactId>jetty-util</artifactId>
|
||||||
<version>${jetty_version}</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionCandidateResource</class>
|
||||||
|
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
|
||||||
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
|
||||||
|
|
||||||
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#foreach ( $res in $resources )
|
#foreach ( $res in $resources )
|
||||||
<bean id="my${res.name}Dao${versionCapitalized}"
|
<bean id="my${res.name}Dao${versionCapitalized}"
|
||||||
## Some resource types have customized DAOs for resource specific functionality
|
## Some resource types have customized DAOs for resource specific functionality
|
||||||
#if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet'))
|
#if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'Subscription' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet'))
|
||||||
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
|
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
|
||||||
#else
|
#else
|
||||||
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
|
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -523,6 +523,11 @@
|
||||||
<artifactId>spring-web</artifactId>
|
<artifactId>spring-web</artifactId>
|
||||||
<version>${spring_version}</version>
|
<version>${spring_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>${spring_version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.thymeleaf</groupId>
|
<groupId>org.thymeleaf</groupId>
|
||||||
<artifactId>thymeleaf</artifactId>
|
<artifactId>thymeleaf</artifactId>
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
JPA server did not correctly index search parameters of type "reference" where the
|
JPA server did not correctly index search parameters of type "reference" where the
|
||||||
path had multiple entries (i.e. "Resource.path1 | Resource.path2")
|
path had multiple entries (i.e. "Resource.path1 | Resource.path2")
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JPA server _history operations (server, type, instance) not correctly set the
|
||||||
|
Bundle.entry.request.method to POST or PUT for create and updates of the resource.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.2" date="2015-09-18">
|
<release version="1.2" date="2015-09-18">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue