View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty5.util;
18  
19  import io.netty5.util.internal.PlatformDependent;
20  
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.text.ParseException;
24  import java.text.SimpleDateFormat;
25  import java.util.Enumeration;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.Set;
30  import java.util.TreeMap;
31  
32  /**
33   * Retrieves the version information of available Netty artifacts.
34   * <p>
35   * This class retrieves the version information from {@code META-INF/io.netty5.versions.properties}, which is
36   * generated in build time.  Note that it may not be possible to retrieve the information completely, depending on
37   * your environment, such as the specified {@link ClassLoader}, the current {@link SecurityManager}.
38   * </p>
39   */
40  public final class Version {
41  
42      private static final String PROP_VERSION = ".version";
43      private static final String PROP_BUILD_DATE = ".buildDate";
44      private static final String PROP_COMMIT_DATE = ".commitDate";
45      private static final String PROP_SHORT_COMMIT_HASH = ".shortCommitHash";
46      private static final String PROP_LONG_COMMIT_HASH = ".longCommitHash";
47      private static final String PROP_REPO_STATUS = ".repoStatus";
48  
49      /**
50       * Retrieves the version information of Netty artifacts using the current
51       * {@linkplain Thread#getContextClassLoader() context class loader}.
52       *
53       * @return A {@link Map} whose keys are Maven artifact IDs and whose values are {@link Version}s
54       */
55      public static Map<String, Version> identify() {
56          return identify(null);
57      }
58  
59      /**
60       * Retrieves the version information of Netty artifacts using the specified {@link ClassLoader}.
61       *
62       * @return A {@link Map} whose keys are Maven artifact IDs and whose values are {@link Version}s
63       */
64      public static Map<String, Version> identify(ClassLoader classLoader) {
65          if (classLoader == null) {
66              classLoader = PlatformDependent.getContextClassLoader();
67          }
68  
69          // Collect all properties.
70          Properties props = new Properties();
71          try {
72              Enumeration<URL> resources = classLoader.getResources("META-INF/io.netty5.versions.properties");
73              while (resources.hasMoreElements()) {
74                  URL url = resources.nextElement();
75                  try (InputStream in = url.openStream()) {
76                      props.load(in);
77                  }
78                  // Ignore.
79              }
80          } catch (Exception ignore) {
81              // Not critical. Just ignore.
82          }
83  
84          // Collect all artifactIds.
85          Set<String> artifactIds = new HashSet<>();
86          for (Object o: props.keySet()) {
87              String k = (String) o;
88  
89              int dotIndex = k.indexOf('.');
90              if (dotIndex <= 0) {
91                  continue;
92              }
93  
94              String artifactId = k.substring(0, dotIndex);
95  
96              // Skip the entries without required information.
97              if (!props.containsKey(artifactId + PROP_VERSION) ||
98                  !props.containsKey(artifactId + PROP_BUILD_DATE) ||
99                  !props.containsKey(artifactId + PROP_COMMIT_DATE) ||
100                 !props.containsKey(artifactId + PROP_SHORT_COMMIT_HASH) ||
101                 !props.containsKey(artifactId + PROP_LONG_COMMIT_HASH) ||
102                 !props.containsKey(artifactId + PROP_REPO_STATUS)) {
103                 continue;
104             }
105 
106             artifactIds.add(artifactId);
107         }
108 
109         Map<String, Version> versions = new TreeMap<>();
110         for (String artifactId: artifactIds) {
111             versions.put(
112                     artifactId,
113                     new Version(
114                             artifactId,
115                             props.getProperty(artifactId + PROP_VERSION),
116                             parseIso8601(props.getProperty(artifactId + PROP_BUILD_DATE)),
117                             parseIso8601(props.getProperty(artifactId + PROP_COMMIT_DATE)),
118                             props.getProperty(artifactId + PROP_SHORT_COMMIT_HASH),
119                             props.getProperty(artifactId + PROP_LONG_COMMIT_HASH),
120                             props.getProperty(artifactId + PROP_REPO_STATUS)));
121         }
122 
123         return versions;
124     }
125 
126     private static long parseIso8601(String value) {
127         try {
128             return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(value).getTime();
129         } catch (ParseException ignored) {
130             return 0;
131         }
132     }
133 
134     /**
135      * Prints the version information to {@link System#err}.
136      */
137     public static void main(String[] args) {
138         for (Version v: identify().values()) {
139             System.err.println(v);
140         }
141     }
142 
143     private final String artifactId;
144     private final String artifactVersion;
145     private final long buildTimeMillis;
146     private final long commitTimeMillis;
147     private final String shortCommitHash;
148     private final String longCommitHash;
149     private final String repositoryStatus;
150 
151     private Version(
152             String artifactId, String artifactVersion,
153             long buildTimeMillis, long commitTimeMillis,
154             String shortCommitHash, String longCommitHash, String repositoryStatus) {
155         this.artifactId = artifactId;
156         this.artifactVersion = artifactVersion;
157         this.buildTimeMillis = buildTimeMillis;
158         this.commitTimeMillis = commitTimeMillis;
159         this.shortCommitHash = shortCommitHash;
160         this.longCommitHash = longCommitHash;
161         this.repositoryStatus = repositoryStatus;
162     }
163 
164     public String artifactId() {
165         return artifactId;
166     }
167 
168     public String artifactVersion() {
169         return artifactVersion;
170     }
171 
172     public long buildTimeMillis() {
173         return buildTimeMillis;
174     }
175 
176     public long commitTimeMillis() {
177         return commitTimeMillis;
178     }
179 
180     public String shortCommitHash() {
181         return shortCommitHash;
182     }
183 
184     public String longCommitHash() {
185         return longCommitHash;
186     }
187 
188     public String repositoryStatus() {
189         return repositoryStatus;
190     }
191 
192     @Override
193     public String toString() {
194         return artifactId + '-' + artifactVersion + '.' + shortCommitHash +
195                ("clean".equals(repositoryStatus)? "" : " (repository: " + repositoryStatus + ')');
196     }
197 }