Don't re-activate already active identical subscriptions

This commit is contained in:
James Agnew 2018-08-17 11:56:33 -04:00
parent ddc4464552
commit 6511545d25
11 changed files with 296 additions and 172 deletions

View File

@ -339,22 +339,29 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
return new ArrayList<>(myIdToSubscription.values()); return new ArrayList<>(myIdToSubscription.values());
} }
public boolean hasSubscription(IIdType theId) { public CanonicalSubscription hasSubscription(IIdType theId) {
Validate.notNull(theId); Validate.notNull(theId);
Validate.notBlank(theId.getIdPart()); Validate.notBlank(theId.getIdPart());
return myIdToSubscription.containsKey(theId.getIdPart()); return myIdToSubscription.get(theId.getIdPart());
} }
/** /**
* Read the existing subscriptions from the database * Read the existing subscriptions from the database
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Scheduled(fixedDelay = 10000) @Scheduled(fixedDelay = 60000)
public void initSubscriptions() { public void initSubscriptions() {
if (!myInitSubscriptionsSemaphore.tryAcquire()) { if (!myInitSubscriptionsSemaphore.tryAcquire()) {
return; return;
} }
try { try {
doInitSubscriptions();
} finally {
myInitSubscriptionsSemaphore.release();
}
}
public Integer doInitSubscriptions() {
ourLog.debug("Starting init subscriptions"); ourLog.debug("Starting init subscriptions");
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Subscription.SP_TYPE, new TokenParam(null, getChannelType().toCode())); map.add(Subscription.SP_TYPE, new TokenParam(null, getChannelType().toCode()));
@ -374,17 +381,20 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
List<IBaseResource> resourceList = subscriptionBundleList.getResources(0, subscriptionBundleList.size()); List<IBaseResource> resourceList = subscriptionBundleList.getResources(0, subscriptionBundleList.size());
Set<String> allIds = new HashSet<>(); Set<String> allIds = new HashSet<>();
int changesCount = 0;
for (IBaseResource resource : resourceList) { for (IBaseResource resource : resourceList) {
String nextId = resource.getIdElement().getIdPart(); String nextId = resource.getIdElement().getIdPart();
allIds.add(nextId); allIds.add(nextId);
mySubscriptionActivatingSubscriber.activateOrRegisterSubscriptionIfRequired(resource); boolean changed = mySubscriptionActivatingSubscriber.activateOrRegisterSubscriptionIfRequired(resource);
if (changed) {
changesCount++;
}
} }
unregisterAllSubscriptionsNotInCollection(allIds); unregisterAllSubscriptionsNotInCollection(allIds);
ourLog.trace("Finished init subscriptions - found {}", resourceList.size()); ourLog.trace("Finished init subscriptions - found {}", resourceList.size());
} finally {
myInitSubscriptionsSemaphore.release(); return changesCount;
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -72,18 +72,6 @@ public class CanonicalSubscription implements Serializable {
myTrigger = theTrigger; myTrigger = theTrigger;
} }
@Override
public boolean equals(Object theO) {
if (this == theO) return true;
if (theO == null || getClass() != theO.getClass()) return false;
CanonicalSubscription that = (CanonicalSubscription) theO;
return new EqualsBuilder()
.append(getIdElementString(), that.getIdElementString())
.isEquals();
}
public Subscription.SubscriptionChannelType getChannelType() { public Subscription.SubscriptionChannelType getChannelType() {
return myChannelType; return myChannelType;
@ -120,6 +108,15 @@ public class CanonicalSubscription implements Serializable {
return myHeaders; return myHeaders;
} }
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
myHeaders = new ArrayList<>();
for (IPrimitiveType<String> next : theHeader) {
if (isNotBlank(next.getValueAsString())) {
myHeaders.add(next.getValueAsString());
}
}
}
public void setHeaders(String theHeaders) { public void setHeaders(String theHeaders) {
myHeaders = new ArrayList<>(); myHeaders = new ArrayList<>();
if (isNotBlank(theHeaders)) { if (isNotBlank(theHeaders)) {
@ -171,19 +168,41 @@ public class CanonicalSubscription implements Serializable {
} }
@Override @Override
public int hashCode() { public boolean equals(Object theO) {
return new HashCodeBuilder(17, 37) if (this == theO) return true;
.append(getIdElementString())
.toHashCode(); if (theO == null || getClass() != theO.getClass()) return false;
CanonicalSubscription that = (CanonicalSubscription) theO;
EqualsBuilder b = new EqualsBuilder();
b.append(myIdElement, that.myIdElement);
b.append(myCriteriaString, that.myCriteriaString);
b.append(myEndpointUrl, that.myEndpointUrl);
b.append(myPayloadString, that.myPayloadString);
b.append(myHeaders, that.myHeaders);
b.append(myChannelType, that.myChannelType);
b.append(myStatus, that.myStatus);
b.append(myTrigger, that.myTrigger);
b.append(myEmailDetails, that.myEmailDetails);
b.append(myRestHookDetails, that.myRestHookDetails);
return b.isEquals();
} }
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) { @Override
myHeaders = new ArrayList<>(); public int hashCode() {
for (IPrimitiveType<String> next : theHeader) { return new HashCodeBuilder(17, 37)
if (isNotBlank(next.getValueAsString())) { .append(myIdElement)
myHeaders.add(next.getValueAsString()); .append(myCriteriaString)
} .append(myEndpointUrl)
} .append(myPayloadString)
.append(myHeaders)
.append(myChannelType)
.append(myStatus)
.append(myTrigger)
.append(myEmailDetails)
.append(myRestHookDetails)
.toHashCode();
} }
public void setIdElement(IIdType theIdElement) { public void setIdElement(IIdType theIdElement) {
@ -234,6 +253,28 @@ public class CanonicalSubscription implements Serializable {
myDeliverLatestVersion = theDeliverLatestVersion; myDeliverLatestVersion = theDeliverLatestVersion;
} }
@Override
public boolean equals(Object theO) {
if (this == theO) return true;
if (theO == null || getClass() != theO.getClass()) return false;
RestHookDetails that = (RestHookDetails) theO;
return new EqualsBuilder()
.append(myStripVersionId, that.myStripVersionId)
.append(myDeliverLatestVersion, that.myDeliverLatestVersion)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(myStripVersionId)
.append(myDeliverLatestVersion)
.toHashCode();
}
public boolean isStripVersionId() { public boolean isStripVersionId() {
return myStripVersionId; return myStripVersionId;
} }

View File

@ -70,7 +70,7 @@ public class SubscriptionActivatingSubscriber {
Validate.notNull(theTaskExecutor); Validate.notNull(theTaskExecutor);
} }
public synchronized void activateOrRegisterSubscriptionIfRequired(final IBaseResource theSubscription) { public synchronized boolean activateOrRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
// Grab the value for "Subscription.channel.type" so we can see if this // Grab the value for "Subscription.channel.type" so we can see if this
// subscriber applies.. // subscriber applies..
String subscriptionChannelType = myCtx String subscriptionChannelType = myCtx
@ -79,7 +79,7 @@ public class SubscriptionActivatingSubscriber {
.getValueAsString(); .getValueAsString();
boolean subscriptionTypeApplies = BaseSubscriptionSubscriber.subscriptionTypeApplies(subscriptionChannelType, myChannelType); boolean subscriptionTypeApplies = BaseSubscriptionSubscriber.subscriptionTypeApplies(subscriptionChannelType, myChannelType);
if (subscriptionTypeApplies == false) { if (subscriptionTypeApplies == false) {
return; return false;
} }
final IPrimitiveType<?> status = myCtx.newTerser().getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_STATUS, IPrimitiveType.class); final IPrimitiveType<?> status = myCtx.newTerser().getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_STATUS, IPrimitiveType.class);
@ -121,25 +121,28 @@ public class SubscriptionActivatingSubscriber {
} }
} }
}); });
return true;
} else { } else {
activateSubscription(activeStatus, theSubscription, requestedStatus); return activateSubscription(activeStatus, theSubscription, requestedStatus);
} }
} else if (activeStatus.equals(statusString)) { } else if (activeStatus.equals(statusString)) {
registerSubscriptionUnlessAlreadyRegistered(theSubscription); return registerSubscriptionUnlessAlreadyRegistered(theSubscription);
} else { } else {
// Status isn't "active" or "requested" // Status isn't "active" or "requested"
unregisterSubscriptionIfRegistered(theSubscription, statusString); return unregisterSubscriptionIfRegistered(theSubscription, statusString);
} }
} }
protected void unregisterSubscriptionIfRegistered(IBaseResource theSubscription, String theStatusString) { protected boolean unregisterSubscriptionIfRegistered(IBaseResource theSubscription, String theStatusString) {
if (mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) { if (mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement()) != null) {
ourLog.info("Removing {} subscription {}", theStatusString, theSubscription.getIdElement().toUnqualified().getValue()); ourLog.info("Removing {} subscription {}", theStatusString, theSubscription.getIdElement().toUnqualified().getValue());
mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement()); mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement());
return true;
} }
return false;
} }
private void activateSubscription(String theActiveStatus, final IBaseResource theSubscription, String theRequestedStatus) { private boolean activateSubscription(String theActiveStatus, final IBaseResource theSubscription, String theRequestedStatus) {
IBaseResource subscription = mySubscriptionDao.read(theSubscription.getIdElement()); IBaseResource subscription = mySubscriptionDao.read(theSubscription.getIdElement());
ourLog.info("Activating subscription {} from status {} to {} for channel {}", subscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus, myChannelType); ourLog.info("Activating subscription {} from status {} to {} for channel {}", subscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus, myChannelType);
@ -147,11 +150,13 @@ public class SubscriptionActivatingSubscriber {
SubscriptionUtil.setStatus(myCtx, subscription, theActiveStatus); SubscriptionUtil.setStatus(myCtx, subscription, theActiveStatus);
subscription = mySubscriptionDao.update(subscription).getResource(); subscription = mySubscriptionDao.update(subscription).getResource();
mySubscriptionInterceptor.submitResourceModifiedForUpdate(subscription); mySubscriptionInterceptor.submitResourceModifiedForUpdate(subscription);
return true;
} catch (final UnprocessableEntityException e) { } catch (final UnprocessableEntityException e) {
ourLog.info("Changing status of {} to ERROR", subscription.getIdElement()); ourLog.info("Changing status of {} to ERROR", subscription.getIdElement());
SubscriptionUtil.setStatus(myCtx, subscription, "error"); SubscriptionUtil.setStatus(myCtx, subscription, "error");
SubscriptionUtil.setReason(myCtx, subscription, e.getMessage()); SubscriptionUtil.setReason(myCtx, subscription, e.getMessage());
mySubscriptionDao.update(subscription); mySubscriptionDao.update(subscription);
return false;
} }
} }
@ -186,14 +191,25 @@ public class SubscriptionActivatingSubscriber {
}); });
} }
private void registerSubscriptionUnlessAlreadyRegistered(IBaseResource theSubscription) { protected boolean registerSubscriptionUnlessAlreadyRegistered(IBaseResource theSubscription) {
if (mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) { 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()); ourLog.info("Updating already-registered active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement()); mySubscriptionInterceptor.unregisterSubscription(theSubscription.getIdElement());
} else { } else {
ourLog.info("Registering active subscription {}", theSubscription.getIdElement().toUnqualified().getValue()); ourLog.info("Registering active subscription {}", theSubscription.getIdElement().toUnqualified().getValue());
} }
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription); mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
return true;
} }
@VisibleForTesting @VisibleForTesting

View File

@ -62,6 +62,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
private static Server ourServer; private static Server ourServer;
protected IGenericClient ourClient; protected IGenericClient ourClient;
protected ResourceCountCache ourResourceCountsCache; protected ResourceCountCache ourResourceCountsCache;
protected static SubscriptionRestHookInterceptor ourReskHookSubscriptionInterceptor;
private TerminologyUploaderProviderR4 myTerminologyUploaderProvider; private TerminologyUploaderProviderR4 myTerminologyUploaderProvider;
private Object ourGraphQLProvider; private Object ourGraphQLProvider;
private boolean ourRestHookSubscriptionInterceptorRequested; private boolean ourRestHookSubscriptionInterceptorRequested;
@ -70,72 +71,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
super(); super();
} }
@AfterClass
public static void afterClassClearContextBaseResourceProviderR4Test() throws Exception {
ourServer.stop();
ourHttpClient.close();
ourServer = null;
ourHttpClient = null;
myValidationSupport.flush();
myValidationSupport = null;
ourWebApplicationContext.close();
ourWebApplicationContext = null;
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static int getNumberOfParametersByName(Parameters theParameters, String theName) {
int retVal = 0;
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
retVal++;
}
}
return retVal;
}
public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return param;
}
}
return new ParametersParameterComponent();
}
public static List<ParametersParameterComponent> getParametersByName(Parameters theParameters, String theName) {
List<ParametersParameterComponent> params = new ArrayList<>();
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
params.add(param);
}
}
return params;
}
public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) {
for (ParametersParameterComponent part : theParameter.getPart()) {
if (part.getName().equals(theName)) {
return part;
}
}
return new ParametersParameterComponent();
}
public static boolean hasParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return true;
}
}
return false;
}
@After @After
public void after() throws Exception { public void after() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
@ -219,6 +154,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);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000); myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
@ -274,4 +210,70 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
Thread.sleep(500); Thread.sleep(500);
} }
@AfterClass
public static void afterClassClearContextBaseResourceProviderR4Test() throws Exception {
ourServer.stop();
ourHttpClient.close();
ourServer = null;
ourHttpClient = null;
myValidationSupport.flush();
myValidationSupport = null;
ourWebApplicationContext.close();
ourWebApplicationContext = null;
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static int getNumberOfParametersByName(Parameters theParameters, String theName) {
int retVal = 0;
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
retVal++;
}
}
return retVal;
}
public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return param;
}
}
return new ParametersParameterComponent();
}
public static List<ParametersParameterComponent> getParametersByName(Parameters theParameters, String theName) {
List<ParametersParameterComponent> params = new ArrayList<>();
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
params.add(param);
}
}
return params;
}
public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) {
for (ParametersParameterComponent part : theParameter.getPart()) {
if (part.getName().equals(theName)) {
return part;
}
}
return new ParametersParameterComponent();
}
public static boolean hasParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return true;
}
}
return false;
}
} }

View File

@ -52,33 +52,6 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
private List<IIdType> mySubscriptionIds = new ArrayList<>(); private List<IIdType> mySubscriptionIds = new ArrayList<>();
private CountingInterceptor myCountingInterceptor; private CountingInterceptor myCountingInterceptor;
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = PortUtil.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
ObservationListener obsListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(obsListener);
ourListenerServer = new Server(ourListenerPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourListenerRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourListenerServer.setHandler(proxyHandler);
ourListenerServer.start();
}
@AfterClass
public static void stopListenerServer() throws Exception {
ourListenerServer.stop();
}
@After @After
public void afterUnregisterRestHookListener() { public void afterUnregisterRestHookListener() {
for (IIdType next : mySubscriptionIds) { for (IIdType next : mySubscriptionIds) {
@ -180,6 +153,19 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0)); assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
} }
@Test
public void testActiveSubscriptionShouldntReActivate() throws Exception {
String criteria = "Observation?code=111111111&_format=xml";
String payload = "application/fhir+json";
createSubscription(criteria, payload, ourListenerServerBase);
waitForRegisteredSubscriptionCount(1);
for (int i = 0; i < 5; i++) {
Integer changes = ourReskHookSubscriptionInterceptor.doInitSubscriptions();
assertEquals(0, changes.intValue());
}
}
@Test @Test
public void testRestHookSubscriptionNoopUpdateDoesntTriggerNewDelivery() throws Exception { public void testRestHookSubscriptionNoopUpdateDoesntTriggerNewDelivery() throws Exception {
String payload = "application/fhir+json"; String payload = "application/fhir+json";
@ -233,7 +219,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true")); .addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
ourLog.info("** About to update subscription"); ourLog.info("** About to update subscription");
ourClient.update().resource(subscription1).execute(); ourClient.update().resource(subscription1).execute();
waitForSize(modCount + 1, ()->myCountingInterceptor.getSentCount()); 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");
@ -400,7 +386,6 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
Assert.assertFalse(observation2.getId().isEmpty()); Assert.assertFalse(observation2.getId().isEmpty());
} }
@Test @Test
public void testUpdateSubscriptionToMatchLater() throws Exception { public void testUpdateSubscriptionToMatchLater() throws Exception {
String payload = "application/xml"; String payload = "application/xml";
@ -586,4 +571,31 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
} }
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = PortUtil.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
ObservationListener obsListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(obsListener);
ourListenerServer = new Server(ourListenerPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourListenerRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourListenerServer.setHandler(proxyHandler);
ourListenerServer.start();
}
@AfterClass
public static void stopListenerServer() throws Exception {
ourListenerServer.stop();
}
} }

View File

@ -230,3 +230,46 @@ DELETE FROM TRM_CODESYSTEM_VER;
DELETE FROM TRM_CODESYSTEM; DELETE FROM TRM_CODESYSTEM;
delete from hfj_resource; delete from hfj_resource;
# Delete All (Oracle)
DROP TABLE HFJ_FORCED_ID CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_ver CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_resource CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_history_tag CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_ver CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_forced_id CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_link CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_link CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_coords CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_date CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_number CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_quantity CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_string CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_token CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_spidx_uri CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_tag CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_search_result CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_res_param_present CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_idx_cmp_string_uniq CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_subscription_stats CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_MAP_GRP_ELM_TGT CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_MAP_GRP_ELEMENT CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_MAP_GROUP CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_MAP CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_DESIG CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_PC_LINK CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT_PROPERTY CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CONCEPT CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CODESYSTEM_VER CASCADE CONSTRAINTS PURGE;
DROP TABLE TRM_CODESYSTEM CASCADE CONSTRAINTS PURGE;
DROP TABLE hfj_resource CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SEARCH_RESULT CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SEARCH_PARM CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SEARCH_INCLUDE CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SEARCH CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SUBSCRIPTION CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_SUBSCRIPTION_FLAG_RES CASCADE CONSTRAINTS PURGE;
DROP TABLE HFJ_TAG_DEF CASCADE CONSTRAINTS PURGE;