001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.utils;
014
015import static org.apache.juneau.common.internal.IOUtils.*;
016
017import java.io.*;
018import java.net.*;
019import java.util.jar.*;
020
021import org.apache.juneau.collections.*;
022import org.apache.juneau.common.internal.*;
023
024/**
025 * Utility class for working with Jar manifest files.
026 *
027 * <p>
028 * Copies the contents of a {@link Manifest} into an {@link JsonMap} so that the various convenience methods on that
029 * class can be used to retrieve values.
030 *
031 * <h5 class='section'>See Also:</h5><ul>
032
033 * </ul>
034 *
035 * @serial exclude
036 */
037public class ManifestFile extends JsonMap {
038
039   private static final long serialVersionUID = 1L;
040
041   /**
042    * Create an instance of this class from a manifest file on the file system.
043    *
044    * @param f The manifest file.
045    * @throws IOException If a problem occurred while trying to read the manifest file.
046    */
047   public ManifestFile(File f) throws IOException {
048      Manifest mf = new Manifest();
049      try (FileInputStream fis = new FileInputStream(f)) {
050         mf.read(fis);
051         load(mf);
052      } catch (IOException e) {
053         throw new IOException("Problem detected in MANIFEST.MF.  Contents below:\n"+read(f), e);
054      }
055   }
056
057   /**
058    * Create an instance of this class from a {@link Manifest} object.
059    *
060    * @param f The manifest to read from.
061    */
062   public ManifestFile(Manifest f) {
063      load(f);
064   }
065
066   /**
067    * Finds and loads the manifest file of the jar file that the specified class is contained within.
068    *
069    * @param c The class to get the manifest file of.
070    * @throws IOException If a problem occurred while trying to read the manifest file.
071    */
072   public ManifestFile(Class<?> c) throws IOException {
073      String className = c.getSimpleName() + ".class";
074      String classPath = c.getResource(className).toString();
075      if (! classPath.startsWith("jar")) {
076         return;
077      }
078      String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +  "/META-INF/MANIFEST.MF";
079      try {
080         Manifest mf = new Manifest(new URL(manifestPath).openStream());
081         load(mf);
082      } catch (MalformedURLException e) {
083         throw ThrowableUtils.cast(IOException.class, e);
084      } catch (IOException e) {
085         e.printStackTrace();
086      }
087   }
088
089   /**
090    * Create an instance of this class loaded from the contents of a reader.
091    *
092    * <p>
093    * Note that the input must end in a newline to pick up the last line!
094    *
095    * @param r The manifest file contents.
096    * @throws IOException If a problem occurred while trying to read the manifest file.
097    */
098   public ManifestFile(Reader r) throws IOException {
099      load(new Manifest(new ByteArrayInputStream(read(r).getBytes(UTF8))));
100   }
101
102   /**
103    * Create an instance of this class loaded from the contents of an input stream.
104    *
105    * <p>
106    * Note that the input must end in a newline to pick up the last line!
107    *
108    * @param is The manifest file contents.
109    * @throws IOException If a problem occurred while trying to read the manifest file.
110    */
111   public ManifestFile(InputStream is) throws IOException {
112      load(new Manifest(is));
113   }
114
115   private void load(Manifest mf) {
116      mf.getMainAttributes().forEach((k,v) -> put(k.toString(), v.toString()));
117   }
118
119   @Override /* Object */
120   public String toString() {
121      StringBuilder sb = new StringBuilder();
122      forEach((k,v) -> sb.append(k).append(": ").append(v));
123      return sb.toString();
124   }
125}