Class 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.
    • Constructor Detail

      • Main

        public Main()
    • 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 Model
        oauthSchema - - true indicates if the oauth model is added to the Physical Data Model
        javaBatchSchema - - true indicates if the model is added to the Physical Data Model
      • 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.
      • 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 now
        tenantName - the tenant's name
        tenantKey - tenant key
      • allocateTenant

        protected void allocateTenant()
        Allocate this tenant, creating new partitions if required.
      • 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 -
      • 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 -