From 5eb0dc2adc658163ea7a3ccf37e5ac2a632914c0 Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Wed, 18 Dec 2019 23:35:41 -0500 Subject: [PATCH 1/3] issue #518 - remove audit db from default config Its simply not used any more, so its silly to keep creating it. I also removed the `resourceName` concept from both the Audit Context object and also the FHIRConfiguration config props. In the future, I'd also like to remove the undocumented `fhirServer/audit/patientIdExtensionUrl` config property, but that one wasn't quite as clean to remove right now...we need to think through whether/how to include the data subject (e.g. Patient) identifier in the audit log in a more FHIR-native way. Finally, I included some unrelated minor updates to the `fhir-persistence-schema` README.md; now the examples all use the `default` tenant...this is the simplest option because otherwise everyone needs to understand our tenancy model just to get something working on Db2. Signed-off-by: Lee Surprenant --- .../ibm/fhir/audit/logging/beans/Context.java | 23 ---------- .../beans/impl/context/FHIRContext.java | 15 ------- .../fhir/audit/cadf/test/AuditBeansTest.java | 12 ------ .../ibm/fhir/config/FHIRConfiguration.java | 3 +- fhir-persistence-schema/README.md | 25 +++++++---- .../config/default/fhir-server-config.json | 42 ++++--------------- .../ibm/fhir/server/util/RestAuditLogger.java | 5 --- 7 files changed, 26 insertions(+), 99 deletions(-) diff --git a/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/Context.java b/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/Context.java index 5684eefc706..e42505f8cf4 100644 --- a/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/Context.java +++ b/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/Context.java @@ -138,14 +138,6 @@ public void setRequestUniqueId(String requestUniqueId) { this.requestUniqueId = requestUniqueId; } - public String getResourceName() { - return resourceName; - } - - public void setResourceName(String resourceName) { - this.resourceName = resourceName; - } - /** * Generates JSON from this object. */ @@ -191,10 +183,6 @@ public static String generate(Context obj) generator.write("purpose", obj.getPurpose()); } - if (obj.getResourceName() != null) { - generator.write("resource_name", obj.getResourceName()); - } - if (obj.getStartTime() != null) { generator.write("start_time", obj.getStartTime()); } @@ -275,12 +263,6 @@ public static Context parse(InputStream in) builder.purpose(purpose); } - t = jsonObject.get("resource_name"); - if (t != null) { - String resourceName = jsonObject.getString("resource_name"); - builder.resourceName(resourceName); - } - t = jsonObject.get("start_time"); if (t != null) { String startTime = jsonObject.getString("start_time"); @@ -387,11 +369,6 @@ public Builder requestUniqueId(String requestUniqueId) { return this; } - public Builder resourceName(String resourceName) { - context.setResourceName(resourceName); - return this; - } - public Context build() { return context; } diff --git a/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/impl/context/FHIRContext.java b/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/impl/context/FHIRContext.java index 9bc0ab8f94a..6c8d8bd006a 100644 --- a/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/impl/context/FHIRContext.java +++ b/fhir-audit/src/main/java/com/ibm/fhir/audit/logging/beans/impl/context/FHIRContext.java @@ -124,10 +124,6 @@ public static String generate(FHIRContext obj) generator.write("purpose", obj.getPurpose()); } - if (obj.getResourceName() != null) { - generator.write("resource_name", obj.getResourceName()); - } - if (obj.getStartTime() != null) { generator.write("start_time", obj.getStartTime()); } @@ -228,12 +224,6 @@ public static FHIRContext parse(InputStream in) builder.purpose(purpose); } - t = jsonObject.get("resource_name"); - if (t != null) { - String resourceName = jsonObject.getString("resource_name"); - builder.resourceName(resourceName); - } - t = jsonObject.get("start_time"); if (t != null) { String startTime = jsonObject.getString("start_time"); @@ -395,11 +385,6 @@ public FHIRBuilder requestUniqueId(String requestUniqueId) { return this; } - public FHIRBuilder resourceName(String resourceName) { - fhirContext.setResourceName(resourceName); - return this; - } - public FHIRContext build() { return fhirContext; } diff --git a/fhir-audit/src/test/java/com/ibm/fhir/audit/cadf/test/AuditBeansTest.java b/fhir-audit/src/test/java/com/ibm/fhir/audit/cadf/test/AuditBeansTest.java index eee499fe423..04b6d4c485a 100644 --- a/fhir-audit/src/test/java/com/ibm/fhir/audit/cadf/test/AuditBeansTest.java +++ b/fhir-audit/src/test/java/com/ibm/fhir/audit/cadf/test/AuditBeansTest.java @@ -130,7 +130,6 @@ public void testContextEmptyNull() throws FHIRException, IOException { assertNull(ctx.getPurpose()); assertNull(ctx.getQueryParameters()); assertNull(ctx.getRequestUniqueId()); - assertNull(ctx.getResourceName()); assertNull(ctx.getStartTime()); } @@ -175,7 +174,6 @@ public void testContext() throws FHIRException, IOException { builder.purpose("purpose"); builder.queryParameters("queryParameters"); builder.requestUniqueId("requestUniqueId"); - builder.resourceName("test"); builder.startTime("startTime"); Context ctx = builder.build(); @@ -204,9 +202,6 @@ public void testContext() throws FHIRException, IOException { assertNotNull(ctx.getRequestUniqueId()); assertEquals(ctx.getRequestUniqueId(), "requestUniqueId"); - assertNotNull(ctx.getResourceName()); - assertEquals(ctx.getResourceName(), "test"); - assertNotNull(ctx.getStartTime()); assertEquals(ctx.getStartTime(), "startTime"); @@ -236,9 +231,6 @@ public void testContext() throws FHIRException, IOException { assertNotNull(ctx.getRequestUniqueId()); assertEquals(ctx.getRequestUniqueId(), "requestUniqueId"); - assertNotNull(ctx.getResourceName()); - assertEquals(ctx.getResourceName(), "test"); - assertNotNull(ctx.getStartTime()); assertEquals(ctx.getStartTime(), "startTime"); } @@ -349,7 +341,6 @@ public void testFHIRContext() throws IOException, FHIRException { builder.purpose("purpose"); builder.queryParameters("queryParameters"); builder.requestUniqueId("requestUniqueId"); - builder.resourceName("test"); builder.startTime("startTime"); builder.clientCertCn("clientCertCn"); @@ -386,9 +377,6 @@ public void testFHIRContext() throws IOException, FHIRException { assertNotNull(fhirCtx.getRequestUniqueId()); assertEquals(fhirCtx.getRequestUniqueId(), "requestUniqueId"); - assertNotNull(fhirCtx.getResourceName()); - assertEquals(fhirCtx.getResourceName(), "test"); - assertNotNull(fhirCtx.getStartTime()); assertEquals(fhirCtx.getStartTime(), "startTime"); diff --git a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java index 7e653d6da99..42fab83473c 100644 --- a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java +++ b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java @@ -48,8 +48,6 @@ public class FHIRConfiguration { public static final String PROPERTY_AUDIT_SERVICE_CLASS_NAME = "fhirServer/audit/serviceClassName"; public static final String PROPERTY_AUDIT_SERVICE_PROPERTIES = "fhirServer/audit/serviceProperties"; public static final String PROPERTY_AUDIT_PATIENT_ID_EXTURL = "fhirServer/audit/patientIdExtensionUrl"; - public static final String PROPERTY_AUDIT_RESOURCE_NAME_EXTURL = "fhirServer/audit/resourceNameExtensionUrl"; - public static final String PROPERTY_UPDATE_CREATE_ENABLED = "fhirServer/persistence/common/updateCreateEnabled"; // Notification config properties public static final String PROPERTY_NOTIFICATION_RESOURCE_TYPES = "fhirServer/notifications/common/includeResourceTypes"; @@ -59,6 +57,7 @@ public class FHIRConfiguration { public static final String PROPERTY_KAFKA_CONNECTIONPROPS = "fhirServer/notifications/kafka/connectionProperties"; // Persistence layer properties + public static final String PROPERTY_UPDATE_CREATE_ENABLED = "fhirServer/persistence/common/updateCreateEnabled"; public static final String PROPERTY_PERSISTENCE_FACTORY = "fhirServer/persistence/factoryClassname"; public static final String PROPERTY_DATASOURCES = "fhirServer/persistence/datasources"; public static final String PROPERTY_JDBC_BOOTSTRAP_DB = "fhirServer/persistence/jdbc/bootstrapDb"; diff --git a/fhir-persistence-schema/README.md b/fhir-persistence-schema/README.md index a0141efde04..b74202ef9c7 100644 --- a/fhir-persistence-schema/README.md +++ b/fhir-persistence-schema/README.md @@ -58,28 +58,33 @@ The following sections include common values for `OPTIONS`: --update-schema ``` -## Add a new tenant (e.g. tenant1) - -Don't forget to copy the tenant-key secret generated by --allocate-tenant. This key must be added to the datasource configuration to authorize the FHIR server to access this tenant. +## Grant privileges to data access user ``` --prop-file fhir.properties --schema-name FHIRDATA ---allocate-tenant tenant1 +--grant-to FHIRSERVER ``` +## Add a new tenant (e.g. default) -## Grant privileges to data access user +Don't forget to copy the tenant-key secret generated by --allocate-tenant. This key must be added to the datasource configuration to authorize the FHIR server to access this tenant. ``` --prop-file fhir.properties --schema-name FHIRDATA ---grant-to FHIRSERVER +--allocate-tenant default ``` +Note: for tenant names other than `default`, the server must determine the tenant id to use for each request. +By default, we get the tenant id from the `X-FHIR-TENANT-ID` header, but to trust this value requires a well-planned approach to security. +Once the server has determined the tenant id for a given request, it uses this to look up the tenantKey and the two are +used in conjunction to create or retrieve data for this tenant. +For more information on multi-tenancy, see section [4.9 Multi-tenancy of the IBM FHIR Server Users Guide](https://ibm.github.io/FHIR/guides/FHIRServerUsersGuide#49-multi-tenancy). + ## Configure tenant-key (example) -Edit `wlp/usr/servers/fhir-server/config/tenant1/fhir-server-config.json` and add the tenant-key captured from the add-tenant step above: +Edit `wlp/usr/servers/fhir-server/config/default/fhir-server-config.json` and add the tenant-key captured from the add-tenant step above: ``` "default": { @@ -101,12 +106,13 @@ Edit `wlp/usr/servers/fhir-server/config/tenant1/fhir-server-config.json` and ad } ``` + ## Test a tenant ``` --prop-file fhir-apikey.properties --schema-name FHIRDATA ---test-tenant TNT1 +--test-tenant default --tenant-key "" ``` @@ -118,9 +124,10 @@ Edit `wlp/usr/servers/fhir-server/config/tenant1/fhir-server-config.json` and ad --update-proc ``` -# Alternative: manually apply the schema changes +# Alternative: manually apply the schema To manually apply the DDL to a Db2 instance: + 1. Print the schema to files by executing the SchemaPrinter: `java -cp ./fhir-database-utils.jar:fhir-persistence-schema.jar com.ibm.fhir.schema.app.SchemaPrinter --to-file` diff --git a/fhir-server/liberty-config/config/default/fhir-server-config.json b/fhir-server/liberty-config/config/default/fhir-server-config.json index 2d4374fd988..7f70f302020 100644 --- a/fhir-server/liberty-config/config/default/fhir-server-config.json +++ b/fhir-server/liberty-config/config/default/fhir-server-config.json @@ -66,6 +66,15 @@ "__comment": "Configuration properties common to all persistence layer implementations", "updateCreateEnabled": true }, + "jdbc": { + "__comment": "Configuration properties for the JDBC persistence implementation", + "bootstrapDb": true, + "__comment": "Use jdbc/fhirProxyDataSource to use the proxy datasource.", + "dataSourceJndiName": "jdbc/fhirProxyDataSource", + "enableCodeSystemsCache": true, + "enableParameterNamesCache": true, + "enableResourceTypesCache": true + }, "datasources": { "default": { "type": "derby", @@ -88,40 +97,7 @@ "sslTrustStoreLocation": "resources/security/fhirTruststore.jks", "sslTrustStorePassword": "change-password" } - }, - "audit": { - "type": "derby", - "connectionProperties": { - "databaseName": "derby/auditDB", - "createDatabase": "create" - } - }, - "_db2_audit": { - "type": "db2", - "connectionProperties": { - "serverName": "localhost", - "portNumber": 50001, - "user": "db2inst1", - "password": "change-password", - "databaseName": "AUDITDB", - "currentSchema": "AUDIT1", - "driverType": 4, - "sslConnection": true, - "sslTrustStoreLocation": "resources/security/fhirTruststore.jks", - "sslTrustStorePassword": "change-password" - } } - }, - "jdbc": { - "__comment": "Configuration properties for the JDBC persistence implementation, Use jdbc/fhirProxyDataSource to use the proxy datasource.", - "bootstrapDb": true, - "dataSourceJndiName": "jdbc/fhirProxyDataSource", - "enableCodeSystemsCache": true, - "enableParameterNamesCache": true, - "enableResourceTypesCache": true - }, - "audit": { - "bootstrapDb": true } } } diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/com/ibm/fhir/server/util/RestAuditLogger.java index 140c0c470e3..d45396c34a2 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/util/RestAuditLogger.java @@ -46,8 +46,6 @@ /** * This class provides convenience methods for FHIR Rest services that need to write FHIR audit log entries. - * @author markd - * */ public class RestAuditLogger { @@ -462,7 +460,6 @@ private static AuditLogEntry populateAuditLogEntry(AuditLogEntry entry, HttpServ StringBuffer requestUrl; String patientIdExtUrl; - String resourceNameExtUrl; List userList = new ArrayList<>(); // Build a list of possible user names. Pick the first non-null user name to include in the audit log entry. @@ -512,8 +509,6 @@ private static AuditLogEntry populateAuditLogEntry(AuditLogEntry entry, HttpServ patientIdExtUrl = FHIRConfigHelper.getStringProperty(FHIRConfiguration.PROPERTY_AUDIT_PATIENT_ID_EXTURL, null); entry.setPatientId(FHIRUtil.getExtensionStringValue(resource, patientIdExtUrl)); - resourceNameExtUrl = FHIRConfigHelper.getStringProperty(FHIRConfiguration.PROPERTY_AUDIT_RESOURCE_NAME_EXTURL, null); - entry.getContext().setResourceName((FHIRUtil.getExtensionStringValue(resource, resourceNameExtUrl))); entry.getContext().setRequestUniqueId(FHIRRequestContext.get().getRequestUniqueId()); log.exiting(CLASSNAME, METHODNAME); From 2bcd522a1a6db1213b4caeb384e91b7972074481 Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Wed, 18 Dec 2019 11:27:23 -0500 Subject: [PATCH 2/3] Update Db2Adapter.createFhirSchemas to succeed when schemas exist Without this, the step always fails if you've already created a schema on this db because FHIR_ADMIN already exists. Now it will succeed but print a WARNING to indicate it already existed. 1. introduced DuplicateNameException and a corresponding helper to identify when schema creation fails due to the schema already existing 2. renamed DuplicateValueException to UniquenessViolationException to avoid confusion with DuplicateNameException 3. updated Db2Adapter to catch the DuplicateNameException and WARN instead of failing Also added log statement at the end of tenant creation so that you don't need to scroll to the very top of the output to find the generated key Signed-off-by: Lee Surprenant --- .../utils/api/DuplicateNameException.java | 23 +++++++++++++++++++ .../utils/api/IDatabaseTranslator.java | 8 +++++++ ...java => UniquenessViolationException.java} | 6 ++--- .../utils/common/CommonDatabaseAdapter.java | 4 ++-- .../fhir/database/utils/db2/Db2Adapter.java | 17 ++++++++++---- .../database/utils/db2/Db2Translator.java | 18 +++++++++++---- .../database/utils/derby/DerbyTranslator.java | 9 ++++++-- .../java/com/ibm/fhir/schema/app/Main.java | 2 ++ 8 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateNameException.java rename fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/{DuplicateValueException.java => UniquenessViolationException.java} (65%) diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateNameException.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateNameException.java new file mode 100644 index 00000000000..fb388ad9117 --- /dev/null +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateNameException.java @@ -0,0 +1,23 @@ +/* + * (C) Copyright IBM Corp. 2019 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.database.utils.api; + +/** + * The name of the object to be created is identical to an existing name. + */ +public class DuplicateNameException extends DataAccessException { + // Generated serial number + private static final long serialVersionUID = 4082017119186491651L; + + public DuplicateNameException(Throwable t) { + super(t); + } + + public DuplicateNameException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/IDatabaseTranslator.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/IDatabaseTranslator.java index 140b975a461..e93a67208a8 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/IDatabaseTranslator.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/IDatabaseTranslator.java @@ -50,6 +50,14 @@ public interface IDatabaseTranslator { */ boolean isDuplicate(SQLException x); + /** + * Check the exception to see if it is reporting that THE NAME OF THE OBJECT TO BE CREATED + * OR THE TARGET OF A RENAME STATEMENT IS IDENTICAL TO THE EXISTING NAME OF THE OBJECT TYPE + * @param x + * @return + */ + boolean isAlreadyExists(SQLException x); + /** * Database timed out waiting to get a lock. This is not the same as a deadlock, of course * @param x diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateValueException.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java similarity index 65% rename from fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateValueException.java rename to fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java index de3d4a97ee2..f41ec7c387f 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/DuplicateValueException.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java @@ -9,16 +9,16 @@ /** * Translation of a duplicate key or value constraint SQLException. */ -public class DuplicateValueException extends DataAccessException { +public class UniquenessViolationException extends DataAccessException { // Generated serial number private static final long serialVersionUID = -2753101534110619540L; - public DuplicateValueException(Throwable t) { + public UniquenessViolationException(Throwable t) { super(t); } - public DuplicateValueException(String msg, Throwable t) { + public UniquenessViolationException(String msg, Throwable t) { super(msg, t); } } diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java index 486c0099240..3e6b3267d09 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java @@ -16,7 +16,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import com.ibm.fhir.database.utils.api.DuplicateValueException; +import com.ibm.fhir.database.utils.api.UniquenessViolationException; import com.ibm.fhir.database.utils.api.IConnectionProvider; import com.ibm.fhir.database.utils.api.IDatabaseAdapter; import com.ibm.fhir.database.utils.api.IDatabaseStatement; @@ -345,7 +345,7 @@ public int allocateTenant(String adminSchemaName, String schemaName, String tena AddTenantDAO adder = new AddTenantDAO(adminSchemaName, tenantId, tenantName); runStatement(adder); } - catch (DuplicateValueException x) { + catch (UniquenessViolationException x) { // Concurrent operation, so try again logger.info("Duplicate value, so try the next one"); tenantId = 0; diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Adapter.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Adapter.java index 40eb0a8ac6c..f93d2a0c4c8 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Adapter.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Adapter.java @@ -20,6 +20,7 @@ import java.util.logging.Logger; import com.ibm.fhir.database.utils.api.DataAccessException; +import com.ibm.fhir.database.utils.api.DuplicateNameException; import com.ibm.fhir.database.utils.api.IConnectionProvider; import com.ibm.fhir.database.utils.api.IDatabaseTarget; import com.ibm.fhir.database.utils.api.ITransaction; @@ -430,9 +431,17 @@ public boolean checkCompatibility(String adminSchema) { @Override public void createFhirSchemas(String schemaName, String adminSchemaName) { - String ddl = "CREATE SCHEMA " + schemaName; - runStatement(ddl); - ddl = "CREATE SCHEMA " + adminSchemaName; - runStatement(ddl); + try { + String ddl = "CREATE SCHEMA " + schemaName; + runStatement(ddl); + } catch (DuplicateNameException e) { + logger.log(Level.WARNING, "The schema '" + schemaName + "' already exists; proceed with caution."); + } + try { + String ddl = "CREATE SCHEMA " + adminSchemaName; + runStatement(ddl); + } catch (DuplicateNameException e) { + logger.log(Level.WARNING, "The schema '" + adminSchemaName + "' already exists; proceed with caution."); + } } } diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java index c86131d71ad..12efd2a50e6 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java @@ -12,7 +12,8 @@ import com.ibm.fhir.database.utils.api.ConnectionDetails; import com.ibm.fhir.database.utils.api.ConnectionException; import com.ibm.fhir.database.utils.api.DataAccessException; -import com.ibm.fhir.database.utils.api.DuplicateValueException; +import com.ibm.fhir.database.utils.api.DuplicateNameException; +import com.ibm.fhir.database.utils.api.UniquenessViolationException; import com.ibm.fhir.database.utils.api.IDatabaseTranslator; import com.ibm.fhir.database.utils.api.LockException; import com.ibm.fhir.database.utils.api.UndefinedNameException; @@ -49,6 +50,12 @@ public boolean isDuplicate(SQLException x) { return "23505".equals(x.getSQLState()); } + @Override + public boolean isAlreadyExists(SQLException x) { + // SQL-Error: -601, SQL State 42710 + return "42710".equals(x.getSQLState()); + } + @Override public boolean isLockTimeout(SQLException x) { // lock timeout (not deadlock) @@ -88,7 +95,10 @@ else if (isConnectionError(x)) { return new ConnectionException(x); } else if (isDuplicate(x)) { - return new DuplicateValueException(x); + return new UniquenessViolationException(x); + } + else if (isAlreadyExists(x)) { + return new DuplicateNameException(x); } else if (isUndefinedName(x)) { return new UndefinedNameException(x); @@ -145,7 +155,7 @@ public String timestampDiff(String left, String right, String alias) { return String.format("timestampdiff(2, %s, %s)", left, right); } else { - return String.format("timestampdiff(2, %s, %s) AS %s", left, right, alias); + return String.format("timestampdiff(2, %s, %s) AS %s", left, right, alias); } } @@ -167,7 +177,7 @@ public String getDriverClassName() { @Override public String getUrl(Properties connectionProperties) { Db2PropertyAdapter adapter = new Db2PropertyAdapter(connectionProperties); - return "jdbc:db2://" + adapter.getHost() + ":" + adapter.getPort() + "/" + adapter.getDatabase(); + return "jdbc:db2://" + adapter.getHost() + ":" + adapter.getPort() + "/" + adapter.getDatabase(); } @Override diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java index 0bc70b7fac7..2a6d2ebaa39 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java @@ -13,7 +13,7 @@ import com.ibm.fhir.database.utils.api.ConnectionDetails; import com.ibm.fhir.database.utils.api.ConnectionException; import com.ibm.fhir.database.utils.api.DataAccessException; -import com.ibm.fhir.database.utils.api.DuplicateValueException; +import com.ibm.fhir.database.utils.api.UniquenessViolationException; import com.ibm.fhir.database.utils.api.IDatabaseTranslator; import com.ibm.fhir.database.utils.api.LockException; import com.ibm.fhir.database.utils.api.UndefinedNameException; @@ -50,6 +50,11 @@ public boolean isDuplicate(SQLException x) { return "23505".equals(x.getSQLState()); } + @Override + public boolean isAlreadyExists(SQLException x) { + return "42710".equals(x.getSQLState()); + } + @Override public boolean isLockTimeout(SQLException x) { return false; @@ -79,7 +84,7 @@ else if (isConnectionError(x)) { return new ConnectionException(x); } else if (isDuplicate(x)) { - return new DuplicateValueException(x); + return new UniquenessViolationException(x); } else if (isUndefinedName(x)) { return new UndefinedNameException(x); diff --git a/fhir-persistence-schema/src/main/java/com/ibm/fhir/schema/app/Main.java b/fhir-persistence-schema/src/main/java/com/ibm/fhir/schema/app/Main.java index 2a61c39e86d..6455c9a9954 100644 --- a/fhir-persistence-schema/src/main/java/com/ibm/fhir/schema/app/Main.java +++ b/fhir-persistence-schema/src/main/java/com/ibm/fhir/schema/app/Main.java @@ -768,6 +768,8 @@ protected void allocateTenant() { throw x; } } + + logger.info("Allocated tenant: " + tenantName + " [key=" + tenantKey + "] with Id = " + tenantId); } /** From 0ca117c94c0405054d9eff067f4a4a3f17fbb318 Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Thu, 19 Dec 2019 07:35:04 -0500 Subject: [PATCH 3/3] Rename based on PR feedback Signed-off-by: Lee Surprenant --- ...ception.java => UniqueConstraintViolationException.java} | 6 +++--- .../fhir/database/utils/common/CommonDatabaseAdapter.java | 4 ++-- .../java/com/ibm/fhir/database/utils/db2/Db2Translator.java | 4 ++-- .../com/ibm/fhir/database/utils/derby/DerbyTranslator.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/{UniquenessViolationException.java => UniqueConstraintViolationException.java} (63%) diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniqueConstraintViolationException.java similarity index 63% rename from fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java rename to fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniqueConstraintViolationException.java index f41ec7c387f..5959ee33f1c 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniquenessViolationException.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/api/UniqueConstraintViolationException.java @@ -9,16 +9,16 @@ /** * Translation of a duplicate key or value constraint SQLException. */ -public class UniquenessViolationException extends DataAccessException { +public class UniqueConstraintViolationException extends DataAccessException { // Generated serial number private static final long serialVersionUID = -2753101534110619540L; - public UniquenessViolationException(Throwable t) { + public UniqueConstraintViolationException(Throwable t) { super(t); } - public UniquenessViolationException(String msg, Throwable t) { + public UniqueConstraintViolationException(String msg, Throwable t) { super(msg, t); } } diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java index 3e6b3267d09..e8e1749d79b 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/common/CommonDatabaseAdapter.java @@ -16,7 +16,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import com.ibm.fhir.database.utils.api.UniquenessViolationException; +import com.ibm.fhir.database.utils.api.UniqueConstraintViolationException; import com.ibm.fhir.database.utils.api.IConnectionProvider; import com.ibm.fhir.database.utils.api.IDatabaseAdapter; import com.ibm.fhir.database.utils.api.IDatabaseStatement; @@ -345,7 +345,7 @@ public int allocateTenant(String adminSchemaName, String schemaName, String tena AddTenantDAO adder = new AddTenantDAO(adminSchemaName, tenantId, tenantName); runStatement(adder); } - catch (UniquenessViolationException x) { + catch (UniqueConstraintViolationException x) { // Concurrent operation, so try again logger.info("Duplicate value, so try the next one"); tenantId = 0; diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java index 12efd2a50e6..831506c45f8 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/db2/Db2Translator.java @@ -13,7 +13,7 @@ import com.ibm.fhir.database.utils.api.ConnectionException; import com.ibm.fhir.database.utils.api.DataAccessException; import com.ibm.fhir.database.utils.api.DuplicateNameException; -import com.ibm.fhir.database.utils.api.UniquenessViolationException; +import com.ibm.fhir.database.utils.api.UniqueConstraintViolationException; import com.ibm.fhir.database.utils.api.IDatabaseTranslator; import com.ibm.fhir.database.utils.api.LockException; import com.ibm.fhir.database.utils.api.UndefinedNameException; @@ -95,7 +95,7 @@ else if (isConnectionError(x)) { return new ConnectionException(x); } else if (isDuplicate(x)) { - return new UniquenessViolationException(x); + return new UniqueConstraintViolationException(x); } else if (isAlreadyExists(x)) { return new DuplicateNameException(x); diff --git a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java index 2a6d2ebaa39..57c5f58376b 100644 --- a/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java +++ b/fhir-database-utils/src/main/java/com/ibm/fhir/database/utils/derby/DerbyTranslator.java @@ -13,7 +13,7 @@ import com.ibm.fhir.database.utils.api.ConnectionDetails; import com.ibm.fhir.database.utils.api.ConnectionException; import com.ibm.fhir.database.utils.api.DataAccessException; -import com.ibm.fhir.database.utils.api.UniquenessViolationException; +import com.ibm.fhir.database.utils.api.UniqueConstraintViolationException; import com.ibm.fhir.database.utils.api.IDatabaseTranslator; import com.ibm.fhir.database.utils.api.LockException; import com.ibm.fhir.database.utils.api.UndefinedNameException; @@ -84,7 +84,7 @@ else if (isConnectionError(x)) { return new ConnectionException(x); } else if (isDuplicate(x)) { - return new UniquenessViolationException(x); + return new UniqueConstraintViolationException(x); } else if (isUndefinedName(x)) { return new UndefinedNameException(x);