GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
SHASum.java
Go to the documentation of this file.
1/**
2 * Copyright 2019 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.common.util;
30
31import java.io.BufferedInputStream;
32import java.io.FileInputStream;
33import java.io.IOException;
34import java.io.FileNotFoundException;
35import java.io.InputStream;
36import java.net.URISyntaxException;
37import java.security.MessageDigest;
38import java.security.NoSuchAlgorithmException;
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.List;
42import java.util.Locale;
43import java.util.regex.Pattern;
44
45import com.jogamp.common.util.cache.TempFileCache;
46import com.jogamp.common.util.cache.TempJarCache;
47
48import jogamp.common.Debug;
49
50/**
51 * Utility class to produce secure hash (SHA) sums over diverse input sources.
52 * <p>
53 * See {@link #updateDigest(MessageDigest, List)}
54 * </p>
55 * <p>
56 * This implementation is being utilized at JogAmp build time to produce various
57 * SHA sums over sources, class files and native libraries to ensure their identity.
58 * See {@link JogampVersion#getImplementationSHASources()},
59 * {@link JogampVersion#getImplementationSHAClasses()}
60 * and {@link JogampVersion#getImplementationSHANatives()}.
61 * </p>
62 * <p>
63 * {@link JogampVersion#getImplementationSHASources()} for module gluegen is produced via:
64 * <pre>
65 * java -cp build/gluegen-rt.jar com.jogamp.common.util.SHASum --algorithm 256 --exclude ".*\\.log" --exclude "make/lib/toolchain" src jcpp/src make
66 * </pre>
67 * </p>
68 * @see #SHASum(MessageDigest, List, List, List)
69 * @see #compute(boolean)
70 * @see TempJarSHASum
71 * @see #main(String[])
72 */
73public class SHASum {
74 private static final boolean DEBUG = Debug.debug("SHASum");
75
76 /**
77 * {@link MessageDigest#update(byte[], int, int) Updates} the given {@code digest}
78 * with the bytes contained by the files denoted by the given {@code filenames} in the given order.
79 * <p>
80 * To retrieve the list of all files traversing through directories, one may use {@link IOUtil#filesOf(List, List, List)}.
81 * </p>
82 * <p>
83 * The SHA implementation is sensitive to the order of input bytes and hence the given filename order.
84 * </p>
85 * <p>
86 * It is advised to pass given list of filenames in lexicographically sorted order to ensure reproducible outcome across all platforms,
87 * one may use {@link #sort(ArrayList)}.
88 * </p>
89 * <p>
90 * As an example, one could write
91 * <pre>
92 * final MessageDigest digest = ...;
93 * final long totalBytes = updateDigest(digest, sort(IOUtil.filesOf(Arrays.asList("sources"), null, null)));
94 * </pre>
95 * </p>
96 * @param digest to be updated digest
97 * @param filenames list of filenames denoting files, which bytes will be used to update the digest
98 * @return total number of bytes read.
99 * @throws FileNotFoundException see {@link FileInputStream#FileInputStream(String)}
100 * @throws IOException see {@link InputStream#read(byte[])}
101 */
102 public static long updateDigest(final MessageDigest digest, final List<String> filenames) throws IOException {
103 long numBytes = 0;
104 final byte buffer[] = new byte[4096]; // avoid Platform.getMachineDataInfo().pageSizeInBytes() due to native dependency
105 for(int i=0; i<filenames.size(); i++) {
106 final InputStream in = new BufferedInputStream(new FileInputStream(filenames.get(i)));
107 try {
108 while (true) {
109 int count;
110 if ((count = in.read(buffer)) == -1) {
111 break;
112 }
113 digest.update(buffer, 0, count);
114 numBytes += count;
115 }
116 } finally {
117 in.close();
118 }
119 }
120 return numBytes;
121 }
122
123 /**
124 * Simple helper to print the given byte-array into a string, here appended to StringBuilder
125 * @param shasum the given byte-array
126 * @param sb optional pre-existing StringBuilder, may be null
127 * @return return given or new StringBuilder with appended hex-string
128 */
129 public static StringBuilder toHexString(final byte[] shasum, StringBuilder sb) {
130 if( null == sb ) {
131 sb = new StringBuilder();
132 }
133 for(int i=0; i<shasum.length; i++) {
134 sb.append(String.format((Locale)null, "%02x", shasum[i]));
135 }
136 return sb;
137 }
138
139 /**
140 * Returns the sorted list of given strings using {@link String#compareTo(String)}'s lexicographically comparison.
141 * @param source given input strings
142 * @return sorted list of given strings
143 */
144 public static List<String> sort(final ArrayList<String> source) {
145 final String s[] = source.toArray(new String[source.size()]);
146 Arrays.sort(s, 0, s.length, null);
147 return Arrays.asList(s);
148 }
149
150 final MessageDigest digest;
151 final List<String> origins;
152 final List<Pattern> excludes, includes;
153
154 /**
155 * Instance to ensure proper {@link #compute(boolean)} of identical SHA sums over same contents within given paths across machines.
156 * <p>
157 * Instantiation of this class is lightweight, {@link #compute(boolean)} performs all operations.
158 * </p>
159 *
160 * @param digest the SHA algorithm
161 * @param origins the mandatory path origins to be used for {@link IOUtil#filesOf(List, List, List)}
162 * @param excludes the optional exclude patterns to be used for {@link IOUtil#filesOf(List, List, List)}
163 * @param includes the optional include patterns to be used for {@link IOUtil#filesOf(List, List, List)}
164 * @throws IllegalArgumentException
165 * @throws IOException
166 * @throws URISyntaxException
167 */
168 public SHASum(final MessageDigest digest, final List<String> origins, final List<Pattern> excludes, final List<Pattern> includes) {
169 this.digest = digest;
170 this.origins = origins;
171 this.excludes = excludes;
172 this.includes = includes;
173 }
174
175 /**
176 * Implementation gathers all files traversing through given paths via {@link IOUtil#filesOf(List, List, List)},
177 * sorts the resulting file list via {@link #sort(ArrayList)} and finally
178 * calculates the SHA sum over its byte content via {@link #updateDigest(MessageDigest, List)}.
179 * <p>
180 * This ensures identical SHA sums over same contents within given paths across machines.
181 * </p>
182 * <p>
183 * This method is heavyweight and performs all operations.
184 * </p>
185 *
186 * @param verbose if true, all used files will be dumped as well as the digest result
187 * @return the resulting SHA value
188 * @throws IOException
189 */
190 public final byte[] compute(final boolean verbose) throws IOException {
191 final List<String> fnamesS = SHASum.sort(IOUtil.filesOf(origins, excludes, includes));
192 if( verbose ) {
193 for(int i=0; i<fnamesS.size(); i++) {
194 System.err.println(fnamesS.get(i));
195 }
196 }
197 final long numBytes = SHASum.updateDigest(digest, fnamesS);
198 final byte[] shasum = digest.digest();
199 if( verbose ) {
200 System.err.println("Digested "+numBytes+" bytes, shasum size "+shasum.length+" bytes");
201 System.err.println("Digested result: "+SHASum.toHexString(shasum, null).toString());
202 }
203 return shasum;
204 }
205
206 public final List<String> getOrigins() { return origins; }
207 public final List<Pattern> getExcludes() { return excludes; }
208 public final List<Pattern> getIncludes() { return includes; }
209
210 /**
211 * {@link SHASum} specialization utilizing {@link TempJarCache} to access jar file content for SHA computation
212 */
213 public static class TempJarSHASum extends SHASum {
214 /**
215 * Instance to ensure proper {@link #compute(boolean)} of identical SHA sums over same contents within given paths across machines.
216 * <p>
217 * Instantiation of this class is lightweight, {@link #compute(boolean)} performs all operations.
218 * </p>
219 * <p>
220 * {@link TempJarCache#getTempFileCache()}'s {@link TempFileCache#getTempDir()} is used as origin for {@link IOUtil#filesOf(List, List, List)}
221 * </p>
222 *
223 * @param digest the SHA algorithm
224 * @param jarclazz a class from the desired classpath jar file used for {@link TempJarCache#addAll(Class, com.jogamp.common.net.Uri)}
225 * @param excludes the optional exclude patterns to be used for {@link IOUtil#filesOf(List, List, List)}
226 * @param includes the optional include patterns to be used for {@link IOUtil#filesOf(List, List, List)}
227 * @throws SecurityException
228 * @throws IllegalArgumentException
229 * @throws IOException
230 * @throws URISyntaxException
231 */
232 public TempJarSHASum(final MessageDigest digest, final Class<?> jarclazz, final List<Pattern> excludes, final List<Pattern> includes)
233 throws SecurityException, IllegalArgumentException, IOException, URISyntaxException
234 {
235 super(digest, Arrays.asList(IOUtil.slashify(TempJarCache.getTempFileCache().getTempDir().getAbsolutePath(), false, false)),
236 excludes, includes);
237 TempJarCache.addAll(jarclazz, JarUtil.getJarFileUri(jarclazz.getName(), jarclazz.getClassLoader()));
238 }
239
240 public final String getOrigin() { return origins.get(0); }
241 }
242
243 /**
244 * Main entry point taking var-arg path or gnu-arguments with a leading '--'.
245 * <p>
246 * Implementation gathers all files traversing through given paths via {@link IOUtil#filesOf(List, List, List)},
247 * sorts the resulting file list via {@link #sort(ArrayList)} and finally
248 * calculates the SHA sum over its byte content via {@link #updateDigest(MessageDigest, List)}.
249 * This ensures identical SHA sums over same contents within given paths.
250 * </p>
251 * <p>
252 * Example to calculate the SHA-256 over our source files as performed for {@link JogampVersion#getImplementationSHASources()}
253 * <pre>
254 * java -cp build/gluegen-rt.jar com.jogamp.common.util.SHASum --algorithm 256 --exclude ".*\\.log" --exclude "make/lib/toolchain" src jcpp/src make
255 * </pre>
256 * </p>
257 * <p>
258 * To validate the implementation, one can gather the sorted list of files (to ensure same order)
259 * <pre>
260 * java -cp build/gluegen-rt.jar com.jogamp.common.util.SHASum --listfilesonly --exclude ".*\\.log" --exclude "make/lib/toolchain" src jcpp/src make >& java.sorted.txt
261 * </pre>
262 * and then calculate the shasum independently
263 * <pre>
264 * find `cat java.sorted.txt` -exec cat {} + | shasum -a 256 -b - | awk '{print $1}'
265 * </pre>
266 * </p>
267 * @param args
268 * @throws IOException
269 * @throws URISyntaxException
270 * @throws IllegalArgumentException
271 */
272 public static void main(final String[] args) throws IOException {
273 boolean listFilesOnly = false;
274 int shabits = 256;
275 int i;
276 final ArrayList<String> pathU = new ArrayList<String>();
277 final ArrayList<Pattern> excludes = new ArrayList<Pattern>();
278 final ArrayList<Pattern> includes = new ArrayList<Pattern>();
279 {
280 for(i=0; i<args.length; i++) {
281 if(null != args[i]) {
282 if( args[i].startsWith("--") ) {
283 // options
284 if( args[i].equals("--algorithm")) {
285 shabits = Integer.parseInt(args[++i]);
286 } else if( args[i].equals("--exclude")) {
287 excludes.add(Pattern.compile(args[++i]));
288 if( DEBUG ) {
289 System.err.println("adding exclude: <"+args[i]+"> -> <"+excludes.get(excludes.size()-1)+">");
290 }
291 } else if( args[i].equals("--include")) {
292 includes.add(Pattern.compile(args[++i]));
293 if( DEBUG ) {
294 System.err.println("adding include: <"+args[i]+"> -> <"+includes.get(includes.size()-1)+">");
295 }
296 } else if( args[i].equals("--listfilesonly")) {
297 listFilesOnly = true;
298 } else {
299 System.err.println("Abort, unknown argument: "+args[i]);
300 return;
301 }
302 } else {
303 pathU.add(args[i]);
304 if( DEBUG ) {
305 System.err.println("adding path: <"+args[i]+">");
306 }
307 }
308 }
309 }
310 if( listFilesOnly ) {
311 final List<String> fnamesS = sort(IOUtil.filesOf(pathU, excludes, includes));
312 for(i=0; i<fnamesS.size(); i++) {
313 System.out.println(fnamesS.get(i));
314 }
315 return;
316 }
317 }
318 final String shaalgo = "SHA-"+shabits;
319 final MessageDigest digest;
320 try {
321 digest = MessageDigest.getInstance(shaalgo);
322 } catch (final NoSuchAlgorithmException e) {
323 System.err.println("Abort, implementation for "+shaalgo+" not available: "+e.getMessage());
324 return;
325 }
326 final SHASum shaSum = new SHASum(digest, pathU, excludes, includes);
327 System.out.println(toHexString(shaSum.compute(DEBUG), null).toString());
328 }
329}
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Definition: IOUtil.java:389
static ArrayList< String > filesOf(final List< String > paths, final List< Pattern > excludes, final List< Pattern > includes)
Retrieve the list of all filenames traversing through given paths.
Definition: IOUtil.java:1452
static Uri getJarFileUri(final String clazzBinName, final ClassLoader cl)
The Class's "com.jogamp.common.GlueGenVersion" Uri jar:sub_protocol:/some/path/gluegen-rt....
Definition: JarUtil.java:303
SHASum specialization utilizing TempJarCache to access jar file content for SHA computation
Definition: SHASum.java:213
TempJarSHASum(final MessageDigest digest, final Class<?> jarclazz, final List< Pattern > excludes, final List< Pattern > includes)
Instance to ensure proper compute(boolean) of identical SHA sums over same contents within given path...
Definition: SHASum.java:232
Utility class to produce secure hash (SHA) sums over diverse input sources.
Definition: SHASum.java:73
final List< String > getOrigins()
Definition: SHASum.java:206
final List< Pattern > getIncludes()
Definition: SHASum.java:208
final byte[] compute(final boolean verbose)
Implementation gathers all files traversing through given paths via IOUtil#filesOf(List,...
Definition: SHASum.java:190
SHASum(final MessageDigest digest, final List< String > origins, final List< Pattern > excludes, final List< Pattern > includes)
Instance to ensure proper compute(boolean) of identical SHA sums over same contents within given path...
Definition: SHASum.java:168
static long updateDigest(final MessageDigest digest, final List< String > filenames)
Updates the given digest with the bytes contained by the files denoted by the given filenames in the ...
Definition: SHASum.java:102
final List< Pattern > getExcludes()
Definition: SHASum.java:207
static List< String > sort(final ArrayList< String > source)
Returns the sorted list of given strings using String#compareTo(String)'s lexicographically compariso...
Definition: SHASum.java:144
static StringBuilder toHexString(final byte[] shasum, StringBuilder sb)
Simple helper to print the given byte-array into a string, here appended to StringBuilder.
Definition: SHASum.java:129
static void main(final String[] args)
Main entry point taking var-arg path or gnu-arguments with a leading '–'.
Definition: SHASum.java:272
File getTempDir()
Temporary directory for individual files (eg.
Static Jar file cache handler using an underlying instance of TempFileCache, see getTempFileCache().
static synchronized final void addAll(final Class<?> certClass, final Uri jarUri)
Adds all types, native libraries, class files and other files (resources) if not yet added.