Class Main


  • public class Main
    extends java.lang.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 Detail

      • MULTITENANT_FEATURE_ENABLED

        public java.util.List<DbType> MULTITENANT_FEATURE_ENABLED
      • STORED_PROCEDURE_ENABLED

        public java.util.List<DbType> STORED_PROCEDURE_ENABLED
      • PRIVILEGES_FEATURE_ENABLED

        public java.util.List<DbType> PRIVILEGES_FEATURE_ENABLED
      • ADMIN_SCHEMANAME

        public static final java.lang.String ADMIN_SCHEMANAME
        See Also:
        Constant Field Values
      • OAUTH_SCHEMANAME

        public static final java.lang.String OAUTH_SCHEMANAME
        See Also:
        Constant Field Values
      • BATCH_SCHEMANAME

        public static final java.lang.String BATCH_SCHEMANAME
        See Also:
        Constant Field Values
    • Constructor Detail

      • Main

        public Main()
    • Method Detail

      • createConnection

        protected java.sql.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
      • buildAdminSchemaModel

        protected void buildAdminSchemaModel​(PhysicalDataModel pdm)
        Add the admin schema objects to the PhysicalDataModel
        Parameters:
        pdm - the data model to build
      • buildOAuthSchemaModel

        protected void buildOAuthSchemaModel​(PhysicalDataModel pdm)
        Add the OAuth schema objects to the given PhysicalDataModel
        Parameters:
        pdm - the model to build
      • buildJavaBatchSchemaModel

        protected void buildJavaBatchSchemaModel​(PhysicalDataModel pdm)
        Add the JavaBatch schema objects to the given PhysicalDataModel
        Parameters:
        pdm -
      • checkCompatibility

        protected boolean checkCompatibility()
        specific feature to check if it is compatible.
        Returns:
      • createSchemas

        protected void createSchemas()
        Create the schemas
      • updateSchemas

        protected void updateSchemas()
        Process the schemas configured to be updated
      • buildFhirDataSchemaModel

        protected void buildFhirDataSchemaModel​(PhysicalDataModel pdm)
        Add FHIR data schema objects to the given PhysicalDataModel
        Parameters:
        pdm - the data model being built
      • updateFhirSchema

        protected void updateFhirSchema()
        Update the FHIR data schema
      • updateOauthSchema

        protected void updateOauthSchema()
        Build and apply the OAuth schema changes
      • updateJavaBatchSchema

        protected void updateJavaBatchSchema()
        Build and apply the JavaBatch schema changes
      • updateSchema

        protected boolean updateSchema​(PhysicalDataModel pdm)
        Update the schema associated with the given PhysicalDataModel
        Returns:
        true if the database is new
      • populateResourceTypeAndParameterNameTableEntries

        protected void populateResourceTypeAndParameterNameTableEntries​(java.lang.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
      • buildCommonModel

        protected void buildCommonModel​(PhysicalDataModel pdm,
                                        boolean addFhirDataSchema,
                                        boolean addOAuthSchema,
                                        boolean addJavaBatchSchema)
        Build a common PhysicalDataModel containing all the requested schemas
        Parameters:
        pdm - the model to construct
        addFhirDataSchema - include objects for the FHIR data schema
        addOAuthSchema - include objects for the Liberty OAuth schema
        addJavaBatchSchema - include objects for the Liberty JavaBatch schema
      • grantPrivilegesForFhirData

        protected void grantPrivilegesForFhirData()
        Apply grants to the FHIR data schema objects
      • grantPrivilegesForOAuth

        protected void grantPrivilegesForOAuth()
        Apply grants to the OAuth schema objects
      • grantPrivilegesForBatch

        protected void grantPrivilegesForBatch()
        Apply grants to the JavaBatch schema objects
      • 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 boolean checkIfTenantNameAndTenantKeyExists​(Db2Adapter adapter,
                                                              java.lang.String tenantName,
                                                              java.lang.String tenantKey,
                                                              boolean skip)
        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
        skip - whether or not to skip over cases where this tenantName/tenantKey combination already exists
        Returns:
        indicates if the tenantName/tenantKey exists
        Throws:
        java.lang.IllegalArgumentException - if the tenantName/tenantKey combination already exists and the skip argument is false
      • 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 java.util.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.
      • revokeTenantKey

        protected void revokeTenantKey()
        revokes a tenant key or if no tenant key is specified remove all of them for the given tenant.
      • parseArgs

        protected void parseArgs​(java.lang.String[] args)
        Parse the command-line arguments, building up the environment and establishing the run-list
        Parameters:
        args -
      • loadPropertyFile

        public void loadPropertyFile​(java.lang.String filename)
        Read the properties from the given file
        Parameters:
        filename -
      • addProperty

        public void addProperty​(java.lang.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
      • applyDataMigrationForV0014

        protected void applyDataMigrationForV0014()
      • 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
      • updateVacuumSettings

        public void updateVacuumSettings()
        updates the vacuum settings for postgres.
      • 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​(java.lang.String[] args)
        Main entry point
        Parameters:
        args -