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}