diff --git a/README.md b/README.md
new file mode 100644
index 00000000..f3399fcb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+See the [wiki associated with this repository](https://github.com/Access4Learning/sif3-framework-java/wiki) for information on:
+
+* contributing to this framework
+* the Java coding style to be used and
+* the structure of the SIF 3 Framework repositories
diff --git a/SIF3InfraREST/DB/.gitignore b/SIF3InfraREST/DB/.gitignore
index 95e8b92f..e468a432 100644
--- a/SIF3InfraREST/DB/.gitignore
+++ b/SIF3InfraREST/DB/.gitignore
@@ -1,2 +1,3 @@
/SIF3InfrastructureERM.mwb
/SIF3InfrastructureERM.mwb.bak
+/JOBEvent.sql
diff --git a/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_SQLite.sql b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_SQLite.sql
new file mode 100644
index 00000000..1cad0029
--- /dev/null
+++ b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_SQLite.sql
@@ -0,0 +1,84 @@
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB (
+ JOB_ID INTEGER PRIMARY KEY AUTOINCREMENT,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED DATETIME NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED DATETIME NULL,
+ EXPIRE_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL);
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_EVENT (
+ JOB_EVENT_ID INTEGER PRIMARY KEY AUTOINCREMENT,
+ JOB_EVENT_DATETIME DATETIME NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR(1) NOT NULL,
+ FULL_UPDATE INTEGER NOT NULL DEFAULT 1,
+ TO_FINGERPRINT_ONLY INTEGER NOT NULL DEFAULT 1,
+ CONSUMER_REQUESTED INTEGER NOT NULL DEFAULT 1,
+ EVENT_PUBLISHED INTEGER NOT NULL DEFAULT 0,
+ PUBLISHED_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL);
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
diff --git a/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mssql.sql b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mssql.sql
new file mode 100644
index 00000000..dd0a3914
--- /dev/null
+++ b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mssql.sql
@@ -0,0 +1,86 @@
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL IDENTITY,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED DATETIME NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED DATETIME NULL,
+ EXPIRE_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL IDENTITY,
+ JOB_EVENT_DATETIME DATETIME NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE BIT DEFAULT (1) NOT NULL,
+ TO_FINGERPRINT_ONLY BIT DEFAULT (1) NOT NULL,
+ CONSUMER_REQUESTED BIT DEFAULT (1) NOT NULL,
+ EVENT_PUBLISHED BIT DEFAULT (0) NOT NULL,
+ PUBLISHED_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
diff --git a/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mysql.sql b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mysql.sql
new file mode 100644
index 00000000..eeac67de
--- /dev/null
+++ b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_mysql.sql
@@ -0,0 +1,89 @@
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID))
+ENGINE = InnoDB;
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB (
+ JOB_ID INT NOT NULL AUTO_INCREMENT,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED DATETIME NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED DATETIME NULL,
+ EXPIRE_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_ID))
+ENGINE = InnoDB;
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL AUTO_INCREMENT,
+ JOB_EVENT_DATETIME DATETIME NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR(1) NOT NULL,
+ FULL_UPDATE TINYINT(1) NOT NULL DEFAULT 1,
+ TO_FINGERPRINT_ONLY TINYINT(1) NOT NULL DEFAULT 1,
+ CONSUMER_REQUESTED TINYINT(1) NOT NULL DEFAULT 1,
+ EVENT_PUBLISHED TINYINT(1) NOT NULL DEFAULT 0,
+ PUBLISHED_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_EVENT_ID))
+ENGINE = InnoDB;
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
diff --git a/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_oracle.sql b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_oracle.sql
new file mode 100644
index 00000000..e773d53e
--- /dev/null
+++ b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_oracle.sql
@@ -0,0 +1,86 @@
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR2(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR2(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR2(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL,
+ JOB_REFID VARCHAR2(36) NOT NULL,
+ SERVICE_NAME VARCHAR2(256) NULL,
+ CURRENT_JOB_STATE VARCHAR2(30) NULL,
+ ENVIRONMENT_REFID VARCHAR2(36) NULL,
+ ADAPTER_TYPE VARCHAR2(20) NOT NULL,
+ FINGERPRINT VARCHAR2(256) NULL,
+ ZONE_ID VARCHAR2(256) NULL,
+ CONTEXT_ID VARCHAR2(255) NULL,
+ CREATED TIMESTAMP NOT NULL,
+ TIMEOUT_PERIOD VARCHAR2(30) NULL,
+ LAST_MODIFIED TIMESTAMP NULL,
+ EXPIRE_DATETIME TIMESTAMP NULL,
+ JOB_XML CLOB NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL,
+ JOB_EVENT_DATETIME TIMESTAMP NOT NULL,
+ JOB_REFID VARCHAR2(36) NOT NULL,
+ SERVICE_NAME VARCHAR2(256) NULL,
+ ENVIRONMENT_REFID VARCHAR2(36) NULL,
+ ADAPTER_TYPE VARCHAR2(20) NOT NULL,
+ FINGERPRINT VARCHAR2(256) NULL,
+ ZONE_ID VARCHAR2(256) NULL,
+ CONTEXT_ID VARCHAR2(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE CHAR DEFAULT 'Y' NOT NULL,
+ TO_FINGERPRINT_ONLY CHAR DEFAULT 'Y' NOT NULL,
+ CONSUMER_REQUESTED CHAR DEFAULT 'Y' NOT NULL,
+ EVENT_PUBLISHED CHAR DEFAULT 'N' NOT NULL,
+ PUBLISHED_DATETIME TIMESTAMP NULL,
+ JOB_XML CLOB NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
diff --git a/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_postgres.sql b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_postgres.sql
new file mode 100644
index 00000000..f1ea4f85
--- /dev/null
+++ b/SIF3InfraREST/DB/DDL/Datafix/current/v0.12.0-v0.13.0/Datafix_Job_postgres.sql
@@ -0,0 +1,86 @@
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED TIMESTAMP NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED TIMESTAMP NULL,
+ EXPIRE_DATETIME TIMESTAMP NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL,
+ JOB_EVENT_DATETIME TIMESTAMP NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE BOOLEAN DEFAULT 1 NOT NULL,
+ TO_FINGERPRINT_ONLY BOOLEAN DEFAULT 1 NOT NULL,
+ CONSUMER_REQUESTED BOOLEAN DEFAULT 1 NOT NULL,
+ EVENT_PUBLISHED BOOLEAN DEFAULT 0 NOT NULL,
+ PUBLISHED_DATETIME TIMESTAMP NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
diff --git a/SIF3InfraREST/DB/DDL/Initial_Inserts.sql b/SIF3InfraREST/DB/DDL/Initial_Inserts.sql
index 0aa79f23..3efe9d36 100644
--- a/SIF3InfraREST/DB/DDL/Initial_Inserts.sql
+++ b/SIF3InfraREST/DB/DDL/Initial_Inserts.sql
@@ -5,3 +5,19 @@ insert into SIF3_ENV_TEMPLATE values ('DEV_LOCAL','devLocal.xml');
insert into SIF3_APP_TEMPLATE (APP_TEMPLATE_ID,SOLUTION_ID,APPLICATION_KEY,PASSWORD,USER_TOKEN,INSTANCE_ID,AUTH_METHOD,ENV_TEMPLATE_ID)
values (1, 'test', 'TestSIS', 'Password1', null, null, 'Basic', 'DEV_LOCAL');
+
+
+-- -----------------------------------------------------
+-- Insert for Default Job Template
+-- -----------------------------------------------------
+-- Consumer
+INSERT INTO SIF3_JOB_TEMPLATE (JOB_TEMPLATE_ID,JOB_URL_NAME,ADAPTER_TYPE,TEMPLATE_FILE_NAME)
+VALUES (1,'RolloverStudents','CONSUMER','rolloverStudentJob.xml');
+
+-- Provider: DIRECT
+INSERT INTO SIF3_JOB_TEMPLATE (JOB_TEMPLATE_ID,JOB_URL_NAME,ADAPTER_TYPE,TEMPLATE_FILE_NAME)
+VALUES (2,'RolloverStudents','ENVIRONMENT_PROVIDER','rolloverStudentJob.xml');
+
+-- Provider: BROKERED
+INSERT INTO SIF3_JOB_TEMPLATE (JOB_TEMPLATE_ID,JOB_URL_NAME,ADAPTER_TYPE,TEMPLATE_FILE_NAME)
+VALUES (3,'RolloverStudents','PROVIDER','rolloverStudentJob.xml');
diff --git a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_SQLite.sql b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_SQLite.sql
index d179648c..d7f218d9 100644
--- a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_SQLite.sql
+++ b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_SQLite.sql
@@ -156,3 +156,88 @@ CREATE TABLE IF NOT EXISTS SIF3_SEC_SERVICE_PARAM (
CREATE INDEX IDX_EXT_SEC_SVC ON SIF3_SEC_SERVICE_PARAM (EXT_SECURITY_SERVICE_ID ASC);
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB (
+ JOB_ID INTEGER PRIMARY KEY AUTOINCREMENT,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED DATETIME NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED DATETIME NULL,
+ EXPIRE_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL);
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS SIF3_JOB_EVENT (
+ JOB_EVENT_ID INTEGER PRIMARY KEY AUTOINCREMENT,
+ JOB_EVENT_DATETIME DATETIME NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR(1) NOT NULL,
+ FULL_UPDATE INTEGER NOT NULL DEFAULT 1,
+ TO_FINGERPRINT_ONLY INTEGER NOT NULL DEFAULT 1,
+ CONSUMER_REQUESTED INTEGER NOT NULL DEFAULT 1,
+ EVENT_PUBLISHED INTEGER NOT NULL DEFAULT 0,
+ PUBLISHED_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL);
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+
diff --git a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mssql.sql b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mssql.sql
index e5b3b767..ae512c6d 100644
--- a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mssql.sql
+++ b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mssql.sql
@@ -92,7 +92,7 @@ CREATE INDEX IDK_SUBSCR_ZONE_CTX_SVC ON SIF3_SUBSCRIPTION (ZONE_ID ASC, CONTEXT_
-- Table SIF3_ENV_TEMPLATE
-- -----------------------------------------------------
-CREATE TABLE SIF3_ENV_TEMPLATE
+CREATE TABLE SIF3_ENV_TEMPLATE
(
ENV_TEMPLATE_ID VARCHAR(50) NOT NULL ,
TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL ,
@@ -102,7 +102,7 @@ CREATE TABLE SIF3_ENV_TEMPLATE
-- -----------------------------------------------------
-- Table SIF3_APP_TEMPLATE
-- -----------------------------------------------------
-CREATE TABLE SIF3_APP_TEMPLATE
+CREATE TABLE SIF3_APP_TEMPLATE
(
APP_TEMPLATE_ID INT NOT NULL ,
SOLUTION_ID VARCHAR(100) NULL ,
@@ -129,7 +129,7 @@ CREATE INDEX IDX_APP_TMPLT_TO_ENV_TMPLT ON SIF3_APP_TEMPLATE (ENV_TEMPLATE_ID AS
-- -----------------------------------------------------
-- Table SIF3_EXT_SECURITY_SERVICE
-- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS SIF3_EXT_SECURITY_SERVICE (
+CREATE TABLE SIF3_EXT_SECURITY_SERVICE (
EXT_SECURITY_SERVICE_ID INT NOT NULL,
AUTH_METHOD VARCHAR(20) NULL,
ADAPTER_TYPE VARCHAR(20) NULL,
@@ -145,7 +145,7 @@ CREATE UNIQUE INDEX UQ_AUTH_METH_ADAP ON SIF3_EXT_SECURITY_SERVICE (AUTH_METHOD
-- -----------------------------------------------------
-- Table SIF3_SEC_SERVICE_PARAM
-- -----------------------------------------------------
-CREATE TABLE IF NOT EXISTS SIF3_SEC_SERVICE_PARAM (
+CREATE TABLE SIF3_SEC_SERVICE_PARAM (
SEC_SERVICE_PARAM_ID INT NOT NULL,
EXT_SECURITY_SERVICE_ID INT NOT NULL,
PARAM_NAME VARCHAR(45) NULL,
@@ -159,3 +159,90 @@ CREATE TABLE IF NOT EXISTS SIF3_SEC_SERVICE_PARAM (
);
CREATE INDEX IDX_EXT_SEC_SVC ON SIF3_SEC_SERVICE_PARAM (EXT_SECURITY_SERVICE_ID ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL IDENTITY,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED DATETIME NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED DATETIME NULL,
+ EXPIRE_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL IDENTITY,
+ JOB_EVENT_DATETIME DATETIME NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE BIT DEFAULT (1) NOT NULL,
+ TO_FINGERPRINT_ONLY BIT DEFAULT (1) NOT NULL,
+ CONSUMER_REQUESTED BIT DEFAULT (1) NOT NULL,
+ EVENT_PUBLISHED BIT DEFAULT (0) NOT NULL,
+ PUBLISHED_DATETIME DATETIME NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
diff --git a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mysql.sql b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mysql.sql
index 20cd841a..fea25d44 100644
--- a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mysql.sql
+++ b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_mysql.sql
@@ -193,6 +193,100 @@ ENGINE = InnoDB;
CREATE INDEX `IDX_EXT_SEC_SVC` ON `SIF3_SEC_SERVICE_PARAM` (`EXT_SECURITY_SERVICE_ID` ASC);
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `SIF3_JOB_TEMPLATE` (
+ `JOB_TEMPLATE_ID` INT NOT NULL,
+ `JOB_URL_NAME` VARCHAR(100) NOT NULL,
+ `ADAPTER_TYPE` VARCHAR(20) NULL,
+ `TEMPLATE_FILE_NAME` VARCHAR(100) NOT NULL,
+ PRIMARY KEY (`JOB_TEMPLATE_ID`))
+ENGINE = InnoDB;
+
+CREATE UNIQUE INDEX `UQ_JOB_URL_NAME` ON `SIF3_JOB_TEMPLATE` (`JOB_URL_NAME` ASC, `ADAPTER_TYPE` ASC);
+
+
+-- -----------------------------------------------------
+-- Table `SIF3_JOB`
+-- -----------------------------------------------------
+DROP TABLE IF EXISTS `SIF3_JOB` ;
+
+CREATE TABLE IF NOT EXISTS `SIF3_JOB` (
+ `JOB_ID` INT NOT NULL AUTO_INCREMENT,
+ `JOB_REFID` VARCHAR(36) NOT NULL,
+ `SERVICE_NAME` VARCHAR(256) NULL,
+ `CURRENT_JOB_STATE` VARCHAR(30) NULL,
+ `ENVIRONMENT_REFID` VARCHAR(36) NULL,
+ `ADAPTER_TYPE` VARCHAR(20) NOT NULL,
+ `FINGERPRINT` VARCHAR(256) NULL,
+ `ZONE_ID` VARCHAR(256) NULL,
+ `CONTEXT_ID` VARCHAR(255) NULL,
+ `CREATED` DATETIME NOT NULL,
+ `TIMEOUT_PERIOD` VARCHAR(30) NULL,
+ `LAST_MODIFIED` DATETIME NULL,
+ `EXPIRE_DATETIME` DATETIME NULL,
+ `JOB_XML` TEXT NULL,
+ PRIMARY KEY (`JOB_ID`))
+ENGINE = InnoDB;
+
+CREATE INDEX `JOB_JOBREFID_ADPTYPE_IDX` ON `SIF3_JOB` (`JOB_REFID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_FINGERPRT_ADPTYPE_IDX` ON `SIF3_JOB` (`FINGERPRINT` ASC, `SERVICE_NAME` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_ENVREFID_ADPTYPE_IDX` ON `SIF3_JOB` (`FINGERPRINT` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_ZONEID_ADPTYPE_IDX` ON `SIF3_JOB` (`ZONE_ID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_REFID_ENVREFID_ADPTYPE_IDX` ON `SIF3_JOB` (`JOB_REFID` ASC, `ENVIRONMENT_REFID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_SVCNNAME_ADPTYPE_IDX` ON `SIF3_JOB` (`SERVICE_NAME` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOB_EXPIRE_IDX` ON `SIF3_JOB` (`EXPIRE_DATETIME` ASC);
+
+-- -----------------------------------------------------
+-- Table `SIF3_JOB_EVENT`
+-- -----------------------------------------------------
+DROP TABLE IF EXISTS `SIF3_JOB_EVENT` ;
+
+CREATE TABLE IF NOT EXISTS `SIF3_JOB_EVENT` (
+ `JOB_EVENT_ID` INT NOT NULL AUTO_INCREMENT,
+ `JOB_EVENT_DATETIME` DATETIME NOT NULL,
+ `JOB_REFID` VARCHAR(36) NOT NULL,
+ `SERVICE_NAME` VARCHAR(256) NULL,
+ `ENVIRONMENT_REFID` VARCHAR(36) NULL,
+ `ADAPTER_TYPE` VARCHAR(20) NOT NULL,
+ `FINGERPRINT` VARCHAR(256) NULL,
+ `ZONE_ID` VARCHAR(256) NULL,
+ `CONTEXT_ID` VARCHAR(256) NULL,
+ `EVENT_TYPE` CHAR(1) NOT NULL,
+ `FULL_UPDATE` TINYINT(1) NOT NULL DEFAULT 1,
+ `TO_FINGERPRINT_ONLY` TINYINT(1) NOT NULL DEFAULT 1,
+ `CONSUMER_REQUESTED` TINYINT(1) NOT NULL DEFAULT 1,
+ `EVENT_PUBLISHED` TINYINT(1) NOT NULL DEFAULT 0,
+ `PUBLISHED_DATETIME` DATETIME NULL,
+ `JOB_XML` TEXT NULL,
+ PRIMARY KEY (`JOB_EVENT_ID`))
+ENGINE = InnoDB;
+
+CREATE INDEX `JOBEVT_DT_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`JOB_EVENT_DATETIME` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_JOBREFID_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`JOB_REFID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_FINGERPRT_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`FINGERPRINT` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_ENVREFID_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`ENVIRONMENT_REFID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_ZONEID_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`ZONE_ID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_DT_EVTY_ADPTY_PUBL_IDX` ON `SIF3_JOB_EVENT` (`JOB_EVENT_DATETIME` ASC, `ADAPTER_TYPE` ASC, `EVENT_TYPE` ASC, `EVENT_PUBLISHED` ASC);
+
+CREATE INDEX `JOBEVT_DT__ADPTY_PUBL_IDX` ON `SIF3_JOB_EVENT` (`JOB_EVENT_DATETIME` ASC, `ADAPTER_TYPE` ASC, `EVENT_PUBLISHED` ASC);
+
+CREATE INDEX `JOBEVT_JOBID_ENVID_ADPTY_IDX` ON `SIF3_JOB_EVENT` (`JOB_REFID` ASC, `ENVIRONMENT_REFID` ASC, `ADAPTER_TYPE` ASC);
+
+CREATE INDEX `JOBEVT_SVCNAME_ADPTYPE_IDX` ON `SIF3_JOB_EVENT` (`SERVICE_NAME` ASC, `ADAPTER_TYPE` ASC);
+
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
diff --git a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_oracle.sql b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_oracle.sql
index 8721b643..1b896bcb 100644
--- a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_oracle.sql
+++ b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_oracle.sql
@@ -159,6 +159,91 @@ REFERENCES SIF3_EXT_SECURITY_SERVICE (EXT_SECURITY_SERVICE_ID);
CREATE INDEX IDX_EXT_SEC_SVC ON SIF3_SEC_SERVICE_PARAM (EXT_SECURITY_SERVICE_ID ASC);
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR2(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR2(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR2(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL,
+ JOB_REFID VARCHAR2(36) NOT NULL,
+ SERVICE_NAME VARCHAR2(256) NULL,
+ CURRENT_JOB_STATE VARCHAR2(30) NULL,
+ ENVIRONMENT_REFID VARCHAR2(36) NULL,
+ ADAPTER_TYPE VARCHAR2(20) NOT NULL,
+ FINGERPRINT VARCHAR2(256) NULL,
+ ZONE_ID VARCHAR2(256) NULL,
+ CONTEXT_ID VARCHAR2(255) NULL,
+ CREATED TIMESTAMP NOT NULL,
+ TIMEOUT_PERIOD VARCHAR2(30) NULL,
+ LAST_MODIFIED TIMESTAMP NULL,
+ EXPIRE_DATETIME TIMESTAMP NULL,
+ JOB_XML CLOB NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL,
+ JOB_EVENT_DATETIME TIMESTAMP NOT NULL,
+ JOB_REFID VARCHAR2(36) NOT NULL,
+ SERVICE_NAME VARCHAR2(256) NULL,
+ ENVIRONMENT_REFID VARCHAR2(36) NULL,
+ ADAPTER_TYPE VARCHAR2(20) NOT NULL,
+ FINGERPRINT VARCHAR2(256) NULL,
+ ZONE_ID VARCHAR2(256) NULL,
+ CONTEXT_ID VARCHAR2(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE CHAR DEFAULT 'Y' NOT NULL,
+ TO_FINGERPRINT_ONLY CHAR DEFAULT 'Y' NOT NULL,
+ CONSUMER_REQUESTED CHAR DEFAULT 'Y' NOT NULL,
+ EVENT_PUBLISHED CHAR DEFAULT 'N' NOT NULL,
+ PUBLISHED_DATETIME TIMESTAMP NULL,
+ JOB_XML CLOB NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
-- -----------------------------------------------------
-- Sequence for AUTO_INCREMENT
-- -----------------------------------------------------
diff --git a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_postgres.sql b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_postgres.sql
index 9e068663..77236a99 100644
--- a/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_postgres.sql
+++ b/SIF3InfraREST/DB/DDL/SIF3InfrastructureERM_DDL_postgres.sql
@@ -159,6 +159,92 @@ REFERENCES SIF3_EXT_SECURITY_SERVICE (EXT_SECURITY_SERVICE_ID);
CREATE INDEX IDX_EXT_SEC_SVC ON SIF3_SEC_SERVICE_PARAM (EXT_SECURITY_SERVICE_ID ASC);
+-- -----------------------------------------------------
+-- Table SIF3_JOB_TEMPLATE
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_TEMPLATE (
+ JOB_TEMPLATE_ID INT NOT NULL,
+ JOB_URL_NAME VARCHAR(100) NOT NULL,
+ ADAPTER_TYPE VARCHAR(20) NULL,
+ TEMPLATE_FILE_NAME VARCHAR(100) NOT NULL,
+ PRIMARY KEY (JOB_TEMPLATE_ID));
+
+CREATE UNIQUE INDEX UQ_JOB_URL_NAME ON SIF3_JOB_TEMPLATE (JOB_URL_NAME ASC, ADAPTER_TYPE ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB (
+ JOB_ID INT NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ CURRENT_JOB_STATE VARCHAR(30) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(255) NULL,
+ CREATED TIMESTAMP NOT NULL,
+ TIMEOUT_PERIOD VARCHAR(30) NULL,
+ LAST_MODIFIED TIMESTAMP NULL,
+ EXPIRE_DATETIME TIMESTAMP NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_ID));
+
+CREATE INDEX JOB_JOBREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_FINGERPRT_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_ZONEID_ADPTYPE_IDX ON SIF3_JOB (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_REFID_ENVREFID_ADPTYPE_IDX ON SIF3_JOB (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_SVCNNAME_ADPTYPE_IDX ON SIF3_JOB (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOB_EXPIRE_IDX ON SIF3_JOB (EXPIRE_DATETIME ASC);
+
+-- -----------------------------------------------------
+-- Table SIF3_JOB_EVENT
+-- -----------------------------------------------------
+CREATE TABLE SIF3_JOB_EVENT (
+ JOB_EVENT_ID INT NOT NULL,
+ JOB_EVENT_DATETIME TIMESTAMP NOT NULL,
+ JOB_REFID VARCHAR(36) NOT NULL,
+ SERVICE_NAME VARCHAR(256) NULL,
+ ENVIRONMENT_REFID VARCHAR(36) NULL,
+ ADAPTER_TYPE VARCHAR(20) NOT NULL,
+ FINGERPRINT VARCHAR(256) NULL,
+ ZONE_ID VARCHAR(256) NULL,
+ CONTEXT_ID VARCHAR(256) NULL,
+ EVENT_TYPE CHAR NOT NULL,
+ FULL_UPDATE BOOLEAN DEFAULT 1 NOT NULL,
+ TO_FINGERPRINT_ONLY BOOLEAN DEFAULT 1 NOT NULL,
+ CONSUMER_REQUESTED BOOLEAN DEFAULT 1 NOT NULL,
+ EVENT_PUBLISHED BOOLEAN DEFAULT 0 NOT NULL,
+ PUBLISHED_DATETIME TIMESTAMP NULL,
+ JOB_XML TEXT NULL,
+ PRIMARY KEY (JOB_EVENT_ID));
+
+CREATE INDEX JOBEVT_DT_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_JOBREFID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_FINGERPRT_ADPTY_IDX ON SIF3_JOB_EVENT (FINGERPRINT ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ENVREFID_ADPTY_IDX ON SIF3_JOB_EVENT (ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_ZONEID_ADPTY_IDX ON SIF3_JOB_EVENT (ZONE_ID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_DT_EVTY_ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_DT__ADPTY_PUBL_IDX ON SIF3_JOB_EVENT (JOB_EVENT_DATETIME ASC, ADAPTER_TYPE ASC, EVENT_PUBLISHED ASC);
+
+CREATE INDEX JOBEVT_JOBID_ENVID_ADPTY_IDX ON SIF3_JOB_EVENT (JOB_REFID ASC, ENVIRONMENT_REFID ASC, ADAPTER_TYPE ASC);
+
+CREATE INDEX JOBEVT_SVCNAME_ADPTYPE_IDX ON SIF3_JOB_EVENT (SERVICE_NAME ASC, ADAPTER_TYPE ASC);
+
-- -----------------------------------------------------
-- Sequence for AUTO_INCREMENT
-- -----------------------------------------------------
diff --git a/SIF3InfraREST/DB/Data/SIF3Infra.sqliteDB b/SIF3InfraREST/DB/Data/SIF3Infra.sqliteDB
index eebd0df0..46c3a492 100644
Binary files a/SIF3InfraREST/DB/Data/SIF3Infra.sqliteDB and b/SIF3InfraREST/DB/Data/SIF3Infra.sqliteDB differ
diff --git a/SIF3InfraREST/README.md b/SIF3InfraREST/README.md
index 7ef370ad..ded8229b 100644
--- a/SIF3InfraREST/README.md
+++ b/SIF3InfraREST/README.md
@@ -25,7 +25,7 @@ Project (note that the version number will change over time):
sif3.framework
sif3-infra-rest
- 0.12.0
+ 0.13.0
```
@@ -165,6 +165,15 @@ project is upgraded properly to the new framework version.**
**Please refer to the detailed release notes in "release/v0.12.0" carefully to ensure that your project is upgraded properly to the
new framework version.**
+## Version from Sept 25, 2018: v0.13.0 - Various changes
+- Fixed a number of minor issues.
+- Added SIFException to many Provider Interface methods to enable providers to throw a "generic" exceptions where the
+ provider can customise the HTTP Status Code and error message to be returned to the consumer.
+- Added Functional Services functionality. Usage of this functionality is detailed in the developer's guide in
+ section 5.11.
+
+**Please refer to the detailed release notes in "release/v0.13.0" carefully to ensure that your project is upgraded properly to the new framework version.**
+
# Download Instructions
How to download this project:
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/pom.xml b/SIF3InfraREST/SIF3Demo/sif3-demo-web/pom.xml
index 6bc4350e..0e3b7c27 100644
--- a/SIF3InfraREST/SIF3Demo/sif3-demo-web/pom.xml
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/pom.xml
@@ -8,7 +8,7 @@
sif3.framework
sif3-demo-web
war
- 0.12.0-Demo
+ 0.13.0-Demo
SIF3 Demo Provider
@@ -41,7 +41,7 @@
sif3.framework
sif3-infra-rest
- 0.12.0-beta
+ 0.13.0-beta
@@ -51,7 +51,7 @@
sifau
sif3-au-datamodel
- 3.4.1
+ 3.4.3
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/BaseFunctionalConsumer.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/BaseFunctionalConsumer.java
new file mode 100644
index 00000000..6a7bcb98
--- /dev/null
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/BaseFunctionalConsumer.java
@@ -0,0 +1,221 @@
+/*
+ * BaseFunctionalConsumer.java
+ * Created: 26 Jul 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package systemic.sif3.demo.rest.consumer.functional;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.util.Date;
+import java.util.HashMap;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import au.com.systemic.framework.utils.DateUtils;
+import sif3.common.model.EventMetadata;
+import sif3.common.model.PagingInfo;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFEvent;
+import sif3.common.model.SIFZone;
+import sif3.common.model.delayed.DelayedResponseReceipt;
+import sif3.common.model.job.PhaseInfo;
+import sif3.common.utils.JAXBUtils;
+import sif3.common.ws.CreateOperationStatus;
+import sif3.common.ws.ErrorDetails;
+import sif3.common.ws.OperationStatus;
+import sif3.common.ws.job.PhaseDataResponse;
+import sif3.common.ws.model.MultiOperationStatusList;
+import sif3.infra.common.model.JobCollectionType;
+import sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer;
+
+/**
+ * Note: All the processABC() methods implemented in this class are for demo purpose only. It simply logs the returned response to any
+ * delayed requests. In actual real world implementations these methods need to be implemented properly, meaning some action is performed
+ * on the data store that relates to the received response.
+ *
+ * @author Joerg Huber
+ */
+public abstract class BaseFunctionalConsumer extends AbstractFunctionalServiceConsumer
+{
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final static String RECORD_MARKER = "\n================================================================================================\n";
+ private HashMap recordCounters = new HashMap();
+ private static Boolean writePayload = null;
+
+ public BaseFunctionalConsumer()
+ {
+ super();
+
+ //Initialise JAXB context for Job classes. Make consumer behave better against race conditions.
+ JAXBUtils.initCtx(getMultiObjectClassInfo().getObjectType());
+ JAXBUtils.initCtx(getSingleObjectClassInfo().getObjectType());
+ }
+
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedJobsCreate(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedJobsCreate(MultiOperationStatusList statusList, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED JOB CREATE Response for "+getServiceURLNamePlural()+":\n"+statusList+"\nDelayed Receipt Details:\n"+receipt);
+
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedJobsDelete(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedJobsDelete(MultiOperationStatusList statusList, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED JOB DELETE Response for "+getServiceURLNamePlural()+":\n"+statusList+"\nDelayed Receipt Details:\n"+receipt);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedJobsQuery(sif3.infra.common.model.JobCollectionType, sif3.common.model.PagingInfo, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedJobsQuery(JobCollectionType jobs, PagingInfo pagingInfo, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED JOB QUERY Response for "+getServiceURLNamePlural()+":\n"+jobs+"\nPagingInfo:\n"+pagingInfo+"\nDelayed Receipt Details:\n"+receipt);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedPhaseQuery(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataResponse, sif3.common.model.PagingInfo, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedPhaseQuery(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, PagingInfo pagingInfo, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED PHASE QUERY Response:\nPhase Info: "+phaseInfo+"\nPagingInfo:\n"+pagingInfo+"\nDelayed Receipt Details:\n"+receipt+"\nPhase Response Data:\n"+phaseDataResponse);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedPhaseCreate(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataResponse, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedPhaseCreate(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED PHASE CREATE Response:\nPhase Info: "+phaseInfo+"\nDelayed Receipt Details:\n"+receipt+"\nPhase Response Data:\n"+phaseDataResponse);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedPhaseUpdate(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataResponse, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedPhaseUpdate(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED PHASE UPDATE Response:\nPhase Info: "+phaseInfo+"\nDelayed Receipt Details:\n"+receipt+"\nPhase Response Data:\n"+phaseDataResponse);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processDelayedPhaseDelete(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataResponse, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedPhaseDelete(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED PHASE DELETE Response:\nPhase Info: "+phaseInfo+"\nDelayed Receipt Details:\n"+receipt+"\nPhase Response Data:\n"+phaseDataResponse);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.BaseConsumer#processDelayedError(sif3.common.ws.ErrorDetails, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void processDelayedError(ErrorDetails error, DelayedResponseReceipt receipt)
+ {
+ logger.debug("Received DELAYED ERROR Response:\n"+error+"\nDelayed Receipt Details:\n"+receipt);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#processJobEvent(sif3.common.model.SIFEvent, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.EventMetadata, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void processJobEvent(SIFEvent sifEvent, SIFZone zone, SIFContext context, EventMetadata metadata, String msgReadID, String consumerID)
+ {
+ String consumerName = getPrettyName()+"(QueueID:"+msgReadID+"; ConsumerID: "+consumerID+")";
+ logger.debug(consumerName +" received an event from Zone = "+zone+", Context = "+context+" and Event Metadata = "+metadata);
+ dumpJobEvent(sifEvent, zone, context, msgReadID, consumerID);
+ }
+
+ /*---------------------*/
+ /*-- Private Methods --*/
+ /*---------------------*/
+
+ private void dumpJobEvent(SIFEvent sifEvent, SIFZone zone, SIFContext context, String msgReadID, String consumerID)
+ {
+// logger.debug(consumerID +" write data to file...");
+ String filename = getFileNameOnly(consumerID);
+ String fullFileName= getFullFileName(consumerID);
+ Integer recordNum = recordCounters.get(filename);
+ if (recordNum == null)
+ {
+ recordNum = 1;
+ recordCounters.put(filename, recordNum);
+ }
+ String timestamp = DateUtils.getISO8601withSecFraction(new Date());
+
+ BufferedWriter out = null;
+ try
+ {
+ FileWriter fstream = new FileWriter(fullFileName, true);
+ out = new BufferedWriter(fstream);
+ out.write(RECORD_MARKER);
+ out.write("Record " + recordNum + " - processed by Thread ID = "+Thread.currentThread().getId()+"\n"+sifEvent.getListSize()+" "+sifEvent.getEventAction().name()+" Events from Queue Reader "+ msgReadID+"\nReceived at "+timestamp+" from Zone = "+zone.getId()+" and Context = "+context.getId());
+ out.write(RECORD_MARKER);
+
+ if (writePayload == null)
+ {
+ writePayload = getServiceProperties().getPropertyAsBool("test.consumer.write.payload", true);
+ }
+
+ if (writePayload)
+ {
+ out.write(getMarshaller().marshal(sifEvent.getSIFObjectList(), MediaType.APPLICATION_XML_TYPE));
+ }
+ recordNum++;
+ recordCounters.put(filename, recordNum);
+ }
+ catch (Exception ex)
+ {
+ logger.error("Failed to write data to dump file with name:" + fullFileName, ex);
+ }
+ finally
+ {
+ if (out != null)
+ {
+ try
+ {
+ out.close();
+ }
+ catch (Exception ex) {} // nothing we can do
+ }
+ }
+ }
+
+ private String getFullFileName(String consumerID)
+ {
+ return getServiceProperties().getPropertyAsString("test.tempDir.output","") + "/" + getFileNameOnly(consumerID);
+
+ }
+
+ private String getFileNameOnly(String consumerID)
+ {
+ return consumerID+".log";
+ }
+}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/RolloverStudentConsumer.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/RolloverStudentConsumer.java
new file mode 100644
index 00000000..97564883
--- /dev/null
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/consumer/functional/RolloverStudentConsumer.java
@@ -0,0 +1,58 @@
+/*
+ * RolloverStudentConsumer.java
+ * Created: 26 Jul 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package systemic.sif3.demo.rest.consumer.functional;
+
+/**
+ * @author Joerg Huber
+ *
+ */
+public class RolloverStudentConsumer extends BaseFunctionalConsumer
+{
+
+ public RolloverStudentConsumer()
+ {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#getServiceURLNamePlural()
+ */
+ @Override
+ public String getServiceURLNamePlural()
+ {
+ return "RolloverStudents";
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.AbstractFunctionalServiceConsumer#getServiceURLNameSingular()
+ */
+ @Override
+ public String getServiceURLNameSingular()
+ {
+ return "RolloverStudent";
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.consumer.BaseConsumer#shutdown()
+ */
+ @Override
+ public void shutdown()
+ {
+ logger.info("Shutting down Functional Service Consumer '"+getServiceURLNamePlural()+"'");
+ }
+}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/StudentPersonalProvider.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/StudentPersonalProvider.java
index 5343a9b6..f541a0be 100644
--- a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/StudentPersonalProvider.java
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/StudentPersonalProvider.java
@@ -39,6 +39,7 @@
import sif3.common.conversion.ModelObjectInfo;
import sif3.common.exception.DataTooLargeException;
import sif3.common.exception.PersistenceException;
+import sif3.common.exception.SIFException;
import sif3.common.exception.UnmarshalException;
import sif3.common.exception.UnsupportedMediaTypeExcpetion;
import sif3.common.exception.UnsupportedQueryException;
@@ -339,7 +340,7 @@ public Object retrieve(SIFZone zone, SIFContext context, PagingInfo pagingInfo,
if (pagingInfo == null)
{
- throw new DataTooLargeException("No paging info is provided. Please provide navigationPage and navigationPageSize.");
+ throw new DataTooLargeException("No paging info is provided.", "Please provide navigationPage and navigationPageSize.", "Provider ("+getProviderName()+")");
}
else
{
@@ -372,7 +373,7 @@ public Object retrieve(SIFZone zone, SIFContext context, PagingInfo pagingInfo,
*/
@Override
public Object retrieveByServicePath(QueryCriteria queryCriteria, SIFZone zone, SIFContext context, PagingInfo pagingInfo, RequestMetadata metadata, ResponseParameters customResponseParams)
- throws PersistenceException, UnsupportedQueryException, DataTooLargeException
+ throws PersistenceException, UnsupportedQueryException, DataTooLargeException, SIFException
{
logger.debug("Performing query by service path.");
if (logger.isDebugEnabled())
@@ -407,12 +408,12 @@ else if ("TeachingGroups".equals(predicates.get(0).getSubject()))
}
else
{
- throw new UnsupportedQueryException("The query condition (driven by the service path) "+queryCriteria+" is not supported by the provider.");
+ throw new UnsupportedQueryException("Unsupported ServicePath.", "The query condition (driven by the service path) "+queryCriteria+" is not supported by the provider.", "Provider ("+getProviderName()+")");
}
}
else // not supported query (only single level service path query supported by this provider)
{
- throw new UnsupportedQueryException("The query condition (driven by the service path) "+queryCriteria+" is not supported by the provider.");
+ throw new UnsupportedQueryException("Unsupported ServicePath.", "The query condition (driven by the service path) "+queryCriteria+" is not supported by the provider.", "Provider ("+getProviderName()+")");
}
}
@@ -421,7 +422,7 @@ else if ("TeachingGroups".equals(predicates.get(0).getSubject()))
* @see sif3.common.interfaces.QueryProvider#retrieveByQBE(java.lang.Object, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.PagingInfo, sif3.common.model.RequestMetadata)
*/
public Object retrieveByQBE(Object exampleObject, SIFZone zone, SIFContext context, PagingInfo pagingInfo, RequestMetadata metadata, ResponseParameters customResponseParams)
- throws PersistenceException, UnsupportedQueryException, DataTooLargeException
+ throws PersistenceException, UnsupportedQueryException, DataTooLargeException, SIFException
{
logger.debug("Performing QBE query for: "+exampleObject);
if (exampleObject instanceof StudentPersonalType)
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/functional/RolloverStudentsProvider.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/functional/RolloverStudentsProvider.java
new file mode 100644
index 00000000..ee58f38b
--- /dev/null
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/main/java/systemic/sif3/demo/rest/provider/functional/RolloverStudentsProvider.java
@@ -0,0 +1,439 @@
+/*
+ * RolloverStudentsProvider.java
+ * Created: 7 Jun 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package systemic.sif3.demo.rest.provider.functional;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import sif3.common.CommonConstants.JobState;
+import sif3.common.CommonConstants.PhaseState;
+import sif3.common.conversion.ModelObjectInfo;
+import sif3.common.exception.DataTooLargeException;
+import sif3.common.exception.PersistenceException;
+import sif3.common.exception.SIFException;
+import sif3.common.exception.UnsupportedMediaTypeExcpetion;
+import sif3.common.exception.UnsupportedQueryException;
+import sif3.common.header.HeaderProperties;
+import sif3.common.model.PagingInfo;
+import sif3.common.model.RequestMetadata;
+import sif3.common.model.ResponseParameters;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFZone;
+import sif3.common.model.job.PhaseInfo;
+import sif3.common.ws.job.PhaseDataRequest;
+import sif3.common.ws.job.PhaseDataResponse;
+import sif3.infra.common.model.JobType;
+import sif3.infra.rest.provider.BaseFunctionalServiceProvider;
+
+/**
+ * @author Joerg Huber
+ *
+ */
+public class RolloverStudentsProvider extends BaseFunctionalServiceProvider
+{
+ private static int numOperations = 0;
+
+ /*--------------------*/
+ /*-- Job Operations --*/
+ /*--------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.provider.BaseFunctionalServiceProvider#createNewJob(sif3.infra.common.model.JobType, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public JobType createNewJob(JobType jobData,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams)
+ throws IllegalArgumentException, PersistenceException, SIFException
+ {
+ logger.debug("Create Job for Functional Service "+getMultiObjectClassInfo().getObjectName()+"...");
+
+ // At the moment we do nothing... A real implementation may need to do some stuff here.
+ if (jobData.getInitialization() != null)
+ {
+ logger.debug("Initialisation values:\nInitialisation Phase: "+jobData.getInitialization().getPhaseName()+"\nInitialisation Parameters: "+jobData.getInitialization().getPayload());
+ }
+ else
+ {
+ logger.debug("No initialisation provided for Job.");
+ }
+
+ // return the final job object. Generally that is the same as the one given to this method.
+ return jobData;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#deleteJob(java.lang.String, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public boolean deleteJob(String jobID,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams)
+ throws IllegalArgumentException, PersistenceException, SIFException
+ {
+ logger.debug("Delete Job for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = '"+jobID+"'");
+
+ // Generally the implementation would check here what needs to be done to remove a job. If the implementation no longer
+ // knows the job (i.e. has been removed in the past) then it must return false. If the job is still known it must be removed and true
+ // must be returned.
+
+ // We pretend to fail every 3rd delete just for the sake of testing real world scenarios where failure occur.
+ return ((numOperations++ % 3) != 0);
+ }
+
+ /*----------------------*/
+ /*-- Phase Operations --*/
+ /*----------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#retrieveDataFromPhase(java.lang.String, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.PagingInfo, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters, javax.ws.rs.core.MediaType)
+ */
+ @Override
+ public PhaseDataResponse retrieveDataFromPhase(PhaseInfo phaseInfo,
+ SIFZone zone,
+ SIFContext context,
+ PagingInfo pagingInfo,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams,
+ MediaType returnMimeType)
+ throws PersistenceException, UnsupportedMediaTypeExcpetion, DataTooLargeException, SIFException
+ {
+ logger.debug("retrieveDataFromPhase for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = "+phaseInfo.getJobID()+" and phase "+phaseInfo.getPhaseName()+" called.");
+ logger.debug("Requested Return Mime Type: "+returnMimeType.toString()+"\nPaging Info: "+pagingInfo);
+
+ // Generally the implementation would check here what needs to be returned for the given phase.
+ // General flow would be:
+ // 1) Check the phase for the given job and based on this...
+ // 2) Query the provider's data store for data.
+ // 3) Marshal the result into String with the format indicated by the 'returnMimeType'.
+ // Suggestion: Use the MarshallFactory class of this framework.
+ // 4) If all is ok then return the resulting String. If anything fails raise appropriate exception.
+ // Note: Null can be returned which indicates no or no further data available for this phase. This will be
+ // translated into an appropriate value being returned to the consumer (SIF Specification states that
+ // this will return a HTTP Status 204 (NO_CONTENT).)
+ //
+ // As part of this call the provider may also update the job and/or phase state using appropriate updateXYZ() methods
+ // of the BaseFunctionalServiceProvider class.
+
+ // For testing an illustration purpose we create the odd error...
+ if ((pagingInfo == null) || (pagingInfo.getPageSize() > 100))
+ {
+ throw new DataTooLargeException("Pagin Info invalid.","Paging info not provided or page size is larger than 100.", "Provider ("+getProviderName()+")");
+ }
+
+ if (phaseInfo.getPhaseName().equalsIgnoreCase("oldYearEnrolment"))
+ {
+ if (!returnMimeType.isCompatible(MediaType.APPLICATION_XML_TYPE))
+ {
+ throw new UnsupportedMediaTypeExcpetion(returnMimeType.toString()+" is not accepted. Valid mime type is "+ MediaType.APPLICATION_XML_TYPE.toString());
+ }
+
+ //make up some dummy data..
+ PhaseDataResponse response = new PhaseDataResponse();
+ response.setMimeType(returnMimeType);
+ response.setData(
+ "\n"+
+ " \n"+
+ " 2017\n"+
+ " 4001\n"+
+ " \n"+
+ " \n"+
+ " 2018\n"+
+ " 4002\n"+
+ " \n"+
+ "");
+
+ return response;
+ }
+
+ // For all other phase we return nothing....
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#createDataInPhase(java.lang.String, java.lang.String, java.lang.String, javax.ws.rs.core.MediaType, boolean, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public PhaseDataResponse createDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseData,
+ boolean useAdvisory,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams,
+ MediaType returnMimeType)
+ throws PersistenceException, UnsupportedMediaTypeExcpetion, SIFException
+ {
+ logger.debug("createDataInPhase for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = "+phaseInfo.getJobID()+" and phase "+phaseInfo.getPhaseName()+" called.");
+ logger.debug("\nData:\n"+phaseData.getData()+"\nData Mime Type: "+phaseData.getMimeType().toString()+"\nUse Advisory: "+useAdvisory);
+
+ // Generally the implementation would check here what needs to be done to create data for the given phase.
+ // General flow would be:
+ // 1) Check the phase for the given job and based on this...
+ // 2) Unmarshal the 'phaseData.data'. The 'phaseData.mimeType' indicates the mime type of the 'data'.
+ // Suggestion: Use the UnmarshallFactory class of this framework. Note it is entirely valid that there is
+ // no data given. It is up to the binding documentation and the agreed contract between consumer
+ // and provider if data is passed to this method.
+ // 3) If all is ok then process the data as requested. If anything fails raise appropriate exception.
+ // 4) If data shall be returned then create appropriate PhaseDataResponse with the data marshalled to a String using
+ // the returnMimeType.
+ //
+ // It is suggested that the implementation of this method may use asynchronous processing, meaning that
+ // The request might be put on an internal queue or stored in DB for later processing. This is important
+ // as it is expected that this method finishes execution in a timely manner so that a connection to the
+ // caller doesn't remain open for a long time.
+ //
+ // As part of this call the provider may also update the job and/or phase state using appropriate updateXYZ() methods
+ // of the BaseFunctionalServiceProvider class.
+
+ // For testing purposes we fail every 5th operation...
+ if ((numOperations++ % 5) == 2)
+ {
+ throw new SIFException(Status.BAD_REQUEST.getStatusCode(), "Failed to create data.", "Data could not be created in phase "+phaseInfo.getPhaseName()+" for job with ID = "+phaseInfo.getJobID(), getMultiObjectClassInfo().getObjectName()+" Provider.");
+ }
+
+ if (phaseInfo.getPhaseName().equalsIgnoreCase("oldYearEnrolment"))
+ {
+ if (!returnMimeType.isCompatible(MediaType.APPLICATION_XML_TYPE))
+ {
+ throw new UnsupportedMediaTypeExcpetion(returnMimeType.toString()+" is not accepted. Valid mime type is "+ MediaType.APPLICATION_XML_TYPE.toString());
+ }
+
+ //make up some dummy data..
+ PhaseDataResponse response = new PhaseDataResponse();
+ response.setMimeType(returnMimeType);
+ response.setStatus(Status.OK); // This is an override of the default of 201!
+ response.setData(
+ "\n"+
+ " \n"+
+ " 2017\n"+
+ " 4001\n"+
+ " 201\n"+
+ " \n"+
+ " \n"+
+ " 2018\n"+
+ " 4002\n"+
+ " 406\n"+
+ " Invalid data for this year and school.\n"+
+ " \n"+
+ "");
+
+ return response;
+ }
+
+ // For all other phases we return null. This should default the response status to 201.
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#updateDataInPhase(java.lang.String, java.lang.String, java.lang.String, javax.ws.rs.core.MediaType, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public PhaseDataResponse updateDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseData,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams,
+ MediaType returnMimeType)
+ throws PersistenceException, UnsupportedMediaTypeExcpetion, SIFException
+ {
+ logger.debug("updateDataInPhase for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = "+phaseInfo.getJobID()+" and phase "+phaseInfo.getPhaseName()+" called.");
+ logger.debug("\nData:\n"+phaseData.getData()+"\nData Mime Type: "+phaseData.getMimeType().toString());
+
+ // Generally the implementation would check here what needs to be done to update data for the given phase.
+ // General flow would be:
+ // 1) Check the phase for the given job and based on this...
+ // 2) Unmarshal the 'phaseData.data'. The 'phaseData.mimeType' indicates the mime type of the 'data'.
+ // Suggestion: Use the UnmarshallFactory class of this framework. Note it is entirely valid that there is
+ // no data given. It is up to the binding documentation and the agreed contract between consumer
+ // and provider if data is passed to this method.
+ // 3) If all is ok then process the data as requested. If anything fails raise appropriate exception.
+ // 4) If data shall be returned then create appropriate PhaseDataResponse with the data marshalled to a String using
+ // the returnMimeType.
+ //
+ // It is suggested that the implementation of this method may use asynchronous processing, meaning that
+ // The request might be put on an internal queue or stored in DB for later processing. This is important
+ // as it is expected that this method finishes execution in a timely manner so that a connection to the
+ // caller doesn't remain open for a long time.
+ //
+ // As part of this call the provider may also update the job and/or phase state using appropriate updateXYZ() methods
+ // of the BaseFunctionalServiceProvider class.
+
+ // For testing purposes we fail every 4th operation...
+ if ((numOperations++ % 4) == 0)
+ {
+ throw new SIFException(Status.BAD_REQUEST.getStatusCode(), "Failed to update data.", "Data could not be updated in phase "+phaseInfo.getPhaseName()+" for job with ID = "+phaseInfo.getJobID(), getMultiObjectClassInfo().getObjectName()+" Provider.");
+ }
+
+ // For the time being we return null. This should default the response status to 200.
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#deleteDataInPhase(java.lang.String, java.lang.String, java.lang.String, javax.ws.rs.core.MediaType, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public PhaseDataResponse deleteDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseData,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams,
+ MediaType returnMimeType)
+ throws PersistenceException, UnsupportedMediaTypeExcpetion, SIFException
+ {
+ logger.debug("deleteDataInPhase for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = "+phaseInfo.getJobID()+" and phase "+phaseInfo.getPhaseName()+" called.");
+ logger.debug("\nData:\n"+phaseData.getData()+"\nData Mime Type: "+phaseData.getMimeType().toString());
+
+
+ // Generally the implementation would check here what needs to be done to delete data for the given phase.
+ // General flow would be:
+ // 1) Check the phase for the given job and based on this...
+ // 2) Unmarshal the 'phaseData.data'. The 'phaseData.mimeType' indicates the mime type of the 'data'.
+ // Suggestion: Use the UnmarshallFactory class of this framework. Note it is entirely valid that there is
+ // no data given. It is up to the binding documentation and the agreed contract between consumer
+ // and provider if data is passed to this method.
+ // 3) If all is ok then process the data as requested. If anything fails raise appropriate exception.
+ // 4) If data shall be returned then create appropriate PhaseDataResponse with the data marshalled to a String using
+ // the returnMimeType.
+ //
+ // It is suggested that the implementation of this method may use asynchronous processing, meaning that
+ // The request might be put on an internal queue or stored in DB for later processing. This is important
+ // as it is expected that this method finishes execution in a timely manner so that a connection to the
+ // caller doesn't remain open for a long time.
+ //
+ // As part of this call the provider may also update the job and/or phase state using appropriate updateXYZ() methods
+ // of the BaseFunctionalServiceProvider class.
+
+ if ((numOperations++ % 4) == 0)
+ {
+ throw new SIFException(Status.BAD_REQUEST.getStatusCode(), "Failed to delete data.", "Data could not be deleted in phase "+phaseInfo.getPhaseName()+" for job with ID = "+phaseInfo.getJobID(), getMultiObjectClassInfo().getObjectName()+" Provider.");
+ }
+
+ // For the time being we return null. This should default the response status to 204.
+ return null;
+ }
+
+ /*----------------------------*/
+ /*-- Phase State Operations --*/
+ /*----------------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceProvider#phaseStateUpdatedByConsumer(java.lang.String, java.lang.String, sif3.common.CommonConstants.PhaseState, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.RequestMetadata, sif3.common.model.ResponseParameters)
+ */
+ @Override
+ public void phaseStateUpdatedByConsumer(PhaseInfo phaseInfo,
+ PhaseState newState,
+ SIFZone zone,
+ SIFContext context,
+ RequestMetadata metadata,
+ ResponseParameters customResponseParams)
+ throws PersistenceException, SIFException
+ {
+ logger.debug("Phase State Updated called by Consumer Job for Functional Service "+getMultiObjectClassInfo().getObjectName()+" and jobID = '"+phaseInfo.getJobID()+"'. Phase to update is "+phaseInfo.getPhaseName()+". New State is "+newState.name());
+
+ // Generally the implementation would check here what needs to be done after such an update.
+ // This could cause many actions to be triggered. Some include the various updateXYZ() methods that are available as part
+ // of this provider class.
+
+ // Below is just a potential example.
+ if (phaseInfo.getPhaseName().equalsIgnoreCase("oldYearEnrolment"))
+ {
+ if (newState == PhaseState.PENDING)
+ {
+ updateJobStateAndPhaseState(phaseInfo.getJobID(), JobState.INPROGRESS, "oldYearEnrolment", PhaseState.INPROGRESS);
+ }
+
+ if (newState == PhaseState.COMPLETED)
+ {
+ // First phase is done. Mark the next phase as in progress.
+ updatePhaseState(phaseInfo.getJobID(), "newYearEnrolment", PhaseState.INPROGRESS);
+ }
+ }
+ else if ((phaseInfo.getPhaseName().equalsIgnoreCase("newYearEnrolment")))
+ {
+ if (newState == PhaseState.COMPLETED)
+ {
+ // Mark the job as complete
+ updateJobState(phaseInfo.getJobID(), JobState.COMPLETED);
+ }
+ if (newState == PhaseState.FAILED)
+ {
+ // Mark the job as complete
+ updateJobState(phaseInfo.getJobID(), JobState.FAILED);
+ }
+ }
+ }
+
+ /*--------------------------------------------*/
+ /*-- Other required housekeeping Operations --*/
+ /*--------------------------------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DataModelLink#getMultiObjectClassInfo()
+ */
+ @Override
+ public ModelObjectInfo getMultiObjectClassInfo()
+ {
+ //This must be the same name as used in the URL or as listed in the SIF3_JOB_TEMPLATE.JOB_URL_NAME column!
+ return new ModelObjectInfo("RolloverStudents", null);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.provider.CoreProvider#shutdown()
+ */
+ @Override
+ public void shutdown()
+ {
+ logger.debug("Shutdown Functional Service for " + getPrettyName());
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.infra.rest.provider.CoreProvider#getCustomServiceInfo(sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.PagingInfo, sif3.common.model.RequestMetadata)
+ */
+ @Override
+ public HeaderProperties getCustomServiceInfo(SIFZone zone,
+ SIFContext context,
+ PagingInfo pagingInfo,
+ RequestMetadata metadata)
+ throws PersistenceException, UnsupportedQueryException, DataTooLargeException
+ {
+ //We can return some specific headers. Most of the time that is really nothing but there might be times where some are required.
+ //For pure illustration purpose we return some values.
+
+ HeaderProperties someHeaders = new HeaderProperties();
+ someHeaders.setHeaderProperty("DemoJobProvider", getMultiObjectClassInfo().getObjectName());
+ return someHeaders;
+ }
+
+ /*
+ * Example how a specific provider can overwrite the valid job end states for a functional service.
+ */
+ @Override
+ public boolean isJobEndState(JobState state)
+ {
+ // In this case we only consider the "COMPLETED" as an end state. By default COMPLETED and FAILED are end states or
+ // values in the provider properties called job.endStates are end states.
+ return state == JobState.COMPLETED;
+ }
+}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/client/TestEventClient.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/client/TestEventClient.java
index 88f99760..9381b010 100644
--- a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/client/TestEventClient.java
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/client/TestEventClient.java
@@ -30,6 +30,7 @@
import sif3.common.exception.ServiceInvokationException;
import sif3.common.header.HeaderProperties;
import sif3.common.header.HeaderValues.EventAction;
+import sif3.common.header.HeaderValues.ServiceType;
import sif3.common.header.RequestHeaderConstants;
import sif3.common.model.SIFEvent;
import sif3.infra.common.env.mgr.BrokeredProviderEnvironmentManager;
@@ -146,7 +147,7 @@ private EventClient getEventClient()
private void sendEvent(EventClient evtClt) throws ServiceInvokationException
{
- System.out.println(evtClt.sendEvents(getEvents(), null, null, getOverrideHeaderProperties()));
+ System.out.println(evtClt.sendEvents(getEvents(), null, null, ServiceType.OBJECT, getOverrideHeaderProperties()));
}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestConsumerLoader.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestConsumerLoader.java
index 5240ebd1..1c3a9ae9 100644
--- a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestConsumerLoader.java
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestConsumerLoader.java
@@ -63,10 +63,27 @@ public void stopService(String serviceID)
// System.out.println(serviceID+" is running (Press Ctrl-C to stop)");
// }
+ private void doWait(long waitTime)
+ {
+ try
+ {
+ System.out.println("Wait for "+waitTime +" seconds...");
+ Object semaphore = new Object();
+ synchronized (semaphore)
+ {
+ semaphore.wait(waitTime * 1000);
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
public static void main(String[] args)
{
System.out.println("Start Testing TestConsumerLoader...");
+ TestConsumerLoader tester = new TestConsumerLoader();
if (ConsumerLoader.initialise(CONSUMER_ID))
{
@@ -74,6 +91,9 @@ public static void main(String[] args)
}
// Put this agent to a blocking wait.....
+ long waitTime = 20; // seconds
+// tester.doWait(waitTime); // to test shutdown procedure
+
try
{
Object semaphore = new Object();
@@ -90,6 +110,8 @@ public static void main(String[] args)
}
ConsumerLoader.shutdown();
+
System.out.println("End Testing TestConsumerLoader.");
+// System.exit(0);
}
}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestRolloverStudentConsumer.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestRolloverStudentConsumer.java
new file mode 100644
index 00000000..ea836e4f
--- /dev/null
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestRolloverStudentConsumer.java
@@ -0,0 +1,562 @@
+/*
+ * TestRolloverStudentConsumer.java
+ * Created: 26 Jul 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package sif3.test.infra.rest.consumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.DOMOutputter;
+
+import au.com.systemic.framework.utils.FileReaderWriter;
+import sif3.common.CommonConstants.PhaseState;
+import sif3.common.header.HeaderProperties;
+import sif3.common.header.HeaderValues.RequestType;
+import sif3.common.model.AttributeValue;
+import sif3.common.model.CustomParameters;
+import sif3.common.model.PagingInfo;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFZone;
+import sif3.common.model.URLQueryParameter;
+import sif3.common.model.ZoneContextInfo;
+import sif3.common.model.job.JobCreateRequestParameter;
+import sif3.common.model.job.PhaseInfo;
+import sif3.common.ws.BulkOperationResponse;
+import sif3.common.ws.CreateOperationStatus;
+import sif3.common.ws.OperationStatus;
+import sif3.common.ws.Response;
+import sif3.common.ws.job.PhaseDataRequest;
+import sif3.infra.common.conversion.InfraMarshalFactory;
+import sif3.infra.common.env.mgr.ConsumerEnvironmentManager;
+import sif3.infra.common.model.JobCollectionType;
+import sif3.infra.common.model.JobType;
+import sif3.infra.common.model.StateCollectionType;
+import sif3.infra.common.model.StateType;
+import sif3.infra.rest.consumer.ConsumerLoader;
+import systemic.sif3.demo.rest.consumer.functional.RolloverStudentConsumer;
+
+/**
+ * @author Joerg Huber
+ *
+ */
+public class TestRolloverStudentConsumer
+{
+ private final static String PATH = "C:/Development/GitHubRepositories/SIF3InfraRest/SIF3InfraREST";
+
+ private static final String BASE_PATH = "/Development/GitHubRepositories/SIF3InfraRest/SIF3InfraREST/TestData/xml/input";
+ private static final String CREATE_PHASE_PAYLOAD = BASE_PATH+"/CreatePhasePayload.xml";
+ private static final String UPDATE_PHASE_PAYLOAD = BASE_PATH+"/CreatePhasePayload.xml";
+ private static final String DELETE_PHASE_PAYLOAD = BASE_PATH+"/DeletePhasePayload.xml";
+
+ private static final String JOB_ID = "17c302dc-2a63-4af4-b020-bec34cacd2df";
+ private static final String CONSUMER_ID = "StudentConsumer";
+// private static final String CONSUMER_ID = "HITSStudentConsumer";
+// private static final String CONSUMER_ID = "BrokeredAttTrackerConsumer";
+// private static final String CONSUMER_ID = "QueueTestConsumer";
+
+
+ private static final RequestType REQUEST_TYPE = RequestType.IMMEDIATE;
+
+ private RolloverStudentConsumer getConsumer()
+ {
+ return new RolloverStudentConsumer();
+ }
+
+ private String getPhasePayload(String payloadFile)
+ {
+ return new String(FileReaderWriter.getFileBytes(payloadFile));
+ }
+
+ private void printResponses(List responses)
+ {
+ if (responses != null)
+ {
+ for (Response response : responses)
+ {
+ printResponse(response);
+ }
+ }
+ else
+ {
+ System.out.println("Responses from attempt to execute a Functional Service: null");
+ }
+ }
+
+ private void printResponse(Response response)
+ {
+ try
+ {
+ if (response != null)
+ {
+ System.out.println("Response:\n"+response);
+ if (response.hasError())
+ {
+ System.out.println("Error for Response: "+response.getError());
+ }
+ else // We may have data
+ {
+ if (response.getHasEntity())
+ {
+ // Check if Job or Phase response
+ if (response.getDataObjectType() == String.class) // This is for phase response
+ {
+ System.out.println("Response is a Phase Operation Response: "+response.getDataObject());
+ }
+ else if ((response.getDataObjectType() == JobType.class) || (response.getDataObjectType() == JobCollectionType.class)) // Job Response
+ {
+ InfraMarshalFactory marshaller = new InfraMarshalFactory();
+ System.out.println("Job Response Object Data:\n"+marshaller.marshalToXML(response.getDataObject()));
+ }
+ else if ((response.getDataObjectType() == StateType.class) || (response.getDataObjectType() == StateCollectionType.class)) // Phase State Response
+ {
+ InfraMarshalFactory marshaller = new InfraMarshalFactory();
+ System.out.println("Job Phase State Response Object Data:\n"+marshaller.marshalToXML(response.getDataObject()));
+ }
+ else
+ {
+ System.out.println("Data Type retrieved in Response is not expected! Cannot unmarshal");
+ }
+ }
+ else // in delayed we may have delayed receipt
+ {
+ System.out.println("No Data Returned. Response Status = "+response.getStatus()+" ("+response.getStatusMessage()+")");
+ System.out.println("Delayed Receipt: "+response.getDelayedReceipt());
+ }
+ }
+ }
+ else
+ {
+ System.out.println("Responses from attempt to execute a Functional Service: null");
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+
+ private org.w3c.dom.Element createInitPayload()
+ {
+ // w3c usage
+ DOMOutputter domOutputter = new DOMOutputter();
+ try
+ {
+ Document document = new Document();
+ document.setRootElement(new Element("payload"));
+ org.w3c.dom.Document domDocument = domOutputter.output(document);
+ org.w3c.dom.Element rootElement = domDocument.getDocumentElement();
+ rootElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "type", "propertiesType");
+
+ org.w3c.dom.Element child = domDocument.createElement("property");
+ child.setAttribute("name", "contextId");
+ child.setTextContent("future");
+ rootElement.appendChild(child);
+
+ child = domDocument.createElement("property");
+ child.setAttribute("name", "initiator");
+ child.setTextContent("user1");
+ rootElement.appendChild(child);
+
+ return rootElement;
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ private void createJobs(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Create Jobs' ...");
+ try
+ {
+ HeaderProperties hdrProps = new HeaderProperties();
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_AUTH_TOKEN, AUTH_TOKEN);
+ URLQueryParameter urlQueryParams = new URLQueryParameter();
+
+ List zoneCtxList = new ArrayList();
+// zoneCtxList.add(new ZoneContextInfo(new SIFZone("auRolloverTestingZone"), null));
+
+ ArrayList jobInits = new ArrayList();
+ jobInits.add(new JobCreateRequestParameter("oldYearEnrolment", null));
+ jobInits.add(new JobCreateRequestParameter("newYearEnrolment", null));
+ jobInits.add(new JobCreateRequestParameter(null, null));
+ jobInits.add(null);
+
+ CustomParameters customParameters = new CustomParameters();
+ customParameters.setHttpHeaderParams(hdrProps);
+ customParameters.setQueryParams(urlQueryParams);
+
+ List> responses = consumer.createJobs(jobInits, zoneCtxList, REQUEST_TYPE, customParameters);
+
+ System.out.println("Responses from attempt to Create Jobs:");
+ if (responses != null)
+ {
+ int i = 1;
+ for (BulkOperationResponse response : responses)
+ {
+ System.out.println("Response "+i+":\n"+response);
+ if (response.hasError())
+ {
+ System.out.println("Error for Response "+i+": "+response.getError());
+ }
+ else // We should have a status list object or a delayed response
+ {
+ System.out.println("Job Response "+i+": "+response.getOperationStatuses());
+ }
+ i++;
+ }
+ }
+ else
+ {
+ System.out.println("Responses from attempt to create Jobs: null");
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+
+ System.out.println("Finished 'Create Jobs'.");
+ }
+
+ private void createJob(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Create Job' ...");
+ try
+ {
+ List zoneCtxList = new ArrayList();
+// zoneCtxList.add(new ZoneContextInfo(new SIFZone("auRolloverTestingZone"), null));
+
+// ArrayList attrValue = new ArrayList();
+// attrValue.add(new AttributeValue("old-year", "2017"));
+// attrValue.add(new AttributeValue("new-year", "2018"));
+ JobCreateRequestParameter jobCreateInfo = new JobCreateRequestParameter("oldYearEnrolment", createInitPayload());
+
+ List responses = consumer.createJob(jobCreateInfo, zoneCtxList);
+
+ System.out.println("Responses from attempt to Create Job:");
+ printResponses(responses);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Create Job'.");
+ }
+
+ private void getJob(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Get Job' ...");
+ try
+ {
+ List zoneCtxList = new ArrayList();
+ zoneCtxList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null)); // Default zone and context
+ zoneCtxList.add(new ZoneContextInfo(new SIFZone("auRolloverTestingZone"), null));
+
+ List responses = consumer.retrievByJobId(JOB_ID, zoneCtxList);
+
+ System.out.println("Responses from attempt to Get Job:");
+ printResponses(responses);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Get Job'.");
+ }
+
+ private void getJobs(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Get Jobs' ...");
+ try
+ {
+ List zoneCtxList = new ArrayList();
+// zoneCtxList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null)); // Default zone and context
+// zoneCtxList.add(new ZoneContextInfo(new SIFZone("auRolloverTestingZone"), null));
+
+ List responses = consumer.retrieveJobs(new PagingInfo(5, 1), zoneCtxList, REQUEST_TYPE, null);
+
+ System.out.println("Responses from attempt to Get Jobs:");
+ printResponses(responses);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Get Jobs'.");
+ }
+
+ private void removeJob(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Remove Job' ...");
+ try
+ {
+ List zoneCtxList = new ArrayList();
+ List responses = consumer.deleteJob(JOB_ID, zoneCtxList);
+ printResponses(responses);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Remove Job'.");
+ }
+
+ private void removeJobs(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Remove Jobs' ...");
+ try
+ {
+ HeaderProperties hdrProps = new HeaderProperties();
+ hdrProps.setHeaderProperty("FS_TEST", "Test Header Value");
+ URLQueryParameter urlQueryParams = new URLQueryParameter();
+ urlQueryParams.setQueryParam("FSQeryParam1", "fsValue1");
+
+ CustomParameters customParameters = new CustomParameters();
+ customParameters.setHttpHeaderParams(hdrProps);
+ customParameters.setQueryParams(urlQueryParams);
+
+ List zoneCtxList = new ArrayList();
+
+ ArrayList jobIDs = new ArrayList();
+ jobIDs.add("98eae51c-fdec-412c-b6e9-9750223301ea");
+ jobIDs.add("eeeae51c-fdec-412c-b6e9-9750223301ea");
+ jobIDs.add("40dcaafb-69d4-490d-9093-e679ecbead96");
+
+ List> responses = consumer.deleteJobs(jobIDs, zoneCtxList, REQUEST_TYPE, customParameters);
+ if (responses != null)
+ {
+ int i = 1;
+ for (BulkOperationResponse response : responses)
+ {
+ System.out.println("Response " + i + ":\n" + response);
+ i++;
+ }
+ }
+ else
+ {
+ System.out.println("Responses from attempt to delete jobs: null");
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Remove Jobs'.");
+ }
+
+ private void getServiceInfo(RolloverStudentConsumer consumer)
+ {
+ System.out.println("Start 'Get Service Info' ...");
+ try
+ {
+ List responses = consumer.getServiceInfo(null, null);
+ printResponses(responses);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Get Service Info'.");
+ }
+
+ private void updateJobPhaseState(RolloverStudentConsumer consumer, String phaseName, PhaseState state)
+ {
+ System.out.println("Start 'Update Phase State' ...");
+ try
+ {
+ Response response = consumer.updatePhaseState(new PhaseInfo(JOB_ID, phaseName), state, null, null, null);
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Update Phase State'.");
+ }
+
+ private void getJobPhaseStates(RolloverStudentConsumer consumer, String phaseName)
+ {
+ System.out.println("Start 'Get Phase States' ...");
+ try
+ {
+ Response response = consumer.getPhaseStates(new PhaseInfo(JOB_ID, phaseName), null, null, null);
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Get Phase States'.");
+ }
+
+ private void retriveFromPhase(RolloverStudentConsumer consumer, String phaseName)
+ {
+ System.out.println("Start 'Retrieve Data from Phase' ...");
+ try
+ {
+ HeaderProperties hdrProps = new HeaderProperties();
+ hdrProps.setHeaderProperty("FS_TEST", "Test Header Value");
+ URLQueryParameter urlQueryParams = new URLQueryParameter();
+ urlQueryParams.setQueryParam("FSQeryParam1", "fsValue1");
+
+ CustomParameters customParameters = new CustomParameters();
+ customParameters.setHttpHeaderParams(hdrProps);
+ customParameters.setQueryParams(urlQueryParams);
+
+ Response response = consumer.retrieveDataFromPhase(new PhaseInfo(JOB_ID, phaseName), MediaType.APPLICATION_XML_TYPE, new PagingInfo(5, 2), null, null, null, REQUEST_TYPE, customParameters);
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Retrieve Data from Phase'.");
+ }
+
+ private void createDataInPhase(RolloverStudentConsumer consumer, String phaseName)
+ {
+ System.out.println("Start 'Create Data in Phase' ...");
+ try
+ {
+ String payload = getPhasePayload(CREATE_PHASE_PAYLOAD);
+ Response response = consumer.createDataInPhase(new PhaseInfo(JOB_ID, phaseName), new PhaseDataRequest(payload, MediaType.APPLICATION_XML_TYPE), MediaType.APPLICATION_XML_TYPE, false, null, null, REQUEST_TYPE, null);
+
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Create Data in Phase'.");
+ }
+
+ private void updateDataInPhase(RolloverStudentConsumer consumer, String phaseName)
+ {
+ System.out.println("Start 'Update Data in Phase' ...");
+ try
+ {
+ HeaderProperties hdrProps = new HeaderProperties();
+ hdrProps.setHeaderProperty("FS_TEST_UPDATE", "Test Header Value fur UPDATE PAHSE");
+ URLQueryParameter urlQueryParams = new URLQueryParameter();
+ urlQueryParams.setQueryParam("FSQeryParam1", "updatePhase");
+
+ CustomParameters customParameters = new CustomParameters();
+ customParameters.setHttpHeaderParams(hdrProps);
+ customParameters.setQueryParams(urlQueryParams);
+
+ String payload = getPhasePayload(UPDATE_PHASE_PAYLOAD);
+ Response response = consumer.updateDataInPhase(new PhaseInfo(JOB_ID, phaseName), new PhaseDataRequest(payload, MediaType.APPLICATION_XML_TYPE), MediaType.APPLICATION_XML_TYPE, null, null, REQUEST_TYPE, customParameters);
+
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Update Data in Phase'.");
+ }
+
+ private void deleteDataInPhase(RolloverStudentConsumer consumer, String phaseName)
+ {
+ System.out.println("Start 'Delete Data in Phase' ...");
+ try
+ {
+ String payload = getPhasePayload(DELETE_PHASE_PAYLOAD);
+ Response response = consumer.deleteDataInPhase(new PhaseInfo(JOB_ID, phaseName), new PhaseDataRequest(payload, MediaType.APPLICATION_XML_TYPE), MediaType.APPLICATION_XML_TYPE, null, null, REQUEST_TYPE, null);
+ printResponse(response);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ System.out.println("Finished 'Delete Data in Phase'.");
+ }
+
+ public static void main(String[] args)
+ {
+ TestRolloverStudentConsumer tester = new TestRolloverStudentConsumer();
+ System.out.println("Start Testing RolloverStudentConsumer...");
+
+ if (ConsumerLoader.initialise(CONSUMER_ID))
+ {
+ System.out.println("Consumer loaded successfully. Environment Data:\n"+ConsumerEnvironmentManager.getInstance().getEnvironmentInfo());
+
+ RolloverStudentConsumer consumer = tester.getConsumer();
+
+ //
+ // Job Operations
+ //
+// tester.createJob(consumer);
+// tester.createJobs(consumer);
+
+
+// tester.getJob(consumer);
+// tester.getJobs(consumer);
+
+// tester.removeJob(consumer);
+// tester.removeJobs(consumer);
+// tester.getServiceInfo(consumer);
+
+ //
+ // Phase State Operations
+ //
+ tester.updateJobPhaseState(consumer, "oldYearEnrolment", PhaseState.PENDING);
+// tester.getJobPhaseStates(consumer, "newYearEnrolment");
+
+ //
+ // Phase Operations
+ //
+// tester.retriveFromPhase(consumer, "oldYearEnrolment");
+// tester.createDataInPhase(consumer, "oldYearEnrolment");
+// tester.updateDataInPhase(consumer, "oldYearEnrolment");
+// tester.deleteDataInPhase(consumer, "oldYearEnrolment");
+
+ // Put this agent to a blocking wait.....
+ if (true)
+ {
+ try
+ {
+ Object semaphore = new Object();
+ synchronized (semaphore) // this will block until CTRL-C is pressed.
+ {
+ System.out.println("==================================================\nTestRolloverStudentConsumer is running (Press Ctrl-C to stop)\n==================================================");
+ semaphore.wait();
+ }
+ }
+ catch (Exception ex)
+ {
+ System.out.println("Blocking wait in TestRolloverStudentConsumer interrupted: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ System.out.println("Finalise Consumer (i.e. disconnect and remove environment).");
+ consumer.finalise();
+ }
+
+ ConsumerLoader.shutdown();
+ System.out.println("End Testing RolloverStudentConsumer.");
+ }
+}
diff --git a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestStudentPersonalConsumer.java b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestStudentPersonalConsumer.java
index 2a49e059..152f5b1a 100644
--- a/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestStudentPersonalConsumer.java
+++ b/SIF3InfraREST/SIF3Demo/sif3-demo-web/src/test/java/sif3/test/infra/rest/consumer/TestStudentPersonalConsumer.java
@@ -485,10 +485,10 @@ private void getServiceInfo(StudentPersonalConsumer consumer, boolean printRepso
// envZoneCtxList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null));
// envZoneCtxList.add(new ZoneContextInfo((SIFZone)null, new SIFContext("secure")));
- List responses = consumer.getServiceInfo(new PagingInfo(10, 0), envZoneCtxList, params);
+ List responses = consumer.getServiceInfo(new PagingInfo(10, 1), envZoneCtxList, params);
if (printRepsonse)
{
- System.out.println("Responses from attempt to Get All Students:");
+ System.out.println("Responses from attempt to Get StudentPersonal Service Info:");
printResponses(responses, consumer);
}
}
@@ -514,7 +514,7 @@ public static void main(String[] args)
StudentPersonalConsumer consumer = tester.getConsumer();
// tester.getStudent(consumer);
- tester.getStudents(consumer, true);
+// tester.getStudents(consumer, true);
// tester.getStudentsByServicePath("SchoolInfos", "24ed508e1ed04bba82198233efa55859", consumer);
// tester.getStudentsByServicePath("TeachingGroups", "64A309DA063A2E35B359D75101A8C3D1", consumer);
// tester.getStudentsByServicePath("RoomInfos", "24ed508e1ed04bba82198233efa55859", consumer);
diff --git a/SIF3InfraREST/SIF3REST/pom.xml b/SIF3InfraREST/SIF3REST/pom.xml
index d3691350..22978f2b 100644
--- a/SIF3InfraREST/SIF3REST/pom.xml
+++ b/SIF3InfraREST/SIF3REST/pom.xml
@@ -7,7 +7,7 @@
sif3.framework
sif3-framework
- 0.12.0-beta
+ 0.13.0-beta
@@ -66,6 +66,11 @@
org.slf4j
slf4j-log4j12
+
+
+ org.quartz-scheduler
+ quartz
+
@@ -82,6 +87,7 @@
${project.version}
-->
+
javax.servlet
javax.servlet-api
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/BaseClient.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/BaseClient.java
index c732afbd..1426bdf2 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/BaseClient.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/BaseClient.java
@@ -21,6 +21,7 @@
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
+import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -48,11 +49,14 @@
import sif3.common.header.HeaderValues;
import sif3.common.header.HeaderValues.MessageType;
import sif3.common.header.HeaderValues.RequestType;
+import sif3.common.header.HeaderValues.ServiceType;
import sif3.common.header.RequestHeaderConstants;
import sif3.common.header.ResponseHeaderConstants;
import sif3.common.model.AuthenticationInfo;
+import sif3.common.model.PagingInfo;
import sif3.common.model.SIFContext;
import sif3.common.model.SIFZone;
+import sif3.common.model.ServiceInfo;
import sif3.common.model.URLQueryParameter;
import sif3.common.model.delayed.DelayedRequestReceipt;
import sif3.common.model.security.TokenCoreInfo;
@@ -63,8 +67,12 @@
import sif3.common.security.SecurityServiceFactory;
import sif3.common.utils.UUIDGenerator;
import sif3.common.ws.BaseResponse;
+import sif3.common.ws.BulkOperationResponse;
+import sif3.common.ws.CreateOperationStatus;
import sif3.common.ws.ErrorDetails;
+import sif3.common.ws.OperationStatus;
import sif3.common.ws.Response;
+import sif3.common.ws.model.MultiOperationStatusList;
import sif3.infra.common.conversion.InfraMarshalFactory;
import sif3.infra.common.conversion.InfraUnmarshalFactory;
import sif3.infra.common.env.types.EnvironmentInfo;
@@ -317,34 +325,79 @@ private void setClientEnvMgr(ClientEnvironmentManager clientEnvMgr)
/*-----------------------*/
protected WebResource buildURI(WebResource svc, String relURI, String resourceID, SIFZone zone, SIFContext ctx, URLQueryParameter urlQueryParams)
{
- UriBuilder uriBuilder = svc.getUriBuilder();
- if (StringUtils.notEmpty(relURI))
- {
- uriBuilder.path(relURI);
- }
- if (StringUtils.notEmpty(resourceID))
- {
- uriBuilder.path(resourceID);
- }
- if ((zone != null) && (StringUtils.notEmpty(zone.getId())))
- {
- uriBuilder.matrixParam("zoneId", zone.getId());
- }
- if ((ctx != null) && (StringUtils.notEmpty(ctx.getId())))
- {
- uriBuilder.matrixParam("contextId", ctx.getId());
- }
-
- //Add custom URL Query Parameters.
- if ((urlQueryParams != null) && (urlQueryParams.getQueryParams() != null))
- {
- for (String paramName : urlQueryParams.getQueryParams().keySet())
- {
- uriBuilder = uriBuilder.queryParam(paramName, urlQueryParams.getQueryParam(paramName));
- }
- }
+ return buildURI(svc, zone, ctx, urlQueryParams, relURI, resourceID);
+// UriBuilder uriBuilder = svc.getUriBuilder();
+// if (StringUtils.notEmpty(relURI))
+// {
+// uriBuilder.path(relURI);
+// }
+// if (StringUtils.notEmpty(resourceID))
+// {
+// uriBuilder.path(resourceID);
+// }
+// if ((zone != null) && (StringUtils.notEmpty(zone.getId())))
+// {
+// uriBuilder.matrixParam("zoneId", zone.getId());
+// }
+// if ((ctx != null) && (StringUtils.notEmpty(ctx.getId())))
+// {
+// uriBuilder.matrixParam("contextId", ctx.getId());
+// }
+//
+// //Add custom URL Query Parameters.
+// if ((urlQueryParams != null) && (urlQueryParams.getQueryParams() != null))
+// {
+// for (String paramName : urlQueryParams.getQueryParams().keySet())
+// {
+// uriBuilder = uriBuilder.queryParam(paramName, urlQueryParams.getQueryParam(paramName));
+// }
+// }
+//
+// return svc.uri(uriBuilder.build());
+ }
+
+ protected WebResource buildURI(WebResource svc, SIFZone zone, SIFContext ctx, URLQueryParameter urlQueryParams, String... uriSegments)
+ {
+ UriBuilder uriBuilder = svc.getUriBuilder();
+ addPathSegments(uriBuilder, uriSegments);
+ if ((zone != null) && (StringUtils.notEmpty(zone.getId())))
+ {
+ uriBuilder.matrixParam("zoneId", zone.getId());
+ }
+ if ((ctx != null) && (StringUtils.notEmpty(ctx.getId())))
+ {
+ uriBuilder.matrixParam("contextId", ctx.getId());
+ }
+
+ //Add custom URL Query Parameters.
+ if ((urlQueryParams != null) && (urlQueryParams.getQueryParams() != null))
+ {
+ for (String paramName : urlQueryParams.getQueryParams().keySet())
+ {
+ uriBuilder = uriBuilder.queryParam(paramName, urlQueryParams.getQueryParam(paramName));
+ }
+ }
- return svc.uri(uriBuilder.build());
+ return svc.uri(uriBuilder.build());
+ }
+
+ /**
+ * This method will add each element of the segments list to the URI. If a segment is null or empty it will be ignored.
+ * After this call the uriBuilder will contain the full path of the URI made of all the segments.
+ *
+ * @param uriBuilder The uriBuilder to be built up with the segments.
+ * @param segments The segments to be added to the URI.
+ *
+ */
+ protected void addPathSegments(UriBuilder uriBuilder, String... segments)
+ {
+ for (String segment : segments)
+ {
+ if (StringUtils.notEmpty(segment))
+ {
+ uriBuilder.path(segment);
+ }
+ }
}
protected WebResource buildURI(WebResource svc, String relURI)
@@ -352,12 +405,133 @@ protected WebResource buildURI(WebResource svc, String relURI)
return buildURI(svc, relURI, null, null, null, null);
}
-
+
+ /**
+ * This method will set the valid/accepted media type for this service. It will then add all header properties given to this
+ * method to the service. Further will also add some "default" header properties such MessageID, timestamp if it is not set yet, and
+ * if required a request ID. For it to be a valid SIF3 service it is expected that the authentication token is already part
+ * of the given header properties. It WON'T be added as part of this method. It is expected that the request and response
+ * mime type parameters are in its correct form. They will not be modified as used as given (i.e. no character encoding
+ * will be added). If they are null though then the framework's request and response mime type will be used as set
+ * in consumer properties file. It will also add any character encoding information to the mime types as set by the framework's
+ * consumer properties file.
+ *
+ * @param service The service to which media type and header properties shall be added.
+ * @param requestMediaType The mime type of the request. If it is not provided it will default to the
+ * internally determined value given by the getRequestMediaType() method and potential
+ * character encoding might be added.
+ * @param responseMediaType The mime type of the response. If it is not provided it will default to the
+ * internally determined value given by the getResponseMediaType() method and potential
+ * character encoding might be added.
+ * @param hdrProperties A set of defined header properties. Should really hold the authentication related properties!
+ * @param requestType The request type to be set in the HTTP headers.
+ * @param includeRequestID TRUE: Add a generated request ID header property
+ * @param includeFingerprint TRUE: Fingerprint will be retrieved from current session. Note for a provider this will be
+ * the provider's fingerprint! This is generally not desired for events. In this case
+ * this parameter should be set to FALSE.
+ * FALSE: Fingerprint from the current session will not be added to the HTTP headers.
+ * @hasPayload TRUE: The request will contain a payload. Required for compression header settings
+ * FALSE: The request is payload free.
+ *
+ * @return A builder class on which a HTTP operation can be invoked on.
+ */
+ protected Builder setRequestHeaderAndMediaTypes(WebResource service,
+ MediaType requestMediaType,
+ MediaType responseMediaType,
+ HeaderProperties hdrProperties,
+ RequestType requestType,
+ boolean includeRequestID,
+ boolean includeFingerprint,
+ boolean hasPayload)
+ {
+ String charEncoding = getClientEnvMgr().getEnvironmentInfo().getCharsetEncoding();
+
+ // Check if we have a request & response media type. If not use the framework's value otherwise use the value given.
+ MediaType finalRequestMediaType = requestMediaType == null ? addEncoding(getRequestMediaType(), charEncoding) : requestMediaType;
+ MediaType finalResponseMediaType = responseMediaType == null ? addEncoding(getResponseMediaType(), charEncoding) : responseMediaType;
+
+ Builder builder = service.type(finalRequestMediaType).accept(finalResponseMediaType);
+
+ // Set some specific SIF HTTP header. First ensure that we have a valid header property structure
+ if (hdrProperties == null)
+ {
+ hdrProperties = new HeaderProperties();
+ }
+
+ // Always set the requestId and messageId.
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_MESSAGE_ID, UUIDGenerator.getUUID());
+ //builder = builder.header(RequestHeaderConstants.HDR_MESSAGE_ID, UUIDGenerator.getUUID());
+
+ // Set the request type.
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_REQUEST_TYPE, ((requestType == null) ? RequestType.IMMEDIATE.name() : requestType.name()));
+
+ // Add fingerprint to HTTP Header if it is known and not yet set. Note for events this value might already be set, so we
+ // should not override it! This should be indicated with the includeFingerprint parameter that would be set to false!
+ if (includeFingerprint)
+ {
+ if (hdrProperties.getHeaderProperty(RequestHeaderConstants.HDR_FINGERPRINT) == null)
+ {
+ if ((getClientEnvMgr().getSIF3Session() != null) && (getClientEnvMgr().getSIF3Session().getFingerprint() != null))
+ {
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_FINGERPRINT, getClientEnvMgr().getSIF3Session().getFingerprint());
+ }
+ }
+ }
+
+ // Sometimes the request ID is not required (i.e. events)
+ if (includeRequestID)
+ {
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_REQUEST_ID, UUIDGenerator.getUUID());
+ }
+
+ // timestamp header must be set but only if it is not set, yet. If the authentication method is SIF_HMACSHA256
+ // then this property should already be set! Don't override because it is critical to the hash of the authentication token.
+ if (hdrProperties.getHeaderProperty(RequestHeaderConstants.HDR_DATE_TIME) == null) // not set yet
+ {
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_DATE_TIME, DateUtils.nowAsISO8601());
+ }
+
+ // Compression related headers
+ if (getUseCompression())
+ {
+ // Request encoding
+ if (hasPayload)
+ {
+ hdrProperties.setHeaderProperty(HttpHeaders.CONTENT_ENCODING, HeaderValues.EncodingType.gzip.name());
+ }
+
+ //Accepted encodings for response
+ hdrProperties.setHeaderProperty(HttpHeaders.ACCEPT_ENCODING, HeaderValues.ACCEPT_ENCODING_ALL);
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Final set of HTTP Headers to be used in request: "+hdrProperties);
+ }
+
+ if ((hdrProperties != null) && (hdrProperties.getHeaderProperties() != null) && (!hdrProperties.getHeaderProperties().isEmpty()))
+ {
+ HashMap hdrMap = hdrProperties.getHeaderProperties();
+ for (String hdrPropertyName : hdrMap.keySet())
+ {
+ String hdrPropertyValue = hdrMap.get(hdrPropertyName);
+ if (StringUtils.notEmpty(hdrPropertyValue))
+ {
+ builder = builder.header(hdrPropertyName, hdrPropertyValue);
+ }
+ }
+ }
+
+ return builder;
+ }
+
/**
* This method will set the valid/accepted media type for this service. It will then add all header properties given to this
* method to the service. Further will also add some "default" header properties such MessageID, timestamp if it is not set yet, and
* if required a request ID. For it to be a valid SIF3 service it is expected that the authentication token is already part
- * of the given header properties. It WON'T be added as part of this method.
+ * of the given header properties. It WON'T be added as part of this method. It will use the request and response mime type
+ * as set by the framework's consumer properties file. It will also add any character encoding information to the mime types as
+ * set by the framework's consumer properties file.
*
* @param service The service to which media type and header properties shall be added.
* @param hdrProperties A set of defined header properties. Should really hold the authentication related properties!
@@ -374,82 +548,7 @@ protected WebResource buildURI(WebResource svc, String relURI)
*/
protected Builder setRequestHeaderAndMediaTypes(WebResource service, HeaderProperties hdrProperties, RequestType requestType, boolean includeRequestID, boolean includeFingerprint, boolean hasPayload)
{
- String charEncoding = getClientEnvMgr().getEnvironmentInfo().getCharsetEncoding();
- Builder builder = service.type(addEncoding(getRequestMediaType(), charEncoding)).accept(addEncoding(getResponseMediaType(), charEncoding));
-
- // Set some specific SIF HTTP header. First ensure that we have a valid header property structure
- if (hdrProperties == null)
- {
- hdrProperties = new HeaderProperties();
- }
-
- // Always set the requestId and messageId.
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_MESSAGE_ID, UUIDGenerator.getUUID());
- //builder = builder.header(RequestHeaderConstants.HDR_MESSAGE_ID, UUIDGenerator.getUUID());
-
- // Set the request type.
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_REQUEST_TYPE, ((requestType == null) ? RequestType.IMMEDIATE.name() : requestType.name()));
-
- // Add fingerprint to HTTP Header if it is known and not yet set. Note for events this value might already be set, so we
- // should not override it! This should be indicated with the includeFingerprint parameter that would be set to false!
- if (includeFingerprint)
- {
- if (hdrProperties.getHeaderProperty(RequestHeaderConstants.HDR_FINGERPRINT) == null)
- {
- if ((getClientEnvMgr().getSIF3Session() != null) && (getClientEnvMgr().getSIF3Session().getFingerprint() != null))
- {
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_FINGERPRINT, getClientEnvMgr().getSIF3Session().getFingerprint());
- }
- }
- }
-
- // Sometimes the request ID is not required (i.e. events)
- if (includeRequestID)
- {
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_REQUEST_ID, UUIDGenerator.getUUID());
- //builder = builder.header(RequestHeaderConstants.HDR_REQUEST_ID, UUIDGenerator.getUUID());
- }
-
- // timestamp header must be set but only if it is not set, yet. If the authentication method is SIF_HMACSHA256
- // then this property should already be set! Don't override because it is critical to the hash of the authentication token.
- if (hdrProperties.getHeaderProperty(RequestHeaderConstants.HDR_DATE_TIME) == null) // not set yet
- {
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_DATE_TIME, DateUtils.nowAsISO8601());
- //builder = builder.header(RequestHeaderConstants.HDR_DATE_TIME, DateUtils.nowAsISO8601());
- }
-
- // Compression related headers
- if (getUseCompression())
- {
- // Request encoding
- if (hasPayload)
- {
- hdrProperties.setHeaderProperty(HttpHeaders.CONTENT_ENCODING, HeaderValues.EncodingType.gzip.name());
- }
-
- //Accepted encodings for response
- hdrProperties.setHeaderProperty(HttpHeaders.ACCEPT_ENCODING, HeaderValues.ACCEPT_ENCODING_ALL);
- }
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Final set of HTTP Headers to be used in request: "+hdrProperties);
- }
-
- if ((hdrProperties != null) && (hdrProperties.getHeaderProperties() != null) && (!hdrProperties.getHeaderProperties().isEmpty()))
- {
- HashMap hdrMap = hdrProperties.getHeaderProperties();
- for (String hdrPropertyName : hdrMap.keySet())
- {
- String hdrPropertyValue = hdrMap.get(hdrPropertyName);
- if (StringUtils.notEmpty(hdrPropertyValue))
- {
- builder = builder.header(hdrPropertyName, hdrPropertyValue);
- }
- }
- }
-
- return builder;
+ return setRequestHeaderAndMediaTypes(service, null, null, hdrProperties, requestType, includeRequestID, includeFingerprint, hasPayload);
}
/**
@@ -484,6 +583,27 @@ protected HeaderProperties createAuthenticationHdr(boolean isEnvCreate, SIF3Sess
return hdrProps;
}
+
+ /**
+ * This method will add the authentication header properties to the given set of header properties. The final set of header
+ * properties is then returned.
+ *
+ * @hdrProperties A set of header properties to which the authentication header shall be added. If it is null then a new set of header porperties
+ * will be returned that only holds the authentication header values.
+ */
+ protected HeaderProperties addAuthenticationHdrProps(HeaderProperties hdrProperties)
+ {
+ if (hdrProperties == null)
+ {
+ hdrProperties = new HeaderProperties();
+ }
+
+ // Add Authentication info to existing header properties
+ hdrProperties.addHeaderProperties(createAuthenticationHdr(false, null));
+
+ return hdrProperties;
+ }
+
protected HeaderProperties extractHeaderInfo(ClientResponse clientResponse)
{
@@ -498,6 +618,41 @@ protected HeaderProperties extractHeaderInfo(ClientResponse clientResponse)
return hdrProps;
}
+ protected void addPagingInfoToHeaders(PagingInfo pagingInfo, HeaderProperties hdrProperties)
+ {
+ if (pagingInfo != null)
+ {
+ Map queryParameters = pagingInfo.getRequestValues();
+ for (String key : queryParameters.keySet())
+ {
+ hdrProperties.setHeaderProperty(key, queryParameters.get(key));
+ }
+ }
+ }
+
+ protected void addDelayedInfo(HeaderProperties hdrProperties, SIFZone zone, SIFContext context, String serviceName, ServiceType serviceType, RequestType requestType)
+ {
+ if (requestType == RequestType.DELAYED)
+ {
+ ServiceInfo serviceInfo = getSIF3Session().getServiceInfoForService(zone, context, serviceName, serviceType);
+ if (serviceInfo != null)
+ {
+ if ((serviceInfo.getRemoteQueueInfo() != null) && (serviceInfo.getRemoteQueueInfo().getQueueID() != null))
+ {
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_QUEUE_ID, serviceInfo.getRemoteQueueInfo().getQueueID());
+ }
+ else // should not be the case if all is called properly but you never know...
+ {
+ logger.error("No SIF Queue configured environment with Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
+ }
+ }
+ else // should not be the case if all is called properly but you never know...
+ {
+ logger.error("No valid service listed in environment ACL for Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
+ }
+ }
+ }
+
/*
* Convenience method for calls that do not support DELAYED request/responses.
*/
@@ -573,6 +728,64 @@ protected Response setResponse(WebResource service, ClientResponse clientRespons
}
return response;
}
+
+ protected BulkOperationResponse setCreateBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
+ {
+ BulkOperationResponse response = new BulkOperationResponse();
+ setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
+ if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
+ (clientResponse.getStatusInfo().getStatusCode() == Status.CREATED.getStatusCode()) ||
+ (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
+ (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
+ {
+ if (response.getHasEntity())
+ {
+ String payload = clientResponse.getEntity(String.class);
+ MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFCreateString(payload, getResponseMediaType());
+ response.setError(statusList.getError());
+ response.setOperationStatuses(statusList.getOperationStatuses());
+ }
+ }
+ else// We are dealing with an error case.
+ {
+ setErrorResponse(response, clientResponse);
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Response from REST Call:\n"+response);
+ }
+ return response;
+ }
+
+ protected BulkOperationResponse setDeleteBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
+ {
+ BulkOperationResponse response = new BulkOperationResponse();
+ setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
+ if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
+ (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
+ (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
+ {
+ if (response.getHasEntity())
+ {
+ String payload = clientResponse.getEntity(String.class);
+ MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFDeleteString(payload, getResponseMediaType());
+ response.setError(statusList.getError());
+ response.setOperationStatuses(statusList.getOperationStatuses());
+
+ }
+ }
+ else// We are dealing with an error case.
+ {
+ setErrorResponse(response, clientResponse);
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Response from REST Call:\n"+response);
+ }
+ return response;
+ }
/*
* This method cannot set the serviceName and serviceType in the Delayed Response Receipt property. It must be set by the caller of this method as this
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/EventClient.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/EventClient.java
index 473b19d8..35e9e3ed 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/EventClient.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/EventClient.java
@@ -23,12 +23,18 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.WebResource.Builder;
+
+import au.com.systemic.framework.utils.StringUtils;
import sif3.common.CommonConstants;
import sif3.common.conversion.MarshalFactory;
import sif3.common.exception.ServiceInvokationException;
import sif3.common.header.HeaderProperties;
import sif3.common.header.HeaderValues;
import sif3.common.header.HeaderValues.EventAction;
+import sif3.common.header.HeaderValues.ServiceType;
import sif3.common.header.HeaderValues.UpdateType;
import sif3.common.header.RequestHeaderConstants;
import sif3.common.model.SIFContext;
@@ -38,11 +44,6 @@
import sif3.infra.common.env.types.ConsumerEnvironment.ConnectorName;
import sif3.infra.common.env.types.ProviderEnvironment;
import sif3.infra.common.interfaces.ClientEnvironmentManager;
-import au.com.systemic.framework.utils.StringUtils;
-
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.WebResource.Builder;
/**
* This class implements the function(s) required to publish REST SIF3 Events to a environment provider and/or broker. The event interface
@@ -112,6 +113,7 @@ public EventClient(ClientEnvironmentManager clientEnvMgr, MediaType requestMedia
* @param event The event to be sent.
* @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
* @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ * @param serviceType The service type of the event (eg. OBJECT, FUNCTIONAL etc).
* @param customHdrFields Custom HTTP header fields to be added to the request.
*
* @return BaseResponse Object holding appropriate values and results of the call. This call won't return any data model objects, just
@@ -119,7 +121,7 @@ public EventClient(ClientEnvironmentManager clientEnvMgr, MediaType requestMedia
*
* @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
*/
- public BaseResponse sendEvents(SIFEvent> event, SIFZone zone, SIFContext context, HeaderProperties customHdrFields) throws ServiceInvokationException
+ public BaseResponse sendEvents(SIFEvent> event, SIFZone zone, SIFContext context, ServiceType serviceType, HeaderProperties customHdrFields) throws ServiceInvokationException
{
if (allOK) // Only send events if all is fine.
{
@@ -143,15 +145,10 @@ public BaseResponse sendEvents(SIFEvent> event, SIFZone zone, SIFContext conte
{
// Don't set zone & context here. They are header parameters in the case of events.
String payloadStr = getDataModelMarshaller().marshal(event.getSIFObjectList(), getRequestMediaType());
-
-// if (logger.isDebugEnabled())
-// {
-// logger.debug("sendEvents: Payload to send:\n"+payloadStr);
-// }
- HeaderProperties headerProps = getEventHeaders(event.getEventAction(), event.getUpdateType(), event.getFingerprint(), zone, context, customHdrFields);
+ HeaderProperties headerProps = getEventHeaders(event.getEventAction(), event.getUpdateType(), event.getFingerprint(), zone, context, serviceType, customHdrFields);
Builder builder = setRequestHeaderAndMediaTypes(service, headerProps, false, false, true);
- logger.debug("Send "+serviceName+" Event with payload size: "+payloadStr.length()+" to Zone = "+((zone == null) ? "default" : zone.getId())+" and Context = "+((context == null) ? "DEFAULT" : context.getId()));
+ logger.debug("Send "+serviceName+" Event to Zone = "+((zone == null) ? "default" : zone.getId())+" and Context = "+((context == null) ? "DEFAULT" : context.getId()));
ClientResponse response = builder.post(ClientResponse.class, payloadStr);
logger.debug("Receive Event Response Status: "+response.getStatus());
@@ -186,7 +183,7 @@ private ProviderEnvironment getProviderEnvironment()
* @param context The context for this event. Can be null.
* @return
*/
- private HeaderProperties getEventHeaders(EventAction eventAction, UpdateType updateType, String fingerprint, SIFZone zone, SIFContext context, HeaderProperties overrideHdrFields)
+ private HeaderProperties getEventHeaders(EventAction eventAction, UpdateType updateType, String fingerprint, SIFZone zone, SIFContext context, ServiceType serviceType, HeaderProperties overrideHdrFields)
{
// Set the override header fields
HeaderProperties hdrProperties = (overrideHdrFields != null) ? new HeaderProperties(overrideHdrFields.getHeaderProperties()) : new HeaderProperties();
@@ -197,7 +194,7 @@ private HeaderProperties getEventHeaders(EventAction eventAction, UpdateType upd
// Add event specific properties
hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_MESSAGE_TYPE, HeaderValues.MessageType.EVENT.name());
hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_EVENT_ACTION, eventAction.name());
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, HeaderValues.ServiceType.OBJECT.name());
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, serviceType.name());
hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_NAME, serviceName);
if (eventAction == EventAction.UPDATE)
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/JobClient.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/JobClient.java
new file mode 100644
index 00000000..4c2c97f3
--- /dev/null
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/JobClient.java
@@ -0,0 +1,902 @@
+/*
+ * JobClient.java
+ * Created: 20 Feb 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package sif3.infra.rest.client;
+
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+
+import au.com.systemic.framework.utils.StringUtils;
+import sif3.common.CommonConstants.PhaseState;
+import sif3.common.exception.ServiceInvokationException;
+import sif3.common.header.HeaderProperties;
+import sif3.common.header.HeaderValues;
+import sif3.common.header.HeaderValues.RequestType;
+import sif3.common.header.HeaderValues.ServiceType;
+import sif3.common.header.RequestHeaderConstants;
+import sif3.common.model.PagingInfo;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFZone;
+import sif3.common.model.URLQueryParameter;
+import sif3.common.model.job.JobCreateRequestParameter;
+import sif3.common.model.job.PhaseInfo;
+import sif3.common.utils.UUIDGenerator;
+import sif3.common.ws.BulkOperationResponse;
+import sif3.common.ws.CreateOperationStatus;
+import sif3.common.ws.OperationStatus;
+import sif3.common.ws.Response;
+import sif3.common.ws.job.PhaseDataRequest;
+import sif3.infra.common.conversion.InfraMarshalFactory;
+import sif3.infra.common.conversion.InfraUnmarshalFactory;
+import sif3.infra.common.env.types.ConsumerEnvironment.ConnectorName;
+import sif3.infra.common.interfaces.ClientEnvironmentManager;
+import sif3.infra.common.model.DeleteIdType;
+import sif3.infra.common.model.DeleteRequestType;
+import sif3.infra.common.model.InitializationType;
+import sif3.infra.common.model.JobCollectionType;
+import sif3.infra.common.model.JobType;
+import sif3.infra.common.model.PhaseStateType;
+import sif3.infra.common.model.StateCollectionType;
+import sif3.infra.common.model.StateType;
+
+/**
+ * @author Joerg Huber
+ *
+ */
+public class JobClient extends BaseClient
+{
+ private String jobNamePlural = null;
+ private String jobNameSingular = null;
+
+ /**
+ * Constructor
+ *
+ * @param clientEnvMgr Session manager to access the clients session information.
+ * @param jobNamePlural The name of the Job. This must match the value of an entry in the SIF3_JOB_TEMPLATE with the JOB_URL_NAME column.
+ * It is also the equivalent to the Plural Form of the Job Name in the Job URL.
+ * @param jobNameSingular The name of the Job in its singular form. Only needed for the createJob operation.
+ */
+ public JobClient(ClientEnvironmentManager clientEnvMgr, String jobNamePlural, String jobNameSingular)
+ {
+ super(clientEnvMgr, clientEnvMgr.getEnvironmentInfo().getConnectorBaseURI(ConnectorName.servicesConnector), clientEnvMgr.getEnvironmentInfo().getMediaType(), clientEnvMgr.getEnvironmentInfo().getMediaType(), new InfraMarshalFactory(), new InfraUnmarshalFactory(), clientEnvMgr.getEnvironmentInfo().getSecureConnection(), clientEnvMgr.getEnvironmentInfo().getCompressionEnabled());
+ setJobNamePlural(jobNamePlural);
+ setJobNameSingular(jobNameSingular);
+ }
+
+ public String getJobNamePlural()
+ {
+ return jobNamePlural;
+ }
+
+ public void setJobNamePlural(String jobNamePlural)
+ {
+ this.jobNamePlural = jobNamePlural;
+ }
+
+ public String getJobNameSingular()
+ {
+ return jobNameSingular;
+ }
+
+ public void setJobNameSingular(String jobNameSingular)
+ {
+ this.jobNameSingular = jobNameSingular;
+ }
+
+
+ /*----------------------------------*/
+ /*-- Create Job Object Operations --*/
+ /*----------------------------------*/
+
+ /**
+ * Will invoke the REST POST method. Before the call it will retrieve the job template with the name of the jobName parameter in the
+ * constructor of this class. It will add the appropriate values of the createJobRequest parameter to the POST request. If there are any
+ * errors then the ServiceInvokationException is raised and the error is logged.
+ * This method will create job refIDs and expects the provider to accept them. MustUseAdvisory will be set to true. The
+ * assigned JobID is in the createJobRequest.jobID property.
+ *
+ * @param createJobRequest Values to be used to produce a Create Job request such as HTTP headers etc.
+ * Values that are null are defaulted according to the SIF Specification.
+ * - initParams = null => No initialisation parameters will be set in Job Object.
+ * - initPhaseName = null => No phase name will be set in the Job Object initialisation section. This must match
+ * a phase name that is part of the job phases.
+ *
+ * @param hdrProperties Header Properties to be added to the header of the POST request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return Response Job Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as unable to marshal the object into its media
+ * type, failure to invoke actual web-service etc.
+ */
+ public Response createJob(JobCreateRequestParameter createJobRequest, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ // Let's initialise the Job Template with the given info.
+ JobType job = initJobData(createJobRequest);
+
+ WebResource service = getService();
+ try
+ {
+ String payloadStr = getInfraMarshaller().marshal(job, getRequestMediaType());
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("createJob: Payload to send:\n"+payloadStr);
+ }
+
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), getJobNameSingular());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, "true");
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, true, true, true).post(ClientResponse.class, payloadStr);
+
+ return setResponse(service, response, JobType.class, hdrProperties, zone, context, true, Status.CREATED, Status.CONFLICT);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'createJob' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method will create a collection of Job request in one HTTP request. For each element in the provided list of
+ * JobCreateRequestParameter one element in the job collection will be created. The final collection is then sent to the service
+ * connector as specified in the SIF Specification.
+ * It is assumed that all jobs are of the same type, indicated with the jobName parameter in the constructor of this class. Each job
+ * element follows the same defaulting as listed in the createJob() method.
+ * This method will create job refIDs and expects the provider to accept them. MustUseAdvisory will be set to true.
+ *
+ * @param createMultipleJobsRequest Values to be used to produce a Create Job request such as HTTP headers etc.
+ * Values that are null are defaulted according to the SIF Specification.
+ * - initParams = null => No initialisation parameters will be set in Job Object.
+ * - initPhaseName = null => No phase name will be set in the Job Object initialisation section.
+ * This must match a phase name that is part of the job phases.
+ *
+ * @param hdrProperties Header Properties to be added to the header of the POST request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Job Object holding appropriate values and results of the call. Note that the payload of the response is NOT
+ * a job object but a list of "create" responses with JobRefIds as defined in the SIF Specification. If more details
+ * about each job shall be retrieved then it is up to the caller of this method to call the getJob() method for each
+ * jobID returned in this response.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as unable to marshal the object into its media
+ * type, failure to invoke actual web-service etc.
+ */
+ public BulkOperationResponse createJobs(List createMultipleJobsRequest, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context, RequestType requestType) throws ServiceInvokationException
+ {
+ // Let's iterate through the jobs...
+ JobCollectionType jobs = new JobCollectionType();
+ for (JobCreateRequestParameter jobParams : createMultipleJobsRequest)
+ {
+ // Let's initialise the Job Template with the given info. There is the possibility that this is null where no initialisation
+ // is required. We should create it and set it, so that a jobId can be assigned.
+ if (jobParams == null)
+ {
+ jobParams = new JobCreateRequestParameter();
+ }
+ jobs.getJob().add(initJobData(jobParams));
+ }
+
+ WebResource service = getService();
+ try
+ {
+ String payloadStr = getInfraMarshaller().marshal(jobs, getRequestMediaType());
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("createJob: Payload to send:\n"+payloadStr);
+ }
+
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, "true");
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, requestType, true, true, true).post(ClientResponse.class, payloadStr);
+
+ return setCreateBulkResponse(service, response, zone, context, requestType, hdrProperties);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'createJobs' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /*---------------------------------*/
+ /*-- Query Job Object Operations --*/
+ /*---------------------------------*/
+
+ /**
+ * This method will retrieve a Job given by its jobID. This call will invoke the REST GET call. The object will
+ * be unmarshalled into the Job Object and be stored as part of the returned Response object. If there are any internal
+ * errors then a ServiceInvokationException will be raised and the error is logged.
+ *
+ * @param jobID The Id of the job to be returned. If the job doesn't exist the appropriate status is set in the returned Response object as defined
+ * by the SIF3 spec.
+ * @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return The Response to this GET call. See the Response class for details of the content of this object.
+ *
+ * @throws ServiceInvokationException An internal error occurred. An error is logged.
+ */
+ public Response getJob(String jobID, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), jobID);
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, true, true, false).get(ClientResponse.class);
+
+ return setResponse(service, response, JobType.class, hdrProperties, zone, context, true, Status.OK, Status.NOT_MODIFIED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'getJob' service (REST GET) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method will retrieve a list of Jobs in the given zone and context. It will invoke the REST GET call (Query). Because no jobID is
+ * provided it will automatically return a list of jobs (collection). Additional parameters of this method indicate what part of the
+ * collection shall be returned (pagingInfo) as well as what zone and context this query shall apply to.
+ *
+ * @param pagingInfo Page information to be set for the provider to determine which results to return.
+ * @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values and results of the call. This is either a job collection or an error object.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response getJobs(PagingInfo pagingInfo, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context, RequestType requestType) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addPagingInfoToHeaders(pagingInfo, hdrProperties);
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, requestType, true, true, false).get(ClientResponse.class);
+
+ return setResponse(service, response, JobCollectionType.class, hdrProperties, zone, context, requestType, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'getJobs' service (REST GET) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /*----------------------------------*/
+ /*-- Delete Job Object Operations --*/
+ /*----------------------------------*/
+
+ /**
+ * Removes the Job with the given 'resourceID'. Before the call it will add the 'hdrProperties' to the header of the DELETE request.
+ * If there are any errors then the ServiceInvokationException is raised and the error is logged.
+ *
+ * @param jobID The Job Id of the job to be deleted. If the job doesn't exist the appropriate status is set in the
+ * returned Response object as defined by the SIF3 spec.
+ * @param hdrProperties Header Properties to be added to the header of the DELETE request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return Response Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response removeJob(String jobID, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), jobID);
+
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, true, true, false).delete(ClientResponse.class);
+
+ return setResponse(service, response, null, hdrProperties, zone, context, true, Status.NO_CONTENT);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'removeJob' service (REST DELETE) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method removes all the Jobs given by the 'resourceIDs' parameter. This method is used to delete many jobs in one call as
+ * defined by the SIF3 spec. The returned list of responses equate to one response per object in the given payload.
+ *
+ * There is an issue with java.net.HttpURLConnection where it doesn't allow an payload for the HTTP DELETE operation. So currently
+ * the implementation of the removeMany fakes such a behaviour and actually calls the HTTP PUT with a HTTP Header called 'methodOverride' as
+ * specified in the SIF 3.x specification.
+ *
+ * @param jobIDs A list of jobs given by their IDs to be deleted.
+ * @param hdrProperties Header Properties to be added to the header of the DELETE request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public BulkOperationResponse removeJobs(List jobIDs, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context, RequestType requestType) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural());
+
+ //Convert List of resources to DeletesTypes
+ DeleteRequestType deleteRequest = getInfraObjectFactory().createDeleteRequestType();
+ deleteRequest.setDeletes(getInfraObjectFactory().createDeleteIdCollection());
+
+ if (jobIDs != null)
+ {
+ for (String resourceID : jobIDs)
+ {
+ DeleteIdType id = getInfraObjectFactory().createDeleteIdType();
+ id.setId(resourceID);
+ deleteRequest.getDeletes().getDelete().add(id);
+ }
+ }
+ String payloadStr = getInfraMarshaller().marshal(deleteRequest, getRequestMediaType());
+
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ // Set specific header so that PUT method knows that a DELETE and not an UPDATE is required!
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_METHOD_OVERRIDE, HeaderValues.MethodType.DELETE.name());
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("removeJobs: Payload to send:\n"+payloadStr);
+ }
+ ClientResponse cltResponse = setRequestHeaderAndMediaTypes(service, hdrProperties, requestType, true, true, true).put(ClientResponse.class, payloadStr);
+
+ return setDeleteBulkResponse(service, cltResponse, zone, context, requestType, hdrProperties);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'removeJobs' service (REST DELETE) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * Will invoke the REST HEAD call. It will be invoked on the Job service. It will not return a payload as per
+ * HTTP Specification of the HEAD method. It will return a number of HTTP Header fields though. These can be retrieved as
+ * part of the returned response object. Because this method almost mirrors the HTTP GET for the getJobs() service all
+ * parameters that would make up the getJobs() method in this class are supported. The exception is the requestType parameter
+ * that are allowed in the getJobs() method. This parameter does not make any sense for this method and is therefore omitted.
+ *
+ * @param pagingInfo Page information to be set for the provider to determine which results to return.
+ * @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return Response Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response getServiceInfo(PagingInfo pagingInfo, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addPagingInfoToHeaders(pagingInfo, hdrProperties);
+
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, RequestType.IMMEDIATE, true, true, false).head();
+
+ return setResponse(service, response, null, hdrProperties, zone, context, RequestType.IMMEDIATE, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'getServiceInfo' Job service (REST HEAD) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /*----------------------------------------------------------------------*/
+ /*-- Update Job Object Operations: NOT ALLOWED according to SIF 3.2.1 --*/
+ /*----------------------------------------------------------------------*/
+
+ /*--------------------------*/
+ /*-- Job Phase Operations --*/
+ /*--------------------------*/
+ /**
+ * This method is used to retrieve data from a given phase. This can be any data. It is up to the implementation of the
+ * functional service to know what that data is for a given phase. This framework is agnostic to that data. The returned
+ * value is a String that must represent the "marshalled" version of the data in the format indicated by the "returnMimeType".
+ * Because the data that can be returned as part of a phase might be a collection, the paging parameter can be provided. If the
+ * data to be returned is considered too large by the provider (implementation dependent) then an appropriate error is
+ * returned (HTTP Status 413 - Response too large).
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job from where the data shall be retrieved. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param returnMimeType The mime type the response data is in. It is expected that the consumer provides that and the provider
+ * should attempt to marshal the data to the given mime type and return the resulting string as
+ * part of this call. If the provider cannot marshal the data to the requested mime type then an
+ * appropriate error is returned to this consumer (HTTP Status 400 - Bad Request).
+ * @param pagingInfo Page information to determine which results to return. Null = Return all (NOT RECOMMENDED! Might be rejected
+ * by provider.).
+ * @param hdrProperties Header Properties to be added to the header of the request. Can be null.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The Zone from which the request is being issued. Can be Null (default Zone)
+ * @param context The Context for which the the request is being issued. Can be Null (default Zone)
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values and results of the call. Because the framework is agnostic to the
+ * data that is returned for a phase the Response.dataObjectType will be set to "String" and the Response.dataObject
+ * will hold the string representation of the returned payload. It is up to the caller of this method to potentially
+ * marshal that payload into an appropriate object.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response retrieveDataFromPhase(PhaseInfo phaseInfo, MediaType returnMimeType, PagingInfo pagingInfo, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context, RequestType requestType)
+ throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addPagingInfoToHeaders(pagingInfo, hdrProperties);
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, returnMimeType, returnMimeType, hdrProperties, requestType, true, true, false).get(ClientResponse.class);
+
+ return setResponse(service, response, String.class, hdrProperties, zone, context, requestType, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'retrieveDataFromPhase' service (REST GET) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method should perform a "create" operation for the data given in the "data" parameter. This can be any data. It
+ * is up to the implementation of the functional service to know what that data is for a given phase. This framework is agnostic
+ * to that data. The data is given in its raw form as received by the framework. The phaseData.mimeType shall be used
+ * by the provider to unmarshal the data into a useful data structure. It is important to note that there might be no return data
+ * for a phase operation (response.dataObject == null). This is indicated with a HTTP status of 204 (No content). However if data
+ * is returned then response.dataObject will hold the data in its string representation and the response.dataObjectType will be
+ * set to String.class. It is expected that the consumer uses the response.mediaType to unmarshal the data to a structure suitable
+ * for the consumer. It is also expected that the response.mediaType would be of the same mime type as indicated by the consumer
+ * in the returnMimeType parameter.
+ *
+ * In case of a delayed request the the response.dataObject and response.dataObjectType are null and the response.delayedReceipt
+ * property will be populated accordingly.
+ *
+ * In case of a failure an appropriate error message is returned in the Response Object. If there is no error returned it is
+ * assumed that this method completed successfully and the appropriate HTTP status is set.
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job from where the data shall be created. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param phaseDataRequest The data and its mime type that is given to the operation of this phase. The data property
+ * of this parameter can be null in cases where no data is provided to this phase. This is
+ * entirely valid according to the SIF Specification. If data is provided (as a String) then
+ * the mime type is set as well, by the consumer, to indicate the format of the data. The
+ * provider can us this mime type to unmarshal the data into the appropriate internal structure.
+ * @param returnMimeType The mime type the response data is in. It is expected that the consumer provides that and the provider
+ * should attempt to marshal the data to the given mime type and return the resulting string as
+ * part of this call. If the provider cannot marshal the data to the requested mime type then an
+ * appropriate error is returned to this consumer (HTTP Status 400 - Bad Request).
+ * @param useAdvisory If new IDs for the created data shall be allocated or used as given. In some cases that may not be applicable
+ * but if it is then this parameter indicates the expected behaviour.
+ * @param hdrProperties Header Properties to be added to the header of the request. Can be null.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The Zone from which the request is being issued. Can be Null (default Zone)
+ * @param context The Context for which the the request is being issued. Can be Null (default Zone)
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values of the call. The response may contain data (response.dataObject) that will be
+ * with a response.dataObjectType=String.class. The consumer is expected to use the response.mediaType to unmarshal the data
+ * into its internal structure. If the status is set to 204 then no content is returned which is also a valid scenario.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response createDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ boolean useAdvisory,
+ HeaderProperties hdrProperties,
+ URLQueryParameter urlQueryParams,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType)
+ throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, String.valueOf(useAdvisory));
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ ClientResponse response = null;
+ if ((phaseDataRequest != null) && (phaseDataRequest.getData() != null))
+ {
+ response = setRequestHeaderAndMediaTypes(service, phaseDataRequest.getMimeType(), returnMimeType, hdrProperties, requestType, true, true, true).post(ClientResponse.class, phaseDataRequest.getData());
+ }
+ else
+ {
+ response = setRequestHeaderAndMediaTypes(service, null, returnMimeType, hdrProperties, requestType, true, true, false).post(ClientResponse.class);
+ }
+
+ return setResponse(service, response, String.class, hdrProperties, zone, context, requestType, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED, Status.CREATED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'createDataInPhase' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method should perform an "update" operation for the data given in the "phaseData.data" (string). This can be any data. It
+ * is up to the implementation of the functional service to know what that data is for a given phase. This framework is agnostic
+ * to that data. The data is given in its raw form as received by the framework. The phaseData.mimeType shall be used
+ * by the provider to unmarshal the data into a useful data structure. It is important to note that there might be no return data
+ * for a phase operation (response.dataObject == null). However if data is returned then response.dataObject will hold the data in
+ * its string representation and the response.dataObjectType will be set to String.class. It is expected that the consumer uses
+ * the response.mediaType to unmarshal the data to a structure suitable for the consumer. It is also expected that the
+ * response.mediaType would be of the same mime type as indicated by the consumer in the returnMimeType parameter.
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job where the data shall be updated. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param phaseDataRequest The data and its mime type that is given to the operation of this phase. The data property
+ * of this parameter can be null in cases where no data is provided to this phase. This is
+ * entirely valid according to the SIF Specification. If data is provided (as a String) then
+ * the mime type is set as well, by the consumer, to indicate the format of the data. The
+ * provider can us this mime type to unmarshal the data into the appropriate internal structure.
+ * @param returnMimeType The mime type the response data is in. It is expected that the consumer provides that and the provider
+ * should attempt to marshal the data to the given mime type and return the resulting string as
+ * part of this call. If the provider cannot marshal the data to the requested mime type then an
+ * appropriate error is returned to this consumer (HTTP Status 400 - Bad Request).
+ * @param hdrProperties Header Properties to be added to the header of the request. Can be null.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The Zone from which the request is being issued. Can be Null (default Zone)
+ * @param context The Context for which the the request is being issued. Can be Null (default Zone)
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values of the call. The response may contain data (response.dataObject) that will be
+ * with a response.dataObjectType=String.class. The consumer is expected to use the response.mediaType to unmarshal the data
+ * into its internal structure.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response updateDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ HeaderProperties hdrProperties,
+ URLQueryParameter urlQueryParams,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType)
+ throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ ClientResponse response = null;
+ if ((phaseDataRequest != null) && (phaseDataRequest.getData() != null))
+ {
+ response = setRequestHeaderAndMediaTypes(service, phaseDataRequest.getMimeType(), returnMimeType, hdrProperties, requestType, true, true, true).put(ClientResponse.class, phaseDataRequest.getData());
+ }
+ else
+ {
+ response = setRequestHeaderAndMediaTypes(service, null, returnMimeType, hdrProperties, requestType, true, true, false).put(ClientResponse.class);
+ }
+
+ return setResponse(service, response, String.class, hdrProperties, zone, context, requestType, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'updateDataInPhase' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method should perform a "delete" operation for the data given in the "phaseData.data" (string). This can be any data. It
+ * is up to the implementation of the functional service to know what that data is for a given phase. This framework is agnostic
+ * to that data. The data is given in its raw form as received by the framework. The phaseData.mimeType shall be used
+ * by the provider to unmarshal the data into a useful data structure. It is important to note that there might be no return data
+ * for a phase operation (response.dataObject == null). However if data is returned then response.dataObject will hold the data in
+ * its string representation and the response.dataObjectType will be set to String.class. It is expected that the consumer uses
+ * the response.mediaType to unmarshal the data to a structure suitable for the consumer. It is also expected that the
+ * response.mediaType would be of the same mime type as indicated by the consumer in the returnMimeType parameter.
+ *
+ * In case of a delayed request the the response.dataObject and response.dataObjectType are null and the response.delayedReceipt
+ * property will be populated accordingly.
+ *
+ * In case of a failure an appropriate error message is returned in the Response Object. If there is no error returned it is
+ * assumed that this method completed successfully and the appropriate HTTP status is set.
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job where the data shall be deleted. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param phaseDataRequest The data and its mime type that is given to the operation of this phase. The data property
+ * of this parameter can be null in cases where no data is provided to this phase. This is
+ * entirely valid according to the SIF Specification. If data is provided (as a String) then
+ * the mime type is set as well, by the consumer, to indicate the format of the data. The
+ * provider can us this mime type to unmarshal the data into the appropriate internal structure.
+ * @param returnMimeType The mime type the response data is in. It is expected that the consumer provides that and the provider
+ * should attempt to marshal the data to the given mime type and return the resulting string as
+ * part of this call. If the provider cannot marshal the data to the requested mime type then an
+ * appropriate error is returned to this consumer (HTTP Status 400 - Bad Request).
+ * @param hdrProperties Header Properties to be added to the header of the request. Can be null.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The Zone from which the request is being issued. Can be Null (default Zone)
+ * @param context The Context for which the the request is being issued. Can be Null (default Zone)
+ * @param requestType Indicating if IMMEDIATE or DELAYED request is desired.
+ *
+ * @return Response Object holding appropriate values of the call. The response may contain data (response.dataObject) that will be
+ * with a response.dataObjectType=String.class. The consumer is expected to use the response.mediaType to unmarshal the data
+ * into its internal structure.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response deleteDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ HeaderProperties hdrProperties,
+ URLQueryParameter urlQueryParams,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType)
+ throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName());
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+
+ // Set specific header so that PUT method knows that a DELETE and not an UPDATE is required!
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_METHOD_OVERRIDE, HeaderValues.MethodType.DELETE.name());
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+ addDelayedInfo(hdrProperties, zone, context, getJobNamePlural(), ServiceType.FUNCTIONAL, requestType);
+
+ ClientResponse response = null;
+ if ((phaseDataRequest != null) && (phaseDataRequest.getData() != null))
+ {
+ response = setRequestHeaderAndMediaTypes(service, phaseDataRequest.getMimeType(), returnMimeType, hdrProperties, requestType, true, true, true).put(ClientResponse.class, phaseDataRequest.getData());
+ }
+ else
+ {
+ response = setRequestHeaderAndMediaTypes(service, null, returnMimeType, hdrProperties, requestType, true, true, false).put(ClientResponse.class);
+ }
+
+ return setResponse(service, response, String.class, hdrProperties, zone, context, requestType, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'deleteDataInPhase' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /*--------------------------------*/
+ /*-- Job Phase State Operations --*/
+ /*--------------------------------*/
+
+ /**
+ * This method attempts to update the state of a given phase for a job.
+ * An error message will be part of the returned Response for the following cases:
+ * - Job doesn't exist.
+ * - If the phase or state is invalid.
+ * - Consumer doesn't have appropriate permissions for this operation.
+ * - Potentially the job is no longer 'update-able' because it has reached an end state (eg. COMPLETED or FAILED).
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job where the state shall be updated. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param newState The value of the new state for the given phase. If null then no action is taken.
+ * @param hdrProperties Header Properties to be added to the header of the request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return Response Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response updatePhaseState(PhaseInfo phaseInfo, PhaseState newState, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ // Create the phase state object
+ StateType newPhaseState = new StateType();
+ newPhaseState.setType(PhaseStateType.valueOf(newState.name()));
+ String payloadStr = getInfraMarshaller().marshal(newPhaseState, getRequestMediaType());
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Phase State: Payload to send:\n"+payloadStr);
+ }
+
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName(), "states", "state");
+
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, true, true, true).post(ClientResponse.class, payloadStr);
+
+ return setResponse(service, response, StateType.class, hdrProperties, zone, context, true, Status.CREATED, Status.CONFLICT);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'updatePhaseState' service (REST POST) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /**
+ * This method attempts to retrieve the state(s) of the given phase. The returned object in the response will be of
+ * type StateCollectionType as defined in the infrastructure data model.
+ * An error message will be part of the returned Response for the following cases:
+ * - Job doesn't exist.
+ * - If the phase is invalid.
+ * - Consumer doesn't have appropriate permissions for this operation.
+ *
+ * @param phaseInfo Hold the jobID and phase name of the job where the states shall be retrieved. If the parameter or
+ * any of its properties must not be null/empty.
+ * @param hdrProperties Header Properties to be added to the header of the request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
+ * @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
+ * @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
+ *
+ * @return Response Object holding appropriate values and results of the call.
+ *
+ * @throws ServiceInvokationException Any underlying errors occurred such as failure to invoke actual web-service etc.
+ */
+ public Response getPhaseStates(PhaseInfo phaseInfo, HeaderProperties hdrProperties, URLQueryParameter urlQueryParams, SIFZone zone, SIFContext context) throws ServiceInvokationException
+ {
+ WebResource service = getService();
+ try
+ {
+ // Create the phase state object
+
+ service = buildURI(service, zone, context, urlQueryParams, getJobNamePlural(), phaseInfo.getJobID(), phaseInfo.getPhaseName(), "states");
+
+ hdrProperties = addAuthenticationHdrProps(hdrProperties);
+ hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, ServiceType.FUNCTIONAL.name());
+
+ ClientResponse response = setRequestHeaderAndMediaTypes(service, hdrProperties, true, true, true).get(ClientResponse.class);
+
+ return setResponse(service, response, StateCollectionType.class, hdrProperties, zone, context, true, Status.OK, Status.NOT_MODIFIED, Status.NO_CONTENT, Status.ACCEPTED);
+ }
+ catch (Exception ex)
+ {
+ String errorMsg = "Failed to invoke 'getPhaseStates' service (REST GET) on URI " + service.getURI() + ": " + ex.getMessage();
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg, ex);
+ }
+ }
+
+ /*---------------------*/
+ /*-- Private Methods --*/
+ /*---------------------*/
+ private JobType initJobData(JobCreateRequestParameter jobInitRequest) throws ServiceInvokationException
+ {
+ // First we get the job template
+ JobType job = getClientEnvMgr().getJobTemplate(getJobNamePlural());
+ if (job == null) // not good. No such job exists in job template store
+ {
+ String errorMsg = "No Job Template with the name " + getJobNamePlural() + " exists. Needs to be configured in appropriate tables first.";
+ logger.error(errorMsg);
+ throw new ServiceInvokationException(errorMsg);
+ }
+
+ // If we get here all good. Populate initialisation parameters.
+ if ((jobInitRequest != null) && (StringUtils.notEmpty(jobInitRequest.getInitPhaseName()) || (jobInitRequest.getInitParams() != null)))
+ {
+ InitializationType initSection = new InitializationType();
+ if (StringUtils.notEmpty(jobInitRequest.getInitPhaseName()))
+ {
+ initSection.setPhaseName(jobInitRequest.getInitPhaseName().trim());
+ }
+ if (jobInitRequest.getInitParams() != null)
+ {
+ initSection.setPayload(jobInitRequest.getInitParams());
+ }
+ job.setInitialization(initSection);
+ }
+ job.setId(UUIDGenerator.getUUID());
+ jobInitRequest.setJobID(job.getId());
+
+ return job;
+ }
+}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/ObjectServiceClient.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/ObjectServiceClient.java
index c42dab79..e20815aa 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/ObjectServiceClient.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/ObjectServiceClient.java
@@ -17,11 +17,13 @@
import java.net.URI;
import java.util.List;
-import java.util.Map;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+
import sif3.common.conversion.MarshalFactory;
import sif3.common.conversion.UnmarshalFactory;
import sif3.common.exception.ServiceInvokationException;
@@ -33,7 +35,6 @@
import sif3.common.model.PagingInfo;
import sif3.common.model.SIFContext;
import sif3.common.model.SIFZone;
-import sif3.common.model.ServiceInfo;
import sif3.common.model.URLQueryParameter;
import sif3.common.ws.BulkOperationResponse;
import sif3.common.ws.CreateOperationStatus;
@@ -44,9 +45,6 @@
import sif3.infra.common.model.DeleteIdType;
import sif3.infra.common.model.DeleteRequestType;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-
/**
* This class is a core client class for all Object Services and their CRUD operations (request connector) for SIF3. It takes care of
* all the little things that define the SIF3 REST transport for all the Object Service operations stated in the SIF3 spec.
@@ -290,6 +288,9 @@ public Response removeSingle(String relURI, String resourceID, HeaderProperties
* @param serviceType Currently this should be OBJECT or SERVICPATH.
* @param pagingInfo Page information to be set for the provider to determine which results to return.
* @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
* @param returnObjectClass The class type into which the object shall be unmarshalled into. The final object is stored in the
* returned Response object.
* @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
@@ -334,6 +335,9 @@ public Response getMany(String relURI, String serviceName, ServiceType serviceTy
* @param exampleObject The example data model object. This must be the single object type.
* @param pagingInfo Page information to be set for the provider to determine which results to return.
* @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
* @param returnObjectClass The class type into which the object shall be unmarshalled into. The final object is stored in the
* returned Response object.
* @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
@@ -481,9 +485,7 @@ public BulkOperationResponse updateMany(String relURI, String s
/**
* This invokes the REST PUT call. This method is used to delete many objects in one call as defined by the SIF3 spec. The
- * returned list of responses equate to one response per object in the given payload. The order of the responses is the same as the
- * order in the original payload. The first response in the BulkOperationResponse list is the response to the create of the first
- * object in the payload etc.
+ * returned list of responses equate to one response per object in the given payload.
*
* There is an issue with java.net.HttpURLConnection where it doesn't allow an payload for the HTTP DELETE operation. So currently
* the implementation of the removeMany fakes such a behaviour and actually calls the HTTP PUT with a HTTP Header called 'methodOverride' as
@@ -549,7 +551,7 @@ public BulkOperationResponse removeMany(String relURI, String s
}
/*-----------------------------------*/
- /*-- Get Service Infor (HTTP HEAD) --*/
+ /*-- Get Service Info (HTTP HEAD) --*/
/*-----------------------------------*/
/**
* Will invoke the REST HEAD call. It will not return a payload as per HTTP Specification of the HEAD method. It will
@@ -563,6 +565,9 @@ public BulkOperationResponse removeMany(String relURI, String s
* the service path name as in the Environment ACL (i.e. schools/{}/students).
* @param pagingInfo Page information to be set for the provider to determine which results to return.
* @param hdrProperties Header Properties to be added to the header of the GET request.
+ * @param urlQueryParams URL query parameters to be added to the request. It is assumed that these are custom
+ * URL query parameters. They are conveyed to the provider unchanged. URL query parameter
+ * names are case sensitive. This parameter can be null.
* @param zone The zone for which this operation shall be invoked. Can be null which indicates the DEFAULT zone.
* @param context The context for which this operation shall be invoked. Can be null which indicates the DEFAULT context.
*
@@ -591,68 +596,66 @@ public Response getServiceInfo(String relURI, String serviceName, PagingInfo pag
}
}
-
-
/*---------------------*/
/*-- Private Methods --*/
/*---------------------*/
- private BulkOperationResponse setCreateBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
- {
- BulkOperationResponse response = new BulkOperationResponse();
- setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
- if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
- (clientResponse.getStatusInfo().getStatusCode() == Status.CREATED.getStatusCode()) ||
- (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
- (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
- {
- if (response.getHasEntity())
- {
- String payload = clientResponse.getEntity(String.class);
- MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFCreateString(payload, getResponseMediaType());
- response.setError(statusList.getError());
- response.setOperationStatuses(statusList.getOperationStatuses());
- }
- }
- else// We are dealing with an error case.
- {
- setErrorResponse(response, clientResponse);
- }
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Response from REST Call:\n"+response);
- }
- return response;
- }
-
- private BulkOperationResponse setDeleteBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
- {
- BulkOperationResponse response = new BulkOperationResponse();
- setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
- if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
- (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
- (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
- {
- if (response.getHasEntity())
- {
- String payload = clientResponse.getEntity(String.class);
- MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFDeleteString(payload, getResponseMediaType());
- response.setError(statusList.getError());
- response.setOperationStatuses(statusList.getOperationStatuses());
-
- }
- }
- else// We are dealing with an error case.
- {
- setErrorResponse(response, clientResponse);
- }
-
- if (logger.isDebugEnabled())
- {
- logger.debug("Response from REST Call:\n"+response);
- }
- return response;
- }
+// private BulkOperationResponse setCreateBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
+// {
+// BulkOperationResponse response = new BulkOperationResponse();
+// setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
+// if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
+// (clientResponse.getStatusInfo().getStatusCode() == Status.CREATED.getStatusCode()) ||
+// (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
+// (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
+// {
+// if (response.getHasEntity())
+// {
+// String payload = clientResponse.getEntity(String.class);
+// MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFCreateString(payload, getResponseMediaType());
+// response.setError(statusList.getError());
+// response.setOperationStatuses(statusList.getOperationStatuses());
+// }
+// }
+// else// We are dealing with an error case.
+// {
+// setErrorResponse(response, clientResponse);
+// }
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("Response from REST Call:\n"+response);
+// }
+// return response;
+// }
+
+// private BulkOperationResponse setDeleteBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
+// {
+// BulkOperationResponse response = new BulkOperationResponse();
+// setBaseResponseData(response, clientResponse, requestHeaders, zone, context, true, requestType, service.getURI().toString());
+// if ((clientResponse.getStatusInfo().getStatusCode() == Status.OK.getStatusCode()) ||
+// (clientResponse.getStatusInfo().getStatusCode() == Status.ACCEPTED.getStatusCode()) ||
+// (clientResponse.getStatusInfo().getStatusCode() == Status.NO_CONTENT.getStatusCode()))
+// {
+// if (response.getHasEntity())
+// {
+// String payload = clientResponse.getEntity(String.class);
+// MultiOperationStatusList statusList = getInfraMapper().toStatusListFromSIFDeleteString(payload, getResponseMediaType());
+// response.setError(statusList.getError());
+// response.setOperationStatuses(statusList.getOperationStatuses());
+//
+// }
+// }
+// else// We are dealing with an error case.
+// {
+// setErrorResponse(response, clientResponse);
+// }
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("Response from REST Call:\n"+response);
+// }
+// return response;
+// }
private BulkOperationResponse setUpdateBulkResponse(WebResource service, ClientResponse clientResponse, SIFZone zone, SIFContext context, RequestType requestType, HeaderProperties requestHeaders)
{
@@ -682,55 +685,38 @@ private BulkOperationResponse setUpdateBulkResponse(WebResource
return response;
}
- private void addPagingInfoToHeaders(PagingInfo pagingInfo, HeaderProperties hdrProperties)
- {
- if (pagingInfo != null)
- {
- Map queryParameters = pagingInfo.getRequestValues();
- for (String key : queryParameters.keySet())
- {
- hdrProperties.setHeaderProperty(key, queryParameters.get(key));
- }
- }
- }
-
- private void addDelayedInfo(HeaderProperties hdrProperties, SIFZone zone, SIFContext context, String serviceName, ServiceType serviceType, RequestType requestType)
- {
- if (requestType == RequestType.DELAYED)
- {
- ServiceInfo serviceInfo = getSIF3Session().getServiceInfoForService(zone, context, serviceName, serviceType);
- if (serviceInfo != null)
- {
- if ((serviceInfo.getRemoteQueueInfo() != null) && (serviceInfo.getRemoteQueueInfo().getQueueID() != null))
- {
- hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_QUEUE_ID, serviceInfo.getRemoteQueueInfo().getQueueID());
- }
- else // should not be the case if all is called properly but you never know...
- {
- logger.error("No SIF Queue configured environment with Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
- }
- }
- else // should not be the case if all is called properly but you never know...
- {
- logger.error("No valid service listed in environment ACL for Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
- }
- }
- }
-
- /*
- * This method will add the authentication header properties to the given set of header properties. The final set of header
- * properties is then returned.
- */
- private HeaderProperties addAuthenticationHdrProps(HeaderProperties hdrProperties)
- {
- if (hdrProperties == null)
- {
- hdrProperties = new HeaderProperties();
- }
-
- // Add Authentication info to existing header properties
- hdrProperties.addHeaderProperties(createAuthenticationHdr(false, null));
-
- return hdrProperties;
- }
+// private void addPagingInfoToHeaders(PagingInfo pagingInfo, HeaderProperties hdrProperties)
+// {
+// if (pagingInfo != null)
+// {
+// Map queryParameters = pagingInfo.getRequestValues();
+// for (String key : queryParameters.keySet())
+// {
+// hdrProperties.setHeaderProperty(key, queryParameters.get(key));
+// }
+// }
+// }
+//
+// private void addDelayedInfo(HeaderProperties hdrProperties, SIFZone zone, SIFContext context, String serviceName, ServiceType serviceType, RequestType requestType)
+// {
+// if (requestType == RequestType.DELAYED)
+// {
+// ServiceInfo serviceInfo = getSIF3Session().getServiceInfoForService(zone, context, serviceName, serviceType);
+// if (serviceInfo != null)
+// {
+// if ((serviceInfo.getRemoteQueueInfo() != null) && (serviceInfo.getRemoteQueueInfo().getQueueID() != null))
+// {
+// hdrProperties.setHeaderProperty(RequestHeaderConstants.HDR_QUEUE_ID, serviceInfo.getRemoteQueueInfo().getQueueID());
+// }
+// else // should not be the case if all is called properly but you never know...
+// {
+// logger.error("No SIF Queue configured environment with Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
+// }
+// }
+// else // should not be the case if all is called properly but you never know...
+// {
+// logger.error("No valid service listed in environment ACL for Service Name = "+serviceName+", Service Type = "+serviceType+", Zone = "+zone.getId()+" and Context = "+context.getId());
+// }
+// }
+// }
}
\ No newline at end of file
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/SubscriptionClient.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/SubscriptionClient.java
index 7a0ab012..0bdbb8a3 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/SubscriptionClient.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/client/SubscriptionClient.java
@@ -113,7 +113,7 @@ public Response subscribe(SubscriptionType subscriptionInfo) throws ServiceInvok
}
if (StringUtils.isEmpty(subscriptionInfo.getServiceType()))
{
- throw new IllegalArgumentException("Property serviceType in subscriptioninfo for method subscribe is empty or null. Must be of value 'OBJECT', 'FUNCTION' or 'UTILITY'");
+ throw new IllegalArgumentException("Property serviceType in subscriptioninfo for method subscribe is empty or null. Must be of value 'OBJECT', 'FUNCTIONAL' or 'UTILITY'");
}
// OK all good now...
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractConsumer.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractConsumer.java
index dcc1c6fb..67ec9cd6 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractConsumer.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractConsumer.java
@@ -18,17 +18,10 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import au.com.systemic.framework.utils.AdvancedProperties;
-import au.com.systemic.framework.utils.StringUtils;
import au.com.systemic.framework.utils.Timer;
import sif3.common.exception.PersistenceException;
import sif3.common.exception.ServiceInvokationException;
@@ -43,6 +36,8 @@
import sif3.common.interfaces.Consumer;
import sif3.common.interfaces.DelayedConsumer;
import sif3.common.interfaces.QueryConsumer;
+import sif3.common.model.ACL.AccessRight;
+import sif3.common.model.ACL.AccessType;
import sif3.common.model.CustomParameters;
import sif3.common.model.PagingInfo;
import sif3.common.model.QueryCriteria;
@@ -50,14 +45,9 @@
import sif3.common.model.SIFContext;
import sif3.common.model.SIFZone;
import sif3.common.model.ServiceInfo;
-import sif3.common.model.ServiceRights.AccessRight;
-import sif3.common.model.ServiceRights.AccessType;
import sif3.common.model.URLQueryParameter;
import sif3.common.model.ZoneContextInfo;
-import sif3.common.model.delayed.DelayedRequestReceipt;
import sif3.common.model.delayed.DelayedResponseReceipt;
-import sif3.common.persist.model.SIF3Session;
-import sif3.common.ws.BaseResponse;
import sif3.common.ws.BulkOperationResponse;
import sif3.common.ws.CreateOperationStatus;
import sif3.common.ws.ErrorDetails;
@@ -67,8 +57,6 @@
import sif3.infra.common.env.mgr.ConsumerEnvironmentManager;
import sif3.infra.common.env.types.ConsumerEnvironment;
import sif3.infra.rest.client.ObjectServiceClient;
-import sif3.infra.rest.queue.LocalConsumerQueue;
-import sif3.infra.rest.queue.LocalMessageConsumer;
/**
* This is the core class that a developer will use to implement their consumers. Each consumer for each object type MUST extend this
@@ -83,30 +71,30 @@
*
* @author Joerg Huber
*/
-public abstract class AbstractConsumer implements Consumer, DelayedConsumer, QueryConsumer
+public abstract class AbstractConsumer extends BaseConsumer implements Consumer, DelayedConsumer, QueryConsumer
{
protected final Logger logger = LoggerFactory.getLogger(getClass());
/* Below variables are for testing purposes only */
- private static Boolean testMode = null;
+// private static Boolean testMode = null;
/* End Testing variables */
- private boolean checkACL = true;
+// private boolean checkACL = true;
// private boolean initOK = true;
/* The next two properties are used for delayed responses or events */
- private LocalConsumerQueue localConsumerQueue = null;
- private ExecutorService service = null;
+// private LocalConsumerQueue localConsumerQueue = null;
+// private ExecutorService service = null;
/*-------------------------------------------------------------*/
/* Abstract method relating to general Consumer functionality. */
/*-------------------------------------------------------------*/
- /**
- * This method is called when a consumer service is shut down. It can be used to free up internally allocated resources
- * as well as clean-up other things.
- */
- public abstract void shutdown();
+// /**
+// * This method is called when a consumer service is shut down. It can be used to free up internally allocated resources
+// * as well as clean-up other things.
+// */
+// public abstract void shutdown();
/*---------------------------------------------------------------------*/
/* Abstract method relating to DELAYED request response functionality. */
@@ -165,16 +153,6 @@ public abstract class AbstractConsumer implements Consumer, DelayedConsumer, Que
*/
public abstract void processDelayedServicePath(Object dataObject, QueryCriteria queryCriteria, PagingInfo pagingInfo, DelayedResponseReceipt receipt);
- /**
- * This method is called when a delayed error response is retrieved from the consumer's queue.
- *
- * @see sif3.common.interfaces.DelayedConsumer#onError(sif3.common.ws.ErrorDetails, sif3.common.model.delayed.DelayedResponseReceipt)
- *
- * @param error See onError() method in DelayedConsumer class.
- * @param receipt See onError() method in DelayedConsumer class.
- */
- public abstract void processDelayedError(ErrorDetails error, DelayedResponseReceipt receipt);
-
/*----------------------------------*/
/* Consumer Implementation Methods. */
/*----------------------------------*/
@@ -186,41 +164,18 @@ public AbstractConsumer()
{
super();
- // Set some properties at this stage for simplicity reasons.
- checkACL = getConsumerEnvironment().getCheckACL();
-
-/*
- //Check a few things to ensure that all core methods are implemented.
- if (getMarshaller() == null)
- {
- logger.error("Consumer "+getConsumerName()+" has not implemented the getMarshaller() method properly. It returns null which is not valid.");
- initOK = false;
- }
- if (getUnmarshaller() == null)
- {
- logger.error("Consumer "+getConsumerName()+" has not implemented the getUnmarshaller() method properly. It returns null which is not valid.");
- initOK = false;
- }
- if (getSingleObjectClassInfo() == null)
- {
- logger.error("Consumer "+getConsumerName()+" has not implemented the getSingleObjectClassInfo() method properly. It returns null which is not valid.");
- initOK = false;
- }
- if (getMultiObjectClassInfo() == null)
- {
- logger.error("Consumer "+getConsumerName()+" has not implemented the getMultiObjectClassInfo() method properly. It returns null which is not valid.");
- initOK = false;
- }
-*/
- if (getConsumerEnvironment().getEventsEnabled() || getConsumerEnvironment().getDelayedEnabled())
- {
- logger.debug("Events and/or Delayed Responses enabled => start local consumer queue for "+getConsumerName());
- createLocalConsumerQueue();
- }
- else
- {
- logger.debug("Events AND Delayed Responses are disabled. Local consumer queues and threads are not started.");
- }
+// // Set some properties at this stage for simplicity reasons.
+// checkACL = getConsumerEnvironment().getCheckACL();
+//
+// if (getConsumerEnvironment().getEventsEnabled() || getConsumerEnvironment().getDelayedEnabled())
+// {
+// logger.debug("Events and/or Delayed Responses enabled => start local consumer queue for "+getConsumerName());
+// createLocalConsumerQueue();
+// }
+// else
+// {
+// logger.debug("Events AND Delayed Responses are disabled. Local consumer queues and threads are not started.");
+// }
}
/**
@@ -228,168 +183,168 @@ public AbstractConsumer()
*
* @return See desc
*/
- public ConsumerEnvironment getConsumerEnvironment()
- {
- return (ConsumerEnvironment)ConsumerEnvironmentManager.getInstance().getEnvironmentInfo();
- }
+// public ConsumerEnvironment getConsumerEnvironment()
+// {
+// return (ConsumerEnvironment)ConsumerEnvironmentManager.getInstance().getEnvironmentInfo();
+// }
- /**
- * Utility method to easily retrieve the property file content for a consumer.
- *
- * @return See desc
- */
- public AdvancedProperties getServiceProperties()
- {
- return ConsumerEnvironmentManager.getInstance().getServiceProperties();
- }
+// /**
+// * Utility method to easily retrieve the property file content for a consumer.
+// *
+// * @return See desc
+// */
+// public AdvancedProperties getServiceProperties()
+// {
+// return ConsumerEnvironmentManager.getInstance().getServiceProperties();
+// }
- /*------------------------------------------------------------------------------------------------------------------------
- * Start of 'Dynamic' HTTP Header Field override section
- *
- * The following set of methods are used for a more configurable way how some HTTP header parameters are set.
- * By default the following HTTP Header fields are retrieved from the consumer's property file and put in corresponding
- * HTTP Header Fields:
- *
- * Property HTTP Header
- * ------------------------------------------------
- * adapter.generator.id generatorId
- * env.application.key applicationKey
- * env.userToken authenticatedUser
- * env.mediaType Content-Type, Accept
- * adapter.mustUseAdvisoryIDs mustUseAdvisory
- * adapter.compression.enabled Content-Encoding, Accept-Encoding
- *
- * Only properties that are not null or empty string will be set in the corresponding HTTP Header.
- *
- * There are situations where and application may need a more 'dynamic' behaviour where the above values are determined
- * at runtime, based on other circumstances and therefore these properties must be retrieved from an other source than the
- * consumer's property file. In such a case the methods below can be overwritten to make them dynamic and controlled by
- * the implementation rather than driven by the consumer's property file. If any of the methods below is overwritten then
- * the value of the over riding method is set in the corresponding HTTP Header field if the return value of the method
- * is not null or an empty string.
- *------------------------------------------------------------------------------------------------------------------------*/
+// /*------------------------------------------------------------------------------------------------------------------------
+// * Start of 'Dynamic' HTTP Header Field override section
+// *
+// * The following set of methods are used for a more configurable way how some HTTP header parameters are set.
+// * By default the following HTTP Header fields are retrieved from the consumer's property file and put in corresponding
+// * HTTP Header Fields:
+// *
+// * Property HTTP Header
+// * ------------------------------------------------
+// * adapter.generator.id generatorId
+// * env.application.key applicationKey
+// * env.userToken authenticatedUser
+// * env.mediaType Content-Type, Accept
+// * adapter.mustUseAdvisoryIDs mustUseAdvisory
+// * adapter.compression.enabled Content-Encoding, Accept-Encoding
+// *
+// * Only properties that are not null or empty string will be set in the corresponding HTTP Header.
+// *
+// * There are situations where and application may need a more 'dynamic' behaviour where the above values are determined
+// * at runtime, based on other circumstances and therefore these properties must be retrieved from an other source than the
+// * consumer's property file. In such a case the methods below can be overwritten to make them dynamic and controlled by
+// * the implementation rather than driven by the consumer's property file. If any of the methods below is overwritten then
+// * the value of the over riding method is set in the corresponding HTTP Header field if the return value of the method
+// * is not null or an empty string.
+// *------------------------------------------------------------------------------------------------------------------------*/
+//
+// /**
+// * This method returns the value of the adapter.generator.id property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The adapter.generator.id property from the consumer's property file
+// */
+// public String getGeneratorID()
+// {
+// return getConsumerEnvironment().getGeneratorID();
+// }
+//
+// /**
+// * This method returns the value of the env.application.key property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The env.application.key property from the consumer's property file
+// */
+// public String getApplicationKey()
+// {
+// return getConsumerEnvironment().getEnvironmentKey().getApplicationKey();
+// }
+//
+// /**
+// * This method returns the value of the env.userToken property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The env.userToken property from the consumer's property file
+// */
+// public String getAuthentictedUser()
+// {
+// return getConsumerEnvironment().getEnvironmentKey().getUserToken();
+// }
+//
+// /**
+// * This method returns the value of the env.mediaType property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The env.mediaType property from the consumer's property file
+// */
+// public MediaType getRequestMediaType()
+// {
+// return getConsumerEnvironment().getMediaType();
+// }
+//
+// /**
+// * This method returns the value of the env.mediaType property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The env.mediaType property from the consumer's property file
+// */
+// public MediaType getResponseMediaType()
+// {
+// return getConsumerEnvironment().getMediaType();
+// }
+//
+// /**
+// * This method returns the value of the adapter.mustUseAdvisoryIDs property from the consumer's property file. If that
+// * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The adapter.mustUseAdvisoryIDs property from the consumer's property file
+// */
+// public boolean getMustUseAdvisory()
+// {
+// return getConsumerEnvironment().getUseAdvisory();
+// }
+//
+// /**
+// * This method returns the value of the adapter.compression.enabled property from the consumer's property file. If
+// * that needs to be overridden by a specific implementation then the specific sub-class should override this method.
+// *
+// * @return The adapter.compression.enabled property from the consumer's property file
+// */
+// public boolean getCompressionEnabled()
+// {
+// return getConsumerEnvironment().getCompressionEnabled();
+// }
+//
+// /*------------------------------------------------------------------------------------------------------------------------
+// * End of 'Dynamic' HTTP Header Field override section
+// *-----------------------------------------------------------------------------------------------------------------------*/
+
+// /**
+// * @return Returns the actual Class Name of this consumer
+// */
+// public String getConsumerName()
+// {
+// return getClass().getSimpleName();
+// }
- /**
- * This method returns the value of the adapter.generator.id property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The adapter.generator.id property from the consumer's property file
- */
- public String getGeneratorID()
- {
- return getConsumerEnvironment().getGeneratorID();
- }
+// /**
+// * @return Returns the Service Name.
+// */
+// public String getServiceName()
+// {
+// return getMultiObjectClassInfo().getObjectName();
+// }
- /**
- * This method returns the value of the env.application.key property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.application.key property from the consumer's property file
- */
- public String getApplicationKey()
- {
- return getConsumerEnvironment().getEnvironmentKey().getApplicationKey();
- }
-
- /**
- * This method returns the value of the env.userToken property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.userToken property from the consumer's property file
- */
- public String getAuthentictedUser()
- {
- return getConsumerEnvironment().getEnvironmentKey().getUserToken();
- }
-
- /**
- * This method returns the value of the env.mediaType property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.mediaType property from the consumer's property file
- */
- public MediaType getRequestMediaType()
- {
- return getConsumerEnvironment().getMediaType();
- }
-
- /**
- * This method returns the value of the env.mediaType property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.mediaType property from the consumer's property file
- */
- public MediaType getResponseMediaType()
- {
- return getConsumerEnvironment().getMediaType();
- }
-
- /**
- * This method returns the value of the adapter.mustUseAdvisoryIDs property from the consumer's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The adapter.mustUseAdvisoryIDs property from the consumer's property file
- */
- public boolean getMustUseAdvisory()
- {
- return getConsumerEnvironment().getUseAdvisory();
- }
-
- /**
- * This method returns the value of the adapter.compression.enabled property from the consumer's property file. If
- * that needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The adapter.compression.enabled property from the consumer's property file
- */
- public boolean getCompressionEnabled()
- {
- return getConsumerEnvironment().getCompressionEnabled();
- }
-
- /*------------------------------------------------------------------------------------------------------------------------
- * End of 'Dynamic' HTTP Header Field override section
- *-----------------------------------------------------------------------------------------------------------------------*/
-
- /**
- * @return Returns the actual Class Name of this consumer
- */
- public String getConsumerName()
- {
- return getClass().getSimpleName();
- }
-
- /**
- * @return Returns the Service Name.
- */
- public String getServiceName()
- {
- return getMultiObjectClassInfo().getObjectName();
- }
-
- /**
- * Utility method. Mainly used for useful logging messages.
- *
- * @return Returns the Adapter Name as defined in the adapter.id property of the consumer property file concatenated with the
- * Consumer Name (class name)
- */
- public String getPrettyName()
- {
- return getConsumerEnvironment().getAdapterName()+" - " + getConsumerName();
- }
-
- /*------------------------------*/
- /* Some Getter & Setter methods */
- /*------------------------------*/
-
- public final LocalConsumerQueue getLocalConsumerQueue()
- {
- return localConsumerQueue;
- }
-
- public final void setLocalConsumerQueue(LocalConsumerQueue localConsumerQueue)
- {
- this.localConsumerQueue = localConsumerQueue;
- }
+// /**
+// * Utility method. Mainly used for useful logging messages.
+// *
+// * @return Returns the Adapter Name as defined in the adapter.id property of the consumer property file concatenated with the
+// * Consumer Name (class name)
+// */
+// public String getPrettyName()
+// {
+// return getConsumerEnvironment().getAdapterName()+" - " + getConsumerName();
+// }
+
+// /*------------------------------*/
+// /* Some Getter & Setter methods */
+// /*------------------------------*/
+//
+// public final LocalConsumerQueue getLocalConsumerQueue()
+// {
+// return localConsumerQueue;
+// }
+//
+// public final void setLocalConsumerQueue(LocalConsumerQueue localConsumerQueue)
+// {
+// this.localConsumerQueue = localConsumerQueue;
+// }
/*-----------------------*/
/*-- Create Operations --*/
@@ -695,7 +650,7 @@ public List retrievByPrimaryKey(String resourceID, List retrieve(PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters) throws PersistenceException, UnsupportedQueryException, ServiceInvokationException
+ public List retrieve(PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters) throws PersistenceException, ServiceInvokationException
{
nullMethodCheck(getMultiObjectClassInfo(), "getMultiObjectClassInfo()");
/*
@@ -716,14 +671,11 @@ public List retrieve(PagingInfo pagingInfo, List zone
return responses;
}
- // Ensure query Intention is not null. if so default to ONE-OFF as per SIF 3.x spec.
- queryIntention = (queryIntention == null) ? QueryIntention.ONE_OFF : queryIntention;
-
// Set default set of HTTP Header fields
HeaderProperties hdrProps = getHeaderProperties(false, customParameters);
- // Add query intention to headers.
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_QUERY_INTENTION, queryIntention.getHTTPHeaderValue());
+ // Add query intention to headers.
+ addQueryIntentionToHeaders(hdrProps, queryIntention);
List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
@@ -764,7 +716,7 @@ public List retrieve(PagingInfo pagingInfo, List zone
* (non-Javadoc)
* @see sif3.common.interfaces.QueryConsumer#retrieveByServicePath(sif3.common.model.QueryCriteria, sif3.common.model.PagingInfo, java.util.List, sif3.common.header.HeaderValues.RequestType, sif3.common.header.HeaderValues.QueryIntention, sif3.common.model.CustomParameters)
*/
- public List retrieveByServicePath(QueryCriteria queryCriteria, PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters) throws PersistenceException, UnsupportedQueryException, ServiceInvokationException
+ public List retrieveByServicePath(QueryCriteria queryCriteria, PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters) throws PersistenceException, ServiceInvokationException
{
nullMethodCheck(getMultiObjectClassInfo(), "getMultiObjectClassInfo()");
/*
@@ -802,7 +754,7 @@ public List retrieveByServicePath(QueryCriteria queryCriteria, PagingI
// Request operation in all zone/contexts as listed.
for (ZoneContextInfo zoneCtx : finalZoneContextList)
{
- ErrorDetails error = allClientChecks(serviceName, AccessRight.QUERY, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+ ErrorDetails error = allClientChecks(serviceName, null, AccessRight.QUERY, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
if (error == null) //all good => Send request
{
@@ -842,7 +794,7 @@ public List retrieveByQBE(Object exampleObject,
List zoneCtxList,
RequestType requestType,
QueryIntention queryIntention,
- CustomParameters customParameters) throws PersistenceException, UnsupportedQueryException, ServiceInvokationException
+ CustomParameters customParameters) throws PersistenceException, ServiceInvokationException
{
nullMethodCheck(getMultiObjectClassInfo(), "getMultiObjectClassInfo()");
/*
@@ -1030,18 +982,17 @@ public List updateSingle(Object data, String resourceID, List getServiceInfo(PagingInfo pagingInfo, List zoneCtxList, CustomParameters customParameters) throws PersistenceException, UnsupportedQueryException, ServiceInvokationException
+ public List getServiceInfo(PagingInfo pagingInfo, List zoneCtxList, CustomParameters customParameters) throws PersistenceException, ServiceInvokationException
{
nullMethodCheck(getMultiObjectClassInfo(), "getMultiObjectClassInfo()");
/*
@@ -1169,45 +1120,45 @@ public void onError(ErrorDetails error, DelayedResponseReceipt receipt)
*
* @return See desc.
*/
- public final LocalConsumerQueue createLocalConsumerQueue()
- {
- if (getLocalConsumerQueue() == null)
- {
- // Create local queue with the capacity indicated with the consumer config
- logger.debug("Create Local Queue for "+getConsumerName());
-
- // Use the local queue as a trigger of threads rather than actual queueing of messages. Use 1 as the minimum
- setLocalConsumerQueue(new LocalConsumerQueue(1, getClass().getSimpleName() + "LocalQueue", getClass().getSimpleName()));
- startListenerThreads();
- }
- return getLocalConsumerQueue();
- }
+// public final LocalConsumerQueue createLocalConsumerQueue()
+// {
+// if (getLocalConsumerQueue() == null)
+// {
+// // Create local queue with the capacity indicated with the consumer config
+// logger.debug("Create Local Queue for "+getConsumerName());
+//
+// // Use the local queue as a trigger of threads rather than actual queueing of messages. Use 1 as the minimum
+// setLocalConsumerQueue(new LocalConsumerQueue(1, getClass().getSimpleName() + "LocalQueue", getClass().getSimpleName()));
+// startListenerThreads();
+// }
+// return getLocalConsumerQueue();
+// }
/*
* Will initialise the threads and add them to the local consumer queue.
*/
- private void startListenerThreads()
- {
- // Start up all consumers for this subscriber.
- int numThreads = getNumOfConsumerThreads();
- logger.debug("Start "+numThreads+" "+getConsumerName()+" threads.");
- logger.debug("Total number of threads before starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
- service = Executors.newFixedThreadPool(numThreads);
- for (int i = 0; i < numThreads; i++)
- {
- String consumerID = getConsumerName()+" "+(i+1);
- logger.debug("Start Consumer "+consumerID);
- LocalMessageConsumer consumer = new LocalMessageConsumer(getLocalConsumerQueue(), consumerID, this);
- service.execute(consumer);
- }
- logger.debug(numThreads+" "+getConsumerName()+" initilaised and started.");
- logger.debug("Total number of threads after starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
- }
-
- private final int getNumOfConsumerThreads()
- {
- return getServiceProperties().getPropertyAsInt("consumer.local.workerThread", getClass().getSimpleName(), 1);
- }
+// private void startListenerThreads()
+// {
+// // Start up all consumers for this subscriber.
+// int numThreads = getNumOfConsumerThreads();
+// logger.debug("Start "+numThreads+" "+getConsumerName()+" threads.");
+// logger.debug("Total number of threads before starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
+// service = Executors.newFixedThreadPool(numThreads);
+// for (int i = 0; i < numThreads; i++)
+// {
+// String consumerID = getConsumerName()+" "+(i+1);
+// logger.debug("Start Consumer "+consumerID);
+// LocalMessageConsumer consumer = new LocalMessageConsumer(getLocalConsumerQueue(), consumerID, this);
+// service.execute(consumer);
+// }
+// logger.debug(numThreads+" "+getConsumerName()+" initilaised and started.");
+// logger.debug("Total number of threads after starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
+// }
+
+// private final int getNumOfConsumerThreads()
+// {
+// return getServiceProperties().getPropertyAsInt("consumer.local.workerThread", getClass().getSimpleName(), 1);
+// }
/*----------------------------*/
/*-- Other required methods --*/
@@ -1237,24 +1188,32 @@ public List filterApprovedCRUDServices(List allService
*/
protected final List getAllApprovedCRUDServices()
{
- SIF3Session sif3Session = ConsumerEnvironmentManager.getInstance().getSIF3Session();
- List allServices = new ArrayList();
-
- // Get OBJECT Services
- List services = sif3Session.getServiceInfoForService(getMultiObjectClassInfo().getObjectName(), ServiceType.OBJECT);
- for (ServiceInfo serviceInfo : services)
- {
- if (serviceInfo.getRights().hasRight(AccessRight.CREATE, AccessType.APPROVED) ||
- serviceInfo.getRights().hasRight(AccessRight.UPDATE, AccessType.APPROVED) ||
- serviceInfo.getRights().hasRight(AccessRight.DELETE, AccessType.APPROVED) ||
- serviceInfo.getRights().hasRight(AccessRight.QUERY, AccessType.APPROVED) )
- {
- allServices.add(serviceInfo);
- }
- }
-
- // Now get SERVICEPATHs. They are only valid for QUERY permissions. No events or other types.
- allServices.addAll(sif3Session.getServiceInfoForService(getMultiObjectClassInfo().getObjectName(), ServiceType.SERVICEPATH, AccessRight.QUERY, AccessType.APPROVED));
+ List allServices = new ArrayList();
+
+ // Get all Object Services
+ allServices.addAll(getAllApprovedServicesForRights(getMultiObjectClassInfo().getObjectName(), ServiceType.OBJECT, AccessRight.CREATE, AccessRight.UPDATE, AccessRight.DELETE, AccessRight.QUERY));
+
+ // Get all ServicePath Services
+ allServices.addAll(getAllApprovedServicesForRights(getMultiObjectClassInfo().getObjectName(), ServiceType.SERVICEPATH, AccessRight.QUERY));
+
+// SIF3Session sif3Session = ConsumerEnvironmentManager.getInstance().getSIF3Session();
+// List allServices = new ArrayList();
+//
+// // Get OBJECT Services
+// List services = sif3Session.getServiceInfoForService(getMultiObjectClassInfo().getObjectName(), ServiceType.OBJECT);
+// for (ServiceInfo serviceInfo : services)
+// {
+// if (serviceInfo.getRights().hasRight(AccessRight.CREATE, AccessType.APPROVED) ||
+// serviceInfo.getRights().hasRight(AccessRight.UPDATE, AccessType.APPROVED) ||
+// serviceInfo.getRights().hasRight(AccessRight.DELETE, AccessType.APPROVED) ||
+// serviceInfo.getRights().hasRight(AccessRight.QUERY, AccessType.APPROVED) )
+// {
+// allServices.add(serviceInfo);
+// }
+// }
+//
+// // Now get SERVICEPATHs. They are only valid for QUERY permissions. No events or other types.
+// allServices.addAll(sif3Session.getServiceInfoForService(getMultiObjectClassInfo().getObjectName(), ServiceType.SERVICEPATH, AccessRight.QUERY, AccessType.APPROVED));
return filterApprovedCRUDServices(allServices);
}
@@ -1286,53 +1245,53 @@ private ObjectServiceClient getClient(ConsumerEnvironment envInfo) throws Illega
}
}
- private SIF3Session getSIF3Session()
- {
- return ConsumerEnvironmentManager.getInstance().getSIF3Session();
- }
-
- private HeaderProperties getHeaderProperties(boolean isCreateOperation, HeaderValues.ServiceType serviceType, CustomParameters customParameters)
- {
- HeaderProperties hdrProps = new HeaderProperties();
-
- // First we add all Custom HTTP Headers. We add SIF defined HTTP header later. This will also ensure that we
- // will override custom properties with SIF defined properties.
- if ((customParameters != null) && (customParameters.getHttpHeaderParams() != null))
- {
- hdrProps = customParameters.getHttpHeaderParams();
- }
-
- // Now we set SIF defined HTTP headers...
-
- // Set the remaining header fields for this type of request
- if (isCreateOperation)
- {
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, (getMustUseAdvisory() ? "true" : "false"));
- }
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, serviceType.name());
-
- // Set values of consumer property file or their overridden value. Note thsetHeaderProperty() method will do the check
- // for null, so no need to do this here.
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_APPLICATION_KEY, getApplicationKey());
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_AUTHENTICATED_USER, getAuthentictedUser());
- hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_GENERATOR_ID, getGeneratorID());
-
- return hdrProps;
- }
+// private SIF3Session getSIF3Session()
+// {
+// return ConsumerEnvironmentManager.getInstance().getSIF3Session();
+// }
+
+// private HeaderProperties getHeaderProperties(boolean isCreateOperation, HeaderValues.ServiceType serviceType, CustomParameters customParameters)
+// {
+// HeaderProperties hdrProps = new HeaderProperties();
+//
+// // First we add all Custom HTTP Headers. We add SIF defined HTTP header later. This will also ensure that we
+// // will override custom properties with SIF defined properties.
+// if ((customParameters != null) && (customParameters.getHttpHeaderParams() != null))
+// {
+// hdrProps = customParameters.getHttpHeaderParams();
+// }
+//
+// // Now we set SIF defined HTTP headers...
+//
+// // Set the remaining header fields for this type of request
+// if (isCreateOperation)
+// {
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, (getMustUseAdvisory() ? "true" : "false"));
+// }
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, serviceType.name());
+//
+// // Set values of consumer property file or their overridden value. Note thsetHeaderProperty() method will do the check
+// // for null, so no need to do this here.
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_APPLICATION_KEY, getApplicationKey());
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_AUTHENTICATED_USER, getAuthentictedUser());
+// hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_GENERATOR_ID, getGeneratorID());
+//
+// return hdrProps;
+// }
private HeaderProperties getHeaderProperties(boolean isCreateOperation, CustomParameters customParameters)
{
- return getHeaderProperties(isCreateOperation, HeaderValues.ServiceType.OBJECT, customParameters);
+ return getHeaderProperties(isCreateOperation, ServiceType.OBJECT, customParameters);
}
- private void setErrorDetails(BaseResponse response, ErrorDetails errorDetails)
- {
- response.setStatus(errorDetails.getErrorCode());
- response.setStatusMessage(errorDetails.getMessage());
- response.setError(errorDetails);
- response.setContentLength(0);
- response.setHasEntity(false);
- }
+// private void setErrorDetails(BaseResponse response, ErrorDetails errorDetails)
+// {
+// response.setStatus(errorDetails.getErrorCode());
+// response.setStatusMessage(errorDetails.getMessage());
+// response.setError(errorDetails);
+// response.setContentLength(0);
+// response.setHasEntity(false);
+// }
/*
* Will perform hasAccess() and requestTypeSupported() checks. This is a convenience method, so that not each operation has to
@@ -1340,65 +1299,65 @@ private void setErrorDetails(BaseResponse response, ErrorDetails errorDetails)
*/
private ErrorDetails allClientChecks(AccessRight right, AccessType accessType, SIFZone zone, SIFContext context, RequestType requestType)
{
- return allClientChecks(getMultiObjectClassInfo().getObjectName(), right, accessType, zone, context, requestType);
- }
-
- private ErrorDetails allClientChecks(String serviceName, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context, RequestType requestType)
- {
- ErrorDetails error = hasAccess(serviceName, right, accessType, zone, context);
- if ((error == null) && (requestType != null))
- {
- error = requestTypeEnabled(requestType);
- }
- return error;
- }
-
- private ErrorDetails hasAccess(String serviceName, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context)
- {
- ErrorDetails error = null;
- if (checkACL)
- {
- if (!getSIF3Session().hasAccess(right, accessType, serviceName, zone, context))
- {
- String zoneID = (zone == null) ? "Default" : zone.getId();
- String contextID = (context == null) ? "Default" : context.getId();
- error = new ErrorDetails(Status.FORBIDDEN.getStatusCode(), "Consumer is not authorized to issue the requested operation.", right.name()+ " access is not set to "+accessType.name()+" for the service " + serviceName +" and the given zone ("+zoneID+") and context ("+contextID+") in the environment "+getSIF3Session().getEnvironmentName(), "Client side check.");
- }
- }
- return error;
+ return allClientChecks(getMultiObjectClassInfo().getObjectName(), null, right, accessType, zone, context, requestType);
}
- private ErrorDetails requestTypeEnabled(RequestType requestType)
- {
- ErrorDetails error = null;
+// private ErrorDetails allClientChecks(String serviceName, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context, RequestType requestType)
+// {
+// ErrorDetails error = hasAccess(serviceName, right, accessType, zone, context);
+// if ((error == null) && (requestType != null))
+// {
+// error = requestTypeEnabled(requestType);
+// }
+// return error;
+// }
- if ((requestType == RequestType.DELAYED) && (!getConsumerEnvironment().getDelayedEnabled()))
- {
- error = new ErrorDetails(Status.BAD_REQUEST.getStatusCode(), "Client side Check: DELAYED requests are not enabled.");
- }
- return error;
- }
-
- private Response createErrorResponse(ErrorDetails error)
- {
- Response response = new Response();
- setErrorDetails(response, error);
- return response;
- }
+// private ErrorDetails hasAccess(String serviceName, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context)
+// {
+// ErrorDetails error = null;
+// if (checkACL)
+// {
+// if (!getSIF3Session().hasAccess(right, accessType, serviceName, null, zone, context))
+// {
+// String zoneID = (zone == null) ? "Default" : zone.getId();
+// String contextID = (context == null) ? "Default" : context.getId();
+// error = new ErrorDetails(Status.FORBIDDEN.getStatusCode(), "Consumer is not authorized to issue the requested operation.", right.name()+ " access is not set to "+accessType.name()+" for the service " + serviceName +" and the given zone ("+zoneID+") and context ("+contextID+") in the environment "+getSIF3Session().getEnvironmentName(), "Client side check.");
+// }
+// }
+// return error;
+// }
- private BulkOperationResponse makeBulkErrorResponseForCreates(ErrorDetails error)
- {
- BulkOperationResponse response = new BulkOperationResponse();
- setErrorDetails(response, error);
- return response;
- }
+// private ErrorDetails requestTypeEnabled(RequestType requestType)
+// {
+// ErrorDetails error = null;
+//
+// if ((requestType == RequestType.DELAYED) && (!getConsumerEnvironment().getDelayedEnabled()))
+// {
+// error = new ErrorDetails(Status.BAD_REQUEST.getStatusCode(), "Client side Check: DELAYED requests are not enabled.");
+// }
+// return error;
+// }
+
+// private Response createErrorResponse(ErrorDetails error)
+// {
+// Response response = new Response();
+// setErrorDetails(response, error);
+// return response;
+// }
- private BulkOperationResponse makeBulkErrorResponse(ErrorDetails error)
- {
- BulkOperationResponse response = new BulkOperationResponse();
- setErrorDetails(response, error);
- return response;
- }
+// private BulkOperationResponse makeBulkErrorResponseForCreates(ErrorDetails error)
+// {
+// BulkOperationResponse response = new BulkOperationResponse();
+// setErrorDetails(response, error);
+// return response;
+// }
+//
+// private BulkOperationResponse makeBulkErrorResponse(ErrorDetails error)
+// {
+// BulkOperationResponse response = new BulkOperationResponse();
+// setErrorDetails(response, error);
+// return response;
+// }
private String getServiceName(QueryCriteria queryCriteria)
{
@@ -1430,85 +1389,80 @@ private String getServicePath(QueryCriteria queryCriteria)
return result;
}
-// @SuppressWarnings("unused")
- private List getFinalZoneCtxList( List zoneCtxList, SIF3Session sif3Session)
- {
- List finalZoneContextList = null;
-
- if (zoneCtxList == null)
- {
- finalZoneContextList = new ArrayList();
- }
- else
- {
- finalZoneContextList = zoneCtxList;
- }
-
- if (finalZoneContextList.size() == 0) //add default context and zone
- {
- // Set zone and context to null which will ensure that the matrix params won't be set and therefore the provider will assume default context & zone
- finalZoneContextList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null));
-
-// finalZoneContextList.add(new ZoneContextInfo(new SIFZone(sif3Session.getDefaultZone().getId(), true), new SIFContext(CommonConstants.DEFAULT_CONTEXT_NAME, true)));
- }
-
- // Check all entries and if 'null' is used as zone or context then we assign the default.
- for (ZoneContextInfo zoneCtxInfo : finalZoneContextList)
- {
- // If zone or zone ID is null then we set the default zone.
- if ((zoneCtxInfo.getZone() == null) || StringUtils.isEmpty(zoneCtxInfo.getZone().getId()))
- {
- zoneCtxInfo.setZone(null); // won't set matrix parameter which means default zone
-// zoneCtxInfo.setZone(new SIFZone(sif3Session.getDefaultZone().getId(), true));
- }
- // If context or context ID is null then we set the default zone.
- if ((zoneCtxInfo.getContext() == null) || StringUtils.isEmpty(zoneCtxInfo.getContext().getId()))
- {
- zoneCtxInfo.setContext(null); // won't set matrix parameter which means default context
-// zoneCtxInfo.setContext(new SIFContext(CommonConstants.DEFAULT_CONTEXT_NAME, true));
- }
- }
-
- return finalZoneContextList;
- }
+// private List getFinalZoneCtxList( List zoneCtxList, SIF3Session sif3Session)
+// {
+// List finalZoneContextList = null;
+//
+// if (zoneCtxList == null)
+// {
+// finalZoneContextList = new ArrayList();
+// }
+// else
+// {
+// finalZoneContextList = zoneCtxList;
+// }
+//
+// if (finalZoneContextList.size() == 0) //add default context and zone
+// {
+// // Set zone and context to null which will ensure that the matrix params won't be set and therefore the provider will assume default context & zone
+// finalZoneContextList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null));
+// }
+//
+// // Check all entries and if 'null' is used as zone or context then we assign the default.
+// for (ZoneContextInfo zoneCtxInfo : finalZoneContextList)
+// {
+// // If zone or zone ID is null then we set the default zone.
+// if ((zoneCtxInfo.getZone() == null) || StringUtils.isEmpty(zoneCtxInfo.getZone().getId()))
+// {
+// zoneCtxInfo.setZone(null); // won't set matrix parameter which means default zone
+// }
+// // If context or context ID is null then we set the default zone.
+// if ((zoneCtxInfo.getContext() == null) || StringUtils.isEmpty(zoneCtxInfo.getContext().getId()))
+// {
+// zoneCtxInfo.setContext(null); // won't set matrix parameter which means default context
+// }
+// }
+//
+// return finalZoneContextList;
+// }
/*
* This method sets the remaining properties in the receipt for delayed responses. There are a few fields that cannot be set at the ObjectServiceClient as
* they are not known or cannot be determined in there but are well known in the abstract consumer.
*/
- private void finaliseDelayedReceipt(DelayedRequestReceipt delayedReceipt, String serviceName, ServiceType serviceType, ResponseAction requestedAction)
- {
- if (delayedReceipt != null)
- {
- //delayedReceipt.setRequestDate(requestDate);
- delayedReceipt.setServiceName(serviceName);
- delayedReceipt.setServiceType(serviceType);
- delayedReceipt.setRequestedAction(requestedAction);
- }
- }
+// private void finaliseDelayedReceipt(DelayedRequestReceipt delayedReceipt, String serviceName, ServiceType serviceType, ResponseAction requestedAction)
+// {
+// if (delayedReceipt != null)
+// {
+// //delayedReceipt.setRequestDate(requestDate);
+// delayedReceipt.setServiceName(serviceName);
+// delayedReceipt.setServiceType(serviceType);
+// delayedReceipt.setRequestedAction(requestedAction);
+// }
+// }
- private void nullMethodCheck(Object objectToCheck, String methodName) throws IllegalArgumentException
- {
- if (objectToCheck == null)
- {
- throw new IllegalArgumentException(methodName+" method not implemented correctly. Returns null which is invalid.");
- }
- }
+// private void nullMethodCheck(Object objectToCheck, String methodName) throws IllegalArgumentException
+// {
+// if (objectToCheck == null)
+// {
+// throw new IllegalArgumentException(methodName+" method not implemented correctly. Returns null which is invalid.");
+// }
+// }
/*
* This method checks if the test.testmode in the consumer's property file is set to TRUE.
*/
- @SuppressWarnings("unused")
- private boolean isTestMode()
- {
- if (testMode == null)
- {
- AdvancedProperties props = getServiceProperties();
- testMode = props.getPropertyAsBool("test.testmode", false);
- }
- return testMode;
- }
+// @SuppressWarnings("unused")
+// private boolean isTestMode()
+// {
+// if (testMode == null)
+// {
+// AdvancedProperties props = getServiceProperties();
+// testMode = props.getPropertyAsBool("test.testmode", false);
+// }
+// return testMode;
+// }
}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractEventConsumer.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractEventConsumer.java
index 4c3daab2..c516c77e 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractEventConsumer.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractEventConsumer.java
@@ -22,13 +22,10 @@
import sif3.common.header.HeaderValues.ServiceType;
import sif3.common.interfaces.EventConsumer;
+import sif3.common.model.ACL.AccessRight;
import sif3.common.model.EventMetadata;
import sif3.common.model.SIFEvent;
import sif3.common.model.ServiceInfo;
-import sif3.common.model.ServiceRights;
-import sif3.common.model.ServiceRights.AccessRight;
-import sif3.common.persist.model.SIF3Session;
-import sif3.infra.common.env.mgr.ConsumerEnvironmentManager;
/**
* This is the core class that a developer will use to implement for a consumer that shall subscribe to events. Each consumer for each object
@@ -69,11 +66,8 @@ public AbstractEventConsumer()
* @see sif3.common.interfaces.EventConsumer#onEvent(sif3.common.model.SIFEvent, sif3.common.model.EventMetadata, java.lang.String, java.lang.String)
*/
@Override
-// public void onEvent(SIFEvent sifEvent, SIFZone zone, SIFContext context, EventMetadata metadata, String msgReadID, String consumerID)
public void onEvent(SIFEvent sifEvent, EventMetadata metadata, String msgReadID, String consumerID)
{
- // Right now all that is required is to call the abstract processEvent() method.
-// processEvent(sifEvent, zone, context, metadata, msgReadID, consumerID);
processEvent(sifEvent, metadata, msgReadID, consumerID);
}
@@ -129,11 +123,12 @@ public List filterEventServices(List envEventServices)
*/
protected final List getEventServices()
{
- SIF3Session sif3Session = ConsumerEnvironmentManager.getInstance().getSIF3Session();
- return filterEventServices(sif3Session.getServiceInfoForService(getMultiObjectClassInfo().getObjectName(), ServiceType.OBJECT, AccessRight.SUBSCRIBE, ServiceRights.AccessType.APPROVED));
+ List eventServices = getAllApprovedServicesForRights(getMultiObjectClassInfo().getObjectName(), ServiceType.OBJECT, AccessRight.SUBSCRIBE);
+
+ return filterEventServices(eventServices);
}
/*---------------------*/
/*-- Private Methods --*/
/*---------------------*/
- }
+}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractFunctionalServiceConsumer.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractFunctionalServiceConsumer.java
new file mode 100644
index 00000000..9117605b
--- /dev/null
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/AbstractFunctionalServiceConsumer.java
@@ -0,0 +1,1265 @@
+/*
+ * AbstractFunctionalServiceConsumer.java
+ * Created: 13 Jul 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package sif3.infra.rest.consumer;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import au.com.systemic.framework.utils.StringUtils;
+import au.com.systemic.framework.utils.Timer;
+import sif3.common.CommonConstants.PhaseState;
+import sif3.common.conversion.MarshalFactory;
+import sif3.common.conversion.ModelObjectInfo;
+import sif3.common.conversion.UnmarshalFactory;
+import sif3.common.exception.PersistenceException;
+import sif3.common.exception.ServiceInvokationException;
+import sif3.common.header.HeaderProperties;
+import sif3.common.header.HeaderValues.EventAction;
+import sif3.common.header.HeaderValues.QueryIntention;
+import sif3.common.header.HeaderValues.RequestType;
+import sif3.common.header.HeaderValues.ResponseAction;
+import sif3.common.header.HeaderValues.ServiceType;
+import sif3.common.header.HeaderValues.UpdateType;
+import sif3.common.interfaces.DelayedConsumer;
+import sif3.common.interfaces.EventConsumer;
+import sif3.common.interfaces.FunctionalServiceConsumer;
+import sif3.common.model.ACL.AccessRight;
+import sif3.common.model.ACL.AccessType;
+import sif3.common.model.CustomParameters;
+import sif3.common.model.EventMetadata;
+import sif3.common.model.PagingInfo;
+import sif3.common.model.QueryCriteria;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFEvent;
+import sif3.common.model.SIFZone;
+import sif3.common.model.ServiceInfo;
+import sif3.common.model.URLQueryParameter;
+import sif3.common.model.ZoneContextInfo;
+import sif3.common.model.delayed.DelayedResponseReceipt;
+import sif3.common.model.job.JobCreateRequestParameter;
+import sif3.common.model.job.PhaseInfo;
+import sif3.common.ws.BulkOperationResponse;
+import sif3.common.ws.CreateOperationStatus;
+import sif3.common.ws.ErrorDetails;
+import sif3.common.ws.OperationStatus;
+import sif3.common.ws.Response;
+import sif3.common.ws.job.PhaseDataRequest;
+import sif3.common.ws.job.PhaseDataResponse;
+import sif3.common.ws.model.MultiOperationStatusList;
+import sif3.infra.common.conversion.InfraMarshalFactory;
+import sif3.infra.common.conversion.InfraUnmarshalFactory;
+import sif3.infra.common.env.mgr.ConsumerEnvironmentManager;
+import sif3.infra.common.env.types.ConsumerEnvironment;
+import sif3.infra.common.model.JobCollectionType;
+import sif3.infra.common.model.JobType;
+import sif3.infra.rest.client.JobClient;
+
+/**
+ * This is the core class that a developer will use to implement their functional service consumers. Each consumer for each functional
+ * service MUST extend this class. It forms the link between the high level consumer implementation and the low level infrastructure
+ * functions which this class abstracts.
+ * This class has some similarities to the Object service classes (AbstractConsumer and AbstarctEventConsumer). However it adds
+ * required functional service operations such a phase and state functions that are only applicable for functional service. Since
+ * functional services operate on the Job Object a number of assumptions can be made that aren't valid for the abstract consumer that
+ * works with SIF3 DM objects. This class makes use of the knowledge of the Job Object and therefore can implement a number of
+ * methods that are otherwise left to the developer (i.e. Eventing, Changes Since etc).
+ * Functional Services have a number of methods that are specific for them such as phase operations. These methods are stubbed out
+ * in this implementations similar to CRUD operations of the abstract consumer that works with Object Services.
+ * It is assumed that the ConsumerLoader.initialise() method has been called before any methods of this class are called.If not then
+ * the behaviour of this class is not defined. In fact each call to any method of this class will first test if initialisation has
+ * succeeded and no action in any of the top level methods will be executed if the ConsumerLoader.initialise() hasn't been called before.
+ *
+ * @author Joerg Huber
+ */
+public abstract class AbstractFunctionalServiceConsumer extends BaseConsumer implements FunctionalServiceConsumer, DelayedConsumer, EventConsumer, Runnable
+{
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ public static final ModelObjectInfo JOBS_INFO = new ModelObjectInfo("jobs", JobCollectionType.class);
+ public static final ModelObjectInfo JOB_INFO = new ModelObjectInfo("job", JobType.class);
+
+ private MarshalFactory marshaller = new InfraMarshalFactory();
+ private UnmarshalFactory unmarshaller = new InfraUnmarshalFactory();
+
+ /*--------------------------------------------------------------------*/
+ /* Abstract method relating to Functional Service Type functionality. */
+ /*--------------------------------------------------------------------*/
+ /**
+ * Must return the name of the Functional Service. This must match the value of an entry in the
+ * SIF3_JOB_TEMPLATE with the JOB_URL_NAME column. It is also the equivalent to the corresponding segment in
+ * in the Functional Service URL (eg. .../serviceConnector/jobs/).
+ *
+ * Example
+ * https://sif3hub.edu.au/services/jobs/endofyearrollovers
+ *
+ * @return A not null or empty value. The name of the functional service in its plural form.
+ */
+ public abstract String getServiceURLNamePlural();
+
+ /**
+ * Must return the name of the Functional Service. This must match the value of an entry in the
+ * SIF3_JOB_TEMPLATE with the JOB_URL_NAME column. It is also the equivalent to the corresponding segment in
+ * in the Functional Service URL (eg. .../serviceConnector/jobs//).
+ *
+ * Example
+ * https://sif3hub.edu.au/services/jobs/endofyearrollovers/endofyearrollover (note there is no 's' in the singular form)
+ *
+ * @return A not null or empty value. The name of the functional service in its singular form.
+ */
+ public abstract String getServiceURLNameSingular();
+
+ /*---------------------------------------------------------------------*/
+ /* Abstract method relating to DELAYED request response functionality. */
+ /*---------------------------------------------------------------------*/
+
+ /*----------------------------*/
+ /*-- Delayed Job Operations --*/
+ /*----------------------------*/
+
+ /**
+ * This method is called when a delayed create response for jobs is retrieved from the consumer's queue.
+ *
+ * @see sif3.common.interfaces.DelayedConsumer#onCreateMany(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ *
+ * @param statusList See onCreateMany() method in DelayedConsumer class.
+ * @param receipt See onCreateMany() method in DelayedConsumer class.
+ */
+ public abstract void processDelayedJobsCreate(MultiOperationStatusList statusList, DelayedResponseReceipt receipt);
+
+ /**
+ * This method is called when a delayed delete jobs response is retrieved from the consumer's queue.
+ *
+ * @see sif3.common.interfaces.DelayedConsumer#onDeleteMany(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ *
+ * @param statusList See onDeleteMany() method in DelayedConsumer class.
+ * @param receipt See onDeleteMany() method in DelayedConsumer class.
+ */
+ public abstract void processDelayedJobsDelete(MultiOperationStatusList statusList, DelayedResponseReceipt receipt);
+
+ /**
+ * This method is called when a delayed jobs query response is retrieved from the consumer's queue.
+ *
+ * @see sif3.common.interfaces.DelayedConsumer#onQuery(java.lang.Object, sif3.common.model.PagingInfo, sif3.common.model.delayed.DelayedResponseReceipt)
+ *
+ * @param jobs See onQuery() method in DelayedConsumer class.
+ * @param pagingInfo See onQuery() method in DelayedConsumer class.
+ * @param receipt See onQuery() method in DelayedConsumer class.
+ */
+ public abstract void processDelayedJobsQuery(JobCollectionType jobs, PagingInfo pagingInfo, DelayedResponseReceipt receipt);
+
+ /*------------------------------*/
+ /*-- Delayed Phase Operations --*/
+ /*------------------------------*/
+
+ /**
+ * This method is called when a delayed phase query response is retrieved from the consumer's queue.
+ *
+ * @param phaseInfo Holds the jobID and phase name for which the delayed response is for.
+ * @param phaseDataResponse Holds data that is returned. Data can be empty. The data is in its raw format, that being a string. Because
+ * the framework is data model agnostic it cannot make any assumptions what the data is. It is an entirely
+ * implementation specific piece of knowledge. Hence only the string is passed to the consumer. It is up to the
+ * consumer to unmarshal the string into a suitable structure. For the unmarshal operation the mime type in this
+ * parameter should be used. It indicates what the data's mime type is.
+ * @param pagingInfo The paging information relating to the query result that is returned. Because a consumer may request
+ * query results in pages it is necessary to pass that paging information back to the consumer as part
+ * of this call. This may allow the consumer to determine how many pages it may expect as well as if it
+ * has paged through all results.
+ * @param receipt Metadata information about the response relating to the original request. This includes but is not
+ * limited to the zoneId, contextId, original request ID etc. as well as all HTTP Headers of the response.
+ */
+ public abstract void processDelayedPhaseQuery(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, PagingInfo pagingInfo, DelayedResponseReceipt receipt);
+
+ /**
+ * This method is called when a delayed phase create response is retrieved from the consumer's queue.
+ *
+ * @param phaseInfo Holds the jobID and phase name for which the delayed response is for.
+ * @param phaseDataResponse Holds data that is returned. Data can be empty. The data is in its raw format, that being a string. Because
+ * the framework is data model agnostic it cannot make any assumptions what the data is. It is an entirely
+ * implementation specific piece of knowledge. Hence only the string is passed to the consumer. It is up to the
+ * consumer to unmarshal the string into a suitable structure. For the unmarshal operation the mime type in this
+ * parameter should be used. It indicates what the data's mime type is.
+ * @param receipt Metadata information about the response relating to the original request. This includes but is not
+ * limited to the zoneId, contextId, original request ID etc. as well as all HTTP Headers of the response.
+ */
+ public abstract void processDelayedPhaseCreate(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt);
+
+ /**
+ * This method is called when a delayed phase update response is retrieved from the consumer's queue.
+ *
+ * @param phaseInfo Holds the jobID and phase name for which the delayed response is for.
+ * @param phaseDataResponse Holds data that is returned. Data can be empty. The data is in its raw format, that being a string. Because
+ * the framework is data model agnostic it cannot make any assumptions what the data is. It is an entirely
+ * implementation specific piece of knowledge. Hence only the string is passed to the consumer. It is up to the
+ * consumer to unmarshal the string into a suitable structure. For the unmarshal operation the mime type in this
+ * parameter should be used. It indicates what the data's mime type is.
+ * @param receipt Metadata information about the response relating to the original request. This includes but is not
+ * limited to the zoneId, contextId, original request ID etc. as well as all HTTP Headers of the response.
+ */
+ public abstract void processDelayedPhaseUpdate(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt);
+
+ /**
+ * This method is called when a delayed phase delete response is retrieved from the consumer's queue.
+ *
+ * @param phaseInfo Holds the jobID and phase name for which the delayed response is for.
+ * @param phaseDataResponse Holds data that is returned. Data can be empty. The data is in its raw format, that being a string. Because
+ * the framework is data model agnostic it cannot make any assumptions what the data is. It is an entirely
+ * implementation specific piece of knowledge. Hence only the string is passed to the consumer. It is up to the
+ * consumer to unmarshal the string into a suitable structure. For the unmarshal operation the mime type in this
+ * parameter should be used. It indicates what the data's mime type is.
+ * @param receipt Metadata information about the response relating to the original request. This includes but is not
+ * limited to the zoneId, contextId, original request ID etc. as well as all HTTP Headers of the response.
+ */
+ public abstract void processDelayedPhaseDelete(PhaseInfo phaseInfo, PhaseDataResponse phaseDataResponse, DelayedResponseReceipt receipt);
+
+ /*------------------------------------------------------*/
+ /* Abstract method relating to Job Event functionality. */
+ /*------------------------------------------------------*/
+ /**
+ * This method is called when a consumer service has received a Job event. This class does implement the actual onEvent()
+ * method from the event interface. It may do some additional work for house keeping purpose so the original onEvent() id processed
+ * as part of this class but then this method is called so that the actual consumer can do its work as required.
+ *
+ * @param sifEvent The event data that has been received and shall be processed by the consumer. This parameter also holds
+ * the zone and context in the limitToZoneCtxList property. It will always only hold one entry in that
+ * list. So the zone can be retrieved with the following call: sifEvent.getLimitToZoneCtxList().get(0).getZone().
+ * However for simplicity reasons the zone and context is already extracted and passed to this method in the
+ * zone anc context parameter.
+ * @param zone The zone from which the event was received from. The framework ensures that this is never null.
+ * @param context The context from which the event was received from. The framework ensures that this is never null.
+ * @param metadata Additional metadata that is known for the event. Typical values include custom HTTP headers, sourceName etc.
+ * @param msgReadID The ID of the SIF queue reader. It is informative only and is only of use where there are multiple concurrent
+ * subscribers on a message queue.
+ * @param consumerID The consumer ID that has been used to receive the event from the event queue. It is informative
+ * only and is only of use where there are multiple event subscribers enabled.
+ */
+ public abstract void processJobEvent(SIFEvent sifEvent, SIFZone zone, SIFContext context, EventMetadata metadata, String msgReadID, String consumerID);
+
+ /*-------------------------*/
+ /* Implementation of Class */
+ /*-------------------------*/
+
+ /**
+ * Constructor.
+ */
+ public AbstractFunctionalServiceConsumer()
+ {
+ super();
+ }
+
+ /*------------------------------------*/
+ /* DataModelLink Interface Methods. --*/
+ /*------------------------------------*/
+
+ /*
+ * This DataModelLink is configured for the Job Object, which is an infrastructure object. Because Functional Services are not
+ * Object Services and therefore we know what object we deal with.
+ */
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DataModelLink#getMarshaller()
+ */
+ @Override
+ public MarshalFactory getMarshaller()
+ {
+ return marshaller;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DataModelLink#getUnmarshaller()
+ */
+ @Override
+ public UnmarshalFactory getUnmarshaller()
+ {
+ return unmarshaller;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DataModelLink#getSingleObjectClassInfo()
+ */
+ @Override
+ public ModelObjectInfo getSingleObjectClassInfo()
+ {
+ return JOB_INFO;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DataModelLink#getMultiObjectClassInfo()
+ */
+ @Override
+ public ModelObjectInfo getMultiObjectClassInfo()
+ {
+ return JOBS_INFO;
+ }
+
+ /*------------------------------------*/
+ /* MinimalConsumer Interface Methods. --*/
+ /*------------------------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.MinimalConsumer#retrievByPrimaryKey(java.lang.String, java.util.List, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List retrievByPrimaryKey(String resourceID, List zoneCtxList, CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+ nullJobNameMethodCheck(getServiceURLNameSingular(), "getServiceURLNameSingular()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List responses = new ArrayList();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.QUERY, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), null);
+ if (error == null) //all good
+ {
+ responses.add(getClient(getConsumerEnvironment()).getJob(resourceID, getHeaderProperties(false, customParameters), urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext()));
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(createErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'retrieve by job by jobID key' for "+getServiceURLNamePlural()+"/"+resourceID+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but but uses JobId in method name which is more intuitive than the generic
+ * interface method..
+ */
+ public List retrievByJobID(String jobID, List zoneCtxList, CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return retrievByPrimaryKey(jobID, zoneCtxList, customParameters);
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List retrievByJobId(String resourceID, List zoneCtxList) throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return retrievByPrimaryKey(resourceID, zoneCtxList, null);
+ }
+
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.MinimalConsumer#retrieve(sif3.common.model.PagingInfo, java.util.List, sif3.common.header.HeaderValues.RequestType, sif3.common.header.HeaderValues.QueryIntention, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List retrieve(PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters)
+ throws PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List responses = new ArrayList();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ // Set default set of HTTP Header fields
+ HeaderProperties hdrProps = getHeaderProperties(false, customParameters);
+
+ // Add query intention to headers.
+ addQueryIntentionToHeaders(hdrProps, queryIntention);
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.QUERY, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+ if (error == null) //all good => Send request
+ {
+ Response response = getClient(getConsumerEnvironment()).getJobs(pagingInfo, hdrProps, urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.QUERY);
+ responses.add(response);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(createErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'retrieve all Jobs' for "+getServiceURLNamePlural()+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but but uses JobId in method name which is more intuitive than the generic
+ * interface method..
+ */
+ public List retrieveJobs(PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention, CustomParameters customParameters)
+ throws PersistenceException, ServiceInvokationException
+ {
+ return retrieve(pagingInfo, zoneCtxList, requestType, queryIntention, customParameters);
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List retrieveJobs(PagingInfo pagingInfo, List zoneCtxList, RequestType requestType, QueryIntention queryIntention)
+ throws PersistenceException, ServiceInvokationException
+ {
+ return retrieve(pagingInfo, zoneCtxList, requestType, queryIntention, null);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.MinimalConsumer#getServiceInfo(sif3.common.model.PagingInfo, java.util.List, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List getServiceInfo(PagingInfo pagingInfo, List zoneCtxList, CustomParameters customParameters)
+ throws PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List responses = new ArrayList();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ // Set default set of HTTP Header fields
+ HeaderProperties hdrProps = getHeaderProperties(false, customParameters);
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.QUERY, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), RequestType.IMMEDIATE);
+ if (error == null) //all good => Send request
+ {
+ Response response = getClient(getConsumerEnvironment()).getServiceInfo(pagingInfo, hdrProps, urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext());
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.HEAD);
+ responses.add(response);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(createErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'retrieve service info' for "+getServiceURLNamePlural()+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List getServiceInfo(PagingInfo pagingInfo, List zoneCtxList) throws PersistenceException, ServiceInvokationException
+ {
+ return getServiceInfo(pagingInfo, zoneCtxList, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see sif3.common.consumer.MinimalConsumer#finalise()
+ */
+ @Override
+ public void finalise()
+ {
+ // Clean up stuff for this consumer
+
+ // Clean up the BaseConsumer
+ super.finalise();
+
+ // Clean up the specific consumer implementation
+ shutdown();
+ }
+
+ /*--------------------------------------*/
+ /* DelayedConsumer Interface Methods. --*/
+ /*--------------------------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onCreateMany(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onCreateMany(MultiOperationStatusList statusList, DelayedResponseReceipt receipt)
+ {
+ processDelayedJobsCreate(statusList, receipt);
+ }
+
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onDeleteMany(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onDeleteMany(MultiOperationStatusList statusList, DelayedResponseReceipt receipt)
+ {
+ processDelayedJobsDelete(statusList, receipt);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onQuery(java.lang.Object, sif3.common.model.PagingInfo, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onQuery(Object dataObject, PagingInfo pagingInfo, DelayedResponseReceipt receipt)
+ {
+ if (dataObject != null)
+ {
+ if (dataObject instanceof JobCollectionType)
+ {
+ processDelayedJobsQuery((JobCollectionType)dataObject, pagingInfo, receipt);
+ }
+ else
+ {
+ // We have received a job query response but the data is not of type JobCollectionType. This is not good. We log an error an
+ // also call the onError method to indicate to the consumer that some odd stuff has come through.
+ processDelayedError(new ErrorDetails(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Invalid Data", "A delayed response to a Job Query has been received but the data is of type "+dataObject.getClass().getSimpleName()+" instead of JobCollectiontype.", "Consumer"), receipt);
+ }
+ }
+ else
+ {
+ processDelayedJobsQuery(null, pagingInfo, receipt);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onError(sif3.common.ws.ErrorDetails, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onError(ErrorDetails error, DelayedResponseReceipt receipt)
+ {
+ processDelayedError(error, receipt);
+ }
+
+ /*
+ * The following methods are not applicable for Job Objects. No implementation required.
+ */
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onServicePath(java.lang.Object, sif3.common.model.QueryCriteria, sif3.common.model.PagingInfo, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onServicePath(Object dataObject, QueryCriteria queryCriteria, PagingInfo pagingInfo, DelayedResponseReceipt receipt)
+ {
+ //There is no ServicePath functionality for functional services or job object defined, so we do not need to implement anything here.
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.DelayedConsumer#onUpdateMany(sif3.common.ws.model.MultiOperationStatusList, sif3.common.model.delayed.DelayedResponseReceipt)
+ */
+ @Override
+ public void onUpdateMany(MultiOperationStatusList statusList, DelayedResponseReceipt receipt)
+ {
+ //Job Objects cannot be updated. No implementation needed here.
+ }
+
+ /*------------------------------------*/
+ /* EventConsumer Interface Methods. --*/
+ /*------------------------------------*/
+
+ @Override
+ public void onEvent(SIFEvent sifEvent, EventMetadata metadata, String msgReadID, String consumerID)
+ {
+ if (sifEvent != null)
+ {
+ // We know from the framework that zone and context is never null. For the time being we just log the event.
+ processJobEvent(sifEvent, sifEvent.getLimitToZoneCtxList().get(0).getZone(), sifEvent.getLimitToZoneCtxList().get(0).getContext(), metadata, msgReadID, consumerID);
+ }
+ }
+
+ @Override
+ public SIFEvent createEventObject(Object sifObjectList, EventAction eventAction, UpdateType updateType)
+ {
+ if (sifObjectList != null)
+ {
+ if (sifObjectList instanceof JobCollectionType)
+ {
+ int size = ((JobCollectionType)sifObjectList).getJob().size();
+ return new SIFEvent((JobCollectionType)sifObjectList, eventAction, updateType, size);
+ }
+ else
+ {
+ logger.error("The given event data is not of type JobCollectionType as expected. Cannot create event object. Return null");
+ }
+ }
+ else
+ {
+ logger.error("The given job event data is null. Cannot create job event object. Return null");
+ }
+ return null; // if something is wrong then we get here.
+ }
+
+ /*------------------------------------------------*/
+ /* FunctionalServiceConsumer Interface Methods. --*/
+ /*------------------------------------------------*/
+
+ /*--------------------*/
+ /*-- Job Operations --*/
+ /*--------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#createJob(sif3.common.model.job.JobCreateRequestParameter, java.util.List, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List createJob(JobCreateRequestParameter createJobRequest, List zoneCtxList, CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+ nullJobNameMethodCheck(getServiceURLNameSingular(), "getServiceURLNameSingular()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List responses = new ArrayList();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.CREATE, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), null);
+ if (error == null) //all good
+ {
+ responses.add(getClient(getConsumerEnvironment()).createJob(createJobRequest, getHeaderProperties(true, customParameters), urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext()));
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(createErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'createJob' for "+getServiceURLNamePlural()+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List createJob(JobCreateRequestParameter createJobRequest, List zoneCtxList) throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return createJob(createJobRequest, zoneCtxList, null);
+ }
+
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#deleteJob(java.lang.String, java.util.List, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List deleteJob(String jobID, List zoneCtxList, CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List responses = new ArrayList();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.DELETE, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), null);
+ if (error == null) //all good
+ {
+ responses.add(getClient(getConsumerEnvironment()).removeJob(jobID, getHeaderProperties(false, customParameters), urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext()));
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(createErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'delete job by jobID' for "+getServiceURLNamePlural()+"/"+jobID+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List deleteJob(String jobID, List zoneCtxList) throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return deleteJob(jobID, zoneCtxList, null);
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#createJobs(java.util.List, java.util.List, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List> createJobs(List createMultipleJobsRequest,
+ List zoneCtxList,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List> responses = new ArrayList>();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.CREATE, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+ if (error == null) //all good => Send request.
+ {
+ BulkOperationResponse response = getClient(getConsumerEnvironment()).createJobs(createMultipleJobsRequest, getHeaderProperties(true, customParameters), urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.CREATE);
+ responses.add(response);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(makeBulkErrorResponseForCreates(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'createJobs' for "+getServiceURLNamePlural()+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List> createJobs(List createMultipleJobsRequest,
+ List zoneCtxList,
+ RequestType requestType)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return createJobs(createMultipleJobsRequest, zoneCtxList, requestType, null);
+ }
+
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#deleteJobs(java.util.List, java.util.List, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public List> deleteJobs(List jobIDs,
+ List zoneCtxList,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ List> responses = new ArrayList>();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return responses;
+ }
+
+ List finalZoneContextList = getFinalZoneCtxList(zoneCtxList, getSIF3Session());
+
+ // Request operation in all zone/contexts as listed.
+ for (ZoneContextInfo zoneCtx : finalZoneContextList)
+ {
+ ErrorDetails error = allClientChecks(AccessRight.DELETE, AccessType.APPROVED, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+ if (error == null) //all good => Send request
+ {
+ BulkOperationResponse response = getClient(getConsumerEnvironment()).removeJobs(jobIDs, getHeaderProperties(false, customParameters), urlQueryParameter, zoneCtx.getZone(), zoneCtx.getContext(), requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getMultiObjectClassInfo().getObjectName(), ServiceType.FUNCTIONAL, ResponseAction.DELETE);
+ responses.add(response);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ responses.add(makeBulkErrorResponse(error));
+ }
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'removeJobs' for "+getServiceURLNamePlural()+": "+timer.timeTaken()+"ms");
+ return responses;
+ }
+
+ /*
+ * Convenience method. The same as above but without the parameter 'customParameters' which is defaulted to null.
+ */
+ public List> deleteJobs(List jobIDs,
+ List zoneCtxList,
+ RequestType requestType)
+ throws IllegalArgumentException, PersistenceException, ServiceInvokationException
+ {
+ return deleteJobs(jobIDs, zoneCtxList, requestType, null);
+ }
+
+ /*----------------------*/
+ /*-- Phase Operations --*/
+ /*----------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#retrieveDataFromPhase(sif3.common.model.job.PhaseInfo, javax.ws.rs.core.MediaType, sif3.common.model.PagingInfo, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response retrieveDataFromPhase(PhaseInfo phaseInfo,
+ MediaType returnMimeType,
+ PagingInfo pagingInfo,
+ QueryIntention queryIntention,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, requestType);
+ if (error == null) //all good => Send request
+ {
+ // Set default set of HTTP Header fields
+ HeaderProperties hdrProps = getHeaderProperties(false, customParameters);
+
+ // Add query intention to headers.
+ addQueryIntentionToHeaders(hdrProps, queryIntention);
+
+ response = getClient(getConsumerEnvironment()).retrieveDataFromPhase(phaseInfo, returnMimeType, pagingInfo, hdrProps, urlQueryParameter, zone, context, requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.QUERY);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'retrive data from phase for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#createDataInPhase(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataRequest, javax.ws.rs.core.MediaType, boolean, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response createDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ boolean useAdvisory,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, requestType);
+ if (error == null) //all good => Send request
+ {
+ response = getClient(getConsumerEnvironment()).createDataInPhase(phaseInfo, phaseDataRequest, returnMimeType, useAdvisory, getHeaderProperties(true, customParameters), urlQueryParameter, zone, context, requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.CREATE);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'create data in phase for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#updateDataInPhase(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataRequest, javax.ws.rs.core.MediaType, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response updateDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, requestType);
+ if (error == null) //all good => Send request
+ {
+ response = getClient(getConsumerEnvironment()).updateDataInPhase(phaseInfo, phaseDataRequest, returnMimeType, getHeaderProperties(false, customParameters), urlQueryParameter, zone, context, requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.UPDATE);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'update data in phase for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#deleteDataInPhase(sif3.common.model.job.PhaseInfo, sif3.common.ws.job.PhaseDataRequest, javax.ws.rs.core.MediaType, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.header.HeaderValues.RequestType, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response deleteDataInPhase(PhaseInfo phaseInfo,
+ PhaseDataRequest phaseDataRequest,
+ MediaType returnMimeType,
+ SIFZone zone,
+ SIFContext context,
+ RequestType requestType,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, requestType);
+ if (error == null) //all good => Send request
+ {
+ response = getClient(getConsumerEnvironment()).deleteDataInPhase(phaseInfo, phaseDataRequest, returnMimeType, getHeaderProperties(false, customParameters), urlQueryParameter, zone, context, requestType);
+
+ // Set the missing delayed response properties. No need to check if it was delayed request as it is checked in the finaliseDelayedReceipt method.
+ finaliseDelayedReceipt(response.getDelayedReceipt(), getServiceURLNamePlural(), ServiceType.FUNCTIONAL, ResponseAction.DELETE);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'delete data in phase for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /*----------------------------*/
+ /*-- Phase State Operations --*/
+ /*----------------------------*/
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#updatePhaseState(sif3.common.model.job.PhaseInfo, sif3.common.CommonConstants.PhaseState, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response updatePhaseState(PhaseInfo phaseInfo,
+ PhaseState newState,
+ SIFZone zone,
+ SIFContext context,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, RequestType.IMMEDIATE);
+ if (error == null) //all good => Send request
+ {
+ response = getClient(getConsumerEnvironment()).updatePhaseState(phaseInfo, newState, getHeaderProperties(false, customParameters), urlQueryParameter, zone, context);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'update phase state for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /* (non-Javadoc)
+ * @see sif3.common.interfaces.FunctionalServiceConsumer#getPhaseStates(sif3.common.model.job.PhaseInfo, sif3.common.model.SIFZone, sif3.common.model.SIFContext, sif3.common.model.CustomParameters)
+ */
+ @Override
+ public Response getPhaseStates(PhaseInfo phaseInfo,
+ SIFZone zone,
+ SIFContext context,
+ CustomParameters customParameters)
+ throws IllegalArgumentException, ServiceInvokationException
+ {
+ nullJobNameMethodCheck(getServiceURLNamePlural(), "getServiceURLNamePlural()");
+
+ Timer timer = new Timer();
+ timer.start();
+ URLQueryParameter urlQueryParameter = customParameters != null ? customParameters.getQueryParams() : null;
+ Response response = new Response();
+
+ if (!getConsumerEnvironment().getIsConnected())
+ {
+ logger.error("No connected environment for "+getConsumerEnvironment().getEnvironmentName()+". See previous erro log entries.");
+ return response;
+ }
+ zone = getFinalZone(zone);
+ context = getFinalContext(context);
+
+ ErrorDetails error = checkAccessToFunctionalService(getServiceURLNamePlural(), zone, context, RequestType.IMMEDIATE);
+ if (error == null) //all good => Send request
+ {
+ response = getClient(getConsumerEnvironment()).getPhaseStates(phaseInfo, getHeaderProperties(false, customParameters), urlQueryParameter, zone, context);
+ }
+ else //pretend to have received a 'fake' error Response
+ {
+ response.setError(error);
+ }
+
+ timer.finish();
+ logger.debug("Time taken to call and process 'get phase states for job "+getServiceURLNamePlural()+"/"+phaseInfo.getJobID()+"/"+phaseInfo.getPhaseName()+": "+timer.timeTaken()+"ms");
+
+ return response;
+ }
+
+ /*----------------------------------------*/
+ /* Implemented Method for Multi-threading */
+ /*----------------------------------------*/
+
+ /**
+ * This method is all that is needed to run the subscriber in its own thread.
+ */
+ @Override
+ public final void run()
+ {
+// logger.debug("============================\n Start "+getConsumerName()+" worker thread.\n======================================");
+ }
+
+ /*----------------------------*/
+ /*-- Other required methods --*/
+ /*----------------------------*/
+ /**
+ * This method is is called when the async processor is initialised. It passes all subscription services for the
+ * given types of service across all zones for the connected environment to this method. It allows the specific
+ * async consumer implementation to remove some of the subscription services it is not interested in. Basically it allows
+ * the implementor to filter out un-needed services before the this consumer subscribes to the local queues. Only
+ * delayed responses for the returned list of service info will then be received by the particular service. Most standard
+ * implementations would not require any overriding of this method. If a specific implementation wishes to filter out
+ * some of the environment provided subscriptions then the sub-class of this class should override this method.
+ *
+ * @param allServices A list of services for this that is allowed to subscribe delayed responses across the environment of this consumer.
+ *
+ * @return The final list of services for which this consumer class shall subscribe to.
+ */
+ public List filterApprovedCRUDServices(List allServices)
+ {
+ return allServices;
+ }
+
+ /*
+ * Returns all CRUD services for which this consumer has access to. This should be a list of different zones, contexts and service types.
+ * It returns all FUNCTIONAL services where the consumer has CREATE, DELETE or QUERY as approved in the ACL. Note Functional services do not
+ * have UPDATE rights.
+ */
+ protected final List getAllApprovedCRUDServices()
+ {
+ List allServices = new ArrayList();
+
+ // Get all Functional Services. Note the service name is the name of the functional service
+ allServices.addAll(getAllApprovedServicesForRights(getServiceURLNamePlural(), ServiceType.FUNCTIONAL, AccessRight.CREATE, AccessRight.DELETE, AccessRight.QUERY));
+
+ return filterApprovedCRUDServices(allServices);
+ }
+
+ /**
+ * This method is is called when the event processor is initialised. It passes all subscription services for the
+ * given FUNCTIONAL service across all zones for the connected environment to this method. It allows the specific FUNCTIONAL
+ * event consumer implementation to remove some of the subscription services it is not interested in. Basically it allows
+ * the implementor to filter out un-needed subscriptions before the event processor subscribes to the queues. Only
+ * events for the returned list of service info will then be received by the particular OBJECT service. Most standard
+ * implementations would not require any overriding of this method. If a specific implementation wishes to filter out
+ * some of the environment provided subscriptions then the sub-class of this class should override this method.
+ *
+ * @param envEventServices A list of services for this that is allowed to subscribe to events across the environment of this consumer.
+ *
+ * @return The final list of services for which this consumer class shall subscribe to.
+ */
+ public List filterEventServices(List envEventServices)
+ {
+ return envEventServices;
+ }
+
+ /*
+ * Return all FUNCTIONAL services that have the right of SUBSCRIBE is set to APPROVED in the ACL.
+ */
+ protected final List getEventServices()
+ {
+ //Note the service name is the name of the functional service
+ return filterEventServices(getAllApprovedServicesForRights(getServiceURLNamePlural(), ServiceType.FUNCTIONAL, AccessRight.SUBSCRIBE));
+ }
+
+ /*---------------------*/
+ /*-- Private Methods --*/
+ /*---------------------*/
+ private JobClient getClient(ConsumerEnvironment envInfo) throws IllegalArgumentException
+ {
+ URI baseURI = envInfo.getConnectorBaseURI(ConsumerEnvironment.ConnectorName.servicesConnector);
+ if (baseURI == null)
+ {
+ logger.error(ConsumerEnvironment.ConnectorName.servicesConnector.toString()+" not defined for environment "+envInfo.getEnvironmentName());
+ return null;
+ }
+ else
+ {
+ return new JobClient(ConsumerEnvironmentManager.getInstance(),getServiceURLNamePlural(), getServiceURLNameSingular());
+ }
+ }
+
+ private HeaderProperties getHeaderProperties(boolean isCreateOperation, CustomParameters customParameters)
+ {
+ return getHeaderProperties(isCreateOperation, ServiceType.FUNCTIONAL, customParameters);
+ }
+
+ /*
+ * Will perform hasAccess() and requestTypeSupported() checks. This is a convenience method, so that not each operation has to
+ * call the two methods sequentially and manage all the flow.
+ */
+ private ErrorDetails allClientChecks(AccessRight right, AccessType accessType, SIFZone zone, SIFContext context, RequestType requestType)
+ {
+ return allClientChecks(getServiceURLNamePlural(), ServiceType.FUNCTIONAL, right, accessType, zone, context, requestType);
+ }
+
+ private void nullJobNameMethodCheck(String serviceURLName, String methodName) throws IllegalArgumentException
+ {
+ if (StringUtils.isEmpty(serviceURLName))
+ {
+ throw new IllegalArgumentException(methodName+" method not implemented correctly. Returns null or empty which is invalid.");
+ }
+ }
+}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/BaseConsumer.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/BaseConsumer.java
new file mode 100644
index 00000000..6e1ec2e9
--- /dev/null
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/BaseConsumer.java
@@ -0,0 +1,632 @@
+/*
+ * BaseConsumer.java
+ * Created: 12 Jul 2018
+ *
+ * Copyright 2018 Systemic Pty Ltd
+ *
+ * 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.
+ */
+
+package sif3.infra.rest.consumer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import au.com.systemic.framework.utils.AdvancedProperties;
+import au.com.systemic.framework.utils.StringUtils;
+import sif3.common.header.HeaderProperties;
+import sif3.common.header.HeaderValues;
+import sif3.common.header.HeaderValues.QueryIntention;
+import sif3.common.header.HeaderValues.RequestType;
+import sif3.common.header.HeaderValues.ResponseAction;
+import sif3.common.header.HeaderValues.ServiceType;
+import sif3.common.header.RequestHeaderConstants;
+import sif3.common.interfaces.MinimalConsumer;
+import sif3.common.model.ACL.AccessRight;
+import sif3.common.model.ACL.AccessType;
+import sif3.common.model.CustomParameters;
+import sif3.common.model.SIFContext;
+import sif3.common.model.SIFZone;
+import sif3.common.model.ServiceInfo;
+import sif3.common.model.ZoneContextInfo;
+import sif3.common.model.delayed.DelayedRequestReceipt;
+import sif3.common.model.delayed.DelayedResponseReceipt;
+import sif3.common.persist.model.SIF3Session;
+import sif3.common.ws.BaseResponse;
+import sif3.common.ws.BulkOperationResponse;
+import sif3.common.ws.CreateOperationStatus;
+import sif3.common.ws.ErrorDetails;
+import sif3.common.ws.OperationStatus;
+import sif3.common.ws.Response;
+import sif3.infra.common.env.mgr.ConsumerEnvironmentManager;
+import sif3.infra.common.env.types.ConsumerEnvironment;
+import sif3.infra.rest.queue.LocalConsumerQueue;
+import sif3.infra.rest.queue.LocalMessageConsumer;
+import sif3.infra.rest.queue.QueueReaderInfo;
+
+/**
+ * It is expected that all consumer implementations extend this class. It has a set of abstract methods that need to be implemented by
+ * a specific consumer class. It is not expected that a developer extends this class. There are a set of standard Abstract Consumers the
+ * developer will implement. This class however forms the base of these Abstract Consumer classes.
+ *
+ * @author Joerg Huber
+ *
+ */
+public abstract class BaseConsumer implements MinimalConsumer
+{
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ /* Below variables are for testing purposes only */
+ private static Boolean testMode = null;
+ /* End Testing variables */
+
+ private boolean checkACL = true;
+
+ /* The next two properties are used for delayed responses or events */
+ private LocalConsumerQueue localConsumerQueue = null;
+// private ExecutorService service = null;
+
+ private QueueReaderInfo service = null;
+
+ /*-------------------------------------------------------------*/
+ /* Abstract method relating to general Consumer functionality. */
+ /*-------------------------------------------------------------*/
+
+ /**
+ * This method is called when a consumer service is shut down. It can be used to free up internally allocated resources
+ * as well as clean-up other things.
+ */
+ public abstract void shutdown();
+
+ /**
+ * This method is called when a delayed error response is retrieved from the consumer's queue.
+ *
+ * @see sif3.common.interfaces.DelayedConsumer#onError(sif3.common.ws.ErrorDetails, sif3.common.model.delayed.DelayedResponseReceipt)
+ *
+ * @param error See onError() method in DelayedConsumer class.
+ * @param receipt See onError() method in DelayedConsumer class.
+ */
+ public abstract void processDelayedError(ErrorDetails error, DelayedResponseReceipt receipt);
+
+ /**
+ * Constructor.
+ */
+ public BaseConsumer()
+ {
+ super();
+
+ // Set some properties at this stage for simplicity reasons.
+ checkACL = getConsumerEnvironment().getCheckACL();
+
+ if (getConsumerEnvironment().getEventsEnabled() || getConsumerEnvironment().getDelayedEnabled())
+ {
+ logger.debug("Events and/or Delayed Responses enabled => start local consumer queue for "+getConsumerName());
+ createLocalConsumerQueue();
+ }
+ else
+ {
+ logger.debug("Events AND Delayed Responses are disabled. Local consumer queues and threads are not started.");
+ }
+ }
+
+ /*-------------------------------*/
+ /* Useful 'Housekeeping' methods */
+ /*-------------------------------*/
+ /**
+ * @return Returns the actual Class Name of this consumer
+ */
+ public String getConsumerName()
+ {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * Utility method. Mainly used for useful logging messages.
+ *
+ * @return Returns the Adapter Name as defined in the adapter.id property of the consumer property file concatenated with the
+ * Consumer Name (class name)
+ */
+ public String getPrettyName()
+ {
+ return getConsumerEnvironment().getAdapterName()+" - " + getConsumerName();
+ }
+
+ /**
+ * Utility method to easily retrieve the consumer environment configuration.
+ *
+ * @return See desc
+ */
+ public ConsumerEnvironment getConsumerEnvironment()
+ {
+ return (ConsumerEnvironment)ConsumerEnvironmentManager.getInstance().getEnvironmentInfo();
+ }
+
+ /**
+ * Utility method to easily retrieve the property file content for a consumer.
+ *
+ * @return See desc
+ */
+ public AdvancedProperties getServiceProperties()
+ {
+ return ConsumerEnvironmentManager.getInstance().getServiceProperties();
+ }
+
+ /**
+ * @return Returns the Service Name.
+ */
+ public String getServiceName()
+ {
+ return getMultiObjectClassInfo().getObjectName();
+ }
+
+ /*------------------------------------------------------------------------------------------------------------------------
+ * Start of 'Dynamic' HTTP Header Field override section
+ *
+ * The following set of methods are used for a more configurable way how some HTTP header parameters are set.
+ * By default the following HTTP Header fields are retrieved from the consumer's property file and put in corresponding
+ * HTTP Header Fields:
+ *
+ * Property HTTP Header
+ * ------------------------------------------------
+ * adapter.generator.id generatorId
+ * env.application.key applicationKey
+ * env.userToken authenticatedUser
+ * env.mediaType Content-Type, Accept
+ * adapter.mustUseAdvisoryIDs mustUseAdvisory
+ * adapter.compression.enabled Content-Encoding, Accept-Encoding
+ *
+ * Only properties that are not null or empty string will be set in the corresponding HTTP Header.
+ *
+ * There are situations where and application may need a more 'dynamic' behaviour where the above values are determined
+ * at runtime, based on other circumstances and therefore these properties must be retrieved from an other source than the
+ * consumer's property file. In such a case the methods below can be overwritten to make them dynamic and controlled by
+ * the implementation rather than driven by the consumer's property file. If any of the methods below is overwritten then
+ * the value of the over riding method is set in the corresponding HTTP Header field if the return value of the method
+ * is not null or an empty string.
+ *------------------------------------------------------------------------------------------------------------------------*/
+
+ /**
+ * This method returns the value of the adapter.generator.id property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The adapter.generator.id property from the consumer's property file
+ */
+ public String getGeneratorID()
+ {
+ return getConsumerEnvironment().getGeneratorID();
+ }
+
+ /**
+ * This method returns the value of the env.application.key property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The env.application.key property from the consumer's property file
+ */
+ public String getApplicationKey()
+ {
+ return getConsumerEnvironment().getEnvironmentKey().getApplicationKey();
+ }
+
+ /**
+ * This method returns the value of the env.userToken property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The env.userToken property from the consumer's property file
+ */
+ public String getAuthentictedUser()
+ {
+ return getConsumerEnvironment().getEnvironmentKey().getUserToken();
+ }
+
+ /**
+ * This method returns the value of the env.mediaType property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The env.mediaType property from the consumer's property file
+ */
+ public MediaType getRequestMediaType()
+ {
+ return getConsumerEnvironment().getMediaType();
+ }
+
+ /**
+ * This method returns the value of the env.mediaType property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The env.mediaType property from the consumer's property file
+ */
+ public MediaType getResponseMediaType()
+ {
+ return getConsumerEnvironment().getMediaType();
+ }
+
+ /**
+ * This method returns the value of the adapter.mustUseAdvisoryIDs property from the consumer's property file. If that
+ * needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The adapter.mustUseAdvisoryIDs property from the consumer's property file
+ */
+ public boolean getMustUseAdvisory()
+ {
+ return getConsumerEnvironment().getUseAdvisory();
+ }
+
+ /**
+ * This method returns the value of the adapter.compression.enabled property from the consumer's property file. If
+ * that needs to be overridden by a specific implementation then the specific sub-class should override this method.
+ *
+ * @return The adapter.compression.enabled property from the consumer's property file
+ */
+ public boolean getCompressionEnabled()
+ {
+ return getConsumerEnvironment().getCompressionEnabled();
+ }
+
+ /*------------------------------------------------------------------------------------------------------------------------
+ * End of 'Dynamic' HTTP Header Field override section
+ *-----------------------------------------------------------------------------------------------------------------------*/
+
+ /*------------------------------*/
+ /* Some Getter & Setter methods */
+ /*------------------------------*/
+
+ public final LocalConsumerQueue getLocalConsumerQueue()
+ {
+ return localConsumerQueue;
+ }
+
+ public final void setLocalConsumerQueue(LocalConsumerQueue localConsumerQueue)
+ {
+ this.localConsumerQueue = localConsumerQueue;
+ }
+
+ /*-------------------*/
+ /* Interface Methods */
+ /*-------------------*/
+ /*
+ * (non-Javadoc)
+ * @see sif3.common.consumer.Consumer#finalise()
+ */
+ @Override
+ public void finalise()
+ {
+ logger.debug("Shut down all Local Consumer Thread Pool for "+getConsumerName());
+ if (service != null)
+ {
+ for (LocalMessageConsumer localReader : service.getLinkedClasses())
+ {
+ localReader.shutdown();
+ }
+
+ service.getService().shutdown();
+ }
+ shutdown();
+ }
+
+
+ /*------------------------------------------------------------------------------------*/
+ /*-- Methods for Local Queues and Threads for Delayed Response and Event management --*/
+ /*------------------------------------------------------------------------------------*/
+
+ /**
+ * Only creates the local consumer queue if it doesn't already exist. The queue (new or existing) is then returned.
+ *
+ * @return See desc.
+ */
+ public final LocalConsumerQueue createLocalConsumerQueue()
+ {
+ if (getLocalConsumerQueue() == null)
+ {
+ // Create local queue with the capacity indicated with the consumer config
+ logger.debug("Create Local Queue for "+getConsumerName());
+
+ // Use the local queue as a trigger of threads rather than actual queueing of messages. Use 1 as the minimum
+ setLocalConsumerQueue(new LocalConsumerQueue(1, getClass().getSimpleName() + "LocalQueue", getClass().getSimpleName()));
+ startListenerThreads();
+ }
+ return getLocalConsumerQueue();
+ }
+
+ /*
+ * Will initialise the threads and add them to the local consumer queue.
+ */
+ private void startListenerThreads()
+ {
+ // Start up all consumers for this subscriber.
+ int numThreads = getNumOfConsumerThreads();
+ logger.debug("Start "+numThreads+" "+getConsumerName()+" threads.");
+ logger.debug("Total number of threads before starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
+
+ service = new QueueReaderInfo(Executors.newFixedThreadPool(numThreads), new ArrayList());
+ for (int i = 0; i < numThreads; i++)
+ {
+ String consumerID = getConsumerName()+" "+(i+1);
+ logger.debug("Start Consumer "+consumerID);
+ LocalMessageConsumer consumer = new LocalMessageConsumer(getLocalConsumerQueue(), consumerID, this);
+ service.getLinkedClasses().add(consumer);
+ service.getService().execute(consumer);
+ }
+ logger.debug(numThreads+" "+getConsumerName()+" initilaised and started.");
+ logger.debug("Total number of threads after starting Local Queue for "+getConsumerName()+" "+Thread.activeCount());
+ }
+
+ private final int getNumOfConsumerThreads()
+ {
+ return getServiceProperties().getPropertyAsInt("consumer.local.workerThread", getClass().getSimpleName(), 1);
+ }
+
+ /*--------------------------------------------------------------------------------*/
+ /*-- Protected Methods to be used by Abstract Consumers that extend this class. --*/
+ /*--------------------------------------------------------------------------------*/
+
+ protected SIF3Session getSIF3Session()
+ {
+ return ConsumerEnvironmentManager.getInstance().getSIF3Session();
+ }
+
+ protected HeaderProperties getHeaderProperties(boolean isCreateOperation, HeaderValues.ServiceType serviceType, CustomParameters customParameters)
+ {
+ HeaderProperties hdrProps = new HeaderProperties();
+
+ // First we add all Custom HTTP Headers. We add SIF defined HTTP header later. This will also ensure that we
+ // will override custom properties with SIF defined properties.
+ if ((customParameters != null) && (customParameters.getHttpHeaderParams() != null))
+ {
+ hdrProps = customParameters.getHttpHeaderParams();
+ }
+
+ // Now we set SIF defined HTTP headers...
+
+ // Set the remaining header fields for this type of request
+ if (isCreateOperation)
+ {
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_ADVISORY, (getMustUseAdvisory() ? "true" : "false"));
+ }
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_SERVICE_TYPE, serviceType.name());
+
+ // Set values of consumer property file or their overridden value. Note thsetHeaderProperty() method will do the check
+ // for null, so no need to do this here.
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_APPLICATION_KEY, getApplicationKey());
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_AUTHENTICATED_USER, getAuthentictedUser());
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_GENERATOR_ID, getGeneratorID());
+
+ return hdrProps;
+ }
+
+ protected ErrorDetails hasAccess(String serviceName, ServiceType serviceType, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context)
+ {
+ ErrorDetails error = null;
+ if (checkACL)
+ {
+ if (!getSIF3Session().hasAccess(right, accessType, serviceName, serviceType, zone, context))
+ {
+ String zoneID = (zone == null) ? "Default" : zone.getId();
+ String contextID = (context == null) ? "Default" : context.getId();
+ error = new ErrorDetails(Status.FORBIDDEN.getStatusCode(), "Consumer is not authorized to issue the requested operation.", right.name()+ " access is not set to "+accessType.name()+" for the service " + serviceName +" and the given zone ("+zoneID+") and context ("+contextID+") in the environment "+getSIF3Session().getEnvironmentName(), "Client side check.");
+ }
+ }
+ return error;
+ }
+
+ protected ErrorDetails allClientChecks(String serviceName, ServiceType serviceType, AccessRight right, AccessType accessType, SIFZone zone, SIFContext context, RequestType requestType)
+ {
+ ErrorDetails error = hasAccess(serviceName, serviceType, right, accessType, zone, context);
+ if ((error == null) && (requestType != null))
+ {
+ error = requestTypeEnabled(requestType);
+ }
+ return error;
+ }
+
+ protected ErrorDetails checkAccessToFunctionalService(String serviceName, SIFZone zone, SIFContext context, RequestType requestType)
+ {
+ ErrorDetails error = null;
+ ServiceInfo serviceInfo = getSIF3Session().getServiceInfoForService(zone, context, serviceName, ServiceType.FUNCTIONAL);
+ if (serviceInfo == null) // Found no service for the given functional service name.
+ {
+ error = new ErrorDetails(Status.FORBIDDEN.getStatusCode(), "Access Denied.", "Consumer is not authorized to access the functional service '"+serviceName+"'.", "Consumer side check.");
+ }
+
+ if ((error == null) && (requestType != null))
+ {
+ error = requestTypeEnabled(requestType);
+ }
+ return error;
+ }
+
+ protected ErrorDetails requestTypeEnabled(RequestType requestType)
+ {
+ ErrorDetails error = null;
+
+ if ((requestType == RequestType.DELAYED) && (!getConsumerEnvironment().getDelayedEnabled()))
+ {
+ error = new ErrorDetails(Status.BAD_REQUEST.getStatusCode(), "Client side Check: DELAYED requests are not enabled.");
+ }
+ return error;
+ }
+
+ protected BulkOperationResponse makeBulkErrorResponseForCreates(ErrorDetails error)
+ {
+ BulkOperationResponse response = new BulkOperationResponse();
+ setErrorDetails(response, error);
+ return response;
+ }
+
+ protected BulkOperationResponse makeBulkErrorResponse(ErrorDetails error)
+ {
+ BulkOperationResponse response = new BulkOperationResponse();
+ setErrorDetails(response, error);
+ return response;
+ }
+
+ protected Response createErrorResponse(ErrorDetails error)
+ {
+ Response response = new Response();
+ setErrorDetails(response, error);
+ return response;
+ }
+
+ /*
+ * This method sets the remaining properties in the receipt for delayed responses. There are a few fields that cannot be set at the ObjectServiceClient as
+ * they are not known or cannot be determined in there but are well known in the abstract consumer.
+ */
+ protected void finaliseDelayedReceipt(DelayedRequestReceipt delayedReceipt, String serviceName, ServiceType serviceType, ResponseAction requestedAction)
+ {
+ if (delayedReceipt != null)
+ {
+ //delayedReceipt.setRequestDate(requestDate);
+ delayedReceipt.setServiceName(serviceName);
+ delayedReceipt.setServiceType(serviceType);
+ delayedReceipt.setRequestedAction(requestedAction);
+ }
+ }
+
+ protected void nullMethodCheck(Object objectToCheck, String methodName) throws IllegalArgumentException
+ {
+ if (objectToCheck == null)
+ {
+ throw new IllegalArgumentException(methodName+" method not implemented correctly. Returns null which is invalid.");
+ }
+ }
+
+ /*
+ * This method checks if the test.testmode in the consumer's property file is set to TRUE.
+ */
+ protected boolean isTestMode()
+ {
+ if (testMode == null)
+ {
+ AdvancedProperties props = getServiceProperties();
+ testMode = props.getPropertyAsBool("test.testmode", false);
+ }
+ return testMode;
+ }
+
+ protected List getFinalZoneCtxList( List zoneCtxList, SIF3Session sif3Session)
+ {
+ List finalZoneContextList = null;
+
+ if (zoneCtxList == null)
+ {
+ finalZoneContextList = new ArrayList();
+ }
+ else
+ {
+ finalZoneContextList = zoneCtxList;
+ }
+
+ if (finalZoneContextList.size() == 0) //add default context and zone
+ {
+ // Set zone and context to null which will ensure that the matrix params won't be set and therefore the provider will assume default context & zone
+ finalZoneContextList.add(new ZoneContextInfo((SIFZone)null, (SIFContext)null));
+ }
+
+ // Check all entries and if 'null' is used as zone or context then we assign the default.
+ for (ZoneContextInfo zoneCtxInfo : finalZoneContextList)
+ {
+ // If zone or zone ID is null then we set the default zone.
+ if ((zoneCtxInfo.getZone() == null) || StringUtils.isEmpty(zoneCtxInfo.getZone().getId()))
+ {
+ zoneCtxInfo.setZone(null); // won't set matrix parameter which means default zone
+ }
+ // If context or context ID is null then we set the default zone.
+ if ((zoneCtxInfo.getContext() == null) || StringUtils.isEmpty(zoneCtxInfo.getContext().getId()))
+ {
+ zoneCtxInfo.setContext(null); // won't set matrix parameter which means default context
+ }
+ }
+
+ return finalZoneContextList;
+ }
+
+ protected SIFZone getFinalZone(SIFZone zone)
+ {
+ // If zone or zone ID is null then we set the default zone. This means set it to null which defaults to the default zone
+ if ((zone == null) || StringUtils.isEmpty(zone.getId()))
+ {
+ return null; // won't set matrix parameter which means default zone
+ }
+
+ return zone;
+ }
+
+ protected SIFContext getFinalContext(SIFContext context)
+ {
+ // If context or context ID is null then we set the default context. This means set it to null which defaults to the default context
+ if ((context == null) || StringUtils.isEmpty(context.getId()))
+ {
+ return null; // won't set matrix parameter which means default context
+ }
+
+ return context;
+ }
+
+ protected void addQueryIntentionToHeaders(HeaderProperties hdrProps, QueryIntention queryIntention)
+ {
+ // Ensure query Intention is not null. if so default to ONE-OFF as per SIF 3.x spec.
+ queryIntention = (queryIntention == null) ? QueryIntention.ONE_OFF : queryIntention;
+
+ // Add query intention to headers.
+ hdrProps.setHeaderProperty(RequestHeaderConstants.HDR_QUERY_INTENTION, queryIntention.getHTTPHeaderValue());
+ }
+
+
+ /*
+ * Returns all services for which this consumer has APPROVE access to at least on of the rights listed. This should be a list of different zones,
+ * contexts and service types.
+ */
+ protected final List getAllApprovedServicesForRights(String serviceName, ServiceType serviceType, AccessRight... rights)
+ {
+ SIF3Session sif3Session = ConsumerEnvironmentManager.getInstance().getSIF3Session();
+ List allServices = new ArrayList();
+
+ // Get Services for given type and name
+ List services = sif3Session.getServiceInfoForService(serviceName, serviceType);
+ for (ServiceInfo serviceInfo : services)
+ {
+ // Check if any of the given rights states APPROVED
+ if (rights != null)
+ {
+ for (AccessRight right : rights)
+ {
+ if (serviceInfo.getRights().hasRight(right, AccessType.APPROVED))
+ {
+ allServices.add(serviceInfo); //found one.
+
+ // we can stop as the service has at least one of the rights approved.
+ break;
+ }
+ }
+ }
+ }
+ return allServices;
+ }
+
+ /*-------------------*/
+ /* Private Methods --*/
+ /*-------------------*/
+
+ private void setErrorDetails(BaseResponse response, ErrorDetails errorDetails)
+ {
+ response.setStatus(errorDetails.getErrorCode());
+ response.setStatusMessage(errorDetails.getMessage());
+ response.setError(errorDetails);
+ response.setContentLength(0);
+ response.setHasEntity(false);
+ }
+}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/ConsumerLoader.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/ConsumerLoader.java
index 9297eafe..6d532857 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/ConsumerLoader.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/consumer/ConsumerLoader.java
@@ -19,7 +19,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
@@ -27,6 +26,7 @@
import au.com.systemic.framework.utils.AdvancedProperties;
import au.com.systemic.framework.utils.StringUtils;
+import sif3.common.CommonConstants;
import sif3.common.exception.PersistenceException;
import sif3.common.interfaces.HibernateProperties;
import sif3.common.model.ServiceInfo;
@@ -37,6 +37,7 @@
import sif3.infra.common.interfaces.EnvironmentConnector;
import sif3.infra.rest.env.connectors.EnvironmentConnectorFactory;
import sif3.infra.rest.queue.LocalConsumerQueue;
+import sif3.infra.rest.queue.QueueReaderInfo;
import sif3.infra.rest.queue.RemoteMessageQueueReader;
import sif3.infra.rest.queue.connectors.ConsumerQueueConnector;
import sif3.infra.rest.queue.connectors.ConsumerSubscriptionConnector;
@@ -69,9 +70,10 @@ public class ConsumerLoader
private ConsumerSubscriptionConnector subscriptionConnector = null;
private List> eventConsumers = new ArrayList>();
private List crudConsumers = new ArrayList();
+ private List fsServiceConsumers = new ArrayList();
- private List msgReaderServices = new ArrayList();
-
+ private List> msgReaderServices = new ArrayList>();
+
/**
* Initialises the consumer based on the given property file. If anything fails to initialise then an error is logged and this method
* returns false. The consumer should not really continue in such a case as its behaviour is not defined and most likely will throw
@@ -202,11 +204,16 @@ private void shutdownConsumer()
ConsumerEnvironmentManager envMgr = ConsumerEnvironmentManager.getInstance();
logger.debug("Shut down all remote message queue reader threads...");
- for (ExecutorService service : msgReaderServices)
+ for (QueueReaderInfo remoteReaderInfo : msgReaderServices)
{
- service.shutdown();
+ for (RemoteMessageQueueReader remoteReader : remoteReaderInfo.getLinkedClasses())
+ {
+ remoteReader.shutdown();
+ }
+
+ remoteReaderInfo.getService().shutdown();
}
-
+
logger.debug("Shut down each consumer ...");
for (AbstractConsumer consumer : crudConsumers)
{
@@ -220,7 +227,13 @@ private void shutdownConsumer()
consumer.finalise();
}
- if (getConsumerEnvironment().getEventsEnabled() || (getConsumerEnvironment().getDelayedEnabled()))
+ for (AbstractFunctionalServiceConsumer consumer : fsServiceConsumers)
+ {
+ logger.debug("Shutdown " + consumer.getClass().getSimpleName());
+ consumer.finalise();
+ }
+
+ if (getConsumerEnvironment().getEventsEnabled() || (getConsumerEnvironment().getDelayedEnabled()))
{
logger.debug("Shutdown Event Subscription Connector...");
if (subscriptionConnector != null)
@@ -245,6 +258,10 @@ private void shutdownConsumer()
logger.debug("Release DB Connections....");
HibernateUtil.shutdown();
+
+ // All done but some background threads may not have fully shut down (eg. ExecutorServices). These may sleep
+ // up to a given time CommonConstants.MAX_SLEEP_MILLISEC. So let's quickly wait for them and the conclude...
+ doWait(CommonConstants.MAX_SLEEP_MILLISEC);
}
private ConsumerEnvironment getConsumerEnvironment()
@@ -264,28 +281,36 @@ private boolean initAsyncProcessor()
List allLocalQueueEventServices = new ArrayList();
List allLocalQueueCRUDServices = new ArrayList();
- for (AbstractEventConsumer> consumer : eventConsumers)
- {
- // Get Event services for this consumer
- if (getConsumerEnvironment().getEventsEnabled())
- {
- addServices(allLocalQueueEventServices, consumer.getEventServices(), consumer.getLocalConsumerQueue());
- }
-
- // Get CRUD services for this consumer
- if (getConsumerEnvironment().getDelayedEnabled())
- {
- addServices(allLocalQueueCRUDServices, consumer.getAllApprovedCRUDServices(), consumer.getLocalConsumerQueue());
- }
- }
+ // Get all services for all Event ONLY consumers
+ if (getConsumerEnvironment().getEventsEnabled())
+ {
+ for (AbstractEventConsumer> consumer : eventConsumers)
+ {
+ addServices(allLocalQueueEventServices, consumer.getEventServices(), consumer.getLocalConsumerQueue());
+ }
+ for (AbstractFunctionalServiceConsumer consumer : fsServiceConsumers)
+ {
+ addServices(allLocalQueueEventServices, consumer.getEventServices(), consumer.getLocalConsumerQueue());
+ }
+ }
- // Get all services for all CRUD ONLY consumers
+ // Get all services for all CRUD consumers
if (getConsumerEnvironment().getDelayedEnabled())
{
for (AbstractConsumer consumer : crudConsumers)
{
addServices(allLocalQueueCRUDServices, consumer.getAllApprovedCRUDServices(), consumer.getLocalConsumerQueue());
}
+
+ for (AbstractFunctionalServiceConsumer consumer : fsServiceConsumers)
+ {
+ addServices(allLocalQueueCRUDServices, consumer.getAllApprovedCRUDServices(), consumer.getLocalConsumerQueue());
+ }
+
+ for (AbstractEventConsumer> consumer : eventConsumers)
+ {
+ addServices(allLocalQueueCRUDServices, consumer.getAllApprovedCRUDServices(), consumer.getLocalConsumerQueue());
+ }
}
if (logger.isDebugEnabled())
@@ -347,20 +372,23 @@ private boolean startReadingRemoteQueues(HashMap queuesInfos)
/*
* Will initialise the threads and add them to the local consumer queue.
+ *
*/
- private ExecutorService startRemoteMessageReaderThreads(QueueInfo queueInfo, int numThreads)
+ private QueueReaderInfo startRemoteMessageReaderThreads(QueueInfo queueInfo, int numThreads)
{
String remoteQueueName = getRemoteQueueName(queueInfo);
logger.debug("Start "+numThreads+" message readers for "+remoteQueueName);
logger.debug("Total number of threads before starting message readers for "+remoteQueueName+" "+Thread.activeCount());
- ExecutorService service = Executors.newFixedThreadPool(numThreads);
+
+ QueueReaderInfo queueReaderInfo = new QueueReaderInfo(Executors.newFixedThreadPool(numThreads), new ArrayList());
for (int i = 0; i < numThreads; i++)
{
try
{
RemoteMessageQueueReader remoteReader = new RemoteMessageQueueReader(queueInfo, i);
logger.debug("Start Remote Reader "+remoteQueueName+" "+i);
- service.execute(remoteReader);
+ queueReaderInfo.getLinkedClasses().add(remoteReader);
+ queueReaderInfo.getService().execute(remoteReader);
}
catch (Exception ex)
{
@@ -369,7 +397,7 @@ private ExecutorService startRemoteMessageReaderThreads(QueueInfo queueInfo, int
}
logger.debug(numThreads+" "+remoteQueueName+" message readers initilaised and started.");
logger.debug("Total number of threads after starting message readers for "+remoteQueueName+" "+Thread.activeCount());
- return service;
+ return queueReaderInfo;
}
private void initialiseConsumers(AdvancedProperties adapterProps)
@@ -400,9 +428,14 @@ else if (classObj instanceof AbstractConsumer)
logger.debug("Added " + classObj.getClass().getSimpleName() + " to crudConsumer only list");
crudConsumers.add((AbstractConsumer) classObj);
}
+ else if (classObj instanceof AbstractFunctionalServiceConsumer)
+ {
+ logger.debug("Added " + classObj.getClass().getSimpleName() + " to fsServiceConsumers list");
+ fsServiceConsumers.add((AbstractFunctionalServiceConsumer) classObj);
+ }
else
{
- logger.error("Consumer class " + className + " doesn't extend AbstractConsumer or AbstractEventConsumer. Cannot initialse the Consumer.");
+ logger.error("Consumer class " + className + " doesn't extend AbstractConsumer, AbstractEventConsumer of AbstractFunctionalServiceConsumer. Cannot initialse the Consumer.");
}
}
catch (Exception ex)
@@ -429,4 +462,19 @@ private String getRemoteQueueName(QueueInfo queueInfo)
{
return queueInfo.getQueue().getName()+" ("+queueInfo.getQueue().getQueueID()+")";
}
+
+ private void doWait(long millisecs)
+ {
+ Object semaphore = new Object();
+ synchronized (semaphore)
+ {
+ try
+ {
+ semaphore.wait(millisecs);
+ }
+ catch (InterruptedException ex)
+ {
+ }
+ }
+ }
}
diff --git a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/provider/BaseEventProvider.java b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/provider/BaseEventProvider.java
index 66e0eb66..52c7a063 100644
--- a/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/provider/BaseEventProvider.java
+++ b/SIF3InfraREST/SIF3REST/src/main/java/sif3/infra/rest/provider/BaseEventProvider.java
@@ -20,27 +20,21 @@
import java.util.List;
-import javax.ws.rs.core.MediaType;
-
import sif3.common.CommonConstants;
import sif3.common.header.HeaderProperties;
import sif3.common.header.HeaderValues.EventAction;
import sif3.common.header.HeaderValues.ServiceType;
-import sif3.common.header.RequestHeaderConstants;
import sif3.common.interfaces.EventProvider;
import sif3.common.interfaces.SIFEventIterator;
+import sif3.common.model.ACL.AccessRight;
+import sif3.common.model.ACL.AccessType;
import sif3.common.model.SIFContext;
import sif3.common.model.SIFEvent;
import sif3.common.model.SIFZone;
import sif3.common.model.ServiceInfo;
-import sif3.common.model.ServiceRights.AccessRight;
-import sif3.common.model.ServiceRights.AccessType;
import sif3.common.model.ZoneContextInfo;
import sif3.common.persist.model.SIF3Session;
-import sif3.common.ws.BaseResponse;
import sif3.infra.common.env.mgr.BrokeredProviderEnvironmentManager;
-import sif3.infra.common.env.mgr.ProviderManagerFactory;
-import sif3.infra.common.interfaces.EnvironmentManager;
import sif3.infra.rest.client.EventClient;
@@ -74,103 +68,6 @@ public int getMaxObjectsInEvent()
return getServiceProperties().getPropertyAsInt(CommonConstants.EVENT_MAX_OBJ, getProviderName(), 10);
}
- /*------------------------------------------------------------------------------------------------------------------------
- * Start of 'Dynamic' HTTP Header Field override section for Events
- *
- * The following set of methods are used for a more configurable way how some HTTP header parameters are set.
- * By default the following HTTP Header fields are retrieved from the provider's property file and put in corresponding
- * HTTP Header Fields of each event:
- *
- * Property HTTP Header
- * ----------------------------------------------------------------
- * adapter.generator.id generatorId
- * env.application.key applicationKey
- * env.userToken authenticatedUser
- * env.mediaType Content-Type, Accept
- * adapter.compression.enabled Content-Encoding, Accept-Encoding
- *
- * Only properties that are not null or empty string will be set in the corresponding HTTP Header.
- *
- * There are situations where and application may need a more 'dynamic' behaviour where the above values are determined
- * at runtime, based on other circumstances and therefore these properties must be retrieved from an other source than the
- * providers's property file. In such a case the methods below can be overwritten to make them dynamic and controlled by
- * the implementation rather than driven by the provider's property file. If any of the methods below is overwritten then
- * the value of the over riding method is set in the corresponding HTTP Header field if the return value of the method
- * is not null or an empty string.
- *------------------------------------------------------------------------------------------------------------------------*/
-
- /**
- * This method returns the value of the adapter.generator.id property from the provider's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The adapter.generator.id property from the provider's property file
- */
- public String getGeneratorID()
- {
- return getProviderEnvironment().getGeneratorID();
- }
-
- /**
- * This method returns the value of the env.application.key property from the provider's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.application.key property from the provider's property file
- */
- public String getApplicationKey()
- {
- return getProviderEnvironment().getEnvironmentKey().getApplicationKey();
- }
-
- /**
- * This method returns the value of the env.userToken property from the provider's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.userToken property from the provider's property file
- */
- public String getAuthentictedUser()
- {
- return getProviderEnvironment().getEnvironmentKey().getUserToken();
- }
-
- /**
- * This method returns the value of the env.mediaType property from the provider's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.mediaType property from the provider's property file
- */
- public MediaType getRequestMediaType()
- {
- return getProviderEnvironment().getMediaType();
- }
-
- /**
- * This method returns the value of the env.mediaType property from the provider's property file. If that
- * needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The env.mediaType property from the provider's property file
- */
- public MediaType getResponseMediaType()
- {
- return getProviderEnvironment().getMediaType();
- }
-
- /**
- * This method returns the value of the adapter.compression.enabled property from the provider's property file. If
- * that needs to be overridden by a specific implementation then the specific sub-class should override this method.
- *
- * @return The adapter.compression.enabled property from the provider's property file
- */
- public boolean getCompressionEnabled()
- {
- return getProviderEnvironment().getCompressionEnabled();
- }
-
-
- /*------------------------------------------------------------------------------------------------------------------------
- * End of 'Dynamic' HTTP Header Field override section
- *-----------------------------------------------------------------------------------------------------------------------*/
-
-
/**
* This method retrieves all events to be published by calling the abstract method getSIFEvents(). The returned list
* is then broadcasted to all zones known to the implementing agent.
@@ -184,13 +81,14 @@ public void broadcastEvents()
int failedRecords = 0;
int maxNumObjPerEvent = getMaxObjectsInEvent();
- SIF3Session sif3Session = getActiveSession();
- if (sif3Session == null)
+ BrokeredProviderEnvironmentManager envMgr = getBrokeredEnvironmentManager();
+ if (envMgr == null) // not a brokered environment. Cannot sent events!
{
- return; //cannot send events. Error already logged.
+ return; // error already logged.
}
- List servicesForProvider = getServicesForProvider(sif3Session);
+ SIF3Session sif3Session = envMgr.getSIF3Session();
+ List servicesForProvider = getServicesForProvider(sif3Session, ServiceType.OBJECT);
// If there are no services for this provider defined then we don't need to get any events at all.
if ((servicesForProvider == null) || (servicesForProvider.size() == 0))
@@ -202,7 +100,7 @@ public void broadcastEvents()
{
// Let's get the Event Client
String serviceName = getServiceName();
- EventClient evtClient = new EventClient(getEnvironmentManager(), getRequestMediaType(), getResponseMediaType(), serviceName, getMarshaller(), getCompressionEnabled());
+ EventClient evtClient = new EventClient(envMgr, getRequestMediaType(), getResponseMediaType(), serviceName, getMarshaller(), getCompressionEnabled());
SIFEventIterator iterator = getSIFEvents();
if (iterator != null)
{
@@ -227,7 +125,7 @@ public void broadcastEvents()
for (ServiceInfo service : servicesForProvider)
{
// Check if provider has rights to publish to given zone and context
- if (hasAccess(service))
+ if (hasAccess(service, AccessRight.PROVIDE, AccessType.APPROVED))
{
failedRecords = failedRecords + prepareEventAndSend(evtClient, sifEvents, service.getZone(), service.getContext(), eventAction);
}
@@ -242,7 +140,7 @@ public void broadcastEvents()
for (ZoneContextInfo zoneCtxInfo : sifEvents.getLimitToZoneCtxList())
{
// Check if provider has rights to publish to given zone and context
- if (sif3Session.hasAccess(AccessRight.PROVIDE, AccessType.APPROVED, serviceName, zoneCtxInfo.getZone(), zoneCtxInfo.getContext()))
+ if (sif3Session.hasAccess(AccessRight.PROVIDE, AccessType.APPROVED, serviceName, null, zoneCtxInfo.getZone(), zoneCtxInfo.getContext()))
{
failedRecords = failedRecords + prepareEventAndSend(evtClient, sifEvents, zoneCtxInfo.getZone(), zoneCtxInfo.getContext(), eventAction);
}
@@ -297,7 +195,7 @@ protected int prepareEventAndSend(EventClient evtClient, SIFEvent