001/*
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2024 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.jpa.provider;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
025import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
026import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
027import ca.uhn.fhir.jpa.model.util.JpaConstants;
028import ca.uhn.fhir.rest.param.DateRangeParam;
029import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
030import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
031import ca.uhn.fhir.util.ParametersUtil;
032import jakarta.servlet.http.HttpServletRequest;
033import org.apache.commons.lang3.StringUtils;
034import org.hl7.fhir.instance.model.api.IBaseParameters;
035import org.hl7.fhir.instance.model.api.IPrimitiveType;
036import org.jboss.logging.MDC;
037import org.springframework.beans.factory.annotation.Autowired;
038
039import java.util.Date;
040import java.util.Enumeration;
041import java.util.Set;
042import java.util.TreeSet;
043
044public abstract class BaseJpaProvider {
045        public static final String REMOTE_ADDR = "req.remoteAddr";
046        public static final String REMOTE_UA = "req.userAgent";
047        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaProvider.class);
048
049        @Autowired
050        protected JpaStorageSettings myStorageSettings;
051
052        @Autowired
053        private FhirContext myContext;
054
055        public BaseJpaProvider() {
056                super();
057        }
058
059        public void setStorageSettingsForUnitTest(JpaStorageSettings theStorageSettings) {
060                myStorageSettings = theStorageSettings;
061        }
062
063        protected ExpungeOptions createExpungeOptions(
064                        IPrimitiveType<? extends Integer> theLimit,
065                        IPrimitiveType<? extends Boolean> theExpungeDeletedResources,
066                        IPrimitiveType<? extends Boolean> theExpungeOldVersions,
067                        IPrimitiveType<? extends Boolean> theExpungeEverything) {
068                ExpungeOptions options = new ExpungeOptions();
069                if (theLimit != null && theLimit.getValue() != null) {
070                        options.setLimit(theLimit.getValue());
071                }
072
073                if (theExpungeOldVersions != null && theExpungeOldVersions.getValue() != null) {
074                        options.setExpungeOldVersions(theExpungeOldVersions.getValue());
075                }
076
077                if (theExpungeDeletedResources != null && theExpungeDeletedResources.getValue() != null) {
078                        options.setExpungeDeletedResources(theExpungeDeletedResources.getValue());
079                }
080
081                if (theExpungeEverything != null && theExpungeEverything.getValue() != null) {
082                        options.setExpungeEverything(theExpungeEverything.getValue());
083                }
084                return options;
085        }
086
087        protected IBaseParameters createExpungeResponse(ExpungeOutcome theOutcome) {
088                IBaseParameters parameters = ParametersUtil.newInstance(getContext());
089                String value = Integer.toString(theOutcome.getDeletedCount());
090                ParametersUtil.addParameterToParameters(
091                                getContext(), parameters, JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, "integer", value);
092                return parameters;
093        }
094
095        public void endRequest(ServletRequestDetails theRequest) {
096                endRequest(theRequest.getServletRequest());
097        }
098
099        public FhirContext getContext() {
100                return myContext;
101        }
102
103        public void setContext(FhirContext theContext) {
104                myContext = theContext;
105        }
106
107        protected DateRangeParam processSinceOrAt(Date theSince, DateRangeParam theAt) {
108                boolean haveAt =
109                                theAt != null && (theAt.getLowerBoundAsInstant() != null || theAt.getUpperBoundAsInstant() != null);
110                if (haveAt && theSince != null) {
111                        String msg = getContext().getLocalizer().getMessage(BaseJpaProvider.class, "cantCombintAtAndSince");
112                        throw new InvalidRequestException(Msg.code(553) + msg);
113                }
114
115                if (haveAt) {
116                        return theAt;
117                }
118
119                return new DateRangeParam(theSince, null);
120        }
121
122        /**
123         * @param theRequest The servlet request
124         */
125        public static void endRequest(HttpServletRequest theRequest) {
126                MDC.remove(REMOTE_ADDR);
127                MDC.remove(REMOTE_UA);
128        }
129
130        public static void startRequest(HttpServletRequest theRequest) {
131                if (theRequest == null) {
132                        return;
133                }
134
135                if (ourLog.isDebugEnabled()) {
136                        Set<String> headerNames = new TreeSet<>();
137                        for (Enumeration<String> enums = theRequest.getHeaderNames(); enums.hasMoreElements(); ) {
138                                headerNames.add(enums.nextElement());
139                        }
140                        ourLog.debug("Request headers: {}", headerNames);
141                }
142
143                Enumeration<String> forwardedFors = theRequest.getHeaders("x-forwarded-for");
144                StringBuilder b = new StringBuilder();
145                for (; forwardedFors != null && forwardedFors.hasMoreElements(); ) {
146                        if (b.length() > 0) {
147                                b.append(" / ");
148                        }
149                        b.append(forwardedFors.nextElement());
150                }
151
152                String forwardedFor = b.toString();
153                String ip = theRequest.getRemoteAddr();
154                if (StringUtils.isBlank(forwardedFor)) {
155                        org.slf4j.MDC.put(REMOTE_ADDR, ip);
156                        ourLog.debug("Request is from address: {}", ip);
157                } else {
158                        org.slf4j.MDC.put(REMOTE_ADDR, forwardedFor);
159                        ourLog.debug("Request is from forwarded address: {}", forwardedFor);
160                }
161
162                String userAgent = StringUtils.defaultString(theRequest.getHeader("user-agent"));
163                org.slf4j.MDC.put(REMOTE_UA, userAgent);
164        }
165
166        public static void startRequest(ServletRequestDetails theRequest) {
167                startRequest(theRequest.getServletRequest());
168        }
169}