package water.init; import water.util.Log; import java.io.*; import java.net.URL; import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** Self-jar file MD5 hash, to help make sure clusters are made from the same jar. */ public abstract class JarHash { static final String JARPATH; // Path to self-jar, or NULL if we cannot find it static public final byte[] JARHASH; // MD5 hash of self-jar, or 0xFF's if we cannot figure it out static { JARPATH = cl_init_jarpath(); JARHASH = cl_init_md5(JARPATH); } private static String cl_init_jarpath() { try { final String ownJar = JarHash.class.getProtectionDomain().getCodeSource().getLocation().getPath(); if( ownJar.endsWith(".jar") ) // Path if run from a Jar return URLDecoder.decode(ownJar, "UTF-8"); if( !ownJar.endsWith(".jar/") ) return null; // Not a Jar? // Some hadoop versions (like Hortonworks) will unpack the jar file on their own. String stem = "h2o.jar"; File f = new File(ownJar + stem); if( !f.exists() ) return null; // Still not a jar return URLDecoder.decode(ownJar + stem, "UTF-8"); } catch( IOException ie ) { return null; } } private static byte[] cl_init_md5(String jarpath) { byte[] ffHash = new byte[16]; Arrays.fill(ffHash, (byte)0xFF); // The default non-MD5 if( jarpath==null ) return ffHash; // Ok, pop Jar open & make MD5 InputStream is = null; try { is = new FileInputStream(jarpath); MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] buf = new byte[4096]; int pos; while( (pos = is.read(buf)) > 0 ) md5.update(buf, 0, pos); return md5.digest(); // haz md5! } catch( IOException | NoSuchAlgorithmException e ) { Log.err(e); // No MD5 algo handy??? } finally { try { if( is != null ) is.close(); } catch( IOException ignore ) { } } return ffHash; } private static final ArrayList<File> RESOURCE_FILES = new ArrayList<>(); public static void registerResourceRoot(File f) { if (f.exists()) { RESOURCE_FILES.add(f); } } // Look for resources (JS files, PNG's, etc) from the self-jar first, then // from a possible local dev build. public static InputStream getResource2(String uri) { try { // If -Dwebdev=1 is set in VM args, we're in front end dev mode, so skip the class loader. // This is to allow the front end scripts/styles/templates to be loaded from the build // directory during development. // Try all registered locations for( File f : RESOURCE_FILES ) { File f2 = new File(f,uri); if( f2.exists() ) return new FileInputStream(f2); } // Fall through to jar file mode. ClassLoader cl = ClassLoader.getSystemClassLoader(); InputStream is = loadResource(uri, cl); if (is == null && (cl=Thread.currentThread().getContextClassLoader())!=null) { is = loadResource(uri, cl); } if (is == null && (cl=JarHash.class.getClassLoader())!=null) { is = loadResource(uri, cl); } if (is != null) return is; } catch (FileNotFoundException ignore) {} Log.warn("Resource not found: " + uri); return null; } private static InputStream loadResource(String uri, ClassLoader cl) { Log.info("Trying to load resource " + uri + " via classloader " + cl,false); InputStream is = cl.getResourceAsStream("resources/www" + uri); if( is != null ) return is; is = cl.getResourceAsStream("resources/main/www" + uri); if( is != null ) return is; // This is the right file location of resource inside jar bundled by gradle is = cl.getResourceAsStream("www" + uri); return is; } /** * Given a path name (without preceding and appending "/"), * return the names of all file and directory names contained * in the path location (not recursive). * * @param path - name of resource path * @return - list of resource names at that path */ public static List<String> getResourcesList(String path) { Set<String> resList = new HashSet<>(); // subdirectories can cause duplicate entries try { // Java doesn't allow simple exploration of resources as directories // when the resources are inside a jar file. This searches the contents // of the jar to get the list URL classUrl = JarHash.class.getResource("/water/H2O.class"); if (classUrl != null && classUrl.getProtocol().equals("jar")) { // extract jarPath from classUrl string String jarPath = classUrl.getPath().substring(5, classUrl.getPath().indexOf("!")); JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8")); Enumeration<JarEntry> files = jar.entries(); // look for all entries within the supplied resource path while (files.hasMoreElements()) { String fName = files.nextElement().getName(); if (fName.startsWith(path + "/")) { String resourceName = fName.substring((path + "/").length()); int checkSubdir = resourceName.indexOf("/"); if (checkSubdir >= 0) // subdir, trim to subdir name resourceName = resourceName.substring(0, checkSubdir); if (resourceName.length() > 0) resList.add(resourceName); } } } else { // not a jar, retrieve resource from file system String resourceName; BufferedReader resources = new BufferedReader(new InputStreamReader(JarHash.class.getResourceAsStream("/gaid"))); if (resources != null) { while ((resourceName = resources.readLine()) != null) if (resourceName.length() > 0) resList.add(resourceName); } } }catch(Exception ignore){ Log.debug("Failed in reading gaid resources."); } return new ArrayList<>(resList); } }