@Override protected void doDownload(Resource rsrc) throws IOException { // download the resource from the specified URL URLConnection conn = ConnectionUtil.open(rsrc.getRemote()); conn.connect(); // make sure we got a satisfactory response code if (conn instanceof HttpURLConnection) { HttpURLConnection hcon = (HttpURLConnection) conn; if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException( "Unable to download resource " + rsrc.getRemote() + ": " + hcon.getResponseCode()); } } long actualSize = conn.getContentLength(); log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize); InputStream in = null; FileOutputStream out = null; long currentSize = 0L; try { in = conn.getInputStream(); out = new FileOutputStream(rsrc.getLocal()); int read; // TODO: look to see if we have a download info file // containing info on potentially partially downloaded data; // if so, use a "Range: bytes=HAVE-" header. // read in the file data while ((read = in.read(_buffer)) != -1) { // write it out to our local copy out.write(_buffer, 0, read); // if we have no observer, then don't bother computing download statistics if (_obs == null) { continue; } // note that we've downloaded some data currentSize += read; updateObserver(rsrc, currentSize, actualSize); } } finally { StreamUtil.close(in); StreamUtil.close(out); } }
@Override protected long checkSize(Resource rsrc) throws IOException { URLConnection conn = ConnectionUtil.open(rsrc.getRemote()); try { // if we're accessing our data via HTTP, we only need a HEAD request if (conn instanceof HttpURLConnection) { HttpURLConnection hcon = (HttpURLConnection) conn; hcon.setRequestMethod("HEAD"); hcon.connect(); // make sure we got a satisfactory response code if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException( "Unable to check up-to-date for " + rsrc.getRemote() + ": " + hcon.getResponseCode()); } } return conn.getContentLength(); } finally { // let it be known that we're done with this connection conn.getInputStream().close(); } }
protected void createPatch( File patch, ArrayList<Resource> orsrcs, ArrayList<Resource> nrsrcs, boolean verbose) throws IOException { MessageDigest md = Digest.getMessageDigest(); JarOutputStream jout = null; try { jout = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(patch))); // for each file in the new application, it either already exists // in the old application, or it is new for (Resource rsrc : nrsrcs) { int oidx = orsrcs.indexOf(rsrc); Resource orsrc = (oidx == -1) ? null : orsrcs.remove(oidx); if (orsrc != null) { // first see if they are the same String odig = orsrc.computeDigest(md, null); String ndig = rsrc.computeDigest(md, null); if (odig.equals(ndig)) { if (verbose) { System.out.println("Unchanged: " + rsrc.getPath()); } // by leaving it out, it will be left as is during the // patching process continue; } // otherwise potentially create a jar diff if (rsrc.getPath().endsWith(".jar")) { if (verbose) { System.out.println("JarDiff: " + rsrc.getPath()); } // here's a juicy one: JarDiff blindly pulls ZipEntry // objects out of one jar file and stuffs them into // another without clearing out things like the // compressed size, so if, for whatever reason (like // different JRE versions or phase of the moon) the // compressed size in the old jar file is different // than the compressed size generated when creating the // jardiff jar file, ZipOutputStream will choke and // we'll be hosed; so we recreate the jar files in // their entirety before running jardiff on 'em File otemp = rebuildJar(orsrc.getLocal()); File temp = rebuildJar(rsrc.getLocal()); jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.PATCH)); jarDiff(otemp, temp, jout); otemp.delete(); temp.delete(); continue; } } if (verbose) { System.out.println("Addition: " + rsrc.getPath()); } jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.CREATE)); pipe(rsrc.getLocal(), jout); } // now any file remaining in orsrcs needs to be removed for (Resource rsrc : orsrcs) { // add an entry with the resource name and the deletion suffix if (verbose) { System.out.println("Removal: " + rsrc.getPath()); } jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.DELETE)); } StreamUtil.close(jout); System.out.println("Created patch file: " + patch); } catch (IOException ioe) { StreamUtil.close(jout); patch.delete(); throw ioe; } }