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