Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
jamesagnew 2019-03-06 16:14:40 -05:00
commit 490e4cb94d
24 changed files with 173 additions and 187 deletions

View File

@ -26,10 +26,12 @@ import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.StaleStateException;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect; import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
@ -68,10 +70,7 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect {
if (isNotBlank(theMessageToPrepend)) { if (isNotBlank(theMessageToPrepend)) {
messageToPrepend = theMessageToPrepend + " - "; messageToPrepend = theMessageToPrepend + " - ";
} }
if (theException.toString().contains("Batch update")) {
theException.toString();
}
// <editor-fold desc="I HATE YOU">
if (theException instanceof ConstraintViolationException) { if (theException instanceof ConstraintViolationException) {
String constraintName = ((ConstraintViolationException) theException).getConstraintName(); String constraintName = ((ConstraintViolationException) theException).getConstraintName();
switch (defaultString(constraintName)) { switch (defaultString(constraintName)) {
@ -83,7 +82,25 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect {
throw new ResourceVersionConflictException(messageToPrepend + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "forcedIdConstraintFailure")); throw new ResourceVersionConflictException(messageToPrepend + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "forcedIdConstraintFailure"));
} }
} }
// </editor-fold>
/*
* It would be nice if we could be more precise here, since technically any optimistic lock
* failure could result in a StaleStateException, but with the error message we're returning
* we're basically assuming it's an optimistic lock failure on HFJ_RESOURCE.
*
* That said, I think this is an OK trade-off. There is a high probability that if this happens
* it is a failure on HFJ_RESOURCE (there aren't many other tables in our schema that
* use @Version at all) and this error message is infinitely more comprehensible
* than the one we'd otherwise return.
*
* The actual StaleStateException is thrown in hibernate's Expectations
* class in a method called "checkBatched" currently. This can all be tested using the
* StressTestR4Test method testMultiThreadedUpdateSameResourceInTransaction()
*/
if (theException instanceof StaleStateException) {
String msg = messageToPrepend + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure");
throw new ResourceVersionConflictException(msg);
}
return super.convertHibernateAccessException(theException); return super.convertHibernateAccessException(theException);
} }

View File

@ -27,11 +27,6 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
public class FhirResourceDaoMessageHeaderDstu2 extends FhirResourceDaoDstu2<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> { public class FhirResourceDaoMessageHeaderDstu2 extends FhirResourceDaoDstu2<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
@Override
public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
public static IBaseBundle throwProcessMessageNotImplemented() { public static IBaseBundle throwProcessMessageNotImplemented() {
throw new NotImplementedOperationException("This operation is not yet implemented on this server"); throw new NotImplementedOperationException("This operation is not yet implemented on this server");
} }

View File

@ -60,6 +60,7 @@ import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.UrlUtil.UrlParts; import ca.uhn.fhir.util.UrlUtil.UrlParts;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource; 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -665,7 +666,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode)); return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
} }
//@formatter:off @Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
/** /**
* Transaction Order, per the spec: * Transaction Order, per the spec:
@ -675,7 +680,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
* Process any PUT interactions * Process any PUT interactions
* Process any GET interactions * Process any GET interactions
*/ */
//@formatter:off
public class TransactionSorter implements Comparator<Entry> { public class TransactionSorter implements Comparator<Entry> {
@Override @Override

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
/* /*
@ -25,7 +23,5 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
*/ */
public interface IFhirResourceDaoMessageHeader<T extends IBaseResource> extends IFhirResourceDao<T> { public interface IFhirResourceDaoMessageHeader<T extends IBaseResource> extends IFhirResourceDao<T> {
// nothing right now
IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage);
} }

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.util.ExpungeOptions;
import ca.uhn.fhir.jpa.util.ExpungeOutcome; import ca.uhn.fhir.jpa.util.ExpungeOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -60,6 +61,12 @@ public interface IFhirSystemDao<T, MT> extends IDao {
*/ */
MT metaGetOperation(RequestDetails theRequestDetails); MT metaGetOperation(RequestDetails theRequestDetails);
/**
* Implementations may implement this method to implement the $process-message
* operation
*/
IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage);
T transaction(RequestDetails theRequestDetails, T theResources); T transaction(RequestDetails theRequestDetails, T theResources);
} }

View File

@ -20,17 +20,9 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.dstu3.model.MessageHeader; import org.hl7.fhir.dstu3.model.MessageHeader;
import org.hl7.fhir.instance.model.api.IBaseBundle;
public class FhirResourceDaoMessageHeaderDstu3 extends FhirResourceDaoDstu3<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> { public class FhirResourceDaoMessageHeaderDstu3 extends FhirResourceDaoDstu3<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
@Override
public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
} }

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
*/ */
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
@ -29,6 +30,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetai
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.dstu3.model.Meta;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -79,6 +81,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal; return retVal;
} }
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
@Override @Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) { public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
@ -86,4 +93,5 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} }

View File

@ -20,17 +20,9 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r4.model.MessageHeader; import org.hl7.fhir.r4.model.MessageHeader;
public class FhirResourceDaoMessageHeaderR4 extends FhirResourceDaoR4<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> { public class FhirResourceDaoMessageHeaderR4 extends FhirResourceDaoR4<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
@Override
public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
} }

View File

@ -21,11 +21,13 @@ package ca.uhn.fhir.jpa.dao.r4;
*/ */
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Meta; import org.hl7.fhir.r4.model.Meta;
@ -64,6 +66,11 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return toMeta(tagDefinitions); return toMeta(tagDefinitions);
} }
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
protected Meta toMeta(Collection<TagDefinition> tagDefinitions) { protected Meta toMeta(Collection<TagDefinition> tagDefinitions) {
Meta retVal = new Meta(); Meta retVal = new Meta();

View File

@ -1,16 +1,6 @@
package ca.uhn.fhir.jpa.provider; package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.MessageHeader; import ca.uhn.fhir.model.dstu2.resource.MessageHeader;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import javax.servlet.http.HttpServletRequest;
/* /*
* #%L * #%L
@ -33,29 +23,5 @@ import javax.servlet.http.HttpServletRequest;
*/ */
public class BaseJpaResourceProviderMessageHeaderDstu2 extends JpaResourceProviderDstu2<MessageHeader> { public class BaseJpaResourceProviderMessageHeaderDstu2 extends JpaResourceProviderDstu2<MessageHeader> {
// nothing now
/**
* /MessageHeader/$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoMessageHeader<MessageHeader>) getDao()).messageHeaderProcessMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
} }

View File

@ -18,11 +18,13 @@ import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.IntegerType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -248,6 +250,28 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus<Bundl
} }
} }
/**
* /$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return getDao().processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
public static Parameters toExpungeResponse(org.hl7.fhir.r4.model.Parameters theRetVal) { public static Parameters toExpungeResponse(org.hl7.fhir.r4.model.Parameters theRetVal) {
Integer count = ((IntegerType) theRetVal.getParameterFirstRep().getValue()).getValue(); Integer count = ((IntegerType) theRetVal.getParameterFirstRep().getValue()).getValue();
return new Parameters() return new Parameters()

View File

@ -1,16 +1,6 @@
package ca.uhn.fhir.jpa.provider.dstu3; package ca.uhn.fhir.jpa.provider.dstu3;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.MessageHeader; import org.hl7.fhir.dstu3.model.MessageHeader;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import javax.servlet.http.HttpServletRequest;
/* /*
* #%L * #%L
@ -33,29 +23,5 @@ import javax.servlet.http.HttpServletRequest;
*/ */
public class BaseJpaResourceProviderMessageHeaderDstu3 extends JpaResourceProviderDstu3<MessageHeader> { public class BaseJpaResourceProviderMessageHeaderDstu3 extends JpaResourceProviderDstu3<MessageHeader> {
// nothing now
/**
* /MessageHeader/$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoMessageHeader<MessageHeader>) getDao()).messageHeaderProcessMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
} }

View File

@ -15,10 +15,12 @@ import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -250,6 +252,28 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus<Bundl
} }
} }
/**
* /$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return getDao().processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
public static void validateFulltextSearchEnabled(IFulltextSearchSvc theSearchDao) { public static void validateFulltextSearchEnabled(IFulltextSearchSvc theSearchDao) {
if (theSearchDao == null || theSearchDao.isDisabled()) { if (theSearchDao == null || theSearchDao.isDisabled()) {
throw new InvalidRequestException("Fulltext searching is disabled on this server"); throw new InvalidRequestException("Fulltext searching is disabled on this server");

View File

@ -33,29 +33,5 @@ import javax.servlet.http.HttpServletRequest;
*/ */
public class BaseJpaResourceProviderMessageHeaderR4 extends JpaResourceProviderR4<MessageHeader> { public class BaseJpaResourceProviderMessageHeaderR4 extends JpaResourceProviderR4<MessageHeader> {
// nothing right now
/**
* /MessageHeader/$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoMessageHeader<MessageHeader>) getDao()).messageHeaderProcessMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
} }

View File

@ -10,12 +10,14 @@ import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -229,6 +231,28 @@ public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle,
return retVal; return retVal;
} }
/**
* /$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return getDao().processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
@Transaction @Transaction
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) { public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest()); startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());

View File

@ -105,31 +105,15 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer
private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) { private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) {
ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType); ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType);
// Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED
/* if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, msg)) {
* We only want to submit the message to the processing queue once the return;
* transaction is committed. We do this in order to make sure that the
* data is actually in the DB, in case it's the database matcher.
*/
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public int getOrder() {
return 0;
}
@Override
public void afterCommit() {
submitResourceModified(msg);
}
});
} else {
submitResourceModified(msg);
} }
submitResourceModified(msg);
} }
private void sendToProcessingChannel(final ResourceModifiedMessage theMessage) { protected void sendToProcessingChannel(final ResourceModifiedMessage theMessage) {
ourLog.trace("Sending resource modified message to processing channel"); ourLog.trace("Sending resource modified message to processing channel");
Validate.notNull(myProcessingChannel, "A SubscriptionMatcherInterceptor has been registered without calling start() on it."); Validate.notNull(myProcessingChannel, "A SubscriptionMatcherInterceptor has been registered without calling start() on it.");
myProcessingChannel.send(new ResourceModifiedJsonMessage(theMessage)); myProcessingChannel.send(new ResourceModifiedJsonMessage(theMessage));
@ -144,12 +128,26 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer
*/ */
@Override @Override
public void submitResourceModified(final ResourceModifiedMessage theMsg) { public void submitResourceModified(final ResourceModifiedMessage theMsg) {
// Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED /*
if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED, theMsg)) { * We only want to submit the message to the processing queue once the
return; * transaction is committed. We do this in order to make sure that the
} * data is actually in the DB, in case it's the database matcher.
*/
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public int getOrder() {
return 0;
}
sendToProcessingChannel(theMsg); @Override
public void afterCommit() {
sendToProcessingChannel(theMsg);
}
});
} else {
sendToProcessingChannel(theMsg);
}
} }
@VisibleForTesting @VisibleForTesting

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -25,7 +24,7 @@ import static org.junit.Assert.fail;
@EnableTransactionManagement() @EnableTransactionManagement()
public class TestR4Config extends BaseJavaConfigR4 { public class TestR4Config extends BaseJavaConfigR4 {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class);
private static int ourMaxThreads; private static int ourMaxThreads;
static { static {

View File

@ -1693,7 +1693,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
.setName("content") .setName("content")
.setResource(bundle); .setResource(bundle);
try { try {
ourClient.operation().onType(MessageHeader.class).named(JpaConstants.OPERATION_PROCESS_MESSAGE).withParameters(parameters).execute(); ourClient.operation().onServer().named(JpaConstants.OPERATION_PROCESS_MESSAGE).withParameters(parameters).execute();
fail(); fail();
} catch (NotImplementedOperationException e) { } catch (NotImplementedOperationException e) {
assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server")); assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server"));

View File

@ -52,7 +52,7 @@ public class ResourceProviderR4BundleTest extends BaseResourceProviderR4Test {
.setName("content") .setName("content")
.setResource(bundle); .setResource(bundle);
try { try {
ourClient.operation().onType(MessageHeader.class).named(JpaConstants.OPERATION_PROCESS_MESSAGE).withParameters(parameters).execute(); ourClient.operation().onServer().named(JpaConstants.OPERATION_PROCESS_MESSAGE).withParameters(parameters).execute();
fail(); fail();
} catch (NotImplementedOperationException e) { } catch (NotImplementedOperationException e) {
assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server")); assertThat(e.getMessage(), containsString("This operation is not yet implemented on this server"));

View File

@ -8,7 +8,6 @@ import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -29,8 +28,8 @@ import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestR4Config.class}) @ContextConfiguration(classes = {TestR4Config.class})
public class InMemorySubscriptionMatcherTestR4 { public class InMemorySubscriptionMatcherR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InMemorySubscriptionMatcherTestR4.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InMemorySubscriptionMatcherR4Test.class);
@Autowired @Autowired
InMemorySubscriptionMatcher myInMemorySubscriptionMatcher; InMemorySubscriptionMatcher myInMemorySubscriptionMatcher;

View File

@ -36,6 +36,7 @@ import java.math.BigDecimal;
import java.util.Objects; import java.util.Objects;
import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
//@formatter:off //@formatter:off
@Embeddable @Embeddable
@ -246,7 +247,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
// Only match on system if it wasn't specified // Only match on system if it wasn't specified
String quantityUnitsString = defaultString(quantity.getUnits()); String quantityUnitsString = defaultString(quantity.getUnits());
if (quantity.getSystem() == null && quantityUnitsString == null) { if (quantity.getSystem() == null && isBlank(quantityUnitsString)) {
if (Objects.equals(getValue(),quantity.getValue())) { if (Objects.equals(getValue(),quantity.getValue())) {
retval = true; retval = true;
} }
@ -257,7 +258,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
Objects.equals(getValue(),quantity.getValue())) { Objects.equals(getValue(),quantity.getValue())) {
retval = true; retval = true;
} }
} else if (quantityUnitsString == null) { } else if (isBlank(quantityUnitsString)) {
if (getSystem().equalsIgnoreCase(quantity.getSystem()) && if (getSystem().equalsIgnoreCase(quantity.getSystem()) &&
Objects.equals(getValue(),quantity.getValue())) { Objects.equals(getValue(),quantity.getValue())) {
retval = true; retval = true;

View File

@ -131,7 +131,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
*/ */
private List<HookInvoker> scanInterceptorForHookMethods(Object theInterceptor, int theTypeOrder) { private List<HookInvoker> scanInterceptorForHookMethods(Object theInterceptor, int theTypeOrder) {
ArrayList<HookInvoker> retVal = new ArrayList<>(); ArrayList<HookInvoker> retVal = new ArrayList<>();
for (Method nextMethod : theInterceptor.getClass().getDeclaredMethods()) { for (Method nextMethod : theInterceptor.getClass().getMethods()) {
Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class); Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class);
if (hook != null) { if (hook != null) {

View File

@ -14,7 +14,7 @@ import java.util.Collections;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class InMemorySubscriptionMatcherTestR3 extends BaseSubscriptionDstu3Test { public class InMemorySubscriptionMatcherR3Test extends BaseSubscriptionDstu3Test {
@Autowired @Autowired
SubscriptionStrategyEvaluator mySubscriptionStrategyEvaluator; SubscriptionStrategyEvaluator mySubscriptionStrategyEvaluator;
@Autowired @Autowired

View File

@ -1728,7 +1728,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override @Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder() return new RuleBuilder()
.allow("RULE 1").operation().named("process-message").onType(MessageHeader.class).andRequireExplicitResponseAuthorization().andThen() .allow("RULE 1").operation().named("process-message").onServer().andRequireExplicitResponseAuthorization().andThen()
.build(); .build();
} }
}); });
@ -1743,7 +1743,7 @@ public class AuthorizationInterceptorDstu3Test {
// With body // With body
ourHitMethod = false; ourHitMethod = false;
httpPost = new HttpPost("http://localhost:" + ourPort + "/MessageHeader/$process-message"); httpPost = new HttpPost("http://localhost:" + ourPort + "/$process-message");
httpPost.setEntity(new StringEntity(inputString, ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8))); httpPost.setEntity(new StringEntity(inputString, ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
status = ourClient.execute(httpPost); status = ourClient.execute(httpPost);
response = extractResponseAndClose(status); response = extractResponseAndClose(status);
@ -1753,7 +1753,7 @@ public class AuthorizationInterceptorDstu3Test {
// With body // With body
ourHitMethod = false; ourHitMethod = false;
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MessageHeader/$process-message"); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$process-message");
status = ourClient.execute(httpGet); status = ourClient.execute(httpGet);
response = extractResponseAndClose(status); response = extractResponseAndClose(status);
ourLog.info(response); ourLog.info(response);
@ -3348,21 +3348,6 @@ public class AuthorizationInterceptorDstu3Test {
} }
public static class DummyMessageHeaderResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return MessageHeader.class;
}
@Operation(name = "process-message", idempotent = true)
public Parameters operation0(@OperationParam(name = "content") Bundle theInput) {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
}
public static class DummyDiagnosticReportResourceProvider implements IResourceProvider { public static class DummyDiagnosticReportResourceProvider implements IResourceProvider {
@ -3633,6 +3618,13 @@ public class AuthorizationInterceptorDstu3Test {
return (Parameters) new Parameters().setId("1"); return (Parameters) new Parameters().setId("1");
} }
@Operation(name = "process-message", idempotent = true)
public Parameters processMessage(@OperationParam(name = "content") Bundle theInput) {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
@Transaction() @Transaction()
public Bundle search(IRequestOperationCallback theRequestOperationCallback, @TransactionParam Bundle theInput) { public Bundle search(IRequestOperationCallback theRequestOperationCallback, @TransactionParam Bundle theInput) {
ourHitMethod = true; ourHitMethod = true;
@ -3664,13 +3656,12 @@ public class AuthorizationInterceptorDstu3Test {
DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider(); DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider();
DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider(); DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider();
DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider(); DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider();
DummyMessageHeaderResourceProvider mshProv = new DummyMessageHeaderResourceProvider();
PlainProvider plainProvider = new PlainProvider(); PlainProvider plainProvider = new PlainProvider();
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx); ourServlet = new RestfulServer(ourCtx);
ourServlet.setFhirContext(ourCtx); ourServlet.setFhirContext(ourCtx);
ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv, mshProv); ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv);
ourServlet.setPlainProviders(plainProvider); ourServlet.setPlainProviders(plainProvider);
ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100));
ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON);