package org.gridkit.jvmtool.bstub; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public class JarBuilderTool { private Manifest mf = new Manifest(); private List<String> packages = new ArrayList<String>(); private List<String> services = new ArrayList<String>(); private Set<String> pathEntries = new HashSet<String>(); public Manifest getManifest() { return mf; } public void addPackage(String pkg) { packages.add(pkg.replace('.', '/')); } public void addService(String service) { services.add(service); } public byte[] collectJar() throws IOException { Manifest manifest = mf; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ZipOutputStream jarOut = new ZipOutputStream(bos); if (manifest != null) { ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME); e.setTime(0l); // this to ensure equal hash for equal content jarOut.putNextEntry(e); manifest.write(jarOut); jarOut.closeEntry(); } for(String srv: services) { processService(srv, jarOut); } for(String pkg: packages) { processPath(pkg, jarOut); } pathEntries.clear(); jarOut.close(); byte[] jarFile = bos.toByteArray(); return jarFile; } private void processService(String srv, ZipOutputStream jarOut) throws IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Set<String> imps = new TreeSet<String>(); String sd = "META-INF/services/" + srv; Enumeration<URL> en = cl.getResources(sd); while(en.hasMoreElements()) { URL url = en.nextElement(); Collection<String> c = toLines(url.openStream()); imps.addAll(c); } StringBuilder sb = new StringBuilder(); for(String s: imps) { s = s.trim(); if (!s.isEmpty()) { sb.append(s).append('\n'); } } ZipEntry entry = new ZipEntry(sd); entry.setTime(0); // this is to facilitate content cache if (!pathEntries.contains(entry.getName())) { pathEntries.add(entry.getName()); jarOut.putNextEntry(entry); jarOut.write(sb.toString().getBytes(Charset.forName("UTF8"))); jarOut.closeEntry(); } } private void processPath(String path, ZipOutputStream jarOut) throws MalformedURLException, IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); String basePackage = path; Enumeration<URL> en = cl.getResources(path); while(en.hasMoreElements()) { URL url = en.nextElement(); String urlp = url.toExternalForm(); if (urlp.indexOf('?') > 0) { urlp = urlp.substring(0, urlp.indexOf('?')); } addFiles(jarOut, basePackage, urlp); } } // private static byte[] jarFiles(String path) throws IOException { // ByteArrayOutputStream bos = new ByteArrayOutputStream(); // JarOutputStream jarOut = new JarOutputStream(bos); // int size = addFiles(jarOut, "", new File(path)); // if (size == 0) { // // no files in folder // return null; // } // jarOut.close(); // return bos.toByteArray(); // } // // private static int addFiles(JarOutputStream jarOut, String base, File path) throws IOException { // int count = 0; // for(File file : path.listFiles()) { // if (file.isDirectory()) { // final String dirName = base + file.getName() + "/"; // // JarEntry entry = new JarEntry(dirName); // entry.setTime(0l);// this to ensure equal hash for equal content // jarOut.putNextEntry(entry); // jarOut.closeEntry(); // count += addFiles(jarOut, dirName, file); // } // else { // JarEntry entry = new JarEntry(base + file.getName()); // entry.setTime(file.lastModified()); // jarOut.putNextEntry(entry); // copyStream(new FileInputStream(file), jarOut); // jarOut.closeEntry(); // ++count; // } // } // return count; // } private void addFiles(ZipOutputStream jarOut, String basePackage, String baseUrl) throws IOException, MalformedURLException { if (baseUrl.startsWith("jar:")) { int n = baseUrl.lastIndexOf("!"); if (n < 0) { throw new IllegalArgumentException("Unexpected classpath URL: " + baseUrl); } String fileUrl = baseUrl.substring(4, n); InputStream is = new URL(fileUrl).openStream(); ZipInputStream zis = new ZipInputStream(is); while(true) { ZipEntry ze = zis.getNextEntry(); if (ze != null) { if (matchPath(ze.getName(), basePackage)) { ZipEntry entry = new ZipEntry(ze.getName()); entry.setTime(0); // this is to facilitate content cache if (!pathEntries.contains(entry.getName())) { pathEntries.add(entry.getName()); jarOut.putNextEntry(entry); copyStreamNoClose(zis, jarOut); jarOut.closeEntry(); } } zis.closeEntry(); } else { break; } } } else { InputStream is = new URL(baseUrl).openStream(); for(String line: toLines(is)) { String fpath = baseUrl + "/" + line; String jpath = basePackage + "/" + line; ZipEntry entry = new ZipEntry(jpath); entry.setTime(0); // this is to facilitate content cache if (!pathEntries.contains(entry.getName())) { jarOut.closeEntry(); jarOut.putNextEntry(entry); copyStream(new URL(fpath).openStream(), jarOut); jarOut.closeEntry(); } } } } private boolean matchPath(String path, String basePath) { return path.startsWith(basePath) && path.lastIndexOf('/') <= basePath.length(); } private static void copyStream(InputStream in, OutputStream out) throws IOException { try { byte[] buf = new byte[1 << 12]; while(true) { int n = in.read(buf); if(n >= 0) { out.write(buf, 0, n); } else { break; } } } finally { try { in.close(); } catch(Exception e) { // ignore } } } private static Collection<String> toLines(InputStream is) throws IOException { try { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); List<String> result = new ArrayList<String>(); while(true) { String line = reader.readLine(); if (line == null) { break; } result.add(line); } return result; } finally { try { is.close(); } catch(Exception e) { // ignore } } } private static void copyStreamNoClose(InputStream in, OutputStream out) throws IOException { boolean doClose = true; try { byte[] buf = new byte[1 << 12]; while(true) { int n = in.read(buf); if(n >= 0) { out.write(buf, 0, n); } else { break; } } doClose = false; } finally { if (doClose) { // close if there were exception thrown try { in.close(); } catch(Exception e) { // ignore } } } } }