001package ca.uhn.fhir.util.jar;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2022 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 java.io.InputStream;
024import java.net.MalformedURLException;
025import java.net.URL;
026import java.util.jar.Attributes;
027import java.util.jar.Manifest;
028
029import ca.uhn.fhir.util.XmlUtil;
030
031public class DependencyLogImpl implements IDependencyLog {
032        private static final Attributes.Name BUNDLE_SYMBOLIC_NAME = new Attributes.Name("Bundle-SymbolicName");
033        private static final Attributes.Name BUNDLE_VENDOR = new Attributes.Name("Bundle-Vendor");
034        private static final Attributes.Name BUNDLE_VERSION = new Attributes.Name("Bundle-Version");
035        private static final Attributes.Name IMPLEMENTATION_TITLE = new Attributes.Name("Implementation-Title");
036        private static final Attributes.Name IMPLEMENTATION_VENDOR = new Attributes.Name("Implementation-Vendor");
037        private static final Attributes.Name IMPLEMENTATION_VERSION = new Attributes.Name("Implementation-Version");
038        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
039        
040        @Override
041        public void logStaxImplementation(Class<?> theClass) {
042                try {
043                        URL rootUrl = getRootUrlForClass(theClass);
044                        if (rootUrl == null) {
045                                ourLog.info("Unable to determine location of StAX implementation containing class");
046                        } else {
047                                Manifest manifest;
048                                URL metaInfUrl = new URL(rootUrl, "META-INF/MANIFEST.MF");
049                                InputStream is = metaInfUrl.openStream();
050                                try {
051                                        manifest = new Manifest(is);
052                                } finally {
053                                        is.close();
054                                }
055                                Attributes attrs = manifest.getMainAttributes();
056                                String title = attrs.getValue(IMPLEMENTATION_TITLE);
057                                String symbolicName = attrs.getValue(BUNDLE_SYMBOLIC_NAME);
058                                if (symbolicName != null) {
059                                        int i = symbolicName.indexOf(';');
060                                        if (i != -1) {
061                                                symbolicName = symbolicName.substring(0, i);
062                                        }
063                                }
064                                String vendor = attrs.getValue(IMPLEMENTATION_VENDOR);
065                                if (vendor == null) {
066                                        vendor = attrs.getValue(BUNDLE_VENDOR);
067                                }
068                                String version = attrs.getValue(IMPLEMENTATION_VERSION);
069                                if (version == null) {
070                                        version = attrs.getValue(BUNDLE_VERSION);
071                                }
072                                if (ourLog.isDebugEnabled()) {
073                                        ourLog.debug("FHIR XML procesing will use StAX implementation at {}\n  Title:         {}\n  Symbolic name: {}\n  Vendor:        {}\n  Version:       {}", new Object[] { rootUrl, title, symbolicName, vendor, version } );
074                                } else {
075                                        ourLog.info("FHIR XML procesing will use StAX implementation '{}' version '{}'", title, version);
076                                }
077                        }
078                } catch (Throwable e) {
079                        ourLog.info("Unable to determine StAX implementation: " + e.getMessage());
080                }
081        }
082        
083        private static URL getRootUrlForClass(Class<?> cls) {
084                ClassLoader classLoader = cls.getClassLoader();
085                String resource = cls.getName().replace('.', '/') + ".class";
086                if (classLoader == null) {
087                        // A null class loader means the bootstrap class loader. In this case we use the
088                        // system class loader. This is safe since we can assume that the system class
089                        // loader uses parent first as delegation policy.
090                        classLoader = ClassLoader.getSystemClassLoader();
091                }
092                URL url = classLoader.getResource(resource);
093                if (url == null) {
094                        return null;
095                }
096                String file = url.getFile();
097                if (file.endsWith(resource)) {
098                        try {
099                                return new URL(url.getProtocol(), url.getHost(), url.getPort(), file.substring(0, file.length() - resource.length()));
100                        } catch (MalformedURLException ex) {
101                                return null;
102                        }
103                }
104                return null;
105        }
106}