View Javadoc

1   /*
2    * Copyright 2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.resource.filesystem.heap;
17  
18  import java.io.BufferedInputStream;
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.jar.Attributes;
23  import java.util.jar.JarEntry;
24  import java.util.jar.JarFile;
25  import java.util.jar.Manifest;
26  
27  import javax.annotation.concurrent.ThreadSafe;
28  
29  import org.apache.commons.io.FilenameUtils;
30  import org.apache.commons.io.IOUtils;
31  import org.apache.commons.lang.StringUtils;
32  
33  import de.smartics.properties.resource.domain.ArtifactId;
34  import de.smartics.util.lang.Arg;
35  
36  /**
37   * Loads the artifact identifier from the Manifest file.
38   */
39  @ThreadSafe
40  final class ArtifactIdGrabber
41  {
42    // ********************************* Fields *********************************
43  
44    // --- constants ------------------------------------------------------------
45  
46    // --- members --------------------------------------------------------------
47  
48    /**
49     * The flag determines whether the archive contains properties definition
50     * files (<code>true</code>) or not (<code>false</code>).
51     */
52    private final boolean definitionArchive;
53  
54    /**
55     * The artifact ID as determined from the Manifest file.
56     */
57    private final ArtifactId artifactId;
58  
59    /**
60     * The archive type as determined by the file name extension.
61     */
62    private final String archiveType;
63  
64    /**
65     * The classifier determined by some crude heuristics.
66     */
67    private final String classifier;
68  
69    // ****************************** Initializer *******************************
70  
71    // ****************************** Constructors ******************************
72  
73    private ArtifactIdGrabber(final File archiveFile, final boolean lazy)
74      throws NullPointerException, IOException
75    {
76      Arg.checkNotNull("archiveFile", archiveFile);
77  
78      final JarFile jarFile = new JarFile(archiveFile);
79  
80      this.definitionArchive = isDefinitionArchive(jarFile);
81      if (lazy && !definitionArchive)
82      {
83        this.archiveType = null;
84        this.classifier = null;
85        this.artifactId = null;
86        return;
87      }
88      final String name = archiveFile.getName();
89      this.archiveType = determineType(name);
90      this.classifier = determineClassifier(name);
91      this.artifactId = load(jarFile);
92    }
93  
94    // ****************************** Inner Classes *****************************
95  
96    // ********************************* Methods ********************************
97  
98    // --- init -----------------------------------------------------------------
99  
100   private static boolean isDefinitionArchive(final JarFile jarFile)
101   {
102     final String definitionXmlName =
103         "META-INF/smartics-properties/definition.xml";
104     final JarEntry entry = jarFile.getJarEntry(definitionXmlName);
105     final boolean hasDefinition = entry != null;
106     return hasDefinition;
107   }
108 
109   private static String determineType(final String name)
110   {
111     final String extension = FilenameUtils.getExtension(name);
112     final String type = StringUtils.isNotBlank(extension) ? extension : "jar";
113     return type;
114   }
115 
116   private static String determineClassifier(final String name)
117   {
118     final String baseName = FilenameUtils.getBaseName(name);
119     final int pos = baseName.lastIndexOf('-');
120     if (pos == -1)
121     {
122       return null;
123     }
124 
125     final String classifier = baseName.substring(pos);
126     if ("-SNAPSHOT".equals(classifier)
127         || StringUtils.containsAny(classifier, "0123456789"))
128     {
129       return null;
130     }
131 
132     return classifier;
133   }
134 
135   private ArtifactId load(final JarFile jarFile)
136   {
137     final JarEntry entry = jarFile.getJarEntry(JarFile.MANIFEST_NAME);
138     if (entry == null)
139     {
140       throw new IllegalStateException(String.format(
141           "No Manifest file found in: %s", jarFile.getName()));
142     }
143 
144     final ArtifactId id = readManifestFile(jarFile, entry);
145     return id;
146   }
147 
148   private ArtifactId readManifestFile(final JarFile jarFile,
149       final JarEntry entry) throws IllegalArgumentException,
150     IllegalStateException
151   {
152     InputStream inputStream = null;
153     try
154     {
155       inputStream = jarFile.getInputStream(entry);
156       if (inputStream != null)
157       {
158         inputStream = new BufferedInputStream(inputStream);
159         final Manifest manifest = new Manifest(inputStream);
160 
161         final ArtifactId artifactId = createArtifactId(manifest);
162 
163         return artifactId;
164       }
165 
166       throw new IllegalStateException(
167           "Cannot find Manifest file to determine artifact ID: "
168               + entry.getName());
169     }
170     catch (final IOException e)
171     {
172       throw new IllegalStateException(String.format(
173           "Cannot read Manifest file: %s", jarFile.getName()), e);
174     }
175     finally
176     {
177       IOUtils.closeQuietly(inputStream);
178     }
179   }
180 
181   private ArtifactId createArtifactId(final Manifest manifest)
182     throws IllegalArgumentException
183   {
184     final Attributes attributes = manifest.getMainAttributes();
185     final String groupId =
186         attributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR_ID);
187     final String artifactId =
188         attributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
189     final String version =
190         attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
191 
192     final ArtifactId id =
193         ArtifactId
194             .create(groupId, artifactId, version, archiveType, classifier);
195     return id;
196   }
197 
198   // --- factory --------------------------------------------------------------
199 
200   /**
201    * Creates a grabber instance.
202    *
203    * @param archiveFile the archive file to grab information from.
204    * @throws NullPointerException if {@code archiveFile} is <code>null</code>.
205    * @throws IOException if the file cannot be opened.
206    */
207   static ArtifactIdGrabber create(final File archiveFile)
208     throws NullPointerException, IOException
209   {
210     return new ArtifactIdGrabber(archiveFile, false);
211   }
212 
213   /**
214    * Creates a lazy grabber instance that quits grabbing if the archive does not
215    * provide properties definition information. That is the grabber instance
216    * returns null for each value of {@link #isDefinitionArchive()} returns
217    * <code>false</code>.
218    *
219    * @param archiveFile the archive file to grab information from.
220    * @throws NullPointerException if {@code archiveFile} is <code>null</code>.
221    * @throws IOException if the file cannot be opened.
222    */
223   static ArtifactIdGrabber createLazy(final File archiveFile)
224     throws NullPointerException, IOException
225   {
226     return new ArtifactIdGrabber(archiveFile, true);
227   }
228 
229   // --- get&set --------------------------------------------------------------
230 
231   /**
232    * Returns the flag determines whether the archive contains properties
233    * definition files (<code>true</code>) or not (<code>false</code>).
234    *
235    * @return the flag determines whether the archive contains properties
236    *         definition files (<code>true</code>) or not (<code>false</code>).
237    */
238   public boolean isDefinitionArchive()
239   {
240     return definitionArchive;
241   }
242 
243   /**
244    * Returns the artifact ID as determined from the Manifest file.
245    *
246    * @return the artifact ID as determined from the Manifest file.
247    */
248   public ArtifactId getArtifactId()
249   {
250     return artifactId;
251   }
252 
253   // --- business -------------------------------------------------------------
254 
255   // --- object basics --------------------------------------------------------
256 
257 }