Merge branch 'master' into test-openjdk-11
This commit is contained in:
commit
4599a10180
|
@ -8,7 +8,7 @@ dist: trusty
|
||||||
|
|
||||||
language: java
|
language: java
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk11
|
- openjdk11
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- MAVEN_OPTS="-Xmx10244M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC"
|
- MAVEN_OPTS="-Xmx10244M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Unsupported
|
||||||
|
|
||||||
|
Most of the projects in this module are no longer supported.
|
||||||
|
|
||||||
|
The test in hapi-fhir-jpaserver-cds-example is @Ignored until Chris Schuler is able to make a change to the pom
|
||||||
|
this module depends on.
|
||||||
|
|
||||||
|
## Supported JPA Example:
|
||||||
|
|
||||||
|
The supported HAPI-FHIR JPA example is available in the [hapi-fhir-jpaserver-starter](https://github.com/hapifhir/hapi-fhir-jpaserver-starter)
|
||||||
|
project within the [hapifhir](https://github.com/hapifhir) GitHub Organization.
|
|
@ -10,11 +10,7 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.AfterClass;
|
import org.junit.*;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
@ -26,7 +22,7 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
// FIXME KHS
|
// TODO Remove @Ignore once Chris Schuler has fixed the external jar this project depends on
|
||||||
@Ignore
|
@Ignore
|
||||||
public class CdsExampleTests {
|
public class CdsExampleTests {
|
||||||
private static IGenericClient ourClient;
|
private static IGenericClient ourClient;
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
@ -20,13 +10,20 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemo extends RestfulServer {
|
public class JpaServerDemo extends RestfulServer {
|
||||||
|
|
||||||
|
@ -129,12 +126,10 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Register interceptors for the server based on DaoConfig.getSupportedSubscriptionTypes()
|
||||||
*/
|
*/
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you are hosting this server at a specific DNS name, the server will try to
|
* If you are hosting this server at a specific DNS name, the server will try to
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
@ -20,13 +10,20 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemoDstu2 extends RestfulServer {
|
public class JpaServerDemoDstu2 extends RestfulServer {
|
||||||
|
|
||||||
|
@ -129,12 +126,10 @@ public class JpaServerDemoDstu2 extends RestfulServer {
|
||||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Register interceptors for the server based on DaoConfig.getSupportedSubscriptionTypes()
|
||||||
*/
|
*/
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you are hosting this server at a specific DNS name, the server will try to
|
* If you are hosting this server at a specific DNS name, the server will try to
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
@ -16,12 +7,18 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemo extends RestfulServer {
|
public class JpaServerDemo extends RestfulServer {
|
||||||
|
|
||||||
|
@ -96,12 +93,10 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Register interceptors for the server based on DaoConfig.getSupportedSubscriptionTypes()
|
||||||
*/
|
*/
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you are hosting this server at a specific DNS name, the server will try to
|
* If you are hosting this server at a specific DNS name, the server will try to
|
||||||
|
|
|
@ -99,6 +99,10 @@
|
||||||
<groupId>javax.mail</groupId>
|
<groupId>javax.mail</groupId>
|
||||||
<artifactId>javax.mail-api</artifactId>
|
<artifactId>javax.mail-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.activation</groupId>
|
||||||
|
<artifactId>javax.activation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.helger</groupId>
|
<groupId>com.helger</groupId>
|
||||||
<artifactId>ph-schematron</artifactId>
|
<artifactId>ph-schematron</artifactId>
|
||||||
|
|
|
@ -48,6 +48,12 @@
|
||||||
<groupId>com.helger</groupId>
|
<groupId>com.helger</groupId>
|
||||||
<artifactId>ph-schematron</artifactId>
|
<artifactId>ph-schematron</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.glassfish.jaxb</groupId>
|
||||||
|
<artifactId>jaxb-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.helger</groupId>
|
<groupId>com.helger</groupId>
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.fluentpath;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
|
@ -36,6 +37,15 @@ public interface IFluentPath {
|
||||||
*/
|
*/
|
||||||
<T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType);
|
<T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the given FluentPath expression against the given input and return
|
||||||
|
* the first match (if any)
|
||||||
|
*
|
||||||
|
* @param theInput The input object (generally a resource or datatype)
|
||||||
|
* @param thePath The fluent path expression
|
||||||
|
* @param theReturnType The type to return (in order to avoid casting)
|
||||||
|
*/
|
||||||
|
<T extends IBase> Optional<T> evaluateFirst(IBase theInput, String thePath, Class<T> theReturnType);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,8 @@ public class FhirTerser {
|
||||||
if (theChildDefinition == null)
|
if (theChildDefinition == null)
|
||||||
return null;
|
return null;
|
||||||
if (theCurrentList == null || theCurrentList.isEmpty())
|
if (theCurrentList == null || theCurrentList.isEmpty())
|
||||||
return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName()));
|
return new ArrayList<>(Arrays.asList(theChildDefinition.getElementName()));
|
||||||
List<String> newList = new ArrayList<String>(theCurrentList);
|
List<String> newList = new ArrayList<>(theCurrentList);
|
||||||
newList.add(theChildDefinition.getElementName());
|
newList.add(theChildDefinition.getElementName());
|
||||||
return newList;
|
return newList;
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,13 +218,17 @@
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>javax.xml.bind</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>com.sun.xml.bind</groupId>-->
|
||||||
|
<!--<artifactId>jaxb-core</artifactId>-->
|
||||||
|
<!--</dependency>-->
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>com.sun.xml.bind</groupId>-->
|
||||||
|
<!--<artifactId>jaxb-impl</artifactId>-->
|
||||||
|
<!--</dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
<groupId>org.glassfish.jaxb</groupId>
|
||||||
<artifactId>jaxb-core</artifactId>
|
<artifactId>jaxb-runtime</artifactId>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
|
||||||
<artifactId>jaxb-impl</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -256,27 +256,30 @@ public abstract class BaseApp {
|
||||||
System.err.println("" + ansi().fg(Ansi.Color.WHITE).boldOff());
|
System.err.println("" + ansi().fg(Ansi.Color.WHITE).boldOff());
|
||||||
logCommandUsageNoHeader(command);
|
logCommandUsageNoHeader(command);
|
||||||
runCleanupHookAndUnregister();
|
runCleanupHookAndUnregister();
|
||||||
System.exit(1);
|
exitDueToException(e);
|
||||||
} catch (CommandFailureException e) {
|
} catch (CommandFailureException e) {
|
||||||
ourLog.error(e.getMessage());
|
ourLog.error(e.getMessage());
|
||||||
runCleanupHookAndUnregister();
|
runCleanupHookAndUnregister();
|
||||||
if ("true".equals(System.getProperty("test"))) {
|
exitDueToException(e);
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
ourLog.error("Error during execution: ", t);
|
ourLog.error("Error during execution: ", t);
|
||||||
runCleanupHookAndUnregister();
|
runCleanupHookAndUnregister();
|
||||||
|
exitDueToException(new CommandFailureException("Error: " + t.toString(), t));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitDueToException(Throwable e) {
|
||||||
if ("true".equals(System.getProperty("test"))) {
|
if ("true".equals(System.getProperty("test"))) {
|
||||||
throw new CommandFailureException("Error: " + t.toString(), t);
|
if (e instanceof CommandFailureException) {
|
||||||
|
throw (CommandFailureException)e;
|
||||||
|
}
|
||||||
|
throw new Error(e);
|
||||||
} else {
|
} else {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runCleanupHookAndUnregister() {
|
private void runCleanupHookAndUnregister() {
|
||||||
if (myShutdownHookHasNotRun) {
|
if (myShutdownHookHasNotRun) {
|
||||||
Runtime.getRuntime().removeShutdownHook(myShutdownHook);
|
Runtime.getRuntime().removeShutdownHook(myShutdownHook);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
|
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
@ -21,13 +22,11 @@ import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemo extends RestfulServer {
|
public class JpaServerDemo extends RestfulServer {
|
||||||
|
@ -143,19 +142,14 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
||||||
registerInterceptor(corsInterceptor);
|
registerInterceptor(corsInterceptor);
|
||||||
|
|
||||||
/*
|
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
|
||||||
*/
|
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
DaoConfig daoConfig = myAppCtx.getBean(DaoConfig.class);
|
DaoConfig daoConfig = myAppCtx.getBean(DaoConfig.class);
|
||||||
daoConfig.setAllowExternalReferences(ContextHolder.isAllowExternalRefs());
|
daoConfig.setAllowExternalReferences(ContextHolder.isAllowExternalRefs());
|
||||||
daoConfig.setEnforceReferentialIntegrityOnDelete(!ContextHolder.isDisableReferentialIntegrity());
|
daoConfig.setEnforceReferentialIntegrityOnDelete(!ContextHolder.isDisableReferentialIntegrity());
|
||||||
daoConfig.setEnforceReferentialIntegrityOnWrite(!ContextHolder.isDisableReferentialIntegrity());
|
daoConfig.setEnforceReferentialIntegrityOnWrite(!ContextHolder.isDisableReferentialIntegrity());
|
||||||
daoConfig.setReuseCachedSearchResultsForMillis(ContextHolder.getReuseCachedSearchResultsForMillis());
|
daoConfig.setReuseCachedSearchResultsForMillis(ContextHolder.getReuseCachedSearchResultsForMillis());
|
||||||
|
|
||||||
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,14 +231,14 @@
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>javax.xml.bind</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<!--<dependency>
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
<groupId>com.sun.xml.bind</groupId>
|
||||||
<artifactId>jaxb-core</artifactId>
|
<artifactId>jaxb-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
<groupId>com.sun.xml.bind</groupId>
|
||||||
<artifactId>jaxb-impl</artifactId>
|
<artifactId>jaxb-impl</artifactId>
|
||||||
</dependency>
|
</dependency>-->
|
||||||
|
|
||||||
<!-- Test Database -->
|
<!-- Test Database -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -360,18 +360,10 @@
|
||||||
<artifactId>xml-apis</artifactId>
|
<artifactId>xml-apis</artifactId>
|
||||||
<groupId>xml-apis</groupId>
|
<groupId>xml-apis</groupId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
<exclusion>
|
<!--<exclusion>-->
|
||||||
<groupId>org.jboss.spec.javax.transaction</groupId>
|
<!--<groupId>org.jboss.spec.javax.transaction</groupId>-->
|
||||||
<artifactId>jboss-transaction-api_1.2_spec</artifactId>
|
<!--<artifactId>jboss-transaction-api_1.2_spec</artifactId>-->
|
||||||
</exclusion>
|
<!--</exclusion>-->
|
||||||
<exclusion>
|
|
||||||
<groupId>javax.activation</groupId>
|
|
||||||
<artifactId>activation</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>javax.activation</groupId>
|
|
||||||
<artifactId>javax.activation-api</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -409,9 +401,14 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.transaction</groupId>
|
<groupId>com.sun.activation</groupId>
|
||||||
<artifactId>javax.transaction-api</artifactId>
|
<artifactId>javax.activation</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>javax.transaction</groupId>-->
|
||||||
|
<!--<artifactId>javax.transaction-api</artifactId>-->
|
||||||
|
<!--</dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.mail</groupId>
|
<groupId>javax.mail</groupId>
|
||||||
<artifactId>javax.mail-api</artifactId>
|
<artifactId>javax.mail-api</artifactId>
|
||||||
|
@ -428,10 +425,10 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<!--<dependency>-->
|
||||||
<groupId>com.sun.activation</groupId>
|
<!--<groupId>com.sun.activation</groupId>-->
|
||||||
<artifactId>javax.activation</artifactId>
|
<!--<artifactId>javax.activation</artifactId>-->
|
||||||
</dependency>
|
<!--</dependency>-->
|
||||||
<!--<dependency>
|
<!--<dependency>
|
||||||
<groupId>javax.validation</groupId>
|
<groupId>javax.validation</groupId>
|
||||||
<artifactId>validation-api</artifactId>
|
<artifactId>validation-api</artifactId>
|
||||||
|
@ -596,20 +593,20 @@
|
||||||
as JDK9 no longer includes them by default
|
as JDK9 no longer includes them by default
|
||||||
-->
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>org.glassfish.jaxb</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-runtime</artifactId>
|
||||||
<version>${jaxb_api_version}</version>
|
<version>${jaxb_runtime_version}</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
|
||||||
<artifactId>jaxb-core</artifactId>
|
|
||||||
<version>${jaxb_core_version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.sun.xml.bind</groupId>
|
|
||||||
<artifactId>jaxb-impl</artifactId>
|
|
||||||
<version>${jaxb_core_version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>com.sun.xml.bind</groupId>-->
|
||||||
|
<!--<artifactId>jaxb-core</artifactId>-->
|
||||||
|
<!--<version>${jaxb_core_version}</version>-->
|
||||||
|
<!--</dependency>-->
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>com.sun.xml.bind</groupId>-->
|
||||||
|
<!--<artifactId>jaxb-impl</artifactId>-->
|
||||||
|
<!--<version>${jaxb_core_version}</version>-->
|
||||||
|
<!--</dependency>-->
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
|
@ -2,18 +2,18 @@ package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||||
import ca.uhn.fhir.jpa.dao.DatabaseSearchParamProvider;
|
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||||
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
import ca.uhn.fhir.jpa.subscription.dbmatcher.CompositeInMemoryDaoSubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.config.BaseSubscriptionConfig;
|
import ca.uhn.fhir.jpa.subscription.dbmatcher.DaoSubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscriptionChannelFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
import ca.uhn.fhir.jpa.subscription.module.cache.BlockingQueueSubscriptionChannelFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -59,7 +59,8 @@ import javax.annotation.Nonnull;
|
||||||
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
|
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
|
||||||
@ComponentScan(basePackages = "ca.uhn.fhir.jpa", excludeFilters={
|
@ComponentScan(basePackages = "ca.uhn.fhir.jpa", excludeFilters={
|
||||||
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=BaseConfig.class),
|
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=BaseConfig.class),
|
||||||
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=WebSocketConfigurer.class)})
|
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=WebSocketConfigurer.class),
|
||||||
|
@ComponentScan.Filter(type=FilterType.REGEX, pattern="ca.uhn.fhir.jpa.subscription.module.standalone.*")})
|
||||||
|
|
||||||
public abstract class BaseConfig implements SchedulingConfigurer {
|
public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
|
|
||||||
|
@ -132,34 +133,29 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
protected ISearchParamProvider searchParamProvider() {
|
public InMemorySubscriptionMatcher inMemorySubscriptionMatcher() {
|
||||||
return new DatabaseSearchParamProvider();
|
return new InMemorySubscriptionMatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DaoSubscriptionMatcher daoSubscriptionMatcher() {
|
||||||
|
return new DaoSubscriptionMatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: If you're going to use this, you need to provide a bean
|
* Create a @Primary @Bean if you need a different implementation
|
||||||
* of type {@link ca.uhn.fhir.jpa.subscription.email.IEmailSender}
|
|
||||||
* in your own Spring config
|
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@Lazy
|
public ISubscriptionChannelFactory blockingQueueSubscriptionDeliveryChannelFactory() {
|
||||||
public SubscriptionEmailInterceptor subscriptionEmailInterceptor() {
|
return new BlockingQueueSubscriptionChannelFactory();
|
||||||
return new SubscriptionEmailInterceptor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Lazy
|
@Primary
|
||||||
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
|
public ISubscriptionMatcher subscriptionMatcherCompositeInMemoryDatabase() {
|
||||||
return new SubscriptionRestHookInterceptor();
|
return new CompositeInMemoryDaoSubscriptionMatcher(daoSubscriptionMatcher(), inMemorySubscriptionMatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Lazy
|
|
||||||
public SubscriptionWebsocketInterceptor subscriptionWebsocketInterceptor() {
|
|
||||||
return new SubscriptionWebsocketInterceptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
|
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
|
||||||
theFactory.setJpaDialect(hibernateJpaDialect(theCtx.getLocalizer()));
|
theFactory.setJpaDialect(hibernateJpaDialect(theCtx.getLocalizer()));
|
||||||
theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");
|
theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");
|
||||||
|
|
|
@ -116,7 +116,7 @@ public class BaseDstu2Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ISearchParamRegistry searchParamRegistry() {
|
public ISearchParamRegistry searchParamRegistry() {
|
||||||
return new SearchParamRegistryDstu2(searchParamProvider());
|
return new SearchParamRegistryDstu2();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "mySystemDaoDstu2", autowire = Autowire.BY_NAME)
|
@Bean(name = "mySystemDaoDstu2", autowire = Autowire.BY_NAME)
|
||||||
|
|
|
@ -20,9 +20,7 @@ package ca.uhn.fhir.jpa.config;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketHandler;
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.SubscriptionWebsocketHandler;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class BaseDstu3Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ISearchParamRegistry searchParamRegistry() {
|
public ISearchParamRegistry searchParamRegistry() {
|
||||||
return new SearchParamRegistryDstu3(searchParamProvider());
|
return new SearchParamRegistryDstu3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "mySystemDaoDstu3", autowire = Autowire.BY_NAME)
|
@Bean(name = "mySystemDaoDstu3", autowire = Autowire.BY_NAME)
|
||||||
|
|
|
@ -138,7 +138,7 @@ public class BaseR4Config extends BaseConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ISearchParamRegistry searchParamRegistry() {
|
public ISearchParamRegistry searchParamRegistry() {
|
||||||
return new SearchParamRegistryR4(searchParamProvider());
|
return new SearchParamRegistryR4();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "mySystemDaoR4", autowire = Autowire.BY_NAME)
|
@Bean(name = "mySystemDaoR4", autowire = Autowire.BY_NAME)
|
||||||
|
|
|
@ -2,16 +2,16 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import ca.uhn.fhir.context.*;
|
||||||
import ca.uhn.fhir.jpa.dao.data.*;
|
import ca.uhn.fhir.jpa.dao.data.*;
|
||||||
import ca.uhn.fhir.jpa.dao.index.*;
|
import ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer;
|
||||||
import ca.uhn.fhir.jpa.model.entity.*;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
|
import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.LogicalReferenceHelper;
|
import ca.uhn.fhir.jpa.searchparam.extractor.LogicalReferenceHelper;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
|
@ -59,7 +59,6 @@ import org.hibernate.internal.SessionImpl;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
@ -78,8 +77,6 @@ import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import javax.xml.stream.events.Characters;
|
import javax.xml.stream.events.Characters;
|
||||||
import javax.xml.stream.events.XMLEvent;
|
import javax.xml.stream.events.XMLEvent;
|
||||||
import java.io.CharArrayWriter;
|
|
||||||
import java.text.Normalizer;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -174,23 +171,17 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private ISearchDao mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||||
//@Autowired
|
//@Autowired
|
||||||
//private ISearchResultDao mySearchResultDao;
|
//private ISearchResultDao mySearchResultDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
|
||||||
@Autowired
|
|
||||||
private BeanFactory beanFactory;
|
|
||||||
@Autowired
|
|
||||||
private DaoRegistry myDaoRegistry;
|
private DaoRegistry myDaoRegistry;
|
||||||
@Autowired
|
@Autowired
|
||||||
private SearchParamExtractorService mySearchParamExtractorService;
|
|
||||||
@Autowired
|
|
||||||
private SearchParamWithInlineReferencesExtractor mySearchParamWithInlineReferencesExtractor;
|
private SearchParamWithInlineReferencesExtractor mySearchParamWithInlineReferencesExtractor;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DatabaseSearchParamSynchronizer myDatabaseSearchParamSynchronizer;
|
private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer;
|
||||||
|
@Autowired
|
||||||
|
private SearchBuilderFactory mySearchBuilderFactory;
|
||||||
|
|
||||||
private ApplicationContext myApplicationContext;
|
private ApplicationContext myApplicationContext;
|
||||||
|
|
||||||
|
@ -752,9 +743,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
||||||
return LogicalReferenceHelper.isLogicalReference(myConfig.getModelConfig(), theId);
|
return LogicalReferenceHelper.isLogicalReference(myConfig.getModelConfig(), theId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// TODO KHS inject a searchBuilderFactory into callers of this method and delete this method
|
||||||
public SearchBuilder newSearchBuilder() {
|
public SearchBuilder newSearchBuilder() {
|
||||||
return beanFactory.getBean(SearchBuilder.class, this);
|
return mySearchBuilderFactory.newSearchBuilder(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) {
|
public void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) {
|
||||||
|
@ -1412,7 +1403,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
||||||
* Indexing
|
* Indexing
|
||||||
*/
|
*/
|
||||||
if (thePerformIndexing) {
|
if (thePerformIndexing) {
|
||||||
myDatabaseSearchParamSynchronizer.synchronizeSearchParamsToDatabase(newParams, theEntity, existingParams);
|
myDaoSearchParamSynchronizer.synchronizeSearchParamsToDatabase(newParams, theEntity, existingParams);
|
||||||
mySearchParamWithInlineReferencesExtractor.storeCompositeStringUniques(newParams, theEntity, existingParams);
|
mySearchParamWithInlineReferencesExtractor.storeCompositeStringUniques(newParams, theEntity, existingParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,11 @@ import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
|
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
|
||||||
import ca.uhn.fhir.jpa.model.entity.*;
|
import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
||||||
|
@ -73,17 +71,19 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends BaseHapiFhirDao<T> implements IFhirResourceDao<T> {
|
public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends BaseHapiFhirDao<T> implements IFhirResourceDao<T> {
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected PlatformTransactionManager myPlatformTransactionManager;
|
protected PlatformTransactionManager myPlatformTransactionManager;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected DaoConfig myDaoConfig;
|
protected DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
private MatchResourceUrlService myMatchResourceUrlService;
|
||||||
|
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
private Class<T> myResourceType;
|
private Class<T> myResourceType;
|
||||||
private String mySecondaryPrimaryKeyParamName;
|
private String mySecondaryPrimaryKeyParamName;
|
||||||
@Autowired
|
|
||||||
private MatchResourceUrlService myMatchResourceUrlService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
||||||
|
|
|
@ -4,12 +4,12 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||||
import ca.uhn.fhir.jpa.search.warm.WarmCacheEntry;
|
import ca.uhn.fhir.jpa.search.warm.WarmCacheEntry;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.hl7.fhir.instance.model.Subscription;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -112,7 +112,7 @@ public class DaoConfig {
|
||||||
* update setter javadoc if default changes
|
* update setter javadoc if default changes
|
||||||
*/
|
*/
|
||||||
private boolean myIndexContainedResources = true;
|
private boolean myIndexContainedResources = true;
|
||||||
private List<IServerInterceptor> myInterceptors;
|
private List<IServerInterceptor> myInterceptors = new ArrayList<>();
|
||||||
/**
|
/**
|
||||||
* update setter javadoc if default changes
|
* update setter javadoc if default changes
|
||||||
*/
|
*/
|
||||||
|
@ -484,14 +484,26 @@ public class DaoConfig {
|
||||||
* Returns the interceptors which will be notified of operations.
|
* Returns the interceptors which will be notified of operations.
|
||||||
*
|
*
|
||||||
* @see #setInterceptors(List)
|
* @see #setInterceptors(List)
|
||||||
|
* @deprecated Marked as deprecated as of HAPI 3.7.0. Use {@link #registerInterceptor} or {@link #unregisterInterceptor}instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public List<IServerInterceptor> getInterceptors() {
|
public List<IServerInterceptor> getInterceptors() {
|
||||||
if (myInterceptors == null) {
|
|
||||||
myInterceptors = new ArrayList<>();
|
|
||||||
}
|
|
||||||
return myInterceptors;
|
return myInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerInterceptor(IServerInterceptor theInterceptor) {
|
||||||
|
Validate.notNull(theInterceptor, "Interceptor can not be null");
|
||||||
|
if (!myInterceptors.contains(theInterceptor)) {
|
||||||
|
myInterceptors.add(theInterceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterInterceptor(IServerInterceptor theInterceptor) {
|
||||||
|
Validate.notNull(theInterceptor, "Interceptor can not be null");
|
||||||
|
myInterceptors.remove(theInterceptor);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||||
*/
|
*/
|
||||||
|
@ -1462,6 +1474,47 @@ public class DaoConfig {
|
||||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(theDefaultSearchParamsCanBeOverridden);
|
myModelConfig.setDefaultSearchParamsCanBeOverridden(theDefaultSearchParamsCanBeOverridden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
|
||||||
|
* to the server matching these types will be activated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public DaoConfig addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) {
|
||||||
|
myModelConfig.addSupportedSubscriptionType(theSubscriptionChannelType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
|
||||||
|
* to the server matching these types will be activated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() {
|
||||||
|
return myModelConfig.getSupportedSubscriptionTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void clearSupportedSubscriptionTypesForUnitTest() {
|
||||||
|
myModelConfig.clearSupportedSubscriptionTypesForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If e-mail subscriptions are supported, the From address used when sending e-mails
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getEmailFromAddress() {
|
||||||
|
return myModelConfig.getEmailFromAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If e-mail subscriptions are supported, the From address used when sending e-mails
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void setEmailFromAddress(String theEmailFromAddress) {
|
||||||
|
myModelConfig.setEmailFromAddress(theEmailFromAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public enum IndexEnabledEnum {
|
public enum IndexEnabledEnum {
|
||||||
ENABLED,
|
ENABLED,
|
||||||
|
|
|
@ -20,17 +20,19 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
public class DatabaseSearchParamProvider implements ISearchParamProvider {
|
@Service
|
||||||
|
public class DaoSearchParamProvider implements ISearchParamProvider {
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformTransactionManager myTxManager;
|
private PlatformTransactionManager myTxManager;
|
||||||
@Autowired
|
@Autowired
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.websocket;
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -20,24 +20,11 @@ package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
import org.springframework.beans.factory.annotation.Lookup;
|
||||||
import ca.uhn.fhir.jpa.subscription.CanonicalSubscription;
|
import org.springframework.stereotype.Service;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
|
||||||
import org.springframework.messaging.MessageHandler;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class SubscriptionWebsocketInterceptor extends BaseSubscriptionInterceptor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Optional<MessageHandler> createDeliveryHandler(CanonicalSubscription theSubscription) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Subscription.SubscriptionChannelType getChannelType() {
|
|
||||||
return Subscription.SubscriptionChannelType.WEBSOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public abstract class SearchBuilderFactory {
|
||||||
|
@Lookup
|
||||||
|
public abstract SearchBuilder newSearchBuilder(BaseHapiFhirDao theBaseHapiFhirResourceDao);
|
||||||
}
|
}
|
|
@ -41,8 +41,8 @@ import javax.persistence.PersistenceContext;
|
||||||
import javax.persistence.PersistenceContextType;
|
import javax.persistence.PersistenceContextType;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DatabaseResourceLinkResolver implements IResourceLinkResolver {
|
public class DaoResourceLinkResolver implements IResourceLinkResolver {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DatabaseResourceLinkResolver.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DaoResourceLinkResolver.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
|
@ -35,7 +35,7 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DatabaseSearchParamSynchronizer {
|
public class DaoSearchParamSynchronizer {
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
|
|
|
@ -77,9 +77,9 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
@Autowired
|
@Autowired
|
||||||
ResourceLinkExtractor myResourceLinkExtractor;
|
ResourceLinkExtractor myResourceLinkExtractor;
|
||||||
@Autowired
|
@Autowired
|
||||||
DatabaseResourceLinkResolver myDatabaseResourceLinkResolver;
|
DaoResourceLinkResolver myDaoResourceLinkResolver;
|
||||||
@Autowired
|
@Autowired
|
||||||
DatabaseSearchParamSynchronizer myDatabaseSearchParamSynchronizer;
|
DaoSearchParamSynchronizer myDaoSearchParamSynchronizer;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
|
|
||||||
extractInlineReferences(theResource);
|
extractInlineReferences(theResource);
|
||||||
|
|
||||||
myResourceLinkExtractor.extractResourceLinks(theParams, theEntity, theResource, theUpdateTime, myDatabaseResourceLinkResolver);
|
myResourceLinkExtractor.extractResourceLinks(theParams, theEntity, theResource, theUpdateTime, myDaoResourceLinkResolver);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
||||||
|
@ -258,12 +258,12 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
|
|
||||||
// Store composite string uniques
|
// Store composite string uniques
|
||||||
if (myDaoConfig.isUniqueIndexesEnabled()) {
|
if (myDaoConfig.isUniqueIndexesEnabled()) {
|
||||||
for (ResourceIndexedCompositeStringUnique next : myDatabaseSearchParamSynchronizer.subtract(existingParams.compositeStringUniques, theParams.compositeStringUniques)) {
|
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(existingParams.compositeStringUniques, theParams.compositeStringUniques)) {
|
||||||
ourLog.debug("Removing unique index: {}", next);
|
ourLog.debug("Removing unique index: {}", next);
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
theEntity.getParamsCompositeStringUnique().remove(next);
|
theEntity.getParamsCompositeStringUnique().remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedCompositeStringUnique next : myDatabaseSearchParamSynchronizer.subtract(theParams.compositeStringUniques, existingParams.compositeStringUniques)) {
|
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.compositeStringUniques, existingParams.compositeStringUniques)) {
|
||||||
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
|
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
|
||||||
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
|
|
|
@ -28,7 +28,9 @@ import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
// Note: this class is not annotated with @Service because we want to
|
||||||
|
// explicitly define it in BaseConfig.java. This is done so that
|
||||||
|
// implementors can override if they want to.
|
||||||
public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider {
|
public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -58,4 +58,6 @@ public interface IResourceReindexingSvc {
|
||||||
* to be used by unit tests.
|
* to be used by unit tests.
|
||||||
*/
|
*/
|
||||||
void cancelAndPurgeAllJobs();
|
void cancelAndPurgeAllJobs();
|
||||||
|
|
||||||
|
int countReindexJobs();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,11 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
@ -98,6 +98,8 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
private EntityManager myEntityManager;
|
private EntityManager myEntityManager;
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setReindexJobDaoForUnitTest(IResourceReindexJobDao theReindexJobDao) {
|
void setReindexJobDaoForUnitTest(IResourceReindexJobDao theReindexJobDao) {
|
||||||
|
@ -186,7 +188,6 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
runReindexingPass();
|
runReindexingPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.NEVER)
|
@Transactional(Transactional.TxType.NEVER)
|
||||||
public Integer runReindexingPass() {
|
public Integer runReindexingPass() {
|
||||||
|
@ -203,7 +204,7 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer doReindexingPassInsideLock() {
|
private int doReindexingPassInsideLock() {
|
||||||
expungeJobsMarkedAsDeleted();
|
expungeJobsMarkedAsDeleted();
|
||||||
return runReindexJobs();
|
return runReindexJobs();
|
||||||
}
|
}
|
||||||
|
@ -233,13 +234,13 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int runReindexJobs() {
|
private int runReindexJobs() {
|
||||||
Collection<ResourceReindexJobEntity> jobs = myTxTemplate.execute(t -> myReindexJobDao.findAll(PageRequest.of(0, 10), false));
|
Collection<ResourceReindexJobEntity> jobs = getResourceReindexJobEntities();
|
||||||
assert jobs != null;
|
|
||||||
|
|
||||||
if (jobs.size() > 0) {
|
if (jobs.size() > 0) {
|
||||||
ourLog.info("Running {} reindex jobs: {}", jobs.size(), jobs);
|
ourLog.info("Running {} reindex jobs: {}", jobs.size(), jobs);
|
||||||
} else {
|
} else {
|
||||||
ourLog.debug("Running {} reindex jobs: {}", jobs.size(), jobs);
|
ourLog.debug("Running {} reindex jobs: {}", jobs.size(), jobs);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -255,6 +256,17 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countReindexJobs() {
|
||||||
|
return getResourceReindexJobEntities().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<ResourceReindexJobEntity> getResourceReindexJobEntities() {
|
||||||
|
Collection<ResourceReindexJobEntity> jobs = myTxTemplate.execute(t -> myReindexJobDao.findAll(PageRequest.of(0, 10), false));
|
||||||
|
assert jobs != null;
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
private void markJobAsDeleted(ResourceReindexJobEntity theJob) {
|
private void markJobAsDeleted(ResourceReindexJobEntity theJob) {
|
||||||
ourLog.info("Marking reindexing job ID[{}] as deleted", theJob.getId());
|
ourLog.info("Marking reindexing job ID[{}] as deleted", theJob.getId());
|
||||||
myTxTemplate.execute(t -> {
|
myTxTemplate.execute(t -> {
|
||||||
|
@ -263,6 +275,11 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setSearchParamRegistryForUnitTest(ISearchParamRegistry theSearchParamRegistry) {
|
||||||
|
mySearchParamRegistry = theSearchParamRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
private int runReindexJob(ResourceReindexJobEntity theJob) {
|
private int runReindexJob(ResourceReindexJobEntity theJob) {
|
||||||
if (theJob.getSuspendedUntil() != null) {
|
if (theJob.getSuspendedUntil() != null) {
|
||||||
if (theJob.getSuspendedUntil().getTime() > System.currentTimeMillis()) {
|
if (theJob.getSuspendedUntil().getTime() > System.currentTimeMillis()) {
|
||||||
|
@ -274,6 +291,16 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
AtomicInteger counter = new AtomicInteger();
|
AtomicInteger counter = new AtomicInteger();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On the first time we run a particular reindex job, let's make sure we
|
||||||
|
* have the latest search parameters loaded. A common reason to
|
||||||
|
* be reindexing is that the search parameters have changed in some way, so
|
||||||
|
* this makes sure we're on the latest versions
|
||||||
|
*/
|
||||||
|
if (theJob.getThresholdLow() == null) {
|
||||||
|
mySearchParamRegistry.forceRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate range
|
// Calculate range
|
||||||
Date low = theJob.getThresholdLow() != null ? theJob.getThresholdLow() : BEGINNING_OF_TIME;
|
Date low = theJob.getThresholdLow() != null ? theJob.getThresholdLow() : BEGINNING_OF_TIME;
|
||||||
Date high = theJob.getThresholdHigh();
|
Date high = theJob.getThresholdHigh();
|
||||||
|
@ -461,7 +488,7 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc {
|
||||||
|
|
||||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceTable.getResourceType());
|
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceTable.getResourceType());
|
||||||
long expectedVersion = resourceTable.getVersion();
|
long expectedVersion = resourceTable.getVersion();
|
||||||
IBaseResource resource = dao.read(resourceTable.getIdDt().toVersionless(), null,true);
|
IBaseResource resource = dao.read(resourceTable.getIdDt().toVersionless(), null, true);
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
throw new InternalErrorException("Could not find resource version " + resourceTable.getIdDt().toUnqualified().getValue() + " in database");
|
throw new InternalErrorException("Could not find resource version " + resourceTable.getIdDt().toUnqualified().getValue() + " in database");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,630 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|
||||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
|
|
||||||
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.matcher.SubscriptionMatcherCompositeInMemoryDatabase;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.matcher.SubscriptionMatcherDatabase;
|
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|
||||||
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.google.common.collect.Multimaps;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
|
||||||
import org.springframework.messaging.MessageChannel;
|
|
||||||
import org.springframework.messaging.MessageHandler;
|
|
||||||
import org.springframework.messaging.SubscribableChannel;
|
|
||||||
import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2018 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> extends ServerOperationInterceptorAdapter {
|
|
||||||
|
|
||||||
static final String SUBSCRIPTION_STATUS = "Subscription.status";
|
|
||||||
static final String SUBSCRIPTION_TYPE = "Subscription.channel.type";
|
|
||||||
private static final Integer MAX_SUBSCRIPTION_RESULTS = 1000;
|
|
||||||
private static boolean ourForcePayloadEncodeAndDecodeForUnitTests;
|
|
||||||
private final Object myInitSubscriptionsLock = new Object();
|
|
||||||
private SubscribableChannel myProcessingChannel;
|
|
||||||
private Map<String, SubscribableChannel> myDeliveryChannel;
|
|
||||||
private ExecutorService myProcessingExecutor;
|
|
||||||
private int myExecutorThreadCount;
|
|
||||||
private SubscriptionActivatingSubscriber mySubscriptionActivatingSubscriber;
|
|
||||||
private MessageHandler mySubscriptionCheckingSubscriber;
|
|
||||||
private ConcurrentHashMap<String, CanonicalSubscription> myIdToSubscription = new ConcurrentHashMap<>();
|
|
||||||
private ConcurrentHashMap<String, SubscribableChannel> mySubscribableChannel = new ConcurrentHashMap<>();
|
|
||||||
private Multimap<String, MessageHandler> myIdToDeliveryHandler = Multimaps.synchronizedListMultimap(ArrayListMultimap.create());
|
|
||||||
private Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionInterceptor.class);
|
|
||||||
private ThreadPoolExecutor myDeliveryExecutor;
|
|
||||||
private LinkedBlockingQueue<Runnable> myProcessingExecutorQueue;
|
|
||||||
@Autowired
|
|
||||||
private FhirContext myCtx;
|
|
||||||
@Autowired(required = false)
|
|
||||||
@Qualifier("myEventDefinitionDaoR4")
|
|
||||||
private IFhirResourceDao<org.hl7.fhir.r4.model.EventDefinition> myEventDefinitionDaoR4;
|
|
||||||
@Autowired()
|
|
||||||
private PlatformTransactionManager myTxManager;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier(BaseConfig.TASK_EXECUTOR_NAME)
|
|
||||||
private AsyncTaskExecutor myAsyncTaskExecutor;
|
|
||||||
@Autowired
|
|
||||||
private SubscriptionMatcherCompositeInMemoryDatabase mySubscriptionMatcherCompositeInMemoryDatabase;
|
|
||||||
@Autowired
|
|
||||||
private SubscriptionMatcherDatabase mySubscriptionMatcherDatabase;
|
|
||||||
@Autowired
|
|
||||||
private DaoRegistry myDaoRegistry;
|
|
||||||
@Autowired
|
|
||||||
private BeanFactory beanFactory;
|
|
||||||
@Autowired
|
|
||||||
private MatchUrlService myMatchUrlService;
|
|
||||||
private Semaphore myInitSubscriptionsSemaphore = new Semaphore(1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public BaseSubscriptionInterceptor() {
|
|
||||||
super();
|
|
||||||
setExecutorThreadCount(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonicalSubscription canonicalize(S theSubscription) {
|
|
||||||
switch (myCtx.getVersion().getVersion()) {
|
|
||||||
case DSTU2:
|
|
||||||
return canonicalizeDstu2(theSubscription);
|
|
||||||
case DSTU3:
|
|
||||||
return canonicalizeDstu3(theSubscription);
|
|
||||||
case R4:
|
|
||||||
return canonicalizeR4(theSubscription);
|
|
||||||
default:
|
|
||||||
throw new ConfigurationException("Subscription not supported for version: " + myCtx.getVersion().getVersion());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonicalSubscription canonicalizeDstu2(IBaseResource theSubscription) {
|
|
||||||
ca.uhn.fhir.model.dstu2.resource.Subscription subscription = (ca.uhn.fhir.model.dstu2.resource.Subscription) theSubscription;
|
|
||||||
|
|
||||||
CanonicalSubscription retVal = new CanonicalSubscription();
|
|
||||||
try {
|
|
||||||
retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(subscription.getStatus()));
|
|
||||||
retVal.setChannelType(org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.fromCode(subscription.getChannel().getType()));
|
|
||||||
retVal.setCriteriaString(subscription.getCriteria());
|
|
||||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
|
||||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
|
||||||
retVal.setIdElement(subscription.getIdElement());
|
|
||||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new InternalErrorException(theE);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonicalSubscription canonicalizeDstu3(IBaseResource theSubscription) {
|
|
||||||
org.hl7.fhir.dstu3.model.Subscription subscription = (org.hl7.fhir.dstu3.model.Subscription) theSubscription;
|
|
||||||
|
|
||||||
CanonicalSubscription retVal = new CanonicalSubscription();
|
|
||||||
try {
|
|
||||||
retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(subscription.getStatus().toCode()));
|
|
||||||
retVal.setChannelType(org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.fromCode(subscription.getChannel().getType().toCode()));
|
|
||||||
retVal.setCriteriaString(subscription.getCriteria());
|
|
||||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
|
||||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
|
||||||
retVal.setIdElement(subscription.getIdElement());
|
|
||||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
|
||||||
|
|
||||||
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.EMAIL) {
|
|
||||||
String from;
|
|
||||||
String subjectTemplate;
|
|
||||||
String bodyTemplate;
|
|
||||||
try {
|
|
||||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
|
||||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
|
||||||
}
|
|
||||||
retVal.getEmailDetails().setFrom(from);
|
|
||||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) {
|
|
||||||
String stripVersionIds;
|
|
||||||
String deliverLatestVersion;
|
|
||||||
try {
|
|
||||||
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
|
|
||||||
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
|
||||||
}
|
|
||||||
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
|
|
||||||
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new InternalErrorException(theE);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonicalSubscription canonicalizeR4(IBaseResource theSubscription) {
|
|
||||||
org.hl7.fhir.r4.model.Subscription subscription = (org.hl7.fhir.r4.model.Subscription) theSubscription;
|
|
||||||
|
|
||||||
CanonicalSubscription retVal = new CanonicalSubscription();
|
|
||||||
retVal.setStatus(subscription.getStatus());
|
|
||||||
retVal.setChannelType(subscription.getChannel().getType());
|
|
||||||
retVal.setCriteriaString(subscription.getCriteria());
|
|
||||||
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
|
||||||
retVal.setHeaders(subscription.getChannel().getHeader());
|
|
||||||
retVal.setIdElement(subscription.getIdElement());
|
|
||||||
retVal.setPayloadString(subscription.getChannel().getPayload());
|
|
||||||
|
|
||||||
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.EMAIL) {
|
|
||||||
String from;
|
|
||||||
String subjectTemplate;
|
|
||||||
try {
|
|
||||||
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
|
||||||
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
|
||||||
}
|
|
||||||
retVal.getEmailDetails().setFrom(from);
|
|
||||||
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) {
|
|
||||||
String stripVersionIds;
|
|
||||||
String deliverLatestVersion;
|
|
||||||
try {
|
|
||||||
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
|
|
||||||
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
|
|
||||||
} catch (FHIRException theE) {
|
|
||||||
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
|
||||||
}
|
|
||||||
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
|
|
||||||
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<org.hl7.fhir.r4.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
|
|
||||||
if (topicExts.size() > 0) {
|
|
||||||
IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive();
|
|
||||||
if (!"EventDefinition".equals(ref.getReferenceElement().getResourceType())) {
|
|
||||||
throw new PreconditionFailedException("Topic reference must be an EventDefinition");
|
|
||||||
}
|
|
||||||
|
|
||||||
org.hl7.fhir.r4.model.EventDefinition def = myEventDefinitionDaoR4.read(ref.getReferenceElement());
|
|
||||||
retVal.addTrigger(new CanonicalSubscription.CanonicalEventDefinition(def));
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SubscribableChannel createDeliveryChannel(CanonicalSubscription theSubscription) {
|
|
||||||
String subscriptionId = theSubscription.getIdElement(myCtx).getIdPart();
|
|
||||||
|
|
||||||
LinkedBlockingQueue<Runnable> executorQueue = new LinkedBlockingQueue<>(1000);
|
|
||||||
BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
|
|
||||||
.namingPattern("subscription-delivery-" + subscriptionId + "-%d")
|
|
||||||
.daemon(false)
|
|
||||||
.priority(Thread.NORM_PRIORITY)
|
|
||||||
.build();
|
|
||||||
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
|
|
||||||
@Override
|
|
||||||
public void rejectedExecution(Runnable theRunnable, ThreadPoolExecutor theExecutor) {
|
|
||||||
ourLog.info("Note: Executor queue is full ({} elements), waiting for a slot to become available!", executorQueue.size());
|
|
||||||
StopWatch sw = new StopWatch();
|
|
||||||
try {
|
|
||||||
executorQueue.put(theRunnable);
|
|
||||||
} catch (InterruptedException theE) {
|
|
||||||
throw new RejectedExecutionException("Task " + theRunnable.toString() +
|
|
||||||
" rejected from " + theE.toString());
|
|
||||||
}
|
|
||||||
ourLog.info("Slot become available after {}ms", sw.getMillis());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ThreadPoolExecutor deliveryExecutor = new ThreadPoolExecutor(
|
|
||||||
1,
|
|
||||||
getExecutorThreadCount(),
|
|
||||||
0L,
|
|
||||||
TimeUnit.MILLISECONDS,
|
|
||||||
executorQueue,
|
|
||||||
threadFactory,
|
|
||||||
rejectedExecutionHandler);
|
|
||||||
|
|
||||||
return new ExecutorSubscribableChannel(deliveryExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an empty handler if the interceptor will manually handle registration and unregistration
|
|
||||||
*/
|
|
||||||
protected abstract Optional<MessageHandler> createDeliveryHandler(CanonicalSubscription theSubscription);
|
|
||||||
|
|
||||||
public abstract Subscription.SubscriptionChannelType getChannelType();
|
|
||||||
|
|
||||||
protected MessageChannel getDeliveryChannel(CanonicalSubscription theSubscription) {
|
|
||||||
return mySubscribableChannel.get(theSubscription.getIdElement(myCtx).getIdPart());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExecutorQueueSizeForUnitTests() {
|
|
||||||
return myProcessingExecutorQueue.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExecutorThreadCount() {
|
|
||||||
return myExecutorThreadCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExecutorThreadCount(int theExecutorThreadCount) {
|
|
||||||
Validate.inclusiveBetween(1, Integer.MAX_VALUE, theExecutorThreadCount);
|
|
||||||
myExecutorThreadCount = theExecutorThreadCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, CanonicalSubscription> getIdToSubscription() {
|
|
||||||
return Collections.unmodifiableMap(myIdToSubscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubscribableChannel getProcessingChannel() {
|
|
||||||
return myProcessingChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProcessingChannel(SubscribableChannel theProcessingChannel) {
|
|
||||||
myProcessingChannel = theProcessingChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CanonicalSubscription> getRegisteredSubscriptions() {
|
|
||||||
return new ArrayList<>(myIdToSubscription.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
public CanonicalSubscription hasSubscription(IIdType theId) {
|
|
||||||
Validate.notNull(theId);
|
|
||||||
Validate.notBlank(theId.getIdPart());
|
|
||||||
return myIdToSubscription.get(theId.getIdPart());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the existing subscriptions from the database
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Scheduled(fixedDelay = 60000)
|
|
||||||
public void initSubscriptions() {
|
|
||||||
if (!myInitSubscriptionsSemaphore.tryAcquire()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
doInitSubscriptions();
|
|
||||||
} finally {
|
|
||||||
myInitSubscriptionsSemaphore.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer doInitSubscriptions() {
|
|
||||||
synchronized (myInitSubscriptionsLock) {
|
|
||||||
ourLog.debug("Starting init subscriptions");
|
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
|
||||||
map.add(Subscription.SP_TYPE, new TokenParam(null, getChannelType().toCode()));
|
|
||||||
map.add(Subscription.SP_STATUS, new TokenOrListParam()
|
|
||||||
.addOr(new TokenParam(null, Subscription.SubscriptionStatus.REQUESTED.toCode()))
|
|
||||||
.addOr(new TokenParam(null, Subscription.SubscriptionStatus.ACTIVE.toCode())));
|
|
||||||
map.setLoadSynchronousUpTo(MAX_SUBSCRIPTION_RESULTS);
|
|
||||||
|
|
||||||
RequestDetails req = new ServletSubRequestDetails();
|
|
||||||
req.setSubRequest(true);
|
|
||||||
|
|
||||||
IFhirResourceDao<?> subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
|
||||||
IBundleProvider subscriptionBundleList = subscriptionDao.search(map, req);
|
|
||||||
if (subscriptionBundleList.size() >= MAX_SUBSCRIPTION_RESULTS) {
|
|
||||||
ourLog.error("Currently over " + MAX_SUBSCRIPTION_RESULTS + " subscriptions. Some subscriptions have not been loaded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IBaseResource> resourceList = subscriptionBundleList.getResources(0, subscriptionBundleList.size());
|
|
||||||
|
|
||||||
Set<String> allIds = new HashSet<>();
|
|
||||||
int changesCount = 0;
|
|
||||||
for (IBaseResource resource : resourceList) {
|
|
||||||
String nextId = resource.getIdElement().getIdPart();
|
|
||||||
allIds.add(nextId);
|
|
||||||
boolean changed = mySubscriptionActivatingSubscriber.activateOrRegisterSubscriptionIfRequired(resource);
|
|
||||||
if (changed) {
|
|
||||||
changesCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterAllSubscriptionsNotInCollection(allIds);
|
|
||||||
ourLog.trace("Finished init subscriptions - found {}", resourceList.size());
|
|
||||||
|
|
||||||
return changesCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@PreDestroy
|
|
||||||
public void preDestroy() {
|
|
||||||
getProcessingChannel().unsubscribe(mySubscriptionCheckingSubscriber);
|
|
||||||
unregisterAllSubscriptionsNotInCollection(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerHandler(String theSubscriptionId, MessageHandler theHandler) {
|
|
||||||
mySubscribableChannel.get(theSubscriptionId).subscribe(theHandler);
|
|
||||||
myIdToDeliveryHandler.put(theSubscriptionId, theHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
public CanonicalSubscription registerSubscription(IIdType theId, S theSubscription) {
|
|
||||||
Validate.notNull(theId);
|
|
||||||
String subscriptionId = theId.getIdPart();
|
|
||||||
Validate.notBlank(subscriptionId);
|
|
||||||
Validate.notNull(theSubscription);
|
|
||||||
|
|
||||||
CanonicalSubscription canonicalized = canonicalize(theSubscription);
|
|
||||||
SubscribableChannel deliveryChannel = createDeliveryChannel(canonicalized);
|
|
||||||
Optional<MessageHandler> deliveryHandler = createDeliveryHandler(canonicalized);
|
|
||||||
|
|
||||||
mySubscribableChannel.put(subscriptionId, deliveryChannel);
|
|
||||||
myIdToSubscription.put(subscriptionId, canonicalized);
|
|
||||||
|
|
||||||
deliveryHandler.ifPresent(handler -> registerHandler(subscriptionId, handler));
|
|
||||||
|
|
||||||
return canonicalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void registerSubscriptionCheckingSubscriber() {
|
|
||||||
if (mySubscriptionCheckingSubscriber == null) {
|
|
||||||
mySubscriptionCheckingSubscriber = beanFactory.getBean(SubscriptionCheckingSubscriber.class, getChannelType(), this, mySubscriptionMatcherCompositeInMemoryDatabase);
|
|
||||||
}
|
|
||||||
getProcessingChannel().subscribe(mySubscriptionCheckingSubscriber);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
|
||||||
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) {
|
|
||||||
ResourceModifiedMessage msg = new ResourceModifiedMessage();
|
|
||||||
msg.setId(theResource.getIdElement());
|
|
||||||
msg.setOperationType(ResourceModifiedMessage.OperationTypeEnum.DELETE);
|
|
||||||
submitResourceModified(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
|
|
||||||
submitResourceModifiedForUpdate(theNewResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
void submitResourceModifiedForUpdate(IBaseResource theNewResource) {
|
|
||||||
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) {
|
|
||||||
ResourceModifiedMessage msg = new ResourceModifiedMessage();
|
|
||||||
msg.setId(theNewResource.getIdElement());
|
|
||||||
msg.setOperationType(theOperationType);
|
|
||||||
msg.setNewPayload(myCtx, theNewResource);
|
|
||||||
if (ourForcePayloadEncodeAndDecodeForUnitTests) {
|
|
||||||
msg.clearPayloadDecoded();
|
|
||||||
}
|
|
||||||
submitResourceModified(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendToProcessingChannel(final ResourceModifiedMessage theMessage) {
|
|
||||||
ourLog.trace("Registering synchronization to send resource modified message to processing channel");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We only actually submit this item work working after the
|
|
||||||
*/
|
|
||||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
ourLog.trace("Sending resource modified message to processing channel");
|
|
||||||
getProcessingChannel().send(new ResourceModifiedJsonMessage(theMessage));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ourLog.trace("Sending resource modified message to processing channel");
|
|
||||||
getProcessingChannel().send(new ResourceModifiedJsonMessage(theMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setAsyncTaskExecutorForUnitTest(AsyncTaskExecutor theAsyncTaskExecutor) {
|
|
||||||
myAsyncTaskExecutor = theAsyncTaskExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFhirContext(FhirContext theCtx) {
|
|
||||||
myCtx = theCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setTxManager(PlatformTransactionManager theTxManager) {
|
|
||||||
myTxManager = theTxManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void start() {
|
|
||||||
if (myCtx.getVersion().getVersion() == FhirVersionEnum.R4) {
|
|
||||||
Validate.notNull(myEventDefinitionDaoR4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getProcessingChannel() == null) {
|
|
||||||
myProcessingExecutorQueue = new LinkedBlockingQueue<>(1000);
|
|
||||||
RejectedExecutionHandler rejectedExecutionHandler = (theRunnable, theExecutor) -> {
|
|
||||||
ourLog.info("Note: Executor queue is full ({} elements), waiting for a slot to become available!", myProcessingExecutorQueue.size());
|
|
||||||
StopWatch sw = new StopWatch();
|
|
||||||
try {
|
|
||||||
myProcessingExecutorQueue.put(theRunnable);
|
|
||||||
} catch (InterruptedException theE) {
|
|
||||||
throw new RejectedExecutionException("Task " + theRunnable.toString() +
|
|
||||||
" rejected from " + theE.toString());
|
|
||||||
}
|
|
||||||
ourLog.info("Slot become available after {}ms", sw.getMillis());
|
|
||||||
};
|
|
||||||
ThreadFactory threadFactory = new BasicThreadFactory.Builder()
|
|
||||||
.namingPattern("subscription-proc-%d")
|
|
||||||
.daemon(false)
|
|
||||||
.priority(Thread.NORM_PRIORITY)
|
|
||||||
.build();
|
|
||||||
myProcessingExecutor = new ThreadPoolExecutor(
|
|
||||||
1,
|
|
||||||
getExecutorThreadCount(),
|
|
||||||
0L,
|
|
||||||
TimeUnit.MILLISECONDS,
|
|
||||||
myProcessingExecutorQueue,
|
|
||||||
threadFactory,
|
|
||||||
rejectedExecutionHandler);
|
|
||||||
setProcessingChannel(new ExecutorSubscribableChannel(myProcessingExecutor));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mySubscriptionActivatingSubscriber == null) {
|
|
||||||
IFhirResourceDao<?> subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
|
||||||
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(subscriptionDao, getChannelType(), this, myTxManager, myAsyncTaskExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerSubscriptionCheckingSubscriber();
|
|
||||||
|
|
||||||
TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager);
|
|
||||||
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
|
||||||
initSubscriptions();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an internal API - Use with caution!
|
|
||||||
*/
|
|
||||||
public void submitResourceModified(final ResourceModifiedMessage theMsg) {
|
|
||||||
mySubscriptionActivatingSubscriber.handleMessage(theMsg.getOperationType(), theMsg.getId(myCtx), theMsg.getNewPayload(myCtx));
|
|
||||||
sendToProcessingChannel(theMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unregisterAllSubscriptionsNotInCollection(Collection<String> theAllIds) {
|
|
||||||
for (String next : new ArrayList<>(myIdToSubscription.keySet())) {
|
|
||||||
if (!theAllIds.contains(next)) {
|
|
||||||
ourLog.info("Unregistering Subscription/{}", next);
|
|
||||||
CanonicalSubscription subscription = myIdToSubscription.get(next);
|
|
||||||
unregisterSubscription(subscription.getIdElement(myCtx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterHandler(String theSubscriptionId, MessageHandler theMessageHandler) {
|
|
||||||
SubscribableChannel channel = mySubscribableChannel.get(theSubscriptionId);
|
|
||||||
if (channel != null) {
|
|
||||||
channel.unsubscribe(theMessageHandler);
|
|
||||||
if (channel instanceof DisposableBean) {
|
|
||||||
try {
|
|
||||||
((DisposableBean) channel).destroy();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ourLog.error("Failed to destroy channel bean", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mySubscribableChannel.remove(theSubscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
public CanonicalSubscription unregisterSubscription(IIdType theId) {
|
|
||||||
Validate.notNull(theId);
|
|
||||||
|
|
||||||
String subscriptionId = theId.getIdPart();
|
|
||||||
Validate.notBlank(subscriptionId);
|
|
||||||
|
|
||||||
for (MessageHandler next : new ArrayList<>(myIdToDeliveryHandler.get(subscriptionId))) {
|
|
||||||
unregisterHandler(subscriptionId, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
mySubscribableChannel.remove(subscriptionId);
|
|
||||||
|
|
||||||
return myIdToSubscription.remove(subscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFhirResourceDao<?> getSubscriptionDao() {
|
|
||||||
return myDaoRegistry.getSubscriptionDao();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFhirResourceDao getDao(Class type) {
|
|
||||||
return myDaoRegistry.getResourceDao(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceDaos(List<IFhirResourceDao> theResourceDaos) {
|
|
||||||
myDaoRegistry.setResourceDaos(theResourceDaos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateCriteria(final S theResource) {
|
|
||||||
CanonicalSubscription subscription = canonicalize(theResource);
|
|
||||||
String criteria = subscription.getCriteriaString();
|
|
||||||
try {
|
|
||||||
RuntimeResourceDefinition resourceDef = CacheWarmingSvcImpl.parseUrlResourceType(myCtx, criteria);
|
|
||||||
myMatchUrlService.translateMatchUrl(criteria, resourceDef);
|
|
||||||
} catch (InvalidRequestException e) {
|
|
||||||
throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + criteria + " " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void setForcePayloadEncodeAndDecodeForUnitTests(boolean theForcePayloadEncodeAndDecodeForUnitTests) {
|
|
||||||
ourForcePayloadEncodeAndDecodeForUnitTests = theForcePayloadEncodeAndDecodeForUnitTests;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.messaging.MessageHandler;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
public abstract class BaseSubscriptionSubscriber implements MessageHandler {
|
|
||||||
|
|
||||||
private final Subscription.SubscriptionChannelType myChannelType;
|
|
||||||
private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
|
|
||||||
@Autowired
|
|
||||||
DaoRegistry myDaoRegistry;
|
|
||||||
private IFhirResourceDao<?> mySubscriptionDao;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public BaseSubscriptionSubscriber(Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
|
|
||||||
myChannelType = theChannelType;
|
|
||||||
mySubscriptionInterceptor = theSubscriptionInterceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused") // Don't delete, used in Smile
|
|
||||||
public void setDaoRegistry(DaoRegistry theDaoRegistry) {
|
|
||||||
myDaoRegistry = theDaoRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void setSubscriptionDao() {
|
|
||||||
mySubscriptionDao = myDaoRegistry.getSubscriptionDao();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Subscription.SubscriptionChannelType getChannelType() {
|
|
||||||
return myChannelType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FhirContext getContext() {
|
|
||||||
return getSubscriptionDao().getContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFhirResourceDao getSubscriptionDao() {
|
|
||||||
return mySubscriptionDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseSubscriptionInterceptor getSubscriptionInterceptor() {
|
|
||||||
return mySubscriptionInterceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
|
|
||||||
*/
|
|
||||||
protected boolean subscriptionTypeApplies(CanonicalSubscription theSubscription) {
|
|
||||||
Subscription.SubscriptionChannelType channelType = getChannelType();
|
|
||||||
String subscriptionType = theSubscription.getChannelType().toCode();
|
|
||||||
return subscriptionTypeApplies(subscriptionType, channelType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
|
|
||||||
*/
|
|
||||||
static boolean subscriptionTypeApplies(String theSubscriptionChannelTypeCode, Subscription.SubscriptionChannelType theChannelType) {
|
|
||||||
boolean subscriptionTypeApplies = false;
|
|
||||||
if (theSubscriptionChannelTypeCode != null) {
|
|
||||||
if (theChannelType.toCode().equals(theSubscriptionChannelTypeCode)) {
|
|
||||||
subscriptionTypeApplies = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subscriptionTypeApplies;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ActiveSubscription;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.IResourceRetriever;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DaoResourceRetriever implements IResourceRetriever {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(ActiveSubscription.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
DaoRegistry myDaoRegistry;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException {
|
||||||
|
RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType());
|
||||||
|
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass());
|
||||||
|
return dao.read(payloadId.toVersionless());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,247 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionCannonicalizer;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||||
|
import ca.uhn.fhir.util.SubscriptionUtil;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.hl7.fhir.instance.model.Subscription;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for transitioning subscription resources from REQUESTED to ACTIVE
|
||||||
|
* Once activated, the subscription is added to the SubscriptionRegistry.
|
||||||
|
*
|
||||||
|
* Also validates criteria. If invalid, rejects the subscription without persisting the subscription.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class SubscriptionActivatingInterceptor extends ServerOperationInterceptorAdapter {
|
||||||
|
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingInterceptor.class);
|
||||||
|
|
||||||
|
private static boolean ourWaitForSubscriptionActivationSynchronouslyForUnitTest;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager myTransactionManager;
|
||||||
|
@Autowired
|
||||||
|
@Qualifier(BaseConfig.TASK_EXECUTOR_NAME)
|
||||||
|
private AsyncTaskExecutor myTaskExecutor;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionRegistry mySubscriptionRegistry;
|
||||||
|
@Autowired
|
||||||
|
private DaoRegistry myDaoRegistry;
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionCannonicalizer mySubscriptionCannonicalizer;
|
||||||
|
@Autowired
|
||||||
|
private MatchUrlService myMatchUrlService;
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
|
|
||||||
|
public boolean activateOrRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
|
||||||
|
// Grab the value for "Subscription.channel.type" so we can see if this
|
||||||
|
// subscriber applies..
|
||||||
|
String subscriptionChannelTypeCode = myFhirContext
|
||||||
|
.newTerser()
|
||||||
|
.getSingleValueOrNull(theSubscription, SubscriptionMatcherInterceptor.SUBSCRIPTION_TYPE, IPrimitiveType.class)
|
||||||
|
.getValueAsString();
|
||||||
|
|
||||||
|
Subscription.SubscriptionChannelType subscriptionChannelType = Subscription.SubscriptionChannelType.fromCode(subscriptionChannelTypeCode);
|
||||||
|
// Only activate supported subscriptions
|
||||||
|
if (!myDaoConfig.getSupportedSubscriptionTypes().contains(subscriptionChannelType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final IPrimitiveType<?> status = myFhirContext.newTerser().getSingleValueOrNull(theSubscription, SubscriptionMatcherInterceptor.SUBSCRIPTION_STATUS, IPrimitiveType.class);
|
||||||
|
String statusString = status.getValueAsString();
|
||||||
|
|
||||||
|
final String requestedStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
|
||||||
|
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
|
||||||
|
if (requestedStatus.equals(statusString)) {
|
||||||
|
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||||
|
/*
|
||||||
|
* If we're in a transaction, we don't want to try and change the status from
|
||||||
|
* requested to active within the same transaction because it's too late by
|
||||||
|
* the time we get here to make modifications to the payload.
|
||||||
|
*
|
||||||
|
* So, we register a synchronization, meaning that when the transaction is
|
||||||
|
* finished, we'll schedule a task to do this in a separate worker thread
|
||||||
|
* to avoid any possibility of conflict.
|
||||||
|
*/
|
||||||
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||||
|
@Override
|
||||||
|
public void afterCommit() {
|
||||||
|
Future<?> activationFuture = myTaskExecutor.submit(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
activateSubscription(activeStatus, theSubscription, requestedStatus);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're running in a unit test, it's nice to be predictable in
|
||||||
|
* terms of order... In the real world it's a recipe for deadlocks
|
||||||
|
*/
|
||||||
|
if (ourWaitForSubscriptionActivationSynchronouslyForUnitTest) {
|
||||||
|
try {
|
||||||
|
activationFuture.get(5, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ourLog.error("Failed to activate subscription", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return activateSubscription(activeStatus, theSubscription, requestedStatus);
|
||||||
|
}
|
||||||
|
} else if (activeStatus.equals(statusString)) {
|
||||||
|
return mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(theSubscription);
|
||||||
|
} else {
|
||||||
|
// Status isn't "active" or "requested"
|
||||||
|
return mySubscriptionRegistry.unregisterSubscriptionIfRegistered(theSubscription, statusString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean activateSubscription(String theActiveStatus, final IBaseResource theSubscription, String theRequestedStatus) {
|
||||||
|
IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
||||||
|
IBaseResource subscription = subscriptionDao.read(theSubscription.getIdElement());
|
||||||
|
|
||||||
|
ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus);
|
||||||
|
try {
|
||||||
|
SubscriptionUtil.setStatus(myFhirContext, subscription, theActiveStatus);
|
||||||
|
subscription = subscriptionDao.update(subscription).getResource();
|
||||||
|
submitResourceModifiedForUpdate(subscription);
|
||||||
|
return true;
|
||||||
|
} catch (final UnprocessableEntityException e) {
|
||||||
|
ourLog.info("Changing status of {} to ERROR", subscription.getIdElement());
|
||||||
|
SubscriptionUtil.setStatus(myFhirContext, subscription, "error");
|
||||||
|
SubscriptionUtil.setReason(myFhirContext, subscription, e.getMessage());
|
||||||
|
subscriptionDao.update(subscription);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void submitResourceModifiedForUpdate(IBaseResource theNewResource) {
|
||||||
|
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||||
|
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) {
|
||||||
|
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||||
|
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) {
|
||||||
|
submitResourceModified(new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void submitResourceModified(final ResourceModifiedMessage theMsg) {
|
||||||
|
IIdType id = theMsg.getId(myFhirContext);
|
||||||
|
if (!id.getResourceType().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (theMsg.getOperationType()) {
|
||||||
|
case DELETE:
|
||||||
|
mySubscriptionRegistry.unregisterSubscription(id);
|
||||||
|
break;
|
||||||
|
case CREATE:
|
||||||
|
case UPDATE:
|
||||||
|
final IBaseResource subscription = theMsg.getNewPayload(myFhirContext);
|
||||||
|
validateCriteria(subscription);
|
||||||
|
activateAndRegisterSubscriptionIfRequiredInTransaction(subscription);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateCriteria(final IBaseResource theResource) {
|
||||||
|
CanonicalSubscription subscription = mySubscriptionCannonicalizer.canonicalize(theResource);
|
||||||
|
String criteria = subscription.getCriteriaString();
|
||||||
|
try {
|
||||||
|
RuntimeResourceDefinition resourceDef = CacheWarmingSvcImpl.parseUrlResourceType(myFhirContext, criteria);
|
||||||
|
myMatchUrlService.translateMatchUrl(criteria, resourceDef);
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + criteria + " " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activateAndRegisterSubscriptionIfRequiredInTransaction(IBaseResource theSubscription) {
|
||||||
|
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
|
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||||
|
activateOrRegisterSubscriptionIfRequired(theSubscription);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void setWaitForSubscriptionActivationSynchronouslyForUnitTest(boolean theWaitForSubscriptionActivationSynchronouslyForUnitTest) {
|
||||||
|
ourWaitForSubscriptionActivationSynchronouslyForUnitTest = theWaitForSubscriptionActivationSynchronouslyForUnitTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,220 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|
||||||
import ca.uhn.fhir.util.SubscriptionUtil;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
|
||||||
import org.springframework.messaging.MessagingException;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class SubscriptionActivatingSubscriber {
|
|
||||||
private static boolean ourWaitForSubscriptionActivationSynchronouslyForUnitTest;
|
|
||||||
private final IFhirResourceDao mySubscriptionDao;
|
|
||||||
private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
|
|
||||||
private final PlatformTransactionManager myTransactionManager;
|
|
||||||
private final AsyncTaskExecutor myTaskExecutor;
|
|
||||||
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
|
|
||||||
private FhirContext myCtx;
|
|
||||||
private Subscription.SubscriptionChannelType myChannelType;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor, PlatformTransactionManager theTransactionManager, AsyncTaskExecutor theTaskExecutor) {
|
|
||||||
mySubscriptionDao = theSubscriptionDao;
|
|
||||||
mySubscriptionInterceptor = theSubscriptionInterceptor;
|
|
||||||
myChannelType = theChannelType;
|
|
||||||
myCtx = theSubscriptionDao.getContext();
|
|
||||||
myTransactionManager = theTransactionManager;
|
|
||||||
myTaskExecutor = theTaskExecutor;
|
|
||||||
Validate.notNull(theTaskExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean activateOrRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
|
|
||||||
// Grab the value for "Subscription.channel.type" so we can see if this
|
|
||||||
// subscriber applies..
|
|
||||||
String subscriptionChannelType = myCtx
|
|
||||||
.newTerser()
|
|
||||||
.getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_TYPE, IPrimitiveType.class)
|
|
||||||
.getValueAsString();
|
|
||||||
boolean subscriptionTypeApplies = BaseSubscriptionSubscriber.subscriptionTypeApplies(subscriptionChannelType, myChannelType);
|
|
||||||
if (subscriptionTypeApplies == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IPrimitiveType<?> status = myCtx.newTerser().getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_STATUS, IPrimitiveType.class);
|
|
||||||
String statusString = status.getValueAsString();
|
|
||||||
|
|
||||||
final String requestedStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
|
|
||||||
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
|
|
||||||
if (requestedStatus.equals(statusString)) {
|
|
||||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
||||||
/*
|
|
||||||
* If we're in a transaction, we don't want to try and change the status from
|
|
||||||
* requested to active within the same transaction because it's too late by
|
|
||||||
* the time we get here to make modifications to the payload.
|
|
||||||
*
|
|
||||||
* So, we register a synchronization, meaning that when the transaction is
|
|
||||||
* finished, we'll schedule a task to do this in a separate worker thread
|
|
||||||
* to avoid any possibility of conflict.
|
|
||||||
*/
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
Future<?> activationFuture = myTaskExecutor.submit(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activateSubscription(activeStatus, theSubscription, requestedStatus);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we're running in a unit test, it's nice to be predictable in
|
|
||||||
* terms of order... In the real world it's a recipe for deadlocks
|
|
||||||
*/
|
|
||||||
if (ourWaitForSubscriptionActivationSynchronouslyForUnitTest) {
|
|
||||||
try {
|
|
||||||
activationFuture.get(5, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception e) {
|
|
||||||
ourLog.error("Failed to activate subscription", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return activateSubscription(activeStatus, theSubscription, requestedStatus);
|
|
||||||
}
|
|
||||||
} else if (activeStatus.equals(statusString)) {
|
|
||||||
return registerSubscriptionUnlessAlreadyRegistered(theSubscription);
|
|
||||||
} else {
|
|
||||||
// Status isn't "active" or "requested"
|
|
||||||
return unregisterSubscriptionIfRegistered(theSubscription, statusString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean unregisterSubscriptionIfRegistered(IBaseResource theSubscription, String theStatusString) {
|
|
||||||
if (mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement()) != null) {
|
|
||||||
ourLog.info("Removing {} subscription {}", theStatusString, theSubscription.getIdElement().toUnqualified().getValue());
|
|
||||||
mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean activateSubscription(String theActiveStatus, final IBaseResource theSubscription, String theRequestedStatus) {
|
|
||||||
IBaseResource subscription = mySubscriptionDao.read(theSubscription.getIdElement());
|
|
||||||
|
|
||||||
ourLog.info("Activating subscription {} from status {} to {} for channel {}", subscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus, myChannelType);
|
|
||||||
try {
|
|
||||||
SubscriptionUtil.setStatus(myCtx, subscription, theActiveStatus);
|
|
||||||
subscription = mySubscriptionDao.update(subscription).getResource();
|
|
||||||
mySubscriptionInterceptor.submitResourceModifiedForUpdate(subscription);
|
|
||||||
return true;
|
|
||||||
} catch (final UnprocessableEntityException e) {
|
|
||||||
ourLog.info("Changing status of {} to ERROR", subscription.getIdElement());
|
|
||||||
SubscriptionUtil.setStatus(myCtx, subscription, "error");
|
|
||||||
SubscriptionUtil.setReason(myCtx, subscription, e.getMessage());
|
|
||||||
mySubscriptionDao.update(subscription);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
|
||||||
public void handleMessage(ResourceModifiedMessage.OperationTypeEnum theOperationType, IIdType theId, final IBaseResource theSubscription) throws MessagingException {
|
|
||||||
if (!theId.getResourceType().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (theOperationType) {
|
|
||||||
case DELETE:
|
|
||||||
mySubscriptionInterceptor.unregisterSubscription(theId);
|
|
||||||
break;
|
|
||||||
case CREATE:
|
|
||||||
case UPDATE:
|
|
||||||
mySubscriptionInterceptor.validateCriteria(theSubscription);
|
|
||||||
activateAndRegisterSubscriptionIfRequiredInTransaction(theSubscription);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void activateAndRegisterSubscriptionIfRequiredInTransaction(IBaseResource theSubscription) {
|
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
|
|
||||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
||||||
@Override
|
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
|
||||||
activateOrRegisterSubscriptionIfRequired(theSubscription);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized boolean registerSubscriptionUnlessAlreadyRegistered(IBaseResource theSubscription) {
|
|
||||||
CanonicalSubscription existingSubscription = mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement());
|
|
||||||
CanonicalSubscription newSubscription = mySubscriptionInterceptor.canonicalize(theSubscription);
|
|
||||||
|
|
||||||
if (existingSubscription != null) {
|
|
||||||
if (newSubscription.equals(existingSubscription)) {
|
|
||||||
// No changes
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingSubscription != null) {
|
|
||||||
ourLog.info("Updating already-registered active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
|
|
||||||
mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement());
|
|
||||||
} else {
|
|
||||||
ourLog.info("Registering active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
|
|
||||||
}
|
|
||||||
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void setWaitForSubscriptionActivationSynchronouslyForUnitTest(boolean theWaitForSubscriptionActivationSynchronouslyForUnitTest) {
|
|
||||||
ourWaitForSubscriptionActivationSynchronouslyForUnitTest = theWaitForSubscriptionActivationSynchronouslyForUnitTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.hl7.fhir.instance.model.Subscription;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SubscriptionInterceptorLoader {
|
||||||
|
@Autowired
|
||||||
|
DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
@Autowired
|
||||||
|
SubscriptionActivatingInterceptor mySubscriptionActivatingInterceptor;
|
||||||
|
|
||||||
|
public void registerInterceptors() {
|
||||||
|
Set<Subscription.SubscriptionChannelType> supportedSubscriptionTypes = myDaoConfig.getSupportedSubscriptionTypes();
|
||||||
|
|
||||||
|
if (!supportedSubscriptionTypes.isEmpty()) {
|
||||||
|
myDaoConfig.registerInterceptor(mySubscriptionActivatingInterceptor);
|
||||||
|
myDaoConfig.registerInterceptor(mySubscriptionMatcherInterceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void unregisterInterceptorsForUnitTest() {
|
||||||
|
myDaoConfig.unregisterInterceptor(mySubscriptionActivatingInterceptor);
|
||||||
|
myDaoConfig.unregisterInterceptor(mySubscriptionMatcherInterceptor);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.SubscriptionChannel;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscriptionChannelFactory;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceModifiedJsonMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.SubscriptionCheckingSubscriber;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.messaging.SubscribableChannel;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SubscriptionMatcherInterceptor extends ServerOperationInterceptorAdapter {
|
||||||
|
private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatcherInterceptor.class);
|
||||||
|
|
||||||
|
static final String SUBSCRIPTION_STATUS = "Subscription.status";
|
||||||
|
static final String SUBSCRIPTION_TYPE = "Subscription.channel.type";
|
||||||
|
private static boolean ourForcePayloadEncodeAndDecodeForUnitTests;
|
||||||
|
private SubscribableChannel myProcessingChannel;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionCheckingSubscriber mySubscriptionCheckingSubscriber;
|
||||||
|
@Autowired
|
||||||
|
private ISubscriptionChannelFactory mySubscriptionChannelFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public SubscriptionMatcherInterceptor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void start() {
|
||||||
|
if (myProcessingChannel == null) {
|
||||||
|
myProcessingChannel = mySubscriptionChannelFactory.newMatchingChannel("subscription-matching");
|
||||||
|
}
|
||||||
|
myProcessingChannel.subscribe(mySubscriptionCheckingSubscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@PreDestroy
|
||||||
|
public void preDestroy() {
|
||||||
|
myProcessingChannel.unsubscribe(mySubscriptionCheckingSubscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||||
|
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) {
|
||||||
|
submitResourceModified(theResource, ResourceModifiedMessage.OperationTypeEnum.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||||
|
submitResourceModified(theNewResource, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType) {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType);
|
||||||
|
if (ourForcePayloadEncodeAndDecodeForUnitTests) {
|
||||||
|
msg.clearPayloadDecoded();
|
||||||
|
}
|
||||||
|
submitResourceModified(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sendToProcessingChannel(final ResourceModifiedMessage theMessage) {
|
||||||
|
ourLog.trace("Sending resource modified message to processing channel");
|
||||||
|
myProcessingChannel.send(new ResourceModifiedJsonMessage(theMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFhirContext(FhirContext theCtx) {
|
||||||
|
myFhirContext = theCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an internal API - Use with caution!
|
||||||
|
*/
|
||||||
|
public void submitResourceModified(final ResourceModifiedMessage theMsg) {
|
||||||
|
sendToProcessingChannel(theMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void setForcePayloadEncodeAndDecodeForUnitTests(boolean theForcePayloadEncodeAndDecodeForUnitTests) {
|
||||||
|
ourForcePayloadEncodeAndDecodeForUnitTests = theForcePayloadEncodeAndDecodeForUnitTests;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public SubscriptionChannel getProcessingChannelForUnitTest() {
|
||||||
|
return (SubscriptionChannel) myProcessingChannel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,13 +22,16 @@ package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
|
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscriptionChannelFactory;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
@ -38,6 +41,7 @@ import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
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.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.ValidateUtil;
|
import ca.uhn.fhir.util.ValidateUtil;
|
||||||
|
@ -71,26 +75,31 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc, ApplicationContextAware {
|
public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc, ApplicationContextAware {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionTriggeringProvider.class);
|
||||||
|
|
||||||
public static final int DEFAULT_MAX_SUBMIT = 10000;
|
public static final int DEFAULT_MAX_SUBMIT = 10000;
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionTriggeringProvider.class);
|
|
||||||
private final List<SubscriptionTriggeringJobDetails> myActiveJobs = new ArrayList<>();
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoRegistry myDaoRegistry;
|
private DaoRegistry myDaoRegistry;
|
||||||
private List<BaseSubscriptionInterceptor<?>> mySubscriptionInterceptorList;
|
@Autowired
|
||||||
private int myMaxSubmitPerPass = DEFAULT_MAX_SUBMIT;
|
private DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
private ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MatchUrlService myMatchUrlService;
|
private MatchUrlService myMatchUrlService;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
|
||||||
|
private final List<SubscriptionTriggeringJobDetails> myActiveJobs = new ArrayList<>();
|
||||||
|
private int myMaxSubmitPerPass = DEFAULT_MAX_SUBMIT;
|
||||||
private ApplicationContext myAppCtx;
|
private ApplicationContext myAppCtx;
|
||||||
private ExecutorService myExecutorService;
|
private ExecutorService myExecutorService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseParameters triggerSubscription(List<UriParam> theResourceIds, List<StringParam> theSearchUrls, @IdParam IIdType theSubscriptionId) {
|
public IBaseParameters triggerSubscription(List<UriParam> theResourceIds, List<StringParam> theSearchUrls, @IdParam IIdType theSubscriptionId) {
|
||||||
if (mySubscriptionInterceptorList.isEmpty()) {
|
if (myDaoConfig.getSupportedSubscriptionTypes().isEmpty()) {
|
||||||
throw new PreconditionFailedException("Subscription processing not active on this server");
|
throw new PreconditionFailedException("Subscription processing not active on this server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,18 +302,13 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
|
|
||||||
ourLog.info("Submitting resource {} to subscription {}", theResourceToTrigger.getIdElement().toUnqualifiedVersionless().getValue(), theSubscriptionId);
|
ourLog.info("Submitting resource {} to subscription {}", theResourceToTrigger.getIdElement().toUnqualifiedVersionless().getValue(), theSubscriptionId);
|
||||||
|
|
||||||
ResourceModifiedMessage msg = new ResourceModifiedMessage();
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResourceToTrigger, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
msg.setId(theResourceToTrigger.getIdElement());
|
|
||||||
msg.setOperationType(ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
|
||||||
msg.setSubscriptionId(new IdType(theSubscriptionId).toUnqualifiedVersionless().getValue());
|
msg.setSubscriptionId(new IdType(theSubscriptionId).toUnqualifiedVersionless().getValue());
|
||||||
msg.setNewPayload(myFhirContext, theResourceToTrigger);
|
|
||||||
|
|
||||||
return myExecutorService.submit(() -> {
|
return myExecutorService.submit(() -> {
|
||||||
for (int i = 0; ; i++) {
|
for (int i = 0; ; i++) {
|
||||||
try {
|
try {
|
||||||
for (BaseSubscriptionInterceptor<?> next : mySubscriptionInterceptorList) {
|
mySubscriptionMatcherInterceptor.submitResourceModified(msg);
|
||||||
next.submitResourceModified(msg);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (i >= 3) {
|
if (i >= 3) {
|
||||||
|
@ -347,13 +351,6 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void start() {
|
public void start() {
|
||||||
mySubscriptionInterceptorList = ObjectUtils.defaultIfNull(mySubscriptionInterceptorList, Collections.emptyList());
|
|
||||||
mySubscriptionInterceptorList = new ArrayList<>();
|
|
||||||
Collection values1 = myAppCtx.getBeansOfType(BaseSubscriptionInterceptor.class).values();
|
|
||||||
Collection<BaseSubscriptionInterceptor<?>> values = (Collection<BaseSubscriptionInterceptor<?>>) values1;
|
|
||||||
mySubscriptionInterceptorList.addAll(values);
|
|
||||||
|
|
||||||
|
|
||||||
LinkedBlockingQueue<Runnable> executorQueue = new LinkedBlockingQueue<>(1000);
|
LinkedBlockingQueue<Runnable> executorQueue = new LinkedBlockingQueue<>(1000);
|
||||||
BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
|
BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
|
||||||
.namingPattern("SubscriptionTriggering-%d")
|
.namingPattern("SubscriptionTriggering-%d")
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription.dbcache;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscriptionProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DaoSubscriptionProvider implements ISubscriptionProvider {
|
||||||
|
@Autowired
|
||||||
|
DaoRegistry myDaoRegistry;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionActivatingInterceptor mySubscriptionActivatingInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBundleProvider search(SearchParameterMap theMap) {
|
||||||
|
IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
||||||
|
RequestDetails req = new ServletSubRequestDetails();
|
||||||
|
req.setSubRequest(true);
|
||||||
|
|
||||||
|
return subscriptionDao.search(theMap, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean loadSubscription(IBaseResource theResource) {
|
||||||
|
return mySubscriptionActivatingInterceptor.activateOrRegisterSubscriptionIfRequired(theResource);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.matcher;
|
package ca.uhn.fhir.jpa.subscription.dbmatcher;
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -21,34 +21,38 @@ package ca.uhn.fhir.jpa.subscription.matcher;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
public class CompositeInMemoryDaoSubscriptionMatcher implements ISubscriptionMatcher {
|
||||||
public class SubscriptionMatcherCompositeInMemoryDatabase implements ISubscriptionMatcher {
|
private Logger ourLog = LoggerFactory.getLogger(CompositeInMemoryDaoSubscriptionMatcher.class);
|
||||||
private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatcherCompositeInMemoryDatabase.class);
|
|
||||||
|
|
||||||
@Autowired
|
private final DaoSubscriptionMatcher myDaoSubscriptionMatcher;
|
||||||
SubscriptionMatcherDatabase mySubscriptionMatcherDatabase;
|
private final InMemorySubscriptionMatcher myInMemorySubscriptionMatcher;
|
||||||
@Autowired
|
|
||||||
SubscriptionMatcherInMemory mySubscriptionMatcherInMemory;
|
|
||||||
@Autowired
|
@Autowired
|
||||||
DaoConfig myDaoConfig;
|
DaoConfig myDaoConfig;
|
||||||
|
|
||||||
|
public CompositeInMemoryDaoSubscriptionMatcher(DaoSubscriptionMatcher theDaoSubscriptionMatcher, InMemorySubscriptionMatcher theInMemorySubscriptionMatcher) {
|
||||||
|
myDaoSubscriptionMatcher = theDaoSubscriptionMatcher;
|
||||||
|
myInMemorySubscriptionMatcher = theInMemorySubscriptionMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubscriptionMatchResult match(String criteria, ResourceModifiedMessage msg) {
|
public SubscriptionMatchResult match(String criteria, ResourceModifiedMessage msg) {
|
||||||
SubscriptionMatchResult result;
|
SubscriptionMatchResult result;
|
||||||
if (myDaoConfig.isEnableInMemorySubscriptionMatching()) {
|
if (myDaoConfig.isEnableInMemorySubscriptionMatching()) {
|
||||||
result = mySubscriptionMatcherInMemory.match(criteria, msg);
|
result = myInMemorySubscriptionMatcher.match(criteria, msg);
|
||||||
if (!result.supported()) {
|
if (!result.supported()) {
|
||||||
ourLog.info("Criteria {} not supported by InMemoryMatcher: {}. Reverting to DatabaseMatcher", criteria, result.getUnsupportedReason());
|
ourLog.info("Criteria {} not supported by InMemoryMatcher: {}. Reverting to DatabaseMatcher", criteria, result.getUnsupportedReason());
|
||||||
result = mySubscriptionMatcherDatabase.match(criteria, msg);
|
result = myDaoSubscriptionMatcher.match(criteria, msg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = mySubscriptionMatcherDatabase.match(criteria, msg);
|
result = myDaoSubscriptionMatcher.match(criteria, msg);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.matcher;
|
package ca.uhn.fhir.jpa.subscription.dbmatcher;
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -24,10 +24,12 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
|
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.subscription.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.matcher.SubscriptionMatchResult;
|
||||||
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.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -35,13 +37,9 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
public class DaoSubscriptionMatcher implements ISubscriptionMatcher {
|
||||||
@Lazy
|
private Logger ourLog = LoggerFactory.getLogger(DaoSubscriptionMatcher.class);
|
||||||
public class SubscriptionMatcherDatabase implements ISubscriptionMatcher {
|
|
||||||
private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatcherDatabase.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
|
@ -1,89 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.email;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.CanonicalSubscription;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.messaging.MessageHandler;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: If you're going to use this, you need to provide a bean
|
|
||||||
* of type {@link ca.uhn.fhir.jpa.subscription.email.IEmailSender}
|
|
||||||
* in your own Spring config
|
|
||||||
*/
|
|
||||||
public class SubscriptionEmailInterceptor extends BaseSubscriptionInterceptor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is set to autowired=false just so that implementors can supply this
|
|
||||||
* with a mechanism other than autowiring if they want
|
|
||||||
*/
|
|
||||||
@Autowired(required = false)
|
|
||||||
private IEmailSender myEmailSender;
|
|
||||||
@Autowired
|
|
||||||
BeanFactory myBeanFactory;
|
|
||||||
private String myDefaultFromAddress = "noreply@unknown.com";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Optional<MessageHandler> createDeliveryHandler(CanonicalSubscription theSubscription) {
|
|
||||||
return Optional.of(myBeanFactory.getBean(SubscriptionDeliveringEmailSubscriber.class, getChannelType(), this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType getChannelType() {
|
|
||||||
return org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.EMAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The "from" address to use for any sent emails that to not explicitly specity a from address
|
|
||||||
*/
|
|
||||||
public String getDefaultFromAddress() {
|
|
||||||
return myDefaultFromAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The "from" address to use for any sent emails that to not explicitly specity a from address
|
|
||||||
*/
|
|
||||||
public void setDefaultFromAddress(String theDefaultFromAddress) {
|
|
||||||
Validate.notBlank(theDefaultFromAddress, "theDefaultFromAddress must not be null or blank");
|
|
||||||
myDefaultFromAddress = theDefaultFromAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEmailSender getEmailSender() {
|
|
||||||
return myEmailSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the email sender (this method does not need to be explicitly called if you
|
|
||||||
* are using autowiring to supply the sender)
|
|
||||||
*/
|
|
||||||
public void setEmailSender(IEmailSender theEmailSender) {
|
|
||||||
myEmailSender = theEmailSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.matcher;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.subscription.ResourceModifiedMessage;
|
|
||||||
|
|
||||||
public interface ISubscriptionMatcher {
|
|
||||||
boolean match(String criteria, ResourceModifiedMessage msg);
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.resthook;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.CanonicalSubscription;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.messaging.MessageHandler;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class SubscriptionRestHookInterceptor extends BaseSubscriptionInterceptor {
|
|
||||||
@Autowired
|
|
||||||
BeanFactory myBeanFactory;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Optional<MessageHandler> createDeliveryHandler(CanonicalSubscription theSubscription) {
|
|
||||||
SubscriptionDeliveringRestHookSubscriber value = myBeanFactory.getBean(SubscriptionDeliveringRestHookSubscriber.class, getChannelType(), this);
|
|
||||||
return Optional.of(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType getChannelType() {
|
|
||||||
return org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType.RESTHOOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -23,51 +23,6 @@ package ca.uhn.fhir.jpa.util;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
|
||||||
public class JpaConstants {
|
public class JpaConstants {
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* This extension should be of type <code>string</code> and should be
|
|
||||||
* placed on the <code>Subscription.channel</code> element
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final String EXT_SUBSCRIPTION_EMAIL_FROM = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* This extension should be of type <code>string</code> and should be
|
|
||||||
* placed on the <code>Subscription.channel</code> element
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This extension URL indicates whether a REST HOOK delivery should
|
|
||||||
* include the version ID when delivering.
|
|
||||||
* <p>
|
|
||||||
* This extension should be of type <code>boolean</code> and should be
|
|
||||||
* placed on the <code>Subscription.channel</code> element.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This extension URL indicates whether a REST HOOK delivery should
|
|
||||||
* reload the resource and deliver the latest version always. This
|
|
||||||
* could be useful for example if a resource which triggers a
|
|
||||||
* subscription gets updated many times in short succession and there
|
|
||||||
* is no value in delivering the older versions.
|
|
||||||
* <p>
|
|
||||||
* Note that if the resource is now deleted, this may cause
|
|
||||||
* the delivery to be cancelled altogether.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This extension should be of type <code>boolean</code> and should be
|
|
||||||
* placed on the <code>Subscription.channel</code> element.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version";
|
|
||||||
/**
|
/**
|
||||||
* Operation name for the $expunge operation
|
* Operation name for the $expunge operation
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.SubscriptionDeliveringRestHookSubscriber;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.MessagingException;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
public class StoppableSubscriptionDeliveringRestHookSubscriber extends SubscriptionDeliveringRestHookSubscriber {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(StoppableSubscriptionDeliveringRestHookSubscriber.class);
|
||||||
|
|
||||||
|
private boolean myPauseEveryMessage = false;
|
||||||
|
private CountDownLatch myCountDownLatch;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message theMessage) throws MessagingException {
|
||||||
|
if (myCountDownLatch != null) {
|
||||||
|
myCountDownLatch.countDown();
|
||||||
|
}
|
||||||
|
if (myPauseEveryMessage) {
|
||||||
|
waitIfPaused();
|
||||||
|
}
|
||||||
|
super.handleMessage(theMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void waitIfPaused() {
|
||||||
|
try {
|
||||||
|
if (myPauseEveryMessage) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
ourLog.error("interrupted", theE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
myPauseEveryMessage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unPause() {
|
||||||
|
myPauseEveryMessage = false;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCountDownLatch(CountDownLatch theCountDownLatch) {
|
||||||
|
myCountDownLatch = theCountDownLatch;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,23 @@
|
||||||
package ca.uhn.fhir.jpa.config;
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.IEmailSender;
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.IEmailSender;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.JavaMailEmailSender;
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.JavaMailEmailSender;
|
||||||
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.support.ProxyDataSourceBuilder;
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
|
||||||
import org.springframework.context.annotation.*;
|
import org.springframework.context.annotation.*;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import javax.persistence.EntityManagerFactory;
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(TestJPAConfig.class)
|
@Import(TestJPAConfig.class)
|
||||||
|
|
|
@ -2,12 +2,12 @@ package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import org.springframework.beans.BeansException;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.SubscriptionDeliveringRestHookSubscriber;
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.context.ApplicationContextAware;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
|
|
||||||
|
@ -38,4 +38,16 @@ public class TestJPAConfig {
|
||||||
public UnregisterScheduledProcessor unregisterScheduledProcessor(Environment theEnv) {
|
public UnregisterScheduledProcessor unregisterScheduledProcessor(Environment theEnv) {
|
||||||
return new UnregisterScheduledProcessor(theEnv);
|
return new UnregisterScheduledProcessor(theEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public SubscriptionTestUtil subscriptionTestUtil() {
|
||||||
|
return new SubscriptionTestUtil();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public SubscriptionDeliveringRestHookSubscriber stoppableSubscriptionDeliveringRestHookSubscriber() {
|
||||||
|
return new StoppableSubscriptionDeliveringRestHookSubscriber();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
package ca.uhn.fhir.jpa.config;
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|
||||||
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;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import javax.persistence.EntityManagerFactory;
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@ -102,8 +96,8 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
|
|
||||||
DataSource dataSource = ProxyDataSourceBuilder
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
.create(retVal)
|
.create(retVal)
|
||||||
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
// .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||||
// .countQuery(new ThreadQueryCountHolder())
|
// .countQuery(new ThreadQueryCountHolder())
|
||||||
.countQuery(singleQueryCountHolder())
|
.countQuery(singleQueryCountHolder())
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -16,6 +16,7 @@ import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
|
||||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
|
@ -181,6 +182,8 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
protected IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> myValueSetDao;
|
protected IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> myValueSetDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
@Autowired
|
||||||
|
protected SubscriptionLoader mySubscriptionLoader;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeCreateInterceptor() {
|
public void beforeCreateInterceptor() {
|
||||||
|
|
|
@ -2,8 +2,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingSubscriber;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.dstu3.model.Subscription;
|
import org.hl7.fhir.dstu3.model.Subscription;
|
||||||
|
@ -23,19 +22,20 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Test {
|
public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SubscriptionRestHookInterceptor myInterceptor;
|
private DaoConfig myDaoConfig;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterResetDao() {
|
public void afterResetDao() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
||||||
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
|
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -83,8 +83,6 @@ public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Tes
|
||||||
});
|
});
|
||||||
|
|
||||||
myEntityManager.clear();
|
myEntityManager.clear();
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,9 +102,6 @@ public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Tes
|
||||||
assertNotNull(id.getIdPart());
|
assertNotNull(id.getIdPart());
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,9 +119,6 @@ public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Tes
|
||||||
IIdType id = mySubscriptionDao.create(s).getId().toUnqualifiedVersionless();
|
IIdType id = mySubscriptionDao.create(s).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,9 +137,6 @@ public class FhirResourceDaoDstu3InvalidSubscriptionTest extends BaseJpaDstu3Tes
|
||||||
assertNotNull(id.getIdPart());
|
assertNotNull(id.getIdPart());
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -15,7 +15,7 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||||
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
|
||||||
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
|
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
|
@ -223,8 +223,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
@Qualifier("mySearchParameterDaoR4")
|
@Qualifier("mySearchParameterDaoR4")
|
||||||
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
|
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
|
||||||
@Autowired
|
|
||||||
protected ISearchParamRegistry mySearchParamRegsitry;
|
protected ISearchParamRegistry mySearchParamRegsitry;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
||||||
|
@ -273,6 +271,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
protected ICacheWarmingSvc myCacheWarmingSvc;
|
protected ICacheWarmingSvc myCacheWarmingSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
|
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
|
||||||
|
@Autowired
|
||||||
|
protected SubscriptionRegistry mySubscriptionRegistry;
|
||||||
|
|
||||||
@After()
|
@After()
|
||||||
public void afterCleanupDao() {
|
public void afterCleanupDao() {
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.*;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
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.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||||
import org.junit.*;
|
import org.hl7.fhir.r4.model.Task;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
@SuppressWarnings({"unchecked", "deprecation"})
|
||||||
public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
|
public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCreatePlaceholdersR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCreatePlaceholdersR4Test.class);
|
||||||
|
@ -25,6 +28,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
|
||||||
@After
|
@After
|
||||||
public final void afterResetDao() {
|
public final void afterResetDao() {
|
||||||
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(new DaoConfig().isAutoCreatePlaceholderReferenceTargets());
|
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(new DaoConfig().isAutoCreatePlaceholderReferenceTargets());
|
||||||
|
myDaoConfig.setResourceClientIdStrategy(new DaoConfig().getResourceClientIdStrategy());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -97,7 +101,7 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWithBadReferenceIsPermitted() {
|
public void testUpdateWithBadReferenceIsPermittedAlphanumeric() {
|
||||||
assertFalse(myDaoConfig.isAutoCreatePlaceholderReferenceTargets());
|
assertFalse(myDaoConfig.isAutoCreatePlaceholderReferenceTargets());
|
||||||
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
||||||
|
|
||||||
|
@ -105,11 +109,49 @@ public class FhirResourceDaoCreatePlaceholdersR4Test extends BaseJpaR4Test {
|
||||||
o.setStatus(ObservationStatus.FINAL);
|
o.setStatus(ObservationStatus.FINAL);
|
||||||
IIdType id = myObservationDao.create(o, mySrd).getId();
|
IIdType id = myObservationDao.create(o, mySrd).getId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
myPatientDao.read(new IdType("Patient/FOO"));
|
||||||
|
fail();
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
o = new Observation();
|
o = new Observation();
|
||||||
o.setId(id);
|
o.setId(id);
|
||||||
o.setStatus(ObservationStatus.FINAL);
|
o.setStatus(ObservationStatus.FINAL);
|
||||||
o.getSubject().setReference("Patient/FOO");
|
o.getSubject().setReference("Patient/FOO");
|
||||||
myObservationDao.update(o, mySrd);
|
myObservationDao.update(o, mySrd);
|
||||||
|
|
||||||
|
myPatientDao.read(new IdType("Patient/FOO"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithBadReferenceIsPermittedNumeric() {
|
||||||
|
assertFalse(myDaoConfig.isAutoCreatePlaceholderReferenceTargets());
|
||||||
|
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
||||||
|
myDaoConfig.setResourceClientIdStrategy(DaoConfig.ClientIdStrategyEnum.ANY);
|
||||||
|
|
||||||
|
Observation o = new Observation();
|
||||||
|
o.setStatus(ObservationStatus.FINAL);
|
||||||
|
IIdType id = myObservationDao.create(o, mySrd).getId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
myPatientDao.read(new IdType("Patient/999999999999999"));
|
||||||
|
fail();
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
o = new Observation();
|
||||||
|
o.setId(id);
|
||||||
|
o.setStatus(ObservationStatus.FINAL);
|
||||||
|
o.getSubject().setReference("Patient/999999999999999");
|
||||||
|
myObservationDao.update(o, mySrd);
|
||||||
|
|
||||||
|
|
||||||
|
myPatientDao.read(new IdType("Patient/999999999999999"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -2,8 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingSubscriber;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -12,7 +11,6 @@ import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
@ -23,19 +21,16 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SubscriptionRestHookInterceptor myInterceptor;
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterResetDao() {
|
public void afterResetDao() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
||||||
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
|
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -83,8 +78,6 @@ public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
});
|
});
|
||||||
|
|
||||||
myEntityManager.clear();
|
myEntityManager.clear();
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,9 +97,6 @@ public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
assertNotNull(id.getIdPart());
|
assertNotNull(id.getIdPart());
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,9 +114,6 @@ public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
IIdType id = mySubscriptionDao.create(s).getId().toUnqualifiedVersionless();
|
IIdType id = mySubscriptionDao.create(s).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,9 +132,6 @@ public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
assertNotNull(id.getIdPart());
|
assertNotNull(id.getIdPart());
|
||||||
|
|
||||||
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
BaseHapiFhirDao.setValidationDisabledForUnitTest(false);
|
||||||
|
|
||||||
myInterceptor.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
|
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||||
|
@ -89,6 +90,11 @@ public class SearchParamExtractorR4Test {
|
||||||
public Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
|
public Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSearchParamProviderForUnitTest(ISearchParamProvider theSearchParamProvider) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.provider;
|
||||||
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu2.BaseJpaDstu2Test;
|
import ca.uhn.fhir.jpa.dao.dstu2.BaseJpaDstu2Test;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
|
@ -46,7 +45,6 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
|
||||||
protected static Server ourServer;
|
protected static Server ourServer;
|
||||||
protected static String ourServerBase;
|
protected static String ourServerBase;
|
||||||
protected static GenericWebApplicationContext ourWebApplicationContext;
|
protected static GenericWebApplicationContext ourWebApplicationContext;
|
||||||
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
|
|
||||||
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
||||||
protected static PlatformTransactionManager ourTxManager;
|
protected static PlatformTransactionManager ourTxManager;
|
||||||
protected static Integer ourConnectionPoolSize;
|
protected static Integer ourConnectionPoolSize;
|
||||||
|
@ -101,7 +99,6 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
|
||||||
ourWebApplicationContext.setParent(myAppCtx);
|
ourWebApplicationContext.setParent(myAppCtx);
|
||||||
ourWebApplicationContext.refresh();
|
ourWebApplicationContext.refresh();
|
||||||
|
|
||||||
ourRestHookSubscriptionInterceptor = ourWebApplicationContext.getBean(SubscriptionRestHookInterceptor.class);
|
|
||||||
ourTxManager = ourWebApplicationContext.getBean(PlatformTransactionManager.class);
|
ourTxManager = ourWebApplicationContext.getBean(PlatformTransactionManager.class);
|
||||||
|
|
||||||
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
|
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
|
||||||
|
|
|
@ -7,8 +7,6 @@ import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3;
|
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||||
|
@ -60,8 +58,6 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
protected static GenericWebApplicationContext ourWebApplicationContext;
|
protected static GenericWebApplicationContext ourWebApplicationContext;
|
||||||
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
|
||||||
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
protected static DatabaseBackedPagingProvider ourPagingProvider;
|
||||||
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
|
|
||||||
protected static SubscriptionEmailInterceptor ourEmailSubscriptionInterceptor;
|
|
||||||
protected static ISearchDao mySearchEntityDao;
|
protected static ISearchDao mySearchEntityDao;
|
||||||
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
@ -158,8 +154,6 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
myValidationSupport = wac.getBean(JpaValidationSupportChainDstu3.class);
|
myValidationSupport = wac.getBean(JpaValidationSupportChainDstu3.class);
|
||||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||||
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
||||||
ourRestHookSubscriptionInterceptor = wac.getBean(SubscriptionRestHookInterceptor.class);
|
|
||||||
ourEmailSubscriptionInterceptor = wac.getBean(SubscriptionEmailInterceptor.class);
|
|
||||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
|
||||||
ourSubscriptionTriggeringProvider = wac.getBean(SubscriptionTriggeringProvider.class);
|
ourSubscriptionTriggeringProvider = wac.getBean(SubscriptionTriggeringProvider.class);
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@ package ca.uhn.fhir.jpa.provider.r4;
|
||||||
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR4;
|
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR4;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionMatcherInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
|
||||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
@ -33,6 +34,7 @@ import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.context.ContextLoader;
|
import org.springframework.web.context.ContextLoader;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
|
@ -61,7 +63,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
protected static ISearchDao mySearchEntityDao;
|
protected static ISearchDao mySearchEntityDao;
|
||||||
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
protected static GenericWebApplicationContext ourWebApplicationContext;
|
protected static GenericWebApplicationContext ourWebApplicationContext;
|
||||||
protected static SubscriptionRestHookInterceptor ourReskHookSubscriptionInterceptor;
|
protected static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
protected IGenericClient ourClient;
|
protected IGenericClient ourClient;
|
||||||
protected ResourceCountCache ourResourceCountsCache;
|
protected ResourceCountCache ourResourceCountsCache;
|
||||||
|
@ -69,6 +71,9 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
private Object ourGraphQLProvider;
|
private Object ourGraphQLProvider;
|
||||||
private boolean ourRestHookSubscriptionInterceptorRequested;
|
private boolean ourRestHookSubscriptionInterceptorRequested;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected SubscriptionLoader mySubscriptionLoader;
|
||||||
|
|
||||||
public BaseResourceProviderR4Test() {
|
public BaseResourceProviderR4Test() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -156,7 +161,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||||
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
mySearchEntityDao = wac.getBean(ISearchDao.class);
|
||||||
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
|
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
|
||||||
ourReskHookSubscriptionInterceptor = wac.getBean(SubscriptionRestHookInterceptor.class);
|
ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
||||||
|
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
|
||||||
|
|
||||||
|
@ -177,19 +182,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is lazy created so we only ask for it if its needed
|
|
||||||
*/
|
|
||||||
protected SubscriptionRestHookInterceptor getRestHookSubscriptionInterceptor() {
|
|
||||||
SubscriptionRestHookInterceptor retVal = ourWebApplicationContext.getBean(SubscriptionRestHookInterceptor.class);
|
|
||||||
ourRestHookSubscriptionInterceptorRequested = true;
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean hasRestHookSubscriptionInterceptor() {
|
|
||||||
return ourRestHookSubscriptionInterceptorRequested;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean shouldLogClient() {
|
protected boolean shouldLogClient() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -206,21 +198,20 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void waitForRegisteredSubscriptionCount(int theSize) throws Exception {
|
protected void waitForActivatedSubscriptionCount(int theSize) throws Exception {
|
||||||
for (int i = 0; ; i++) {
|
for (int i = 0; ; i++) {
|
||||||
if (i == 10) {
|
if (i == 10) {
|
||||||
fail("Failed to init subscriptions");
|
fail("Failed to init subscriptions");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
getRestHookSubscriptionInterceptor().doInitSubscriptions();
|
mySubscriptionLoader.initSubscriptions();
|
||||||
break;
|
break;
|
||||||
} catch (ResourceVersionConflictException e) {
|
} catch (ResourceVersionConflictException e) {
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SubscriptionRestHookInterceptor interceptor = getRestHookSubscriptionInterceptor();
|
TestUtil.waitForSize(theSize, () -> mySubscriptionRegistry.size());
|
||||||
TestUtil.waitForSize(theSize, () -> interceptor.getRegisteredSubscriptions().size());
|
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
@ -19,13 +21,10 @@ import org.apache.http.entity.StringEntity;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
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.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -235,6 +234,32 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateReflexResourceTheHardWay() throws IOException, ServletException {
|
||||||
|
ServerOperationInterceptorAdapter interceptor = new ReflexInterceptor();
|
||||||
|
|
||||||
|
ourRestServer.registerInterceptor(interceptor);
|
||||||
|
try {
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType pid = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Bundle observations = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource("Observation")
|
||||||
|
.where(Observation.SUBJECT.hasId(pid))
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertEquals(1, observations.getEntry().size());
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(observations));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
ourRestServer.unregisterInterceptor(interceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
|
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
|
||||||
String methodName = "testCreateResourceWithVersionedReference";
|
String methodName = "testCreateResourceWithVersionedReference";
|
||||||
|
@ -353,6 +378,26 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ReflexInterceptor extends ServerOperationInterceptorAdapter {
|
||||||
|
@Override
|
||||||
|
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||||
|
if (theResource instanceof Patient) {
|
||||||
|
((ServletRequestDetails) theRequest).getServletRequest().setAttribute("CREATED_PATIENT", theResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||||
|
Patient createdPatient = (Patient) theRequestDetails.getServletRequest().getAttribute("CREATED_PATIENT");
|
||||||
|
if (createdPatient != null) {
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.setSubject(new Reference(createdPatient.getId()));
|
||||||
|
|
||||||
|
ourClient.create().resource(observation).execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClassClearContext() {
|
public static void afterClassClearContext() {
|
||||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
|
|
@ -2772,6 +2772,105 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
|
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTerminologyWithCompleteCs_Expand() throws Exception {
|
||||||
|
|
||||||
|
CodeSystem cs = new CodeSystem();
|
||||||
|
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||||
|
cs.setUrl("http://cs");
|
||||||
|
CodeSystem.ConceptDefinitionComponent a = cs.addConcept()
|
||||||
|
.setCode("A");
|
||||||
|
a.addConcept().setCode("A1");
|
||||||
|
a.addConcept().setCode("A2");
|
||||||
|
CodeSystem.ConceptDefinitionComponent b = cs.addConcept()
|
||||||
|
.setCode("B");
|
||||||
|
b.addConcept().setCode("B1");
|
||||||
|
b.addConcept().setCode("B2");
|
||||||
|
ourClient.create().resource(cs).execute();
|
||||||
|
|
||||||
|
ValueSet vs = new ValueSet();
|
||||||
|
vs.setUrl("http://vs");
|
||||||
|
vs.getCompose()
|
||||||
|
.addInclude()
|
||||||
|
.setSystem("http://cs")
|
||||||
|
.addFilter()
|
||||||
|
.setProperty("concept")
|
||||||
|
.setOp(ValueSet.FilterOperator.ISA)
|
||||||
|
.setValue("A");
|
||||||
|
IIdType vsid = ourClient.create().resource(vs).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
HttpGet read = new HttpGet(ourServerBase + "/" + vsid.getValue() + "/$expand");
|
||||||
|
try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
|
||||||
|
String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
ourLog.info(text);
|
||||||
|
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
|
||||||
|
assertThat(text, containsString("\"A\""));
|
||||||
|
assertThat(text, containsString("\"A1\""));
|
||||||
|
assertThat(text, not(containsString("\"B\"")));
|
||||||
|
assertThat(text, not(containsString("\"B1\"")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HttpGet read = new HttpGet(ourServerBase + "/Observation?patient=P5000000302&_sort:desc=code&code:in=http://fkcfhir.org/fhir/vs/ccdacapddialysisorder");
|
||||||
|
// try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
|
||||||
|
// String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
// ourLog.info(text);
|
||||||
|
// assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
|
||||||
|
// assertThat(text, not(containsString("\"text\",\"type\"")));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTerminologyWithCompleteCs_SearchForConceptIn() throws Exception {
|
||||||
|
|
||||||
|
CodeSystem cs = new CodeSystem();
|
||||||
|
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
|
||||||
|
cs.setUrl("http://cs");
|
||||||
|
CodeSystem.ConceptDefinitionComponent a = cs.addConcept()
|
||||||
|
.setCode("A");
|
||||||
|
a.addConcept().setCode("A1");
|
||||||
|
a.addConcept().setCode("A2");
|
||||||
|
CodeSystem.ConceptDefinitionComponent b = cs.addConcept()
|
||||||
|
.setCode("B");
|
||||||
|
b.addConcept().setCode("B1");
|
||||||
|
b.addConcept().setCode("B2");
|
||||||
|
ourClient.create().resource(cs).execute();
|
||||||
|
|
||||||
|
ValueSet vs = new ValueSet();
|
||||||
|
vs.setUrl("http://vs");
|
||||||
|
vs.getCompose()
|
||||||
|
.addInclude()
|
||||||
|
.setSystem("http://cs")
|
||||||
|
.addFilter()
|
||||||
|
.setProperty("concept")
|
||||||
|
.setOp(ValueSet.FilterOperator.ISA)
|
||||||
|
.setValue("A");
|
||||||
|
ourClient.create().resource(vs).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getCode().addCoding().setSystem("http://cs").setCode("A1");
|
||||||
|
obs.setValue(new StringType("OBS1"));
|
||||||
|
obs.setStatus(ObservationStatus.FINAL);
|
||||||
|
ourClient.create().resource(obs).execute();
|
||||||
|
|
||||||
|
Observation obs2 = new Observation();
|
||||||
|
obs2.getCode().addCoding().setSystem("http://cs").setCode("B1");
|
||||||
|
obs2.setStatus(ObservationStatus.FINAL);
|
||||||
|
obs2.setValue(new StringType("OBS2"));
|
||||||
|
ourClient.create().resource(obs2).execute();
|
||||||
|
|
||||||
|
HttpGet read = new HttpGet(ourServerBase + "/Observation?code:in=http://vs");
|
||||||
|
try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
|
||||||
|
String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
ourLog.info(text);
|
||||||
|
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
|
||||||
|
assertThat(text, containsString("\"OBS1\""));
|
||||||
|
assertThat(text, not(containsString("\"OBS2\"")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchBundleDoesntIncludeTextElement() throws Exception {
|
public void testSearchBundleDoesntIncludeTextElement() throws Exception {
|
||||||
HttpGet read = new HttpGet(ourServerBase + "/Patient?_format=json");
|
HttpGet read = new HttpGet(ourServerBase + "/Patient?_format=json");
|
||||||
|
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
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;
|
||||||
|
@ -63,6 +64,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<Date> myHighCaptor;
|
private ArgumentCaptor<Date> myHighCaptor;
|
||||||
private ResourceReindexJobEntity mySingleJob;
|
private ResourceReindexJobEntity mySingleJob;
|
||||||
|
@Mock
|
||||||
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FhirContext getContext() {
|
protected FhirContext getContext() {
|
||||||
|
@ -87,6 +90,7 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
||||||
mySvc.setReindexJobDaoForUnitTest(myReindexJobDao);
|
mySvc.setReindexJobDaoForUnitTest(myReindexJobDao);
|
||||||
mySvc.setResourceTableDaoForUnitTest(myResourceTableDao);
|
mySvc.setResourceTableDaoForUnitTest(myResourceTableDao);
|
||||||
mySvc.setTxManagerForUnitTest(myTxManager);
|
mySvc.setTxManagerForUnitTest(myTxManager);
|
||||||
|
mySvc.setSearchParamRegistryForUnitTest(mySearchParamRegistry);
|
||||||
mySvc.start();
|
mySvc.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +179,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest {
|
||||||
verify(myReindexJobDao, times(1)).getReindexCount(any());
|
verify(myReindexJobDao, times(1)).getReindexCount(any());
|
||||||
verify(myReindexJobDao, times(1)).setReindexCount(any(), anyInt());
|
verify(myReindexJobDao, times(1)).setReindexCount(any(), anyInt());
|
||||||
verifyNoMoreInteractions(myReindexJobDao);
|
verifyNoMoreInteractions(myReindexJobDao);
|
||||||
|
|
||||||
|
verify(mySearchParamRegistry, times(1)).forceRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
import ca.uhn.fhir.jpa.subscription.module.SubscriptionChannel;
|
||||||
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
@ -26,7 +24,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
import org.springframework.messaging.SubscribableChannel;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -39,7 +37,6 @@ import java.util.List;
|
||||||
public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR4Test.class);
|
||||||
|
|
||||||
|
|
||||||
private static int ourListenerPort;
|
private static int ourListenerPort;
|
||||||
private static RestfulServer ourListenerRestServer;
|
private static RestfulServer ourListenerRestServer;
|
||||||
private static Server ourListenerServer;
|
private static Server ourListenerServer;
|
||||||
|
@ -50,9 +47,9 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
@Autowired
|
@Autowired
|
||||||
private SingleQueryCountHolder myCountHolder;
|
private SingleQueryCountHolder myCountHolder;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected DaoConfig myDaoConfig;
|
protected SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoRegistry myDaoRegistry;
|
protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
|
||||||
protected CountingInterceptor myCountingInterceptor;
|
protected CountingInterceptor myCountingInterceptor;
|
||||||
|
|
||||||
|
@ -65,7 +62,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
BaseSubscriptionInterceptor.setForcePayloadEncodeAndDecodeForUnitTests(false);
|
SubscriptionMatcherInterceptor.setForcePayloadEncodeAndDecodeForUnitTests(false);
|
||||||
|
|
||||||
for (IIdType next : mySubscriptionIds) {
|
for (IIdType next : mySubscriptionIds) {
|
||||||
IIdType nextId = next.toUnqualifiedVersionless();
|
IIdType nextId = next.toUnqualifiedVersionless();
|
||||||
|
@ -81,12 +78,12 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
ourRestServer.registerInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -101,12 +98,12 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
for (IBaseResource next : BundleUtil.toListOfResources(myFhirCtx, allSubscriptions)) {
|
for (IBaseResource next : BundleUtil.toListOfResources(myFhirCtx, allSubscriptions)) {
|
||||||
ourClient.delete().resource(next).execute();
|
ourClient.delete().resource(next).execute();
|
||||||
}
|
}
|
||||||
waitForRegisteredSubscriptionCount(0);
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
|
||||||
ExecutorSubscribableChannel processingChannel = (ExecutorSubscribableChannel) getRestHookSubscriptionInterceptor().getProcessingChannel();
|
SubscriptionChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
|
||||||
processingChannel.setInterceptors(new ArrayList<>());
|
processingChannel.clearInterceptorsForUnitTest();
|
||||||
myCountingInterceptor = new CountingInterceptor();
|
myCountingInterceptor = new CountingInterceptor();
|
||||||
processingChannel.addInterceptor(myCountingInterceptor);
|
processingChannel.addInterceptorForUnitTest(myCountingInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,7 +132,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
|
|
||||||
|
|
||||||
protected void waitForQueueToDrain() throws InterruptedException {
|
protected void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@ -156,8 +153,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
|
|
||||||
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||||
|
|
||||||
String observationId = methodOutcome.getId().getIdPart();
|
observation.setId(methodOutcome.getId());
|
||||||
observation.setId(observationId);
|
|
||||||
|
|
||||||
return observation;
|
return observation;
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.MessageChannel;
|
import org.springframework.messaging.MessageChannel;
|
|
@ -1,10 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.subscription.FhirClientSearchParamProvider;
|
import ca.uhn.fhir.jpa.subscription.module.standalone.FhirClientSearchParamProvider;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
@ -20,18 +18,18 @@ import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class FhirClientSearchParamProviderTest extends BaseSubscriptionsR4Test {
|
public class FhirClientSearchParamProviderTest extends BaseSubscriptionsR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
BaseSearchParamRegistry mySearchParamRegistry;
|
ISearchParamRegistry mySearchParamRegistry;
|
||||||
@Autowired
|
@Autowired
|
||||||
ISearchParamProvider origSearchParamProvider;
|
ISearchParamProvider origSearchParamProvider;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void useFhirClientSearchParamProvider() {
|
public void useFhirClientSearchParamProvider() {
|
||||||
mySearchParamRegistry.setSearchParamProvider(new FhirClientSearchParamProvider(ourClient));
|
mySearchParamRegistry.setSearchParamProviderForUnitTest(new FhirClientSearchParamProvider(ourClient));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void revert() {
|
public void revert() {
|
||||||
mySearchParamRegistry.setSearchParamProvider(origSearchParamProvider);
|
mySearchParamRegistry.setSearchParamProviderForUnitTest(origSearchParamProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -48,7 +46,7 @@ public class FhirClientSearchParamProviderTest extends BaseSubscriptionsR4Test {
|
||||||
mySearchParameterDao.create(sp);
|
mySearchParameterDao.create(sp);
|
||||||
mySearchParamRegsitry.forceRefresh();
|
mySearchParamRegsitry.forceRefresh();
|
||||||
createSubscription(criteria, "application/json");
|
createSubscription(criteria, "application/json");
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
{
|
{
|
||||||
Observation observation = new Observation();
|
Observation observation = new Observation();
|
||||||
|
@ -81,8 +79,5 @@ public class FhirClientSearchParamProviderTest extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(2, ourUpdatedObservations);
|
waitForSize(2, ourUpdatedObservations);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscriptionProvider;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.standalone.FhirClientSubscriptionProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import org.hl7.fhir.dstu3.model.Subscription;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class FhirClientSubscriptionProviderTest extends BaseSubscriptionsR4Test {
|
||||||
|
@Autowired
|
||||||
|
SubscriptionLoader mySubscriptionLoader;
|
||||||
|
@Autowired
|
||||||
|
ISubscriptionProvider origSubscriptionProvider;
|
||||||
|
@Autowired
|
||||||
|
AutowireCapableBeanFactory autowireCapableBeanFactory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void useFhirClientSubscriptionProvider() {
|
||||||
|
FhirClientSubscriptionProvider subscriptionProvider = new FhirClientSubscriptionProvider(ourClient);
|
||||||
|
// This bean is only available in the standalone subscription context, so we have to autowire it manually.
|
||||||
|
autowireCapableBeanFactory.autowireBean(subscriptionProvider);
|
||||||
|
mySubscriptionLoader.setSubscriptionProviderForUnitTest(subscriptionProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void revert() {
|
||||||
|
mySubscriptionLoader.setSubscriptionProviderForUnitTest(origSubscriptionProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String myCode = "1000000050";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionLoaderFhirClient() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + myCode + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + myCode + "111&_format=xml";
|
||||||
|
|
||||||
|
|
||||||
|
List<Subscription> subs = new ArrayList<>();
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
sendObservation(myCode, "SNOMED-CT");
|
||||||
|
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import org.hl7.fhir.r4.model.*;
|
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
|
||||||
public class FhirR4Util {
|
public class FhirR4Util {
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.LinkedBlockingQueueSubscriptionChannel;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.ActiveSubscription;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.IEmailSender;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.JavaMailEmailSender;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.SubscriptionDeliveringEmailSubscriber;
|
||||||
|
import org.hl7.fhir.instance.model.Subscription;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
public class SubscriptionTestUtil {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionTestUtil.class);
|
||||||
|
|
||||||
|
private JavaMailEmailSender myEmailSender;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionInterceptorLoader mySubscriptionInterceptorLoader;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionRegistry mySubscriptionRegistry;
|
||||||
|
|
||||||
|
public int getExecutorQueueSize() {
|
||||||
|
LinkedBlockingQueueSubscriptionChannel channel = (LinkedBlockingQueueSubscriptionChannel) mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
|
||||||
|
return channel.getQueueSizeForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO KHS replace this and similar functions with CountdownLatch
|
||||||
|
public void waitForQueueToDrain() throws InterruptedException {
|
||||||
|
Thread.sleep(100);
|
||||||
|
ourLog.info("Executor work queue has {} items", getExecutorQueueSize());
|
||||||
|
if (getExecutorQueueSize() > 0) {
|
||||||
|
while (getExecutorQueueSize() > 0) {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
ourLog.info("Executor work queue has {} items", getExecutorQueueSize());
|
||||||
|
}
|
||||||
|
Thread.sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerEmailInterceptor() {
|
||||||
|
myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL);
|
||||||
|
mySubscriptionInterceptorLoader.registerInterceptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerRestHookInterceptor() {
|
||||||
|
myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
|
mySubscriptionInterceptorLoader.registerInterceptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerWebSocketInterceptor() {
|
||||||
|
myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.WEBSOCKET);
|
||||||
|
mySubscriptionInterceptorLoader.registerInterceptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterSubscriptionInterceptor() {
|
||||||
|
myDaoConfig.clearSupportedSubscriptionTypesForUnitTest();
|
||||||
|
mySubscriptionInterceptorLoader.unregisterInterceptorsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getExecutorQueueSizeForUnitTests() {
|
||||||
|
return getExecutorQueueSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initEmailSender(int theListenerPort) {
|
||||||
|
myEmailSender = new JavaMailEmailSender();
|
||||||
|
myEmailSender.setSmtpServerHostname("localhost");
|
||||||
|
myEmailSender.setSmtpServerPort(theListenerPort);
|
||||||
|
myEmailSender.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailSender(IIdType theIdElement) {
|
||||||
|
ActiveSubscription activeSubscription = mySubscriptionRegistry.get(theIdElement.getIdPart());
|
||||||
|
SubscriptionDeliveringEmailSubscriber subscriber = (SubscriptionDeliveringEmailSubscriber) activeSubscription.getDeliveryHandlerForUnitTest();
|
||||||
|
subscriber.setEmailSender(myEmailSender);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEmailSender getEmailSender() {
|
||||||
|
return myEmailSender;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,7 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.email;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.JavaMailEmailSender;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
|
@ -22,8 +19,6 @@ import org.junit.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
|
@ -42,12 +37,7 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
private List<IIdType> mySubscriptionIds = new ArrayList<>();
|
private List<IIdType> mySubscriptionIds = new ArrayList<>();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SubscriptionEmailInterceptor mySubscriber;
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
@Autowired
|
|
||||||
private List<IFhirResourceDao<?>> myResourceDaos;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier(BaseConfig.TASK_EXECUTOR_NAME)
|
|
||||||
private AsyncTaskExecutor myAsyncTaskExecutor;
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
|
@ -58,38 +48,16 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
ourClient.delete().resourceById(next).execute();
|
ourClient.delete().resourceById(next).execute();
|
||||||
}
|
}
|
||||||
mySubscriptionIds.clear();
|
mySubscriptionIds.clear();
|
||||||
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
ourRestServer.unregisterInterceptor(mySubscriber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
super.before();
|
super.before();
|
||||||
|
|
||||||
JavaMailEmailSender emailSender = new JavaMailEmailSender();
|
mySubscriptionTestUtil.initEmailSender(ourListenerPort);
|
||||||
emailSender.setSmtpServerHostname("localhost");
|
|
||||||
emailSender.setSmtpServerPort(ourListenerPort);
|
|
||||||
emailSender.start();
|
|
||||||
|
|
||||||
mySubscriber.setEmailSender(emailSender);
|
mySubscriptionTestUtil.registerEmailInterceptor();
|
||||||
mySubscriber.setResourceDaos(myResourceDaos);
|
|
||||||
mySubscriber.setFhirContext(myFhirCtx);
|
|
||||||
mySubscriber.setTxManager(ourTxManager);
|
|
||||||
mySubscriber.setAsyncTaskExecutorForUnitTest(myAsyncTaskExecutor);
|
|
||||||
mySubscriber.start();
|
|
||||||
ourRestServer.registerInterceptor(mySubscriber);
|
|
||||||
|
|
||||||
// ourLog.info("Sending test email to warm up the server");
|
|
||||||
// EmailDetails details = new EmailDetails();
|
|
||||||
// details.setFrom("a@a.com");
|
|
||||||
// details.setTo(Arrays.asList("b@b.com"));
|
|
||||||
// details.setSubjectTemplate("SUBJ");
|
|
||||||
// details.setBodyTemplate("BODY");
|
|
||||||
// emailSender.send(details);
|
|
||||||
// ourLog.info("Done sending test email to warm up the server");
|
|
||||||
// Store store = ourTestSmtp.getManagers().getImapHostManager().getStore();
|
|
||||||
// MailFolder mailbox = store.getMailbox(ImapConstants.USER_NAMESPACE);
|
|
||||||
// mailbox.deleteAllMessages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Subscription createSubscription(String criteria, String payload, String endpoint) throws InterruptedException {
|
private Subscription createSubscription(String criteria, String payload, String endpoint) throws InterruptedException {
|
||||||
|
@ -108,7 +76,7 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
subscription.setId(methodOutcome.getId().getIdPart());
|
subscription.setId(methodOutcome.getId().getIdPart());
|
||||||
mySubscriptionIds.add(methodOutcome.getId());
|
mySubscriptionIds.add(methodOutcome.getId());
|
||||||
|
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
|
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
@ -138,8 +106,8 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload, "to1@example.com,to2@example.com");
|
Subscription subscription1 = createSubscription(criteria1, payload, "to1@example.com,to2@example.com");
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
|
mySubscriptionTestUtil.setEmailSender(subscription1.getIdElement());
|
||||||
assertEquals(0, Arrays.asList(ourTestSmtp.getReceivedMessages()).size());
|
assertEquals(0, Arrays.asList(ourTestSmtp.getReceivedMessages()).size());
|
||||||
|
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
|
@ -2,9 +2,9 @@ package ca.uhn.fhir.jpa.subscription.email;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionConstants;
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.icegreen.greenmail.store.FolderException;
|
import com.icegreen.greenmail.store.FolderException;
|
||||||
|
@ -13,6 +13,7 @@ import com.icegreen.greenmail.util.ServerSetup;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
|
@ -21,7 +22,7 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the rest-hook subscriptions
|
* Test the rest-hook subscriptions
|
||||||
|
@ -29,6 +30,10 @@ import static org.junit.Assert.*;
|
||||||
public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmailSubscriptionDstu3Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmailSubscriptionDstu3Test.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
|
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
|
||||||
private static int ourListenerPort;
|
private static int ourListenerPort;
|
||||||
private static List<String> ourContentTypes = new ArrayList<>();
|
private static List<String> ourContentTypes = new ArrayList<>();
|
||||||
|
@ -51,23 +56,17 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(ourEmailSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterEmailListener() throws FolderException {
|
public void beforeRegisterEmailListener() throws FolderException {
|
||||||
ourTestSmtp.purgeEmailFromAllMailboxes();
|
ourTestSmtp.purgeEmailFromAllMailboxes();
|
||||||
;
|
mySubscriptionTestUtil.registerEmailInterceptor();
|
||||||
ourRestServer.registerInterceptor(ourEmailSubscriptionInterceptor);
|
|
||||||
|
|
||||||
JavaMailEmailSender emailSender = new JavaMailEmailSender();
|
mySubscriptionTestUtil.initEmailSender(ourListenerPort);
|
||||||
emailSender.setSmtpServerHostname("localhost");
|
|
||||||
emailSender.setSmtpServerPort(ourListenerPort);
|
|
||||||
emailSender.start();
|
|
||||||
|
|
||||||
ourEmailSubscriptionInterceptor.setEmailSender(emailSender);
|
myDaoConfig.setEmailFromAddress("123@hapifhir.io");
|
||||||
ourEmailSubscriptionInterceptor.setDefaultFromAddress("123@hapifhir.io");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Subscription createSubscription(String theCriteria, String thePayload) throws InterruptedException {
|
private Subscription createSubscription(String theCriteria, String thePayload) throws InterruptedException {
|
||||||
|
@ -115,8 +114,9 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
String code = "1000000050";
|
String code = "1000000050";
|
||||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
createSubscription(criteria1, payload);
|
Subscription subscription = createSubscription(criteria1, payload);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
mySubscriptionTestUtil.setEmailSender(subscription.getIdElement());
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
@ -150,19 +150,18 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
Assert.assertNotNull(subscriptionTemp);
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
|
|
||||||
subscriptionTemp.getChannel().addExtension()
|
subscriptionTemp.getChannel().addExtension()
|
||||||
.setUrl(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
|
.setUrl(SubscriptionConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
|
||||||
.setValue(new StringType("mailto:myfrom@from.com"));
|
.setValue(new StringType("mailto:myfrom@from.com"));
|
||||||
subscriptionTemp.getChannel().addExtension()
|
subscriptionTemp.getChannel().addExtension()
|
||||||
.setUrl(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
|
.setUrl(SubscriptionConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
|
||||||
.setValue(new StringType("This is a subject"));
|
.setValue(new StringType("This is a subject"));
|
||||||
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());
|
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
|
|
||||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(subscriptionTemp));
|
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(subscriptionTemp));
|
||||||
|
|
||||||
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
mySubscriptionTestUtil.setEmailSender(subscriptionTemp.getIdElement());
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
@ -197,10 +196,10 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
Subscription subscriptionTemp = ourClient.read(Subscription.class, sub1.getId());
|
Subscription subscriptionTemp = ourClient.read(Subscription.class, sub1.getId());
|
||||||
Assert.assertNotNull(subscriptionTemp);
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
subscriptionTemp.getChannel().addExtension()
|
subscriptionTemp.getChannel().addExtension()
|
||||||
.setUrl(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
|
.setUrl(SubscriptionConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
|
||||||
.setValue(new StringType("myfrom@from.com"));
|
.setValue(new StringType("myfrom@from.com"));
|
||||||
subscriptionTemp.getChannel().addExtension()
|
subscriptionTemp.getChannel().addExtension()
|
||||||
.setUrl(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
|
.setUrl(SubscriptionConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
|
||||||
.setValue(new StringType("This is a subject"));
|
.setValue(new StringType("This is a subject"));
|
||||||
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());
|
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
|
@ -208,6 +207,7 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
ourLog.info("Subscription ID is: {}", id.getValue());
|
ourLog.info("Subscription ID is: {}", id.getValue());
|
||||||
|
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
mySubscriptionTestUtil.setEmailSender(subscriptionTemp.getIdElement());
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
@ -238,7 +238,7 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourEmailSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.email;
|
package ca.uhn.fhir.jpa.subscription.email;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.EmailDetails;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.JavaMailEmailSender;
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import com.icegreen.greenmail.util.GreenMail;
|
import com.icegreen.greenmail.util.GreenMail;
|
||||||
import com.icegreen.greenmail.util.GreenMailUtil;
|
import com.icegreen.greenmail.util.GreenMailUtil;
|
||||||
|
@ -15,7 +17,7 @@ import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class JavaMailEmailSenderTest {
|
public class JavaMailEmailSenderTest {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.matcher;
|
package ca.uhn.fhir.jpa.subscription.module.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.subscription.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.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
@ -29,18 +29,18 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration(classes = {TestR4Config.class})
|
@ContextConfiguration(classes = {TestR4Config.class})
|
||||||
public class SubscriptionMatcherInMemoryTestR4 {
|
public class InMemorySubscriptionMatcherTestR4 {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionMatcherInMemoryTestR4.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InMemorySubscriptionMatcherTestR4.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
SubscriptionMatcherInMemory mySubscriptionMatcherInMemory;
|
InMemorySubscriptionMatcher myInMemorySubscriptionMatcher;
|
||||||
@Autowired
|
@Autowired
|
||||||
FhirContext myContext;
|
FhirContext myContext;
|
||||||
|
|
||||||
private SubscriptionMatchResult match(IBaseResource resource, SearchParameterMap params) {
|
private SubscriptionMatchResult match(IBaseResource resource, SearchParameterMap params) {
|
||||||
String criteria = params.toNormalizedQueryString(myContext);
|
String criteria = params.toNormalizedQueryString(myContext);
|
||||||
ourLog.info("Criteria: <{}>", criteria);
|
ourLog.info("Criteria: <{}>", criteria);
|
||||||
return mySubscriptionMatcherInMemory.match(criteria, resource);
|
return myInMemorySubscriptionMatcher.match(criteria, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertUnsupported(IBaseResource resource, SearchParameterMap params) {
|
private void assertUnsupported(IBaseResource resource, SearchParameterMap params) {
|
||||||
|
@ -380,18 +380,16 @@ public class SubscriptionMatcherInMemoryTestR4 {
|
||||||
params.add(Patient.SP_FAMILY, new StringParam("testSearchNameParam01Fam"));
|
params.add(Patient.SP_FAMILY, new StringParam("testSearchNameParam01Fam"));
|
||||||
try {
|
try {
|
||||||
String criteria = params.toNormalizedQueryString(myContext);
|
String criteria = params.toNormalizedQueryString(myContext);
|
||||||
ResourceModifiedMessage msg = new ResourceModifiedMessage();
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(myContext, patient, ResourceModifiedMessage.OperationTypeEnum.CREATE);
|
||||||
msg.setSubscriptionId("Subscription/123");
|
msg.setSubscriptionId("Subscription/123");
|
||||||
msg.setId(new IdType("Patient/ABC"));
|
msg.setId(new IdType("Patient/ABC"));
|
||||||
msg.setNewPayload(myContext, patient);
|
SubscriptionMatchResult result = myInMemorySubscriptionMatcher.match(criteria, msg);
|
||||||
SubscriptionMatchResult result = mySubscriptionMatcherInMemory.match(criteria, msg);
|
|
||||||
fail();
|
fail();
|
||||||
} catch (InternalErrorException e){
|
} catch (InternalErrorException e){
|
||||||
assertEquals("Failure processing resource ID[Patient/ABC] for subscription ID[Subscription/123]: Invalid resource reference found at path[Patient.managingOrganization] - Does not contain resource type - urn:uuid:13720262-b392-465f-913e-54fb198ff954", e.getMessage());
|
assertEquals("Failure processing resource ID[Patient/ABC] for subscription ID[Subscription/123]: Invalid resource reference found at path[Patient.managingOrganization] - Does not contain resource type - urn:uuid:13720262-b392-465f-913e-54fb198ff954", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchResourceReferenceOnlyCorrectPath() {
|
public void testSearchResourceReferenceOnlyCorrectPath() {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
|
@ -1,9 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingSubscriber;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
@ -20,6 +20,7 @@ import org.hl7.fhir.r4.model.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -28,7 +29,6 @@ import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
|
@ -41,20 +41,23 @@ public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourc
|
||||||
private static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
private static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
||||||
private static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
|
private static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterResetSubscriptionActivatingInterceptor() {
|
public void afterResetSubscriptionActivatingInterceptor() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
ourRestServer.unregisterInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeSetSubscriptionActivatingInterceptor() {
|
public void beforeSetSubscriptionActivatingInterceptor() {
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
||||||
getRestHookSubscriptionInterceptor().initSubscriptions();
|
mySubscriptionLoader.initSubscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,11 +108,8 @@ public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourc
|
||||||
createSubscription(criteria1, payload, ourListenerServerBase);
|
createSubscription(criteria1, payload, ourListenerServerBase);
|
||||||
createSubscription(criteria2, payload, ourListenerServerBase);
|
createSubscription(criteria2, payload, ourListenerServerBase);
|
||||||
|
|
||||||
ourRestServer.registerInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
getRestHookSubscriptionInterceptor().initSubscriptions();
|
mySubscriptionLoader.initSubscriptions();
|
||||||
|
|
||||||
assertTrue(hasRestHookSubscriptionInterceptor());
|
|
||||||
|
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -120,9 +120,7 @@ public class RestHookActivatesPreExistingSubscriptionsR4Test extends BaseResourc
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
if (hasRestHookSubscriptionInterceptor()) {
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(getRestHookSubscriptionInterceptor());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ObservationListener implements IResourceProvider {
|
public static class ObservationListener implements IResourceProvider {
|
|
@ -1,8 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||||
|
@ -27,11 +28,13 @@ import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
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.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the rest-hook subscriptions
|
* Test the rest-hook subscriptions
|
||||||
|
@ -47,6 +50,9 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
private static List<String> ourUpdatedObservations = Lists.newArrayList();
|
private static List<String> ourUpdatedObservations = Lists.newArrayList();
|
||||||
private List<IIdType> mySubscriptionIds = new ArrayList<IIdType>();
|
private List<IIdType> mySubscriptionIds = new ArrayList<IIdType>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
ourLog.info("** AFTER **");
|
ourLog.info("** AFTER **");
|
||||||
|
@ -62,12 +68,12 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -279,7 +285,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
|
@ -309,18 +315,6 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
ourListenerServer.stop();
|
ourListenerServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void waitForQueueToDrain(BaseSubscriptionInterceptor theSubscriptionInterceptor) throws InterruptedException {
|
|
||||||
Thread.sleep(100);
|
|
||||||
ourLog.info("Executor work queue has {} items", theSubscriptionInterceptor.getExecutorQueueSizeForUnitTests());
|
|
||||||
if (theSubscriptionInterceptor.getExecutorQueueSizeForUnitTests() > 0) {
|
|
||||||
while (theSubscriptionInterceptor.getExecutorQueueSizeForUnitTests() > 0) {
|
|
||||||
Thread.sleep(50);
|
|
||||||
}
|
|
||||||
ourLog.info("Executor work queue has {} items", theSubscriptionInterceptor.getExecutorQueueSizeForUnitTests());
|
|
||||||
}
|
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ObservationListener implements IResourceProvider {
|
public static class ObservationListener implements IResourceProvider {
|
||||||
|
|
||||||
@Create
|
@Create
|
|
@ -1,9 +1,10 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
@ -21,6 +22,7 @@ import org.hl7.fhir.dstu3.model.*;
|
||||||
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.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -45,6 +47,9 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
private static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
private static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
||||||
private List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
private List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
ourLog.info("**** Starting @After *****");
|
ourLog.info("**** Starting @After *****");
|
||||||
|
@ -61,13 +66,12 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -346,7 +350,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
|
@ -1,42 +1,26 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionMatcherInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionInterceptor;
|
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionConstants;
|
||||||
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
|
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
|
||||||
import net.ttddyy.dsproxy.QueryCount;
|
|
||||||
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.junit.*;
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.hasItem;
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
|
@ -46,7 +30,16 @@ import static org.junit.Assert.*;
|
||||||
* Test the rest-hook subscriptions
|
* Test the rest-hook subscriptions
|
||||||
*/
|
*/
|
||||||
public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestR4Test.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR4Test.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupStoppableSubscriptionDeliveringRestHookSubscriber() {
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(null);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
|
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
|
||||||
|
@ -58,7 +51,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
createSubscription(criteria1, payload);
|
createSubscription(criteria1, payload);
|
||||||
createSubscription(criteria2, payload);
|
createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -75,10 +68,10 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
String payload = "application/fhir+json";
|
String payload = "application/fhir+json";
|
||||||
createSubscription(criteria, payload);
|
createSubscription(criteria, payload);
|
||||||
|
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
Integer changes = ourReskHookSubscriptionInterceptor.doInitSubscriptions();
|
int changes = this.mySubscriptionLoader.doInitSubscriptionsForUnitTest();
|
||||||
assertEquals(0, changes.intValue());
|
assertEquals(0, changes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +85,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
createSubscription(criteria1, payload);
|
createSubscription(criteria1, payload);
|
||||||
createSubscription(criteria2, payload);
|
createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
Observation obs = sendObservation(code, "SNOMED-CT");
|
Observation obs = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -122,32 +115,137 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
String code = "1000000050";
|
String code = "1000000050";
|
||||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
waitForRegisteredSubscriptionCount(0);
|
waitForActivatedSubscriptionCount(0);
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
int modCount = myCountingInterceptor.getSentCount();
|
|
||||||
subscription1
|
|
||||||
.getChannel()
|
|
||||||
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
|
|
||||||
subscription1
|
|
||||||
.getChannel()
|
|
||||||
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
|
|
||||||
ourLog.info("** About to update subscription");
|
|
||||||
ourClient.update().resource(subscription1).execute();
|
|
||||||
waitForSize(modCount + 1, () -> myCountingInterceptor.getSentCount());
|
|
||||||
|
|
||||||
ourLog.info("** About to send observation");
|
ourLog.info("** About to send observation");
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
waitForSize(0, ourCreatedObservations);
|
||||||
waitForSize(1, ourUpdatedObservations);
|
waitForSize(1, ourUpdatedObservations);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
assertEquals(observation1.getIdElement().getIdPart(), ourUpdatedObservations.get(0).getIdElement().getIdPart());
|
IdType idElement = ourUpdatedObservations.get(0).getIdElement();
|
||||||
assertEquals(null, ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
assertEquals(observation1.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
|
// VersionId is present
|
||||||
|
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
|
||||||
|
|
||||||
|
subscription1
|
||||||
|
.getChannel()
|
||||||
|
.addExtension(SubscriptionConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
|
||||||
|
ourLog.info("** About to update subscription");
|
||||||
|
|
||||||
|
int modCount = myCountingInterceptor.getSentCount();
|
||||||
|
ourClient.update().resource(subscription1).execute();
|
||||||
|
waitForSize(modCount + 1, () -> myCountingInterceptor.getSentCount());
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(1));
|
||||||
|
|
||||||
|
idElement = ourUpdatedObservations.get(1).getIdElement();
|
||||||
|
assertEquals(observation2.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
|
// Now VersionId is stripped
|
||||||
|
assertEquals(null, idElement.getVersionIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionDoesntGetLatestVersionByDefault() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||||
|
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation.getComment());
|
||||||
|
|
||||||
|
observation.setComment("changed");
|
||||||
|
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||||
|
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation.getComment());
|
||||||
|
|
||||||
|
// Wait for our two delivery channel threads to be paused
|
||||||
|
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||||
|
// Open the floodgates!
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation1 = ourUpdatedObservations.get(0);
|
||||||
|
Observation observation2 = ourUpdatedObservations.get(1);
|
||||||
|
|
||||||
|
assertEquals("1", observation1.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation1.getComment());
|
||||||
|
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation2.getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionGetsLatestVersionWithFlag() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
|
||||||
|
Subscription subscription = newSubscription(criteria1, payload);
|
||||||
|
subscription
|
||||||
|
.getChannel()
|
||||||
|
.addExtension(SubscriptionConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||||
|
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation.getComment());
|
||||||
|
|
||||||
|
observation.setComment("changed");
|
||||||
|
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||||
|
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation.getComment());
|
||||||
|
|
||||||
|
// Wait for our two delivery channel threads to be paused
|
||||||
|
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||||
|
// Open the floodgates!
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation1 = ourUpdatedObservations.get(0);
|
||||||
|
Observation observation2 = ourUpdatedObservations.get(1);
|
||||||
|
|
||||||
|
assertEquals("2", observation1.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation1.getComment());
|
||||||
|
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation2.getComment());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -160,7 +258,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -240,7 +338,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -318,7 +416,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
ourLog.info("** About to send obervation");
|
ourLog.info("** About to send obervation");
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
@ -384,7 +482,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
||||||
BaseSubscriptionInterceptor.setForcePayloadEncodeAndDecodeForUnitTests(true);
|
SubscriptionMatcherInterceptor.setForcePayloadEncodeAndDecodeForUnitTests(true);
|
||||||
|
|
||||||
String payload = "application/xml";
|
String payload = "application/xml";
|
||||||
|
|
||||||
|
@ -392,7 +490,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
createSubscription(criteria1, payload);
|
createSubscription(criteria1, payload);
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
ourLog.info("** About to send obervation");
|
ourLog.info("** About to send obervation");
|
||||||
|
|
||||||
|
@ -490,7 +588,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
Subscription subscription1 = createSubscription(criteria1, payload);
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
Subscription subscription2 = createSubscription(criteria2, payload);
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
waitForRegisteredSubscriptionCount(2);
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -526,7 +624,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Add some headers, and we'll also turn back to requested status for fun
|
// Add some headers, and we'll also turn back to requested status for fun
|
||||||
Subscription subscription = createSubscription(criteria1, payload);
|
Subscription subscription = createSubscription(criteria1, payload);
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
subscription.getChannel().addHeader("X-Foo: FOO");
|
subscription.getChannel().addHeader("X-Foo: FOO");
|
||||||
subscription.getChannel().addHeader("X-Bar: BAR");
|
subscription.getChannel().addHeader("X-Bar: BAR");
|
||||||
|
@ -553,7 +651,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
Subscription subscription = createSubscription(criteria1, payload);
|
Subscription subscription = createSubscription(criteria1, payload);
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
sendObservation(code, "SNOMED-CT");
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
@ -644,7 +742,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
mySearchParameterDao.create(sp);
|
mySearchParameterDao.create(sp);
|
||||||
mySearchParamRegsitry.forceRefresh();
|
mySearchParamRegsitry.forceRefresh();
|
||||||
createSubscription(criteria, "application/json");
|
createSubscription(criteria, "application/json");
|
||||||
waitForRegisteredSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
{
|
{
|
||||||
Observation bodySite = new Observation();
|
Observation bodySite = new Observation();
|
|
@ -1,8 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||||
|
@ -25,6 +26,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,6 +44,9 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu2Test extends B
|
||||||
private static String ourListenerServerBase;
|
private static String ourListenerServerBase;
|
||||||
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
ourLog.info("** AFTER **");
|
ourLog.info("** AFTER **");
|
||||||
|
@ -51,12 +56,12 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu2Test extends B
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
myDaoConfig.getInterceptors().remove(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
myDaoConfig.getInterceptors().add(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -64,11 +69,11 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu2Test extends B
|
||||||
ourCreatedObservations.clear();
|
ourCreatedObservations.clear();
|
||||||
ourUpdatedObservations.clear();
|
ourUpdatedObservations.clear();
|
||||||
|
|
||||||
ourRestHookSubscriptionInterceptor.initSubscriptions();
|
mySubscriptionLoader.initSubscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
|
||||||
import ca.uhn.fhir.rest.annotation.*;
|
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the rest-hook subscriptions
|
* Test the rest-hook subscriptions
|
||||||
|
@ -38,6 +39,9 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu3Test extends B
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestWithInterceptorRegisteredToDaoConfigDstu3Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestWithInterceptorRegisteredToDaoConfigDstu3Test.class);
|
||||||
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldLogClient() {
|
protected boolean shouldLogClient() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -51,20 +55,20 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu3Test extends B
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
myDaoConfig.getInterceptors().remove(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
myDaoConfig.getInterceptors().add(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeReset() {
|
public void beforeReset() {
|
||||||
ourCreatedObservations.clear();
|
ourCreatedObservations.clear();
|
||||||
ourUpdatedObservations.clear();
|
ourUpdatedObservations.clear();
|
||||||
SubscriptionActivatingSubscriber.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
SubscriptionActivatingInterceptor.setWaitForSubscriptionActivationSynchronouslyForUnitTest(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Subscription createSubscription(String criteria, String payload, String endpoint) throws InterruptedException {
|
private Subscription createSubscription(String criteria, String payload, String endpoint) throws InterruptedException {
|
||||||
|
@ -87,7 +91,7 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigDstu3Test extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observation sendObservation(String code, String system) throws InterruptedException {
|
private Observation sendObservation(String code, String system) throws InterruptedException {
|
|
@ -1,28 +1,30 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
|
||||||
import org.hl7.fhir.r4.model.*;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.junit.*;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.annotation.*;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the rest-hook subscriptions
|
* Test the rest-hook subscriptions
|
||||||
|
@ -37,6 +39,9 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.class);
|
||||||
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
private static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldLogClient() {
|
protected boolean shouldLogClient() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -50,12 +55,12 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
myDaoConfig.getInterceptors().remove(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
myDaoConfig.getInterceptors().add(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -84,11 +89,11 @@ public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests());
|
ourLog.info("QUEUE HAS {} ITEMS", mySubscriptionTestUtil.getExecutorQueueSizeForUnitTests());
|
||||||
while (getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests() > 0) {
|
while (mySubscriptionTestUtil.getExecutorQueueSizeForUnitTests() > 0) {
|
||||||
Thread.sleep(250);
|
Thread.sleep(250);
|
||||||
}
|
}
|
||||||
ourLog.info("QUEUE HAS {} ITEMS", getRestHookSubscriptionInterceptor().getExecutorQueueSizeForUnitTests());
|
ourLog.info("QUEUE HAS {} ITEMS", mySubscriptionTestUtil.getExecutorQueueSizeForUnitTests());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observation sendObservation(String code, String system) throws InterruptedException {
|
private Observation sendObservation(String code, String system) throws InterruptedException {
|
|
@ -1,7 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.FhirR4Util;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -11,6 +13,7 @@ import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -44,6 +47,9 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
|
||||||
private String mySubscriptionId;
|
private String mySubscriptionId;
|
||||||
private List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
private List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@After
|
@After
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
|
@ -64,7 +70,7 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,6 +82,9 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignored because this feature isn't implemented yet
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void testSubscriptionAddedTrigger() {
|
public void testSubscriptionAddedTrigger() {
|
||||||
|
@ -122,7 +131,7 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
ourRestServer.registerInterceptor(getRestHookSubscriptionInterceptor());
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
|
@ -1,9 +1,11 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTriggeringSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.util.JpaConstants;
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
@ -50,6 +52,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
|
||||||
private static List<String> ourContentTypes = new ArrayList<>();
|
private static List<String> ourContentTypes = new ArrayList<>();
|
||||||
private List<IIdType> mySubscriptionIds = new ArrayList<>();
|
private List<IIdType> mySubscriptionIds = new ArrayList<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void afterUnregisterRestHookListener() {
|
public void afterUnregisterRestHookListener() {
|
||||||
ourLog.info("**** Starting @After *****");
|
ourLog.info("**** Starting @After *****");
|
||||||
|
@ -66,7 +71,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
|
||||||
ourLog.info("Done deleting all subscriptions");
|
ourLog.info("Done deleting all subscriptions");
|
||||||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
|
|
||||||
mySubscriptionTriggeringSvc.cancelAll();
|
mySubscriptionTriggeringSvc.cancelAll();
|
||||||
mySubscriptionTriggeringSvc.setMaxSubmitPerPass(null);
|
mySubscriptionTriggeringSvc.setMaxSubmitPerPass(null);
|
||||||
|
@ -79,7 +84,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeRegisterRestHookListener() {
|
public void beforeRegisterRestHookListener() {
|
||||||
ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -376,7 +381,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForQueueToDrain() throws InterruptedException {
|
private void waitForQueueToDrain() throws InterruptedException {
|
||||||
RestHookTestDstu2Test.waitForQueueToDrain(ourRestHookSubscriptionInterceptor);
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ObservationListener implements IResourceProvider {
|
public static class ObservationListener implements IResourceProvider {
|
|
@ -1,6 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.FhirDstu2Util;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||||
|
@ -27,7 +29,7 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
||||||
@Ignore
|
@Ignore
|
|
@ -1,6 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.FhirDstu3Util;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
@ -18,7 +20,7 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
||||||
@Ignore
|
@Ignore
|
|
@ -1,6 +1,7 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.FhirR4Util;
|
||||||
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
@ -19,7 +20,7 @@ import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
|
||||||
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
// This is currently disabled as the criteria mechanism was a non-standard experiment
|
|
@ -1,7 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.FhirDstu2Util;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||||
|
@ -21,13 +23,14 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
||||||
|
@ -52,11 +55,13 @@ public class WebsocketWithSubscriptionIdDstu2Test extends BaseResourceProviderDs
|
||||||
private WebSocketClient myWebSocketClient;
|
private WebSocketClient myWebSocketClient;
|
||||||
private SocketImplementation mySocketImplementation;
|
private SocketImplementation mySocketImplementation;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
super.after();
|
super.after();
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
ourRestServer.unregisterInterceptor(interceptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -69,8 +74,7 @@ public class WebsocketWithSubscriptionIdDstu2Test extends BaseResourceProviderDs
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
super.before();
|
super.before();
|
||||||
|
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.registerWebSocketInterceptor();
|
||||||
ourRestServer.registerInterceptor(interceptor);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create patient
|
* Create patient
|
|
@ -1,7 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.FhirDstu3Util;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
@ -12,13 +14,14 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
||||||
|
@ -46,12 +49,14 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
|
||||||
private WebSocketClient myWebSocketClient;
|
private WebSocketClient myWebSocketClient;
|
||||||
private SocketImplementation mySocketImplementation;
|
private SocketImplementation mySocketImplementation;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
super.after();
|
super.after();
|
||||||
|
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
ourRestServer.unregisterInterceptor(interceptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -67,8 +72,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
|
||||||
myDaoConfig.setSubscriptionEnabled(true);
|
myDaoConfig.setSubscriptionEnabled(true);
|
||||||
myDaoConfig.setSubscriptionPollDelay(0L);
|
myDaoConfig.setSubscriptionPollDelay(0L);
|
||||||
|
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.registerWebSocketInterceptor();
|
||||||
ourRestServer.registerInterceptor(interceptor);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create patient
|
* Create patient
|
|
@ -1,8 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.subscription.r4;
|
package ca.uhn.fhir.jpa.subscription.websocket;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.FhirR4Util;
|
||||||
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
@ -13,13 +14,14 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
|
||||||
|
@ -47,12 +49,14 @@ public class WebsocketWithSubscriptionIdR4Test extends BaseResourceProviderR4Tes
|
||||||
private WebSocketClient myWebSocketClient;
|
private WebSocketClient myWebSocketClient;
|
||||||
private SocketImplementation mySocketImplementation;
|
private SocketImplementation mySocketImplementation;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@After
|
@After
|
||||||
public void after() throws Exception {
|
public void after() throws Exception {
|
||||||
super.after();
|
super.after();
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
ourRestServer.unregisterInterceptor(interceptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -66,8 +70,7 @@ public class WebsocketWithSubscriptionIdR4Test extends BaseResourceProviderR4Tes
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
super.before();
|
super.before();
|
||||||
|
|
||||||
SubscriptionWebsocketInterceptor interceptor = ourWebApplicationContext.getBean(SubscriptionWebsocketInterceptor.class);
|
mySubscriptionTestUtil.registerWebSocketInterceptor();
|
||||||
ourRestServer.registerInterceptor(interceptor);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create patient
|
* Create patient
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
|
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
|
||||||
|
@ -23,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -583,6 +585,50 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a custom ValueSet against a custom CodeSystem expands correctly
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCustomValueSetExpansion() {
|
||||||
|
|
||||||
|
CodeSystem cs= new CodeSystem();
|
||||||
|
cs.setUrl("http://codesystems-r-us");
|
||||||
|
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
|
||||||
|
IIdType csId = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
TermCodeSystemVersion version = new TermCodeSystemVersion();
|
||||||
|
version.getConcepts().add(new TermConcept(version, "A"));
|
||||||
|
version.getConcepts().add(new TermConcept(version, "B"));
|
||||||
|
version.getConcepts().add(new TermConcept(version, "C"));
|
||||||
|
version.getConcepts().add(new TermConcept(version, "D"));
|
||||||
|
runInTransaction(()->{
|
||||||
|
ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong());
|
||||||
|
version.setResource(resTable);
|
||||||
|
myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", version);
|
||||||
|
});
|
||||||
|
|
||||||
|
org.hl7.fhir.dstu3.model.ValueSet vs = new org.hl7.fhir.dstu3.model.ValueSet();
|
||||||
|
vs.setUrl("http://valuesets-r-us");
|
||||||
|
vs.getCompose()
|
||||||
|
.addInclude()
|
||||||
|
.setSystem(cs.getUrl())
|
||||||
|
.addConcept(new org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent().setCode("A"))
|
||||||
|
.addConcept(new org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent().setCode("C"));
|
||||||
|
myValueSetDao.create(vs);
|
||||||
|
|
||||||
|
org.hl7.fhir.dstu3.model.ValueSet expansion = myValueSetDao.expandByIdentifier(vs.getUrl(), null);
|
||||||
|
List<String> expansionCodes = expansion
|
||||||
|
.getExpansion()
|
||||||
|
.getContains()
|
||||||
|
.stream()
|
||||||
|
.map(t -> t.getCode())
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertEquals(Lists.newArrayList("A","C"), expansionCodes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<String> toCodesContains(List<ValueSet.ValueSetExpansionContainsComponent> theContains) {
|
public static List<String> toCodesContains(List<ValueSet.ValueSetExpansionContainsComponent> theContains) {
|
||||||
List<String> retVal = new ArrayList<>();
|
List<String> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,18 @@
|
||||||
## Running hapi-fhir-jpaserver-example in Tomcat from IntelliJ
|
# Unsupported
|
||||||
|
|
||||||
|
This [hapi-fhir-jpaserver-example](https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-jpaserver-example) project is no longer supported.
|
||||||
|
|
||||||
|
|
||||||
|
## Supported JPA Example
|
||||||
|
|
||||||
|
The supported HAPI-FHIR JPA example is available in the [hapi-fhir-jpaserver-starter](https://github.com/hapifhir/hapi-fhir-jpaserver-starter)
|
||||||
|
project within the [hapifhir](https://github.com/hapifhir) GitHub Organization.
|
||||||
|
|
||||||
|
## Previous Documentation
|
||||||
|
|
||||||
|
Below is the original documentation for this project. Note that this documentation is no longer being updated.
|
||||||
|
|
||||||
|
#### Running hapi-fhir-jpaserver-example in Tomcat from IntelliJ
|
||||||
|
|
||||||
Install Tomcat.
|
Install Tomcat.
|
||||||
|
|
||||||
|
@ -33,7 +47,7 @@ Point your browser (or fiddler, or what have you) to `http://localhost:8080/hapi
|
||||||
You should get an empty bundle back.
|
You should get an empty bundle back.
|
||||||
|
|
||||||
|
|
||||||
## Running hapi-fhir-jpaserver-example in a Docker container
|
#### Running hapi-fhir-jpaserver-example in a Docker container
|
||||||
|
|
||||||
Execute the `build-docker-image.sh` script to build the docker image.
|
Execute the `build-docker-image.sh` script to build the docker image.
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,6 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
@ -26,12 +9,22 @@ import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.*;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemo extends RestfulServer {
|
public class JpaServerDemo extends RestfulServer {
|
||||||
|
|
||||||
|
@ -134,12 +127,10 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Register interceptors for the server based on DaoConfig.getSupportedSubscriptionTypes()
|
||||||
*/
|
*/
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you are hosting this server at a specific DNS name, the server will try to
|
* If you are hosting this server at a specific DNS name, the server will try to
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
|
|
||||||
package ca.uhn.fhir.jpa.demo;
|
package ca.uhn.fhir.jpa.demo;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
@ -20,12 +10,20 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.*;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaServerDemoDstu2 extends RestfulServer {
|
public class JpaServerDemoDstu2 extends RestfulServer {
|
||||||
|
|
||||||
|
@ -128,12 +126,10 @@ public class JpaServerDemoDstu2 extends RestfulServer {
|
||||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Register interceptors for the server based on DaoConfig.getSupportedSubscriptionTypes()
|
||||||
*/
|
*/
|
||||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
SubscriptionInterceptorLoader subscriptionInterceptorLoader = myAppCtx.getBean(SubscriptionInterceptorLoader.class);
|
||||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
subscriptionInterceptorLoader.registerInterceptors();
|
||||||
this.registerInterceptor(interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If you are hosting this server at a specific DNS name, the server will try to
|
* If you are hosting this server at a specific DNS name, the server will try to
|
||||||
|
|
|
@ -22,10 +22,19 @@ package ca.uhn.fhir.jpa.migrate;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableColumnTypeTask;
|
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableColumnTypeTask;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
|
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
|
||||||
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
|
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
|
||||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
|
||||||
|
import org.hibernate.engine.jdbc.env.internal.NormalizingIdentifierHelperImpl;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.*;
|
||||||
|
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||||
|
import org.hibernate.engine.jdbc.spi.TypeInfo;
|
||||||
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
|
||||||
|
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
|
||||||
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.jdbc.core.ColumnMapRowMapper;
|
import org.springframework.jdbc.core.ColumnMapRowMapper;
|
||||||
|
@ -53,7 +62,7 @@ public class JdbcUtils {
|
||||||
DatabaseMetaData metadata;
|
DatabaseMetaData metadata;
|
||||||
try {
|
try {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), theTableName, false, true);
|
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), false, true);
|
||||||
|
|
||||||
Set<String> indexNames = new HashSet<>();
|
Set<String> indexNames = new HashSet<>();
|
||||||
while (indexes.next()) {
|
while (indexes.next()) {
|
||||||
|
@ -81,7 +90,7 @@ public class JdbcUtils {
|
||||||
DatabaseMetaData metadata;
|
DatabaseMetaData metadata;
|
||||||
try {
|
try {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), theTableName, false, false);
|
ResultSet indexes = metadata.getIndexInfo(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), false, false);
|
||||||
|
|
||||||
while (indexes.next()) {
|
while (indexes.next()) {
|
||||||
String indexName = indexes.getString("INDEX_NAME");
|
String indexName = indexes.getString("INDEX_NAME");
|
||||||
|
@ -112,7 +121,7 @@ public class JdbcUtils {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
String catalog = connection.getCatalog();
|
String catalog = connection.getCatalog();
|
||||||
String schema = connection.getSchema();
|
String schema = connection.getSchema();
|
||||||
ResultSet indexes = metadata.getColumns(catalog, schema, theTableName, null);
|
ResultSet indexes = metadata.getColumns(catalog, schema, massageIdentifier(metadata, theTableName), null);
|
||||||
|
|
||||||
while (indexes.next()) {
|
while (indexes.next()) {
|
||||||
|
|
||||||
|
@ -165,7 +174,7 @@ public class JdbcUtils {
|
||||||
DatabaseMetaData metadata;
|
DatabaseMetaData metadata;
|
||||||
try {
|
try {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
ResultSet indexes = metadata.getCrossReference(connection.getCatalog(), connection.getSchema(), theTableName, connection.getCatalog(), connection.getSchema(), theForeignTable);
|
ResultSet indexes = metadata.getCrossReference(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theForeignTable));
|
||||||
|
|
||||||
Set<String> columnNames = new HashSet<>();
|
Set<String> columnNames = new HashSet<>();
|
||||||
while (indexes.next()) {
|
while (indexes.next()) {
|
||||||
|
@ -201,7 +210,7 @@ public class JdbcUtils {
|
||||||
DatabaseMetaData metadata;
|
DatabaseMetaData metadata;
|
||||||
try {
|
try {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
ResultSet indexes = metadata.getColumns(connection.getCatalog(), connection.getSchema(), theTableName, null);
|
ResultSet indexes = metadata.getColumns(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), null);
|
||||||
|
|
||||||
Set<String> columnNames = new HashSet<>();
|
Set<String> columnNames = new HashSet<>();
|
||||||
while (indexes.next()) {
|
while (indexes.next()) {
|
||||||
|
@ -233,27 +242,83 @@ public class JdbcUtils {
|
||||||
|
|
||||||
Set<String> sequenceNames = new HashSet<>();
|
Set<String> sequenceNames = new HashSet<>();
|
||||||
if (dialect.supportsSequences()) {
|
if (dialect.supportsSequences()) {
|
||||||
String sql = dialect.getQuerySequencesString();
|
|
||||||
if (sql != null) {
|
|
||||||
|
|
||||||
Statement statement = null;
|
// Use Hibernate to get a list of current sequences
|
||||||
ResultSet rs = null;
|
SequenceInformationExtractor sequenceInformationExtractor = dialect.getSequenceInformationExtractor();
|
||||||
try {
|
ExtractionContext extractionContext = new ExtractionContext.EmptyExtractionContext() {
|
||||||
statement = connection.createStatement();
|
@Override
|
||||||
rs = statement.executeQuery(sql);
|
public Connection getJdbcConnection() {
|
||||||
|
return connection;
|
||||||
while (rs.next()) {
|
|
||||||
sequenceNames.add(rs.getString(1).toUpperCase());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (rs != null) rs.close();
|
|
||||||
if (statement != null) statement.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServiceRegistry getServiceRegistry() {
|
||||||
|
return super.getServiceRegistry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcEnvironment getJdbcEnvironment() {
|
||||||
|
return new JdbcEnvironment() {
|
||||||
|
@Override
|
||||||
|
public Dialect getDialect() {
|
||||||
|
return dialect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtractedDatabaseMetaData getExtractedDatabaseMetaData() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identifier getCurrentCatalog() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identifier getCurrentSchema() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QualifiedObjectNameFormatter getQualifiedObjectNameFormatter() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierHelper getIdentifierHelper() {
|
||||||
|
return new NormalizingIdentifierHelperImpl(this, null, true, true, true, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameQualifierSupport getNameQualifierSupport() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlExceptionHelper getSqlExceptionHelper() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LobCreatorBuilder getLobCreatorBuilder() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeInfo getTypeInfoForJdbcCode(int jdbcTypeCode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Iterable<SequenceInformation> sequences = sequenceInformationExtractor.extractMetadata(extractionContext);
|
||||||
|
for (SequenceInformation next : sequences) {
|
||||||
|
sequenceNames.add(next.getSequenceName().getSequenceName().getText());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return sequenceNames;
|
return sequenceNames;
|
||||||
} catch (SQLException e ) {
|
} catch (SQLException e) {
|
||||||
throw new InternalErrorException(e);
|
throw new InternalErrorException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -298,7 +363,7 @@ public class JdbcUtils {
|
||||||
DatabaseMetaData metadata;
|
DatabaseMetaData metadata;
|
||||||
try {
|
try {
|
||||||
metadata = connection.getMetaData();
|
metadata = connection.getMetaData();
|
||||||
ResultSet tables = metadata.getColumns(connection.getCatalog(), connection.getSchema(), theTableName, theColumnName);
|
ResultSet tables = metadata.getColumns(connection.getCatalog(), connection.getSchema(), massageIdentifier(metadata, theTableName), null);
|
||||||
|
|
||||||
while (tables.next()) {
|
while (tables.next()) {
|
||||||
String tableName = toUpperCase(tables.getString("TABLE_NAME"), Locale.US);
|
String tableName = toUpperCase(tables.getString("TABLE_NAME"), Locale.US);
|
||||||
|
@ -325,4 +390,14 @@ public class JdbcUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String massageIdentifier(DatabaseMetaData theMetadata, String theCatalog) throws SQLException {
|
||||||
|
String retVal = theCatalog;
|
||||||
|
if (theMetadata.storesLowerCaseIdentifiers()) {
|
||||||
|
retVal = retVal.toLowerCase();
|
||||||
|
} else {
|
||||||
|
retVal = retVal.toUpperCase();
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,13 @@ public abstract class BaseTableColumnTypeTask<T extends BaseTableTask> extends B
|
||||||
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.MSSQL_2012, "datetime2");
|
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.MSSQL_2012, "datetime2");
|
||||||
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.ORACLE_12C, "timestamp");
|
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.ORACLE_12C, "timestamp");
|
||||||
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.POSTGRES_9_4, "timestamp");
|
setColumnType(ColumnTypeEnum.DATE_TIMESTAMP, DriverTypeEnum.POSTGRES_9_4, "timestamp");
|
||||||
|
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.DERBY_EMBEDDED, "boolean");
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.MSSQL_2012, "bit");
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.MARIADB_10_1, "bit");
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.ORACLE_12C, "number(1,0)");
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.POSTGRES_9_4, "boolean");
|
||||||
|
setColumnType(ColumnTypeEnum.BOOLEAN, DriverTypeEnum.MYSQL_5_7, "bit");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColumnTypeEnum getColumnType() {
|
public ColumnTypeEnum getColumnType() {
|
||||||
|
@ -157,6 +164,13 @@ public abstract class BaseTableColumnTypeTask<T extends BaseTableTask> extends B
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
BOOLEAN {
|
||||||
|
@Override
|
||||||
|
public String getDescriptor(Long theColumnLength) {
|
||||||
|
Assert.isTrue(theColumnLength == null, "Must not supply a column length");
|
||||||
|
return "boolean";
|
||||||
|
}
|
||||||
|
},
|
||||||
INT {
|
INT {
|
||||||
@Override
|
@Override
|
||||||
public String getDescriptor(Long theColumnLength) {
|
public String getDescriptor(Long theColumnLength) {
|
||||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.jpa.model.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.Subscription;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -53,6 +55,8 @@ public class ModelConfig {
|
||||||
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>();
|
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>();
|
||||||
private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS);
|
private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS);
|
||||||
private boolean myDefaultSearchParamsCanBeOverridden = false;
|
private boolean myDefaultSearchParamsCanBeOverridden = false;
|
||||||
|
private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
|
||||||
|
private String myEmailFromAddress = "noreply@unknown.com";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to {@code true} the default search params (i.e. the search parameters that are
|
* If set to {@code true} the default search params (i.e. the search parameters that are
|
||||||
|
@ -297,6 +301,46 @@ public class ModelConfig {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
|
||||||
|
* to the server matching these types will be activated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ModelConfig addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) {
|
||||||
|
mySupportedSubscriptionTypes.add(theSubscriptionChannelType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
|
||||||
|
* to the server matching these types will be activated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() {
|
||||||
|
return Collections.unmodifiableSet(mySupportedSubscriptionTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void clearSupportedSubscriptionTypesForUnitTest() {
|
||||||
|
mySupportedSubscriptionTypes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If e-mail subscriptions are supported, the From address used when sending e-mails
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getEmailFromAddress() {
|
||||||
|
return myEmailFromAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If e-mail subscriptions are supported, the From address used when sending e-mails
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void setEmailFromAddress(String theEmailFromAddress) {
|
||||||
|
myEmailFromAddress = theEmailFromAddress;
|
||||||
|
}
|
||||||
|
|
||||||
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
|
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
|
||||||
Validate.notBlank(theUrl, "Base URL must not be null or empty");
|
Validate.notBlank(theUrl, "Base URL must not be null or empty");
|
||||||
|
|
||||||
|
@ -309,5 +353,4 @@ public class ModelConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -610,12 +610,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
if (myHasLinks && myResourceLinks != null) {
|
if (myHasLinks && myResourceLinks != null) {
|
||||||
myResourceLinksField = getResourceLinks()
|
myResourceLinksField = getResourceLinks()
|
||||||
.stream()
|
.stream()
|
||||||
.map(t->{
|
.map(ResourceLink::getTargetResourcePid)
|
||||||
Long retVal = t.getTargetResourcePid();
|
|
||||||
return retVal;
|
|
||||||
})
|
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(t->t.toString())
|
.map(Object::toString)
|
||||||
.collect(Collectors.joining(" "));
|
.collect(Collectors.joining(" "));
|
||||||
} else {
|
} else {
|
||||||
myResourceLinksField = null;
|
myResourceLinksField = null;
|
||||||
|
|
|
@ -82,6 +82,10 @@
|
||||||
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
|
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring -->
|
<!-- Spring -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -38,9 +38,6 @@ import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.PersistenceContext;
|
|
||||||
import javax.persistence.PersistenceContextType;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -61,9 +58,6 @@ public class ResourceLinkExtractor {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
|
||||||
protected EntityManager myEntityManager;
|
|
||||||
|
|
||||||
public void extractResourceLinks(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, IResourceLinkResolver theResourceLinkResolver) {
|
public void extractResourceLinks(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, IResourceLinkResolver theResourceLinkResolver) {
|
||||||
String resourceType = theEntity.getResourceType();
|
String resourceType = theEntity.getResourceType();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -45,30 +44,20 @@ import java.util.*;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implements ISearchParamRegistry {
|
public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implements ISearchParamRegistry {
|
||||||
|
@Autowired
|
||||||
|
private ModelConfig myModelConfig;
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamProvider mySearchParamProvider;
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myFhirContext;
|
||||||
|
|
||||||
private static final int MAX_MANAGED_PARAM_COUNT = 10000;
|
private static final int MAX_MANAGED_PARAM_COUNT = 10000;
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
|
||||||
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
|
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
|
||||||
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap();
|
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap();
|
||||||
private volatile Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> myActiveParamNamesToUniqueSearchParams = Collections.emptyMap();
|
private volatile Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> myActiveParamNamesToUniqueSearchParams = Collections.emptyMap();
|
||||||
@Autowired
|
|
||||||
private FhirContext myCtx;
|
|
||||||
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
||||||
@Autowired
|
|
||||||
private ModelConfig myModelConfig;
|
|
||||||
private volatile long myLastRefresh;
|
private volatile long myLastRefresh;
|
||||||
private ApplicationContext myApplicationContext;
|
|
||||||
private ISearchParamProvider mySearchParamProvider;
|
|
||||||
|
|
||||||
public BaseSearchParamRegistry(ISearchParamProvider theSearchParamProvider) {
|
|
||||||
super();
|
|
||||||
mySearchParamProvider = theSearchParamProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setSearchParamProvider(ISearchParamProvider theSearchParamProvider) {
|
|
||||||
mySearchParamProvider = theSearchParamProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestRefresh() {
|
public void requestRefresh() {
|
||||||
|
@ -128,7 +117,7 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
||||||
return Collections.unmodifiableList(retVal);
|
return Collections.unmodifiableList(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
private Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
||||||
return myBuiltInSearchParams;
|
return myBuiltInSearchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,10 +209,10 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
||||||
public void postConstruct() {
|
public void postConstruct() {
|
||||||
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<>();
|
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<>();
|
||||||
|
|
||||||
Set<String> resourceNames = myCtx.getResourceNames();
|
Set<String> resourceNames = myFhirContext.getResourceNames();
|
||||||
|
|
||||||
for (String resourceName : resourceNames) {
|
for (String resourceName : resourceNames) {
|
||||||
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(resourceName);
|
RuntimeResourceDefinition nextResDef = myFhirContext.getResourceDefinition(resourceName);
|
||||||
String nextResourceName = nextResDef.getName();
|
String nextResourceName = nextResDef.getName();
|
||||||
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<>();
|
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<>();
|
||||||
resourceNameToSearchParams.put(nextResourceName, nameToParam);
|
resourceNameToSearchParams.put(nextResourceName, nameToParam);
|
||||||
|
@ -284,7 +273,7 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String nextBaseName : SearchParameterUtil.getBaseAsStrings(myCtx, nextSp)) {
|
for (String nextBaseName : SearchParameterUtil.getBaseAsStrings(myFhirContext, nextSp)) {
|
||||||
if (isBlank(nextBaseName)) {
|
if (isBlank(nextBaseName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -349,5 +338,9 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
||||||
return getActiveSearchParams(theResourceDef.getName()).values();
|
return getActiveSearchParams(theResourceDef.getName()).values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@Override
|
||||||
|
public void setSearchParamProviderForUnitTest(ISearchParamProvider theSearchParamProvider) {
|
||||||
|
mySearchParamProvider = theSearchParamProvider;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.searchparam.registry;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -59,4 +60,7 @@ public interface ISearchParamRegistry {
|
||||||
RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
|
RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
|
||||||
|
|
||||||
Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
|
Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setSearchParamProviderForUnitTest(ISearchParamProvider theSearchParamProvider);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry<SearchParameter> {
|
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry<SearchParameter> {
|
||||||
|
|
||||||
public SearchParamRegistryDstu2(ISearchParamProvider theSearchParamProvider) {
|
|
||||||
super(theSearchParamProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
||||||
String name = theNextSp.getCode();
|
String name = theNextSp.getCode();
|
||||||
|
|
|
@ -39,10 +39,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry<SearchParameter> {
|
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry<SearchParameter> {
|
||||||
|
|
||||||
public SearchParamRegistryDstu3(ISearchParamProvider theSearchParamProvider) {
|
|
||||||
super(theSearchParamProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
||||||
String name = theNextSp.getCode();
|
String name = theNextSp.getCode();
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue