VersionInfo.java revision 069490a5ca2fd1988d29daf45d892f47ad665115
1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/VersionInfo.java $
3 * $Revision: 554888 $
4 * $Date: 2007-07-10 02:46:36 -0700 (Tue, 10 Jul 2007) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License.  You may obtain a copy of the License at
14 *
15 *   http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.util;
33
34import java.io.IOException;
35import java.io.InputStream;
36import java.util.Map;
37import java.util.Properties;
38import java.util.ArrayList;
39
40
41/**
42 * Provides access to version information for HTTP components.
43 * Instances of this class provide version information for a single module
44 * or informal unit, as explained
45 * <a href="http://wiki.apache.org/jakarta-httpclient/HttpComponents">here</a>.
46 * Static methods are used to extract version information from property
47 * files that are automatically packaged with HTTP component release JARs.
48 * <br/>
49 * All available version information is provided in strings, where
50 * the string format is informal and subject to change without notice.
51 * Version information is provided for debugging output and interpretation
52 * by humans, not for automated processing in applications.
53 *
54 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
55 * @author and others
56 */
57public class VersionInfo {
58
59    /** A string constant for unavailable information. */
60    public final static String UNAVAILABLE = "UNAVAILABLE";
61
62    /** The filename of the version information files. */
63    public final static String VERSION_PROPERTY_FILE = "version.properties";
64
65    // the property names
66    public final static String PROPERTY_MODULE    = "info.module";
67    public final static String PROPERTY_RELEASE   = "info.release";
68    public final static String PROPERTY_TIMESTAMP = "info.timestamp";
69
70
71    /** The package that contains the version information. */
72    private final String infoPackage;
73
74    /** The module from the version info. */
75    private final String infoModule;
76
77    /** The release from the version info. */
78    private final String infoRelease;
79
80    /** The timestamp from the version info. */
81    private final String infoTimestamp;
82
83    /** The classloader from which the version info was obtained. */
84    private final String infoClassloader;
85
86
87    /**
88     * Instantiates version information.
89     *
90     * @param pckg      the package
91     * @param module    the module, or <code>null</code>
92     * @param release   the release, or <code>null</code>
93     * @param time      the build time, or <code>null</code>
94     * @param clsldr    the class loader, or <code>null</code>
95     */
96    protected VersionInfo(String pckg, String module,
97                          String release, String time, String clsldr) {
98        if (pckg == null) {
99            throw new IllegalArgumentException
100                ("Package identifier must not be null.");
101        }
102
103        infoPackage     = pckg;
104        infoModule      = (module  != null) ? module  : UNAVAILABLE;
105        infoRelease     = (release != null) ? release : UNAVAILABLE;
106        infoTimestamp   = (time    != null) ? time    : UNAVAILABLE;
107        infoClassloader = (clsldr  != null) ? clsldr  : UNAVAILABLE;
108    }
109
110
111    /**
112     * Obtains the package name.
113     * The package name identifies the module or informal unit.
114     *
115     * @return  the package name, never <code>null</code>
116     */
117    public final String getPackage() {
118        return infoPackage;
119    }
120
121    /**
122     * Obtains the name of the versioned module or informal unit.
123     * This data is read from the version information for the package.
124     *
125     * @return  the module name, never <code>null</code>
126     */
127    public final String getModule() {
128        return infoModule;
129    }
130
131    /**
132     * Obtains the release of the versioned module or informal unit.
133     * This data is read from the version information for the package.
134     *
135     * @return  the release version, never <code>null</code>
136     */
137    public final String getRelease() {
138        return infoRelease;
139    }
140
141    /**
142     * Obtains the timestamp of the versioned module or informal unit.
143     * This data is read from the version information for the package.
144     *
145     * @return  the timestamp, never <code>null</code>
146     */
147    public final String getTimestamp() {
148        return infoTimestamp;
149    }
150
151    /**
152     * Obtains the classloader used to read the version information.
153     * This is just the <code>toString</code> output of the classloader,
154     * since the version information should not keep a reference to
155     * the classloader itself. That could prevent garbage collection.
156     *
157     * @return  the classloader description, never <code>null</code>
158     */
159    public final String getClassloader() {
160        return infoClassloader;
161    }
162
163
164    /**
165     * Provides the version information in human-readable format.
166     *
167     * @return  a string holding this version information
168     */
169    public String toString() {
170        StringBuffer sb = new StringBuffer
171            (20 + infoPackage.length() + infoModule.length() +
172             infoRelease.length() + infoTimestamp.length() +
173             infoClassloader.length());
174
175        sb.append("VersionInfo(")
176            .append(infoPackage).append(':').append(infoModule);
177
178        // If version info is missing, a single "UNAVAILABLE" for the module
179        // is sufficient. Everything else just clutters the output.
180        if (!UNAVAILABLE.equals(infoRelease))
181            sb.append(':').append(infoRelease);
182        if (!UNAVAILABLE.equals(infoTimestamp))
183            sb.append(':').append(infoTimestamp);
184
185        sb.append(')');
186
187        if (!UNAVAILABLE.equals(infoClassloader))
188            sb.append('@').append(infoClassloader);
189
190        return sb.toString();
191    }
192
193
194    /**
195     * Loads version information for a list of packages.
196     *
197     * @param pckgs     the packages for which to load version info
198     * @param clsldr    the classloader to load from, or
199     *                  <code>null</code> for the thread context classloader
200     *
201     * @return  the version information for all packages found,
202     *          never <code>null</code>
203     */
204    public final static VersionInfo[] loadVersionInfo(String[] pckgs,
205                                                      ClassLoader clsldr) {
206        if (pckgs == null) {
207            throw new IllegalArgumentException
208                ("Package identifier list must not be null.");
209        }
210
211        ArrayList vil = new ArrayList(pckgs.length);
212        for (int i=0; i<pckgs.length; i++) {
213            VersionInfo vi = loadVersionInfo(pckgs[i], clsldr);
214            if (vi != null)
215                vil.add(vi);
216        }
217
218        return (VersionInfo[]) vil.toArray(new VersionInfo[vil.size()]);
219    }
220
221
222    /**
223     * Loads version information for a package.
224     *
225     * @param pckg      the package for which to load version information,
226     *                  for example "org.apache.http".
227     *                  The package name should NOT end with a dot.
228     * @param clsldr    the classloader to load from, or
229     *                  <code>null</code> for the thread context classloader
230     *
231     * @return  the version information for the argument package, or
232     *          <code>null</code> if not available
233     */
234    public final static VersionInfo loadVersionInfo(final String pckg,
235                                                    ClassLoader clsldr) {
236        if (pckg == null) {
237            throw new IllegalArgumentException
238                ("Package identifier must not be null.");
239        }
240
241        if (clsldr == null)
242            clsldr = Thread.currentThread().getContextClassLoader();
243
244        Properties vip = null; // version info properties, if available
245        try {
246            // org.apache.http      becomes
247            // org/apache/http/version.properties
248            InputStream is = clsldr.getResourceAsStream
249                (pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE);
250            if (is != null) {
251                try {
252                    Properties props = new Properties();
253                    props.load(is);
254                    vip = props;
255                } finally {
256                    is.close();
257                }
258            }
259        } catch (IOException ex) {
260            // shamelessly munch this exception
261        }
262
263        VersionInfo result = null;
264        if (vip != null)
265            result = fromMap(pckg, vip, clsldr);
266
267        return result;
268    }
269
270
271    /**
272     * Instantiates version information from properties.
273     *
274     * @param pckg      the package for the version information
275     * @param info      the map from string keys to string values,
276     *                  for example {@link java.util.Properties}
277     * @param clsldr    the classloader, or <code>null</code>
278     *
279     * @return  the version information
280     */
281    protected final static VersionInfo fromMap(String pckg, Map info,
282                                               ClassLoader clsldr) {
283        if (pckg == null) {
284            throw new IllegalArgumentException
285                ("Package identifier must not be null.");
286        }
287
288        String module = null;
289        String release = null;
290        String timestamp = null;
291
292        if (info != null) {
293            module = (String) info.get(PROPERTY_MODULE);
294            if ((module != null) && (module.length() < 1))
295                module = null;
296
297            release = (String) info.get(PROPERTY_RELEASE);
298            if ((release != null) && ((release.length() < 1) ||
299                                      (release.equals("${pom.version}"))))
300                release = null;
301
302            timestamp = (String) info.get(PROPERTY_TIMESTAMP);
303            if ((timestamp != null) &&
304                ((timestamp.length() < 1) ||
305                 (timestamp.equals("${mvn.timestamp}")))
306                )
307                timestamp = null;
308        } // if info
309
310        String clsldrstr = null;
311        if (clsldr != null)
312            clsldrstr = clsldr.toString();
313
314        return new VersionInfo(pckg, module, release, timestamp, clsldrstr);
315    }
316
317} // class VersionInfo
318