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.common.utils.IOUtils.*;
020
021import java.io.*;
022import java.net.*;
023import java.nio.file.*;
024import java.util.jar.*;
025
026import org.apache.juneau.collections.*;
027import org.apache.juneau.common.utils.*;
028
029/**
030 * Utility class for working with Jar manifest files.
031 *
032 * <p>
033 * Copies the contents of a {@link Manifest} into an {@link JsonMap} so that the various convenience methods on that
034 * class can be used to retrieve values.
035 *
036 * <h5 class='section'>See Also:</h5><ul>
037
038 * </ul>
039 *
040 * @serial exclude
041 */
042public class ManifestFile extends JsonMap {
043
044   private static final long serialVersionUID = 1L;
045
046    /**
047     * Create an instance of this class from a manifest file on the file system.
048     *
049     * @param f The manifest file.
050     * @throws IOException If a problem occurred while trying to read the manifest file.
051     */
052    public ManifestFile(File f) throws IOException {
053        Manifest mf = new Manifest();
054        try (FileInputStream fis = new FileInputStream(f)) {
055            mf.read(fis);
056            load(mf);
057        } catch (IOException e) {
058            throw new IOException("Problem detected in MANIFEST.MF.  Contents below:\n"+read(f), e);
059        }
060    }
061
062    /**
063     * Create an instance of this class from a manifest path on the file system.
064     *
065     * @param path The manifest path.
066     * @throws IOException If a problem occurred while trying to read the manifest path.
067     */
068    public ManifestFile(Path path) throws IOException {
069        Manifest mf = new Manifest();
070        try (InputStream fis = Files.newInputStream(path)) {
071            mf.read(fis);
072            load(mf);
073        } catch (IOException e) {
074            throw new IOException("Problem detected in MANIFEST.MF.  Contents below:\n"+read(path), e);
075        }
076    }
077
078   /**
079    * Create an instance of this class from a {@link Manifest} object.
080    *
081    * @param f The manifest to read from.
082    */
083   public ManifestFile(Manifest f) {
084      load(f);
085   }
086
087   /**
088    * Finds and loads the manifest file of the jar file that the specified class is contained within.
089    *
090    * @param c The class to get the manifest file of.
091    * @throws IOException If a problem occurred while trying to read the manifest file.
092    */
093   public ManifestFile(Class<?> c) throws IOException {
094      String className = c.getSimpleName() + ".class";
095      String classPath = c.getResource(className).toString();
096      if (! classPath.startsWith("jar")) {
097         return;
098      }
099      String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +  "/META-INF/MANIFEST.MF";
100      try {
101         Manifest mf = new Manifest(new URL(manifestPath).openStream());
102         load(mf);
103      } catch (MalformedURLException e) {
104         throw ThrowableUtils.cast(IOException.class, e);
105      } catch (IOException e) {
106         e.printStackTrace();
107      }
108   }
109
110   /**
111    * Create an instance of this class loaded from the contents of a reader.
112    *
113    * <p>
114    * Note that the input must end in a newline to pick up the last line!
115    *
116    * @param r The manifest file contents.
117    * @throws IOException If a problem occurred while trying to read the manifest file.
118    */
119   public ManifestFile(Reader r) throws IOException {
120      load(new Manifest(new ByteArrayInputStream(read(r).getBytes(UTF8))));
121   }
122
123   /**
124    * Create an instance of this class loaded from the contents of an input stream.
125    *
126    * <p>
127    * Note that the input must end in a newline to pick up the last line!
128    *
129    * @param is The manifest file contents.
130    * @throws IOException If a problem occurred while trying to read the manifest file.
131    */
132   public ManifestFile(InputStream is) throws IOException {
133      load(new Manifest(is));
134   }
135
136   private void load(Manifest mf) {
137      mf.getMainAttributes().forEach((k,v) -> put(k.toString(), v.toString()));
138   }
139
140   @Override /* Object */
141   public String toString() {
142      StringBuilder sb = new StringBuilder();
143      forEach((k,v) -> sb.append(k).append(": ").append(v));
144      return sb.toString();
145   }
146}