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}