Java Source Code: equip.ect.webstart.UploadManager


   1: /*
   2: <COPYRIGHT>
   3: 
   4: Copyright (c) 2005, University of Nottingham
   5: All rights reserved.
   6: 
   7: Redistribution and use in source and binary forms, with or without
   8: modification, are permitted provided that the following conditions are met:
   9: 
  10:  - Redistributions of source code must retain the above copyright notice, this
  11:    list of conditions and the following disclaimer.
  12: 
  13:  - Redistributions in binary form must reproduce the above copyright notice,
  14:    this list of conditions and the following disclaimer in the documentation
  15:    and/or other materials provided with the distribution.
  16: 
  17:  - Neither the name of the University of Nottingham
  18:    nor the names of its contributors may be used to endorse or promote products
  19:    derived from this software without specific prior written permission.
  20: 
  21: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22: AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  24: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  25: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  26: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  27: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  28: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31: 
  32: </COPYRIGHT>
  33: 
  34: Created by: Chris Greenhalgh (University of Nottingham)
  35: Contributors:
  36:   Chris Greenhalgh (University of Nottingham)
  37: 
  38: */
  39: package equip.ect.webstart;
  40: import java.io.*;
  41: import java.net.*;
  42: 
  43: /** upload local log & persistence files to UploadHttpServer using POSTs.
  44:  * Chris Greenhalgh 2005-04-20
  45:  */
  46: public class UploadManager 
  47:	  {
  48:    static boolean debug = false;
  49:    /** cons
  50:     */
  51:    public UploadManager() 
  52:	      {
  53:    }
  54:    /** do an upload, return OK
  55:     */
  56:    public static boolean upload(File localRoot, URL uploadRoot, boolean updateCurrentLogs, boolean migrateOldLogs) 
  57:	      {
  58:    boolean error = false;
  59:    try 
  60:	      {
  61:        // recursive
  62:        File files [] = localRoot.listFiles();
  63:        for (int fi=0; fi<files.length; fi++) 
  64:	          {
  65:        if (files[fi].isDirectory()) 
  66:	          {
  67:            if (files[fi].getName().equals("history")) 
  68:	              {
  69:            System.err.println("Don't upload the history/ subdirectory!");
  70:            continue;
  71:            }
  72:            if (!upload(files[fi], new URL(uploadRoot, files[fi].getName()+"/"), updateCurrentLogs, migrateOldLogs))
  73:            error = true;
  74:        }
  75:        else 
  76:	          {
  77:            String name = files[fi].getName();
  78:            int ei = name.lastIndexOf(".");
  79:            String extension = (ei < 0) ? "" : name.substring(ei);
  80:            if (ei>=0)
  81:            name = name.substring(0, ei);
  82:            boolean isCurrentLog = false;
  83:            boolean isOldLog = false;
  84:
  85:            //System.out.println("Consider file "+name+" extension "+extension);
  86:
  87:            int numdigits = 0;
  88:            for (numdigits=0; name.length()-1-numdigits>=0 && 
  89:            Character.isDigit(name.charAt(name.length()-1-numdigits)); numdigits++)
  90:            ;
  91:            boolean hasTimestamp = (numdigits>=10);
  92:            long timestamp = 0;
  93:            if (hasTimestamp)
  94:            try 
  95:	              {
  96:                timestamp = new Long(name.substring(name.length()-numdigits)).longValue();
  97:                //System.out.println("File "+files[fi].getName()+" has timestamp "+timestamp+" ("+numdigits+" digits)");
  98:            } 
  99:            catch (NumberFormatException e) 
 100:	              {
 101:                System.err.println("ERROR: parsing presumed timestamp of "+numdigits+" at end of "+name);
 102:            }
 103:
 104:            if (files[fi].getName().equals("persist.xml")) 
 105:	              {
 106:            System.out.println("Found current persist.xml file: "+files[fi]);
 107:            isCurrentLog = true;
 108:            }
 109:            else if (extension.equals(".txt") || extension.equals(".eqbser")) 
 110:	              {
 111:            // probably a logfile or event log
 112:            if (!hasTimestamp) 
 113:	              {
 114:                System.out.println("Warning: found logfile without timestamp: "+files[fi]);
 115:                isCurrentLog = true; //?!
 116:            } 
 117:            else 
 118:	              {
 119:                // newest of this kind??
 120:                boolean newest = true;
 121:                for (int f2=0; newest && f2<files.length; f2++) 
 122:	                  {
 123:                String name2 = files[f2].getName();
 124:                //System.out.println("Check newest against "+name2+" (extension? "+
 125:                //    (name2.endsWith(extension) ? "matches" : "differs")+
 126:                //    "prefix "+(name2.startsWith(name.substring(0, name.length()-numdigits)) ? "matches" : "differs"));
 127:                if (name2.endsWith(extension) && name2.startsWith(name.substring(0, name.length()-numdigits))) 
 128:	                  {
 129:                    long timestamp2 = 0;
 130:                    try 
 131:	                      {
 132:                    timestamp2 = new Long(name2.substring(name.length()-numdigits,
 133:                        name2.length()-extension.length())).longValue();
 134:                    //System.out.println("Alt File "+name2+" has timestamp "+timestamp2+" ("+
 135:                    //    (timestamp2 > timestamp ? "newer" : "older"));
 136:                    if (timestamp2 > timestamp)
 137:                        newest = false;
 138:                    } 
 139:                    catch (NumberFormatException e) 
 140:	                      {
 141:                    System.err.println("ERROR: parsing presumed timestamp of "+numdigits+" in "+name2);
 142:                    }
 143:                }
 144:                }
 145:                isCurrentLog = newest;
 146:                isOldLog = !newest;
 147:            }
 148:            System.out.println("Found "+(isCurrentLog ? "current" : "old")+" log file: "+files[fi]);
 149:            }
 150:            if ((isCurrentLog && updateCurrentLogs) ||
 151:            (isOldLog && migrateOldLogs)) 
 152:	              {
 153:            String uploadName = files[fi].getName();
 154:            if (!hasTimestamp) 
 155:	              {
 156:                long mod = files[fi].lastModified();
 157:                if (mod==0) 
 158:	                  {
 159:                System.err.println("Warning: unable to read last modified time of "+files[fi]+"; using clock");
 160:                mod = System.currentTimeMillis();
 161:                }
 162:                uploadName = name+"-"+mod+extension;
 163:                System.out.println("Adding timestamp to "+files[fi].getName()+" -> "+uploadName);
 164:            }
 165:            if (!uploadFile(files[fi], new URL(uploadRoot, uploadName), isOldLog && migrateOldLogs))
 166:                error = true;
 167:            }
 168:        }
 169:        }
 170:    } 
 171:    catch (Exception e) 
 172:	      {
 173:        System.err.println("ERROR doing upload of "+localRoot+" to "+uploadRoot+": "+e);
 174:        e.printStackTrace(System.err);
 175:        error = true;
 176:    }
 177:    return !error;
 178:    }
 179:    /** upload file, true = ok
 180:     */
 181:    protected static boolean uploadFile(File localFile, URL remoteUrl, boolean deleteOnSuccess) 
 182:	      {
 183:    try 
 184:	      {
 185:        if (!localFile.exists() || !localFile.canRead())
 186:	          {
 187:        System.err.println("Cannot find/read local upload file "+localFile);
 188:        return false;
 189:        }
 190:        long localLength = localFile.length();
 191:        long localModified = localFile.lastModified();
 192:
 193:        // check what if anything is there already
 194:        URLConnection connection = remoteUrl.openConnection();
 195:        if (connection instanceof HttpURLConnection)
 196:        ((HttpURLConnection)connection).setRequestMethod("HEAD");
 197:        //connection.setDoInput(true); // head?
 198:        //connection.setDoOutput(true); // post?
 199:        connection.connect();
 200:        String status = connection.getHeaderField(null);
 201:        long remoteLength = connection.getHeaderFieldInt("Content-Length", -1);
 202:        long remoteModified = connection.getHeaderFieldDate("Last-Modified", 0);
 203:        System.out.println("Check "+remoteUrl);
 204:        System.out.println("Status: "+status);
 205:        System.out.println("HTTP Length="+remoteLength+" lastModified="+remoteModified);
 206:        try 
 207:	          {
 208:        connection.getOutputStream().close();
 209:        } 
 210:        catch (Exception ee) {}
 211:        try 
 212:	          {
 213:        connection.getInputStream().close();
 214:        } 
 215:        catch (Exception ee) {}
 216:
 217:        if (localLength < remoteLength) 
 218:	          {
 219:        System.err.println("ERROR: remote file already longer than local file "+localFile+
 220:            " ("+localLength+" vs "+remoteLength+")");
 221:        return false;
 222:        } 
 223:        else if (remoteLength<0) 
 224:	          {
 225:        remoteLength = 0;
 226:        if (status!=null && status.indexOf(" 2")>=0) 
 227:	          {
 228:            System.out.println("Warning: upload server did not return length for file "+remoteUrl);
 229:            return false;
 230:        }
 231:        }
 232:
 233:        boolean error = false;
 234:        if (localLength==remoteLength) 
 235:	          {
 236:        System.out.println("Local file "+localFile+" already uploaded as "+remoteUrl+" ("+localLength+" bytes)");
 237:        } 
 238:        else 
 239:	          {
 240:        // upload
 241:        FileInputStream fin = new FileInputStream(localFile);
 242:        long skipped = remoteLength>0 ? fin.skip(remoteLength) : 0;
 243:        if (skipped!=remoteLength) 
 244:	          {
 245:            System.err.println("ERROR: Skipping to "+remoteLength+" of file "+localFile+" skipped only "+skipped);
 246:            return false;
 247:        }
 248:
 249:        // append by post
 250:        connection = remoteUrl.openConnection();
 251:        if (connection instanceof HttpURLConnection)
 252:            ((HttpURLConnection)connection).setRequestMethod("POST");
 253:        connection.setDoInput(true); 
 254:        connection.setDoOutput(true);
 255:        connection.connect();
 256:
 257:        OutputStream rout = connection.getOutputStream();
 258:
 259:        byte data[] = new byte[4096];
 260:        long at = remoteLength;
 261:        while (at<localLength) 
 262:	          {
 263:            int size = data.length;
 264:            if (localLength-at < size)
 265:            size = (int)(localLength-at);
 266:            int lcnt = fin.read(data, 0, size);
 267:            if (lcnt<=0) 
 268:	              {
 269:            System.err.println("ERROR uploading data of file "+localFile+" (wanted "+size+", read "+lcnt+")");
 270:            error = true;
 271:            break;
 272:            }
 273:            if (lcnt>0) 
 274:	              {
 275:            //System.out.println("Write "+lcnt+" bytes");
 276:            //System.out.println("Bytes "+data[0]+","+data[1]+","+data[2]+","+data[3]+",...");
 277:            rout.write(data, 0, lcnt);
 278:            }
 279:            at += lcnt;
 280:        }
 281:        //System.out.println("Flush");
 282:        rout.flush();
 283:        //System.out.println("Close");
 284:        rout.close();
 285:        //System.out.println("Close input");
 286:        connection.getInputStream().close();
 287:        System.out.println("Uploaded "+(at-remoteLength)+" bytes of local file "+localFile);
 288:        fin.close();
 289:        }
 290:        if (!error && deleteOnSuccess) 
 291:	          {
 292:        System.out.println("Delete (old) local file "+localFile);
 293:        if (!localFile.delete()) 
 294:            System.out.println("ERROR: unable to delete local file "+localFile);
 295:        }
 296:        return !error;
 297:    } 
 298:    catch (Exception e) 
 299:	      {
 300:        System.err.println("ERROR uploading file "+localFile+" to "+remoteUrl+": "+e);
 301:        e.printStackTrace(System.err);
 302:        return false;
 303:    }
 304:    }
 305:    /** test main
 306:     */
 307:    public static void main(String [] args) 
 308:	      {
 309:    if (args.length!=3) 
 310:	      {
 311:        System.err.println("Usage: java "+UploadManager.class.getName()+" localDir uploadUrl c(urrent)|o(old)|b(oth)");
 312:        System.exit(-1);
 313:    }
 314:    try 
 315:	      {
 316:        File localRoot = new File(args[0]);
 317:        URL uploadRoot = new URL(args[1]);
 318:        boolean updateCurrentLogs = args[2].startsWith("c") || args[2].startsWith("b");
 319:        boolean migrateOldLogs = args[2].startsWith("o") || args[2].startsWith("b");
 320:        UploadManager uploader = new UploadManager();
 321:        uploader.upload(localRoot, uploadRoot, updateCurrentLogs, migrateOldLogs);
 322:    }
 323:    catch (Exception e)
 324:	      {
 325:        System.err.println("ERROR: "+e);
 326:        e.printStackTrace(System.err);
 327:    }
 328:    }
 329:}