001/*- 002 * #%L 003 * HAPI FHIR - Core Library 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.util; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.rest.api.EncodingEnum; 025import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 026import com.google.common.base.Charsets; 027import jakarta.annotation.Nonnull; 028import org.apache.commons.io.IOUtils; 029import org.apache.commons.io.input.BOMInputStream; 030import org.hl7.fhir.instance.model.api.IBaseResource; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import java.io.IOException; 035import java.io.InputStream; 036import java.io.InputStreamReader; 037import java.io.Reader; 038import java.nio.charset.StandardCharsets; 039import java.util.function.Function; 040import java.util.zip.GZIPInputStream; 041 042/** 043 * Use this API with caution, it may change! 044 */ 045public class ClasspathUtil { 046 047 private static final Logger ourLog = LoggerFactory.getLogger(ClasspathUtil.class); 048 049 /** 050 * Non instantiable 051 */ 052 private ClasspathUtil() { 053 // nothing 054 } 055 056 /** 057 * Load a classpath resource, throw an {@link InternalErrorException} if not found 058 * 059 * @throws InternalErrorException If the resource can't be found 060 */ 061 public static String loadResource(String theClasspath) throws InternalErrorException { 062 return loadResource(theClasspath, Function.identity()); 063 } 064 065 /** 066 * Load a classpath resource, throw an {@link InternalErrorException} if not found 067 * 068 * @throws InternalErrorException If the resource can't be found 069 */ 070 @Nonnull 071 public static InputStream loadResourceAsStream(String theClasspath) throws InternalErrorException { 072 String classpath = theClasspath; 073 if (classpath.startsWith("classpath:")) { 074 classpath = classpath.substring("classpath:".length()); 075 } 076 077 InputStream retVal = ClasspathUtil.class.getResourceAsStream(classpath); 078 if (retVal == null) { 079 if (classpath.startsWith("/")) { 080 retVal = ClasspathUtil.class.getResourceAsStream(classpath.substring(1)); 081 } else { 082 retVal = ClasspathUtil.class.getResourceAsStream("/" + classpath); 083 } 084 if (retVal == null) { 085 throw new InternalErrorException(Msg.code(1758) + "Unable to find classpath resource: " + classpath); 086 } 087 } 088 return retVal; 089 } 090 091 public static Reader loadResourceAsReader(String theClasspath) { 092 return new InputStreamReader(loadResourceAsStream(theClasspath), StandardCharsets.UTF_8); 093 } 094 095 /** 096 * Load a classpath resource, throw an {@link InternalErrorException} if not found 097 */ 098 @Nonnull 099 public static String loadResource(String theClasspath, Function<InputStream, InputStream> theStreamTransform) { 100 try (InputStream stream = loadResourceAsStream(theClasspath)) { 101 InputStream newStream = theStreamTransform.apply(stream); 102 return IOUtils.toString(newStream, Charsets.UTF_8); 103 } catch (IOException e) { 104 throw new InternalErrorException(Msg.code(1759) + e); 105 } 106 } 107 108 @Nonnull 109 public static String loadCompressedResource(String theClasspath) { 110 Function<InputStream, InputStream> streamTransform = t -> { 111 try { 112 return new GZIPInputStream(t); 113 } catch (IOException e) { 114 throw new InternalErrorException(Msg.code(1760) + e); 115 } 116 }; 117 return loadResource(theClasspath, streamTransform); 118 } 119 120 /** 121 * Load a classpath resource, throw an {@link InternalErrorException} if not found 122 * 123 * @since 6.4.0 124 */ 125 @Nonnull 126 public static <T extends IBaseResource> T loadCompressedResource( 127 FhirContext theCtx, Class<T> theType, String theClasspath) { 128 String resource = loadCompressedResource(theClasspath); 129 return parseResource(theCtx, theType, resource); 130 } 131 132 @Nonnull 133 public static <T extends IBaseResource> T loadResource(FhirContext theCtx, Class<T> theType, String theClasspath) { 134 String raw = loadResource(theClasspath); 135 return parseResource(theCtx, theType, raw); 136 } 137 138 private static <T extends IBaseResource> T parseResource(FhirContext theCtx, Class<T> theType, String raw) { 139 return EncodingEnum.detectEncodingNoDefault(raw).newParser(theCtx).parseResource(theType, raw); 140 } 141 142 public static void close(InputStream theInput) { 143 try { 144 if (theInput != null) { 145 theInput.close(); 146 } 147 } catch (IOException e) { 148 ourLog.debug("Closing InputStream threw exception", e); 149 } 150 } 151 152 public static Function<InputStream, InputStream> withBom() { 153 return t -> new BOMInputStream(t); 154 } 155 156 public static byte[] loadResourceAsByteArray(String theClasspath) { 157 InputStream stream = loadResourceAsStream(theClasspath); 158 try { 159 return IOUtils.toByteArray(stream); 160 } catch (IOException e) { 161 throw new InternalErrorException(Msg.code(1761) + e); 162 } finally { 163 close(stream); 164 } 165 } 166}