001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.utils; 018 019import static org.apache.juneau.commons.utils.IoUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022import java.io.*; 023import java.net.*; 024import java.nio.file.*; 025import java.util.*; 026import java.util.function.*; 027import java.util.jar.*; 028 029import org.apache.juneau.*; 030import org.apache.juneau.collections.*; 031 032/** 033 * Utility class for working with Jar manifest files. 034 * 035 * <p> 036 * Copies the contents of a {@link Manifest} into an {@link JsonMap} so that the various convenience methods on that 037 * class can be used to retrieve values. 038 * 039 * 040 * @serial exclude 041 */ 042@SuppressWarnings("resource") 043public class ManifestFile extends JsonMap { 044 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * Finds and loads the manifest file of the jar file that the specified class is contained within. 049 * 050 * @param c The class to get the manifest file of. 051 * @throws IOException If a problem occurred while trying to read the manifest file. 052 */ 053 public ManifestFile(Class<?> c) throws IOException { 054 var className = c.getSimpleName() + ".class"; 055 var classPath = c.getResource(className).toString(); 056 if (! classPath.startsWith("jar")) { 057 return; 058 } 059 var manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; 060 try { 061 var mf = new Manifest(new URL(manifestPath).openStream()); 062 load(mf); 063 } catch (MalformedURLException e) { 064 throw castException(IOException.class, e); 065 } catch (IOException e) { 066 e.printStackTrace(); 067 } 068 } 069 070 /** 071 * Create an instance of this class from a manifest file on the file system. 072 * 073 * @param f The manifest file. 074 * @throws IOException If a problem occurred while trying to read the manifest file. 075 */ 076 public ManifestFile(File f) throws IOException { 077 var mf = new Manifest(); 078 try (var fis = new FileInputStream(f)) { 079 mf.read(fis); 080 load(mf); 081 } catch (IOException e) { 082 throw ioex(e, "Problem detected in MANIFEST.MF. Contents below:\n{0}", read(f)); 083 } 084 } 085 086 /** 087 * Create an instance of this class loaded from the contents of an input stream. 088 * 089 * <p> 090 * Note that the input must end in a newline to pick up the last line! 091 * 092 * @param is The manifest file contents. 093 * @throws IOException If a problem occurred while trying to read the manifest file. 094 */ 095 public ManifestFile(InputStream is) throws IOException { 096 load(new Manifest(is)); 097 } 098 099 /** 100 * Create an instance of this class from a {@link Manifest} object. 101 * 102 * @param f The manifest to read from. 103 */ 104 public ManifestFile(Manifest f) { 105 load(f); 106 } 107 108 /** 109 * Create an instance of this class from a manifest path on the file system. 110 * 111 * @param path The manifest path. 112 * @throws IOException If a problem occurred while trying to read the manifest path. 113 */ 114 public ManifestFile(Path path) throws IOException { 115 var mf = new Manifest(); 116 try (var fis = Files.newInputStream(path)) { 117 mf.read(fis); 118 load(mf); 119 } catch (IOException e) { 120 throw ioex(e, "Problem detected in MANIFEST.MF. Contents below:\n{0}", read(path), e); 121 } 122 } 123 124 /** 125 * Create an instance of this class loaded from the contents of a reader. 126 * 127 * <p> 128 * Note that the input must end in a newline to pick up the last line! 129 * 130 * @param r The manifest file contents. 131 * @throws IOException If a problem occurred while trying to read the manifest file. 132 */ 133 public ManifestFile(Reader r) throws IOException { 134 load(new Manifest(new ByteArrayInputStream(read(r).getBytes(UTF8)))); 135 } 136 137 @Override /* Overridden from JsonMap */ 138 public ManifestFile append(Map<String,Object> values) { 139 super.append(values); 140 return this; 141 } 142 143 @Override /* Overridden from JsonMap */ 144 public ManifestFile append(String key, Object value) { 145 super.append(key, value); 146 return this; 147 } 148 149 @Override /* Overridden from JsonMap */ 150 public ManifestFile appendIf(boolean flag, String key, Object value) { 151 super.appendIf(flag, key, value); 152 return this; 153 } 154 155 @Override /* Overridden from JsonMap */ 156 public ManifestFile filtered(Predicate<Object> value) { 157 super.filtered(value); 158 return this; 159 } 160 161 @Override /* Overridden from JsonMap */ 162 public ManifestFile inner(Map<String,Object> inner) { 163 super.inner(inner); 164 return this; 165 } 166 167 @Override /* Overridden from JsonMap */ 168 public ManifestFile keepAll(String...keys) { 169 super.keepAll(keys); 170 return this; 171 } 172 173 @Override /* Overridden from JsonMap */ 174 public ManifestFile modifiable() { 175 return this; 176 } 177 178 @Override /* Overridden from JsonMap */ 179 public ManifestFile session(BeanSession session) { 180 super.session(session); 181 return this; 182 } 183 184 @Override /* Overridden from JsonMap */ 185 public ManifestFile setBeanSession(BeanSession value) { 186 super.setBeanSession(value); 187 return this; 188 } 189 190 @Override /* Overridden from Object */ 191 public String toString() { 192 var sb = new StringBuilder(); 193 forEach((k, v) -> sb.append(k).append(": ").append(v).append("\n")); 194 return sb.toString(); 195 } 196 197 @Override /* Overridden from JsonMap */ 198 public ManifestFile unmodifiable() { 199 return this; 200 } 201 202 private void load(Manifest mf) { 203 mf.getMainAttributes().forEach((k, v) -> put(k.toString(), v.toString())); 204 } 205}