001package ca.uhn.fhir.util; 002 003/*- 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.i18n.Msg; 025import ca.uhn.fhir.rest.api.EncodingEnum; 026import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 027import com.google.common.base.Charsets; 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 javax.annotation.Nonnull; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.InputStreamReader; 038import java.io.Reader; 039import java.nio.charset.StandardCharsets; 040import java.util.function.Function; 041import java.util.zip.GZIPInputStream; 042 043/** 044 * Use this API with caution, it may change! 045 */ 046public class ClasspathUtil { 047 048 private static final Logger ourLog = LoggerFactory.getLogger(ClasspathUtil.class); 049 050 /** 051 * Non instantiable 052 */ 053 private ClasspathUtil() { 054 // nothing 055 } 056 057 /** 058 * Load a classpath resource, throw an {@link InternalErrorException} if not found 059 * 060 * @throws InternalErrorException If the resource can't be found 061 */ 062 public static String loadResource(String theClasspath) throws InternalErrorException { 063 return loadResource(theClasspath, Function.identity()); 064 } 065 066 /** 067 * Load a classpath resource, throw an {@link InternalErrorException} if not found 068 * 069 * @throws InternalErrorException If the resource can't be found 070 */ 071 @Nonnull 072 public static InputStream loadResourceAsStream(String theClasspath) throws InternalErrorException { 073 String classpath = theClasspath; 074 if (classpath.startsWith("classpath:")) { 075 classpath = classpath.substring("classpath:".length()); 076 } 077 078 InputStream retVal = ClasspathUtil.class.getResourceAsStream(classpath); 079 if (retVal == null) { 080 if (classpath.startsWith("/")) { 081 retVal = ClasspathUtil.class.getResourceAsStream(classpath.substring(1)); 082 } else { 083 retVal = ClasspathUtil.class.getResourceAsStream("/" + classpath); 084 } 085 if (retVal == null) { 086 throw new InternalErrorException(Msg.code(1758) + "Unable to find classpath resource: " + classpath); 087 } 088 } 089 return retVal; 090 } 091 092 public static Reader loadResourceAsReader(String theClasspath) { 093 return new InputStreamReader(loadResourceAsStream(theClasspath), StandardCharsets.UTF_8); 094 } 095 096 /** 097 * Load a classpath resource, throw an {@link InternalErrorException} if not found 098 */ 099 @Nonnull 100 public static String loadResource(String theClasspath, Function<InputStream, InputStream> theStreamTransform) { 101 try (InputStream stream = loadResourceAsStream(theClasspath)) { 102 InputStream newStream = theStreamTransform.apply(stream); 103 return IOUtils.toString(newStream, Charsets.UTF_8); 104 } catch (IOException e) { 105 throw new InternalErrorException(Msg.code(1759) + e); 106 } 107 } 108 109 @Nonnull 110 public static String loadCompressedResource(String theClasspath) { 111 Function<InputStream, InputStream> streamTransform = t -> { 112 try { 113 return new GZIPInputStream(t); 114 } catch (IOException e) { 115 throw new InternalErrorException(Msg.code(1760) + e); 116 } 117 }; 118 return loadResource(theClasspath, streamTransform); 119 } 120 121 /** 122 * Load a classpath resource, throw an {@link InternalErrorException} if not found 123 * 124 * @since 6.4.0 125 */ 126 @Nonnull 127 public static <T extends IBaseResource> T loadCompressedResource(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}