Package com.ibm.fhir.schema.app
Class Main
- java.lang.Object
-
- com.ibm.fhir.schema.app.Main
-
public class Main extends Object
Utility app to connect to a database and create/update the IBM FHIR Server schema. The DDL processing is idempotent, with only the necessary changes applied.
This utility also includes an option to exercise the tenant partitioning code.
-
-
Field Summary
Fields Modifier and Type Field Description static String
ADMIN_SCHEMANAME
static String
BATCH_SCHEMANAME
static String
DATA_SCHEMANAME
List<DbType>
MULTITENANT_FEATURE_ENABLED
static String
OAUTH_SCHEMANAME
List<DbType>
PRIVILEGES_FEATURE_ENABLED
List<DbType>
STORED_PROCEDURE_ENABLED
-
Constructor Summary
Constructors Constructor Description Main()
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description void
addProperty(String pair)
Parse the given key=value string and add to the properties being collectedprotected void
addTenantKey()
Add a new tenant key so that we can rotate the values (add a new key, update config files, then remove the old key).protected void
allocateTenant()
Allocate this tenant, creating new partitions if required.protected void
applyDataMigrationForV0010()
Perform the special data migration steps required for the V0010 version of the schemaprotected void
applyModel(PhysicalDataModel pdm, IDatabaseAdapter adapter, ITaskCollector collector, VersionHistoryService vhs)
Start the schema object creation tasks and wait for everything to completeprotected void
backfillResourceChangeLog()
Backfill the RESOURCE_CHANGE_LOG table if it is emptyprotected void
backfillResourceChangeLogDb2(TenantInfo ti)
backfill the resource_change_log table if it is emptyprotected void
buildCommonModel(PhysicalDataModel pdm, boolean fhirSchema, boolean oauthSchema, boolean javaBatchSchema)
builds the common model based on the flags passed inprotected boolean
checkCompatibility()
specific feature to check if it is compatible.protected void
checkIfTenantNameAndTenantKeyExists(Db2Adapter adapter, String tenantName, String tenantKey)
checks if tenant name and tenant key exists.protected void
checkSchemaForTenant()
Run a check to make sure that if the tenant already exists its schema matches the specified schema.protected void
configureConnectionPool()
Create a simple connection pool associated with our data source so that we can perform the DDL deployment in parallelprotected Connection
createConnection()
protected void
createSchemas()
Create the schemasprotected void
deleteTenantMeta()
Delete all the metadata associated with the named tenant.protected void
detachTenantPartitions(PhysicalDataModel pdm, TenantInfo tenantInfo)
Temporarily suspend RI so that tables which are the subject of foreign key relationships can have their partitions dropped.protected void
dropDetachedPartitionTables()
Drop any tables which have previously been detached.protected void
dropDetachedPartitionTables(PhysicalDataModel pdm, TenantInfo tenantInfo)
Drop any tables which have previously been detached.protected void
dropSchema()
Drop all the objects in the admin and data schemas.protected void
dropTenant()
Deallocate this tenant, dropping all the related partitions.protected TenantInfo
freezeTenant()
Mark the tenant so that it can no longer be accessed (this prevents the SET_TENANT method from authenticating the tenantName/tenantKey pair, so the SV_TENANT_ID variable never gets set).protected int
getExitStatus()
Get the program exit status from the environmentprotected TenantInfo
getTenantInfo()
protected List<TenantInfo>
getTenantList()
Get the list of tenants and the schemas each is currently defined in.protected void
grantPrivileges()
Grant the minimum required set of privileges on the FHIR schema objects to the grantTo user.protected boolean
isMultitenant()
Do we want to build the multitenant variant of the schema (currently only supported by DB2)protected void
listTenants()
List the tenants currently configuredvoid
loadPropertyFile(String filename)
Read the properties from the given fileprotected void
logStatusMessage(int status)
Write a final status message - useful for QA to review when checking the outputstatic void
main(String[] args)
Main entry pointprotected void
parseArgs(String[] args)
Parse the command-line arguments, building up the environment and establishing the run-listprotected void
populateResourceTypeAndParameterNameTableEntries(Integer tenantId)
populates for the given tenantId the RESOURCE_TYPE table.protected void
process()
Process the requested operationprotected void
refreshTenants()
Make sure all the tables has a partition created for the configured tenantprotected void
testTenant()
Check that we can call the set_tenant procedure successfully (which means that the tenant record exists in the tenants table)protected void
updateProcedures()
Update the stored procedures used by FHIR to insert records into the FHIR resource tablesprotected void
updateSchema()
Update the schema
-
-
-
Field Detail
-
ADMIN_SCHEMANAME
public static final String ADMIN_SCHEMANAME
- See Also:
- Constant Field Values
-
OAUTH_SCHEMANAME
public static final String OAUTH_SCHEMANAME
- See Also:
- Constant Field Values
-
BATCH_SCHEMANAME
public static final String BATCH_SCHEMANAME
- See Also:
- Constant Field Values
-
DATA_SCHEMANAME
public static final String DATA_SCHEMANAME
- See Also:
- Constant Field Values
-
-
Method Detail
-
createConnection
protected Connection createConnection()
- Returns:
- a created connection to the selected database
-
configureConnectionPool
protected void configureConnectionPool()
Create a simple connection pool associated with our data source so that we can perform the DDL deployment in parallel
-
buildCommonModel
protected void buildCommonModel(PhysicalDataModel pdm, boolean fhirSchema, boolean oauthSchema, boolean javaBatchSchema)
builds the common model based on the flags passed in- Parameters:
pdm
-fhirSchema
- - true indicates if the fhir model is added to the Physical Data ModeloauthSchema
- - true indicates if the oauth model is added to the Physical Data ModeljavaBatchSchema
- - true indicates if the model is added to the Physical Data Model
-
applyModel
protected void applyModel(PhysicalDataModel pdm, IDatabaseAdapter adapter, ITaskCollector collector, VersionHistoryService vhs)
Start the schema object creation tasks and wait for everything to complete- Parameters:
pdm
-adapter
-collector
-vhs
-
-
checkCompatibility
protected boolean checkCompatibility()
specific feature to check if it is compatible.- Returns:
-
createSchemas
protected void createSchemas()
Create the schemas
-
updateSchema
protected void updateSchema()
Update the schema
-
populateResourceTypeAndParameterNameTableEntries
protected void populateResourceTypeAndParameterNameTableEntries(Integer tenantId)
populates for the given tenantId the RESOURCE_TYPE table.- Parameters:
tenantId
- the mt_id that is used to setup the partition. passing in null signals not multi-tenant.
-
dropSchema
protected void dropSchema()
Drop all the objects in the admin and data schemas. Typically used during development.
-
updateProcedures
protected void updateProcedures()
Update the stored procedures used by FHIR to insert records into the FHIR resource tables
-
grantPrivileges
protected void grantPrivileges()
Grant the minimum required set of privileges on the FHIR schema objects to the grantTo user. All tenant data access is via this user, and is the only user the FHIR server itself is configured with.
-
isMultitenant
protected boolean isMultitenant()
Do we want to build the multitenant variant of the schema (currently only supported by DB2)- Returns:
-
addTenantKey
protected void addTenantKey()
Add a new tenant key so that we can rotate the values (add a new key, update config files, then remove the old key). This avoids any service interruption.
-
checkIfTenantNameAndTenantKeyExists
protected void checkIfTenantNameAndTenantKeyExists(Db2Adapter adapter, String tenantName, String tenantKey)
checks if tenant name and tenant key exists.- Parameters:
adapter
- the db2 adapter as this is a db2 feature only nowtenantName
- the tenant's nametenantKey
- tenant key
-
allocateTenant
protected void allocateTenant()
Allocate this tenant, creating new partitions if required.
-
checkSchemaForTenant
protected void checkSchemaForTenant()
Run a check to make sure that if the tenant already exists its schema matches the specified schema. This prevents users from accidentally creating a second instance of a tenant in a different schema
-
getTenantList
protected List<TenantInfo> getTenantList()
Get the list of tenants and the schemas each is currently defined in. Because this tenant-schema mapping isn't stored directly, it has to be inferred by looking up the schema from one of the tables we know should exist. The schema name may therefore be null if the tenant record (in FHIR_ADMIN.TENANTS) exists, but all the tables from that schema have been dropped.- Returns:
-
refreshTenants
protected void refreshTenants()
Make sure all the tables has a partition created for the configured tenant
-
listTenants
protected void listTenants()
List the tenants currently configured
-
testTenant
protected void testTenant()
Check that we can call the set_tenant procedure successfully (which means that the tenant record exists in the tenants table)
-
getTenantInfo
protected TenantInfo getTenantInfo()
-
freezeTenant
protected TenantInfo freezeTenant()
Mark the tenant so that it can no longer be accessed (this prevents the SET_TENANT method from authenticating the tenantName/tenantKey pair, so the SV_TENANT_ID variable never gets set).- Returns:
- the TenantInfo associated with the tenant
-
dropTenant
protected void dropTenant()
Deallocate this tenant, dropping all the related partitions. This needs to be idempotent because there are steps which must be run in separate transactions (due to how Db2 handles detaching partitions). This is further complicated by referential integrity constraints in the schema. See: - https://www.ibm.com/support/knowledgecenter/SSEPGG_11.5.0/com.ibm.db2.luw.admin.partition.doc/doc/t0021576.html The workaround described in the above link is: // Change the RI constraint to informational: 1. ALTER TABLE child ALTER FOREIGN KEY fk NOT ENFORCED; 2. ALTER TABLE parent DETACH PARTITION p0 INTO TABLE pdet; 3. SET INTEGRITY FOR child OFF; // Change the RI constraint back to enforced: 4. ALTER TABLE child ALTER FOREIGN KEY fk ENFORCED; 5. SET INTEGRITY FOR child ALL IMMEDIATE UNCHECKED; 6. Assuming that the CHILD table does not have any dependencies on partition P0, 7. and that no updates on the CHILD table are permitted until this UOW is complete, no RI violation is possible during this UOW. COMMIT WORK; Unfortunately, #7 above essentially requires that all writes cease until the UOW is completed and the integrity is enabled again on all the child (leaf) tables. Of course, this could be relaxed if the application is trusted not to mess up the referential integrity...which we know to be true for the FHIR server persistence layer. If the risk is deemed too high, tenant removal should be performed in a maintenance window.
-
dropDetachedPartitionTables
protected void dropDetachedPartitionTables()
Drop any tables which have previously been detached. The detach process is asynchronous, so this is a sort of garbage collection, sweeping up cruft left in the database.
-
dropDetachedPartitionTables
protected void dropDetachedPartitionTables(PhysicalDataModel pdm, TenantInfo tenantInfo)
Drop any tables which have previously been detached. Once all tables have been dropped, we go on to drop the tablespace. If the tablespace drop is successful, we know the cleanup is complete so we can update the tenant status accordingly- Parameters:
pdm
-tenantInfo
-
-
detachTenantPartitions
protected void detachTenantPartitions(PhysicalDataModel pdm, TenantInfo tenantInfo)
Temporarily suspend RI so that tables which are the subject of foreign key relationships can have their partitions dropped. A bit frustrating to have to go through this, because the child table partition will be detached before the parent anyway, so theoretically this workaround shouldn't be necessary. ALTER TABLE child ALTER FOREIGN KEY fk NOT ENFORCED; ALTER TABLE parent DETACH PARTITION p0 INTO TABLE pdet; SET INTEGRITY FOR child OFF; ALTER TABLE child ALTER FOREIGN KEY fk ENFORCED; SET INTEGRITY FOR child ALL IMMEDIATE UNCHECKED;
-
deleteTenantMeta
protected void deleteTenantMeta()
Delete all the metadata associated with the named tenant.
-
parseArgs
protected void parseArgs(String[] args)
Parse the command-line arguments, building up the environment and establishing the run-list- Parameters:
args
-
-
loadPropertyFile
public void loadPropertyFile(String filename)
Read the properties from the given file- Parameters:
filename
-
-
addProperty
public void addProperty(String pair)
Parse the given key=value string and add to the properties being collected- Parameters:
pair
-
-
applyDataMigrationForV0010
protected void applyDataMigrationForV0010()
Perform the special data migration steps required for the V0010 version of the schema
-
backfillResourceChangeLog
protected void backfillResourceChangeLog()
Backfill the RESOURCE_CHANGE_LOG table if it is empty
-
backfillResourceChangeLogDb2
protected void backfillResourceChangeLogDb2(TenantInfo ti)
backfill the resource_change_log table if it is empty
-
process
protected void process()
Process the requested operation
-
getExitStatus
protected int getExitStatus()
Get the program exit status from the environment- Returns:
-
logStatusMessage
protected void logStatusMessage(int status)
Write a final status message - useful for QA to review when checking the output
-
main
public static void main(String[] args)
Main entry point- Parameters:
args
-
-
-