001/*-
002 * #%L
003 * HAPI FHIR - Master Data Management
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.mdm.svc;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.mdm.api.IMdmLinkExpandSvc;
024import ca.uhn.fhir.mdm.api.IMdmSettings;
025import ca.uhn.fhir.mdm.api.MdmModeEnum;
026import ca.uhn.fhir.mdm.util.EIDHelper;
027
028/**
029 * Holder class that manages two different MDM expansion implementation approaches based on MdmSettings.
030 * <p>
031 * This class addresses the dependency injection challenge where MdmSettings may not be available when these
032 * service objects are created. It holds references to both implementation approaches and determines which ones
033 * to use based on the MdmSettings. setMdmSettings method should be called when MdmSettings is constructed or updated
034 * for proper functioning.
035 * <p>
036 * The two implementation approaches are:
037 * <p>
038 * <strong>1. Eid Match-Only Mode:</strong> A simplified approach where resources are matched based solely on
039 * Enterprise ID (EID) values without creating golden resources or MDM links. This mode is activated when
040 * MdmSettings specify MATCH_ONLY mode and EID systems are defined.
041 * <p>
042 * <strong>2. Full MDM Mode:</strong> The complete MDM solution that creates golden resources and manages
043 * MDM links between resources.
044 * <p>
045 * Each approach has two service implementations, one for mdm expansion for searches and the other for mdm expansion for group bulk export:
046 * <p>
047 * <strong>Eid Match-Only Mode implementations:</strong>
048 * - MdmEidMatchOnlyLinkExpandSvc
049 * - BulkExportMdmEidMatchOnlyResourceExpander
050 * <p>
051 * <strong>Full MDM Mode implementations:</strong>
052 * - MdmLinkExpandSvc
053 * - BulkExportMdmResourceExpander
054 * <p>
055 *
056 * This class holds references to these service objects rather than creating them by itself, because some of these service objects have Spring annotations
057 * like @Transactional, for those annotations to work the objects need to be created by Spring itself.
058 */
059public class MdmExpandersHolder {
060
061        /** MDM configuration settings used to determine which implementation to use */
062        private IMdmSettings myMdmSettings;
063
064        /** Cached instance of the selected link expand service */
065        private IMdmLinkExpandSvc myLinkExpandSvcInstanceToUse;
066
067        /** Cached instance of the selected bulk export resource expander */
068        private IBulkExportMdmResourceExpander myBulkExportMDMResourceExpanderInstanceToUse;
069
070        /** Full MDM link expand service implementation
071         * We have to use the interface as the type here instead of concrete implementing class MdmLinkExpandSvc
072         * because the class has Spring annotations like @Transactional which rely on a Proxy interface implementation and doesn't work if concrete classes are used as beans. */
073        private final IMdmLinkExpandSvc myMdmLinkExpandSvc;
074
075        /** EID-only match expand service implementation */
076        private final MdmEidMatchOnlyExpandSvc myMdmEidMatchOnlyExpandSvc;
077
078        /** Full MDM bulk export resource expander implementation */
079        private final BulkExportMdmResourceExpander myBulkExportMDMResourceExpander;
080
081        /** EID-only match bulk export resource expander implementation */
082        private final BulkExportMdmEidMatchOnlyResourceExpander myBulkExportMDMEidMatchOnlyResourceExpander;
083
084        private final FhirContext myFhirContext;
085
086        public MdmExpandersHolder(
087                        FhirContext theFhirContext,
088                        IMdmLinkExpandSvc theMdmLinkExpandSvc,
089                        MdmEidMatchOnlyExpandSvc theMdmEidMatchOnlyLinkExpandSvc,
090                        BulkExportMdmResourceExpander theBulkExportMDMResourceExpander,
091                        BulkExportMdmEidMatchOnlyResourceExpander theBulkExportMDMEidMatchOnlyResourceExpander) {
092
093                myFhirContext = theFhirContext;
094                myMdmLinkExpandSvc = theMdmLinkExpandSvc;
095                myMdmEidMatchOnlyExpandSvc = theMdmEidMatchOnlyLinkExpandSvc;
096                myBulkExportMDMResourceExpander = theBulkExportMDMResourceExpander;
097                myBulkExportMDMEidMatchOnlyResourceExpander = theBulkExportMDMEidMatchOnlyResourceExpander;
098        }
099
100        /**
101         * Returns the appropriate expand service instance appropriate for the mdm settings
102         */
103        public IMdmLinkExpandSvc getLinkExpandSvcInstance() {
104                if (myLinkExpandSvcInstanceToUse != null) {
105                        // we already determined instance to use, just return it
106                        return myLinkExpandSvcInstanceToUse;
107                }
108
109                myLinkExpandSvcInstanceToUse = determineExpandSvsInstanceToUse();
110
111                return myLinkExpandSvcInstanceToUse;
112        }
113
114        /**
115         * Returns the appropriate bulk export resource expander instance appropriate for the mdm settings
116         */
117        public IBulkExportMdmResourceExpander getBulkExportMDMResourceExpanderInstance() {
118                if (myBulkExportMDMResourceExpanderInstanceToUse != null) {
119                        // we already determined instance to use, just return it
120                        return myBulkExportMDMResourceExpanderInstanceToUse;
121                }
122
123                myBulkExportMDMResourceExpanderInstanceToUse = determineBulkExportMDMResourceExpanderInstanceToUse();
124
125                return myBulkExportMDMResourceExpanderInstanceToUse;
126        }
127
128        /**
129         * Determines which bulk export resource expander to use based on MDM mode and EID configuration.
130         */
131        public IBulkExportMdmResourceExpander determineBulkExportMDMResourceExpanderInstanceToUse() {
132                if (isMatchOnlyWithEidSystems()) {
133                        return myBulkExportMDMEidMatchOnlyResourceExpander;
134                } else {
135                        return myBulkExportMDMResourceExpander;
136                }
137        }
138
139        /**
140         * Determines if MDM is configured in MATCH_ONLY mode and EID systems are defined in the MDM rules.
141         */
142        private boolean isMatchOnlyWithEidSystems() {
143
144                if (myMdmSettings == null) {
145                        // if mdmSettings is not set yet, assume we are using the full mdm mode
146                        // to not break existing code, because previously we were just using the
147                        // full mdm implementation without checking the mdm settings.
148                        // This would be called again when mdmSettings setter is called
149                        return false;
150                }
151                boolean isMatchOnly = myMdmSettings.getMode() == MdmModeEnum.MATCH_ONLY;
152                boolean hasEidSystems = false;
153                if (myMdmSettings.getMdmRules() != null) {
154                        hasEidSystems = myMdmSettings.getMdmRules().getEnterpriseEIDSystems() != null
155                                        && !myMdmSettings.getMdmRules().getEnterpriseEIDSystems().isEmpty();
156                }
157                return isMatchOnly && hasEidSystems;
158        }
159
160        /**
161         * Determines which expand service to use and configures it if necessary.
162         */
163        private IMdmLinkExpandSvc determineExpandSvsInstanceToUse() {
164                if (isMatchOnlyWithEidSystems()) {
165                        myMdmEidMatchOnlyExpandSvc.setMyEidHelper(new EIDHelper(myFhirContext, myMdmSettings));
166                        return myMdmEidMatchOnlyExpandSvc;
167                } else {
168                        return myMdmLinkExpandSvc;
169                }
170        }
171
172        /**
173         * Sets the MDM settings and immediately determines which service implementations to use.
174         * This method is called after MDM settings become available during application startup.
175         */
176        public void setMdmSettings(IMdmSettings theMdmSettings) {
177                myMdmSettings = theMdmSettings;
178                myLinkExpandSvcInstanceToUse = determineExpandSvsInstanceToUse();
179                myBulkExportMDMResourceExpanderInstanceToUse = determineBulkExportMDMResourceExpanderInstanceToUse();
180        }
181}