001/*
002 *  $Source: /cvsroot2/open/projects/WebARTS/ca/bc/webarts/tools/EasyBak.java,v $
003 *  EasyBak.java - A simple Backup utility with remote archiving capabiity.
004 *
005 *  $Revision: 563 $
006 *  $Date: 2012-11-03 19:28:37 -0700 (Sat, 03 Nov 2012) $
007 *  $Locker:  $
008 *
009 *
010 *  Written by Tom Gutwin - WebARTS Design.
011 *  Copyright (C) 2001 WebARTS Design, North Vancouver Canada
012 *  http://www.webarts.bc.ca
013 *
014 *  This program is free software; you can redistribute it and/or modify
015 *  it under the terms of the GNU General Public License as published by
016 *  the Free Software Foundation; either version 2 of the License, or
017 *  (at your option) any later version.
018 *
019 *  This program is distributed in the hope that it will be useful,
020 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
021 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
022 *  GNU General Public License for more details.
023 *
024 *  You should have received a copy of the GNU General Public License
025 *  along with this program; if not, write to the Free Software
026 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
027 */
028package ca.bc.webarts.tools;
029
030import ca.bc.webarts.widgets.Util;
031
032import cz.dhl.ftp.Ftp;
033import cz.dhl.ftp.FtpConnect;
034import cz.dhl.ftp.FtpFile;
035import cz.dhl.io.CoFile;
036import cz.dhl.io.CoLoad;
037import cz.dhl.io.LocalFile;
038
039import java.io.File;
040import java.io.FileOutputStream;
041import java.io.OutputStream;
042import java.io.IOException;
043//import java.io.SecurityException;
044import java.net.InetAddress;
045import java.net.UnknownHostException;
046import java.util.Iterator;
047import java.util.List;
048import java.util.Vector;
049
050import org.dom4j.Attribute;
051import org.dom4j.Document;
052import org.dom4j.DocumentException;
053import org.dom4j.Element;
054import org.dom4j.Node;
055import org.dom4j.io.SAXReader;
056import org.dom4j.util.XMLErrorHandler;
057
058import org.xml.sax.SAXException;
059
060
061/**
062 *  A quick and dirty backup app that zips and sends the zips off to a spec'd
063 *  archive location. <br>
064 *  <br>
065 *  All that is needed is a config file that specifies the files to zip,
066 *  resultant zip filename and the archive location and type (ftp, local, none).
067 *  <br>
068 *  <br>
069 *  The config file is in xml format (default filename is 'EasyBak.xml'. You can
070 *  spec a different filename as the only param on the commandline<br>
071 *  Example configfile:<br>
072 *  <pre>
073 *  &lt;?xml
074 *      version="1.0"
075 *      encoding="UTF-8"?>
076 *  &lt;?xml-stylesheet
077 *      href="http://warp2.webarts.bc.ca/xml/FancyXmlTreeViewer.xsl"
078 *      type="text/xsl"?>
079 *  &lt;EasyBakPlan>
080 *    &lt;Archive
081 *        filename="easyBak.zip" />
082 *    &lt;ArchiveLocation
083 *        type="ftp"
084 *        user="yourFtpUsername"
085 *        pass="yourpassword"
086 *        server="10.0.0.250"
087 *        path="/private/bak" />
088 *    &lt;BackupFile
089 *        filename="/usr/local/export/home/tgutwin/testdir"
090 *        recurse="true" />
091 *  &lt;/EasyBakPlan> </pre>
092 * <br>
093 * <b>This application is released under a GNU license.</b><br>
094 * Author: <a href="mailto:tgutwin@webarts.bc.ca">Tom Gutwin P.Eng.</a><br>
095 * <a href="http://www.webarts.bc.ca">http://www.webarts.bc.ca</a>
096 * @author    tgutwin
097 */
098class EasyBak extends java.lang.Object implements ZipWatcher
099{
100  /**
101   * Constant holding the users file seperator ("/" or "\").
102   */
103  public final static String SYSTEM_FILE_SEPERATOR = File.separator;
104
105  /**
106   *  Constant holding the users line seperator.
107   */
108  public final static String SYSTEM_LINE_SEPERATOR =
109      System.getProperty("line.separator");
110
111  /**  The default config filename. */
112  public final static String CONFIG_FILENAME =
113      "." + SYSTEM_FILE_SEPERATOR + "EasyBak.xml";
114
115  /**  The XML Plan Document. */
116  private Document zipPlanDoc_ = null;
117
118  /**  Flags if validating docs when read in.
119   **/
120  private boolean validatingXsd_ = false;
121
122  /**  The archive servername(ip address), as read from the config file. */
123  private String archiveServer_ = "";
124
125  /**
126   * The archive type, as read from the config file. Must be one of
127   * 'ftp', 'local', or 'none'.
128   */
129  private String archiveType_ = "";
130
131  /**  The archive ftp server user name, as read from the config file.*/
132  private String archiveUser_ = "";
133
134  /**  The archive ftp server password, as read from the config file. */
135  private String archivePass_ = "";
136
137  /**
138   * The archive path for saving the archive to, as read from the config file.
139   * It is used for local or ftp archive types
140   */
141  private String archivePath_ = "";
142
143  /**  The archive name to zip to, as read from the config file. */
144  private String outFileName_ = "";
145
146  /**  The should we add the date to the archivename. */
147  private String datedArchive_ = "";
148
149  /**  The should we add the currentworkstation name to the archivename. */
150  private String labelArchive_ = "";
151
152  /**  The filenames to include in the archive as read from the config file. */
153  private Vector inFilenames_ = new Vector();
154
155  /**  The filenames to EXclude in the archive as read from the config file. */
156  private Vector excludes_ = new Vector();
157
158  /**  The log output name. */
159  private static String logOutFileName_ = "";
160
161  /**  The MAX size of the archive zip files in Mb. */
162  private long maxZipSize_ = 650L ;
163
164  /**
165   * Flag specifying if the zip will go directly to the final local archive
166   * location. It is used only if type is local.
167   **/
168  boolean zipDirectToLocalArchive_ = true;
169
170  /** A thread mutex to signal that the zip file delivery is still inprocess. **/
171  protected static boolean deliveryInProcess_ = false;
172
173
174  /**
175   * Archive Log file that lives with the final zip file. This ends up being
176   * a FileOutputStream
177   **/
178  static OutputStream infoOutStream_ = null;
179
180
181  /**
182   * Constructor for the EasyBak object that uses the default config filename.
183   */
184  public EasyBak()
185  {
186    this(CONFIG_FILENAME);
187  }
188
189
190  /**
191   * Constructor for the EasyBak object that uses the default config filename.
192   *
193   * @param a new filename to use as the config file.
194   */
195  public EasyBak(String configName)
196  {
197    if (readZipConfig(configName) &&
198        outFileName_ != null &&
199        !outFileName_.equals(""))
200    {
201      logOutFileName_ = "."+SYSTEM_FILE_SEPERATOR+
202        outFileName_.substring(0,outFileName_.length()-4)+"_log.txt";
203      System.out.println("Log File:" + logOutFileName_);
204      try
205      {
206        infoOutStream_ = new FileOutputStream(logOutFileName_);
207        // check if we can get access to the delivery location
208        // BEFORE we spend the time to do the zip
209        if (checkDeliveryConnection())
210        {
211          doBak();
212          // before we leave we have to wait till all zips are delivered
213          waitForDeliveryToFinish();
214
215        }
216        else
217        {
218            infoOutStream_.write(("ERROR EasyBak aborted.\n"+
219                               "Cannot validate the archive location.").getBytes());
220            infoOutStream_.write(
221              ((String)"\nEasyBak Zipping Complete.").getBytes());
222            infoOutStream_.close();
223        }
224      }
225      catch (IOException ex)
226      {
227        System.out.println("Cannot init File:" + logOutFileName_ +
228            ". \nDetails :\n" + ex.getMessage());
229      }
230    }
231  }
232
233
234  /**
235   *  The main program for the EasyBak class. It creates an instance of itself
236   *  and the instance does all the work. only one commandline param is accepted
237   * and is optional... a configfilename to override the default.
238   *
239   * @param  args  The command line arguments
240   */
241  public static void main(String[] args)
242  {
243    EasyBak inst = null;
244    if (args.length == 0)
245      inst = new EasyBak();
246    else if (args.length == 1)
247      inst = new EasyBak(args[0]);
248    else
249      System.out.println("Error: too many commandline args.");
250  }
251
252
253  /**
254   *  Abstracts the actual zipping of the files.
255   **/
256  private void doBak()
257  {
258    Zipper zipper = new Zipper();
259    zipper.registerAsZipWatcher(this);
260    zipper.setQuiet_(true);
261    for (int i = 0; i < excludes_.size(); i++)
262    {
263      zipper.addToExcludes((String)excludes_.get(i));
264    }
265    File[] files = new File[inFilenames_.size()];
266    for (int i = 0; i < inFilenames_.size(); i++)
267    {
268      files[i] = new File((String) inFilenames_.get(i));
269    }
270
271    if (outFileName_ != null &&
272        !outFileName_.equals(""))
273    {
274      // first make sure logname is setup
275      if (logOutFileName_ == null ||
276          (logOutFileName_ != null && logOutFileName_.equals("")) )
277      {
278        logOutFileName_ =
279          outFileName_.substring(0,outFileName_.length()-4)+"_log.txt";
280      }
281
282      zipper.setMaxZipFileSize_(maxZipSize_);
283      try
284      {
285        infoOutStream_ = new FileOutputStream(logOutFileName_);
286        //zipper.setInfoOutStream(infoOutStream_);
287        System.out.println("\nStarting to Zip files to "+outFileName_+"...");
288        logInfo("\nStarting to Zip files to "+outFileName_+"...");
289        zipper.zipFiles(files, new File(outFileName_));
290        logInfo("\nEasyBak Zipping Complete.");
291        infoOutStream_.close();
292      }
293      catch (IOException ex)
294      {
295        System.out.println("Cannot init File:" + logOutFileName_ +
296            ". \nDetails :\n" + ex.getMessage());
297      }
298    }
299   }
300
301
302  /**
303   *  Implementation of ZipWatcher. This method gets called when a zip has been
304   *  completed and is ready for pickup and delivery ;)
305   *
306   * @param  filename   Description of the Parameter
307   * @param  completed  Description of the Parameter
308   */
309  public void nextZipDone(final String filename, boolean completed)
310  {
311    System.out.println("Ready For Pickup:" + filename);
312
313    // Run in separate thread.
314    Thread ftpThread =
315      new Thread()
316      {
317        public void run()
318        {
319          deliverZipToArchiveLocation(filename);
320        }
321      };
322
323    ftpThread.start();
324    //watchFTPActive(ftpThread);
325  }
326
327
328  /** In a separate thread, watch the ftp thread and change the deliveryInProcess_ flag to false when done. **/
329  private void watchFTPActive(final Thread watchThread)
330  {
331    long timeWatched = 0;
332    int sleepTime = 5000;
333    final long timeToTerminate = 1800000; // 30 minutes
334
335    // inline Thread to do the watching
336    Thread watchdogThread = new Thread (){
337      long timeWatched = 0;
338      int sleepTime = 500;
339
340      public void run()
341      {
342        while (timeWatched < timeToTerminate &&
343                watchThread.isAlive() &&
344                !watchThread.isInterrupted() )
345        {
346          timeWatched += sleepTime;
347          try
348          {
349            sleep(sleepTime);
350          }
351          catch (InterruptedException iex)
352          {
353          }
354        }
355
356        if (timeWatched >= timeToTerminate)
357        {
358          // this throws a security exception If the interrupt is not allowed
359          watchThread.interrupt();
360        }
361      }
362    };
363
364    deliveryInProcess_ = true;
365    watchdogThread.start();
366    deliveryInProcess_ = false;
367  }
368
369
370  /** Blocks the current thread until the FTP Delivery is finished.
371      It watches for deliveryInProcess_ to be false. **/
372  private void waitForDeliveryToFinish()
373  {
374    long timeWatched = 0;
375    int sleepTime = 5000;
376
377    try
378    {
379      while (deliveryInProcess_)
380        {
381          timeWatched += sleepTime;
382          Util.sleep(sleepTime);
383        }
384    }
385    catch (SecurityException sEx)
386    {
387    }
388  }
389
390
391  /**
392   *  Checks that the specified archive location is present and accessible.
393   *
394   * @return  a boolean if the connection is usable or not.
395   */
396  private boolean checkDeliveryConnection()
397  {
398    boolean retVal = false;
399    String testPath = "";
400    if (archiveType_.equals("ftp"))
401    {
402      logInfo("\nTesting FTP Archive Location.");
403      Ftp ftp = new Ftp();
404      try
405      {
406        /*
407         *  connect & login to host
408         */
409        logInfo("\n    Connecting...");
410        ftp.connect(archiveServer_, Ftp.PORT);
411        ftp.login(archiveUser_, archivePass_);
412        if (ftp != null && ftp.isConnected())
413        {
414          logInfo("\n    Connected!");
415          logInfo("\n    Checking Final dir: "+ archivePath_);
416          retVal = ftp.cd(archivePath_);
417          logInfo("\n     "+(retVal?"success":"faliled"));
418          ftp.disconnect();
419        }
420        ftp=null;
421      }
422      catch (IOException e)
423      {
424        System.out.println(e);
425      }
426    }
427    else
428    {
429      logInfo("\nTesting Local Archive Location.");
430      // check for the archive path
431      if (archiveType_.equals("local"))
432        testPath = archivePath_;
433      else
434        testPath = System.getProperty("user.dir");
435
436      if (testPath != null && !testPath.equals(""))
437      {
438        File testLocation = new File(testPath);
439        logInfo("\n"+ testPath);
440        if ( (testLocation != null) &&
441             testLocation.exists() &&
442             testLocation.isDirectory())
443        {
444          // do a test write
445          try
446          {
447            logInfo("\nTest Write."+archivePath_+File.separator+"t27a98Y1.ffs");
448            File testFile = new File(archivePath_+File.separator+
449                                     "t27a98Y1.ffs");
450            testFile.createNewFile();
451            retVal = (testFile != null) && testFile.canWrite();
452            logInfo("\n..."+(retVal?" suceeded":" failed."));
453            if (retVal) testFile.delete();
454          }
455          catch (IOException ex)
456          {
457            System.out.println("Cannot Test Log Location File:"+ex.getMessage());
458          }
459        }
460      }
461    }
462    return retVal;
463  }
464
465
466  /**
467   *  Sends a string message to the infoOutStream_.
468   *
469   * @param  the message to send
470   */
471  private void logInfo(String logEntry)
472  {
473    if (infoOutStream_ != null && logEntry!= null)
474      try
475      {
476        infoOutStream_.write(logEntry.getBytes());
477      }
478      catch (IOException ex)
479      {
480        System.out.println("Cannot init File:" + logOutFileName_ +
481            ". \nDetails :\n" + ex.getMessage());
482      }
483  }
484
485
486  /**
487   *  Takes a completed zip file and sends/moves it to the specified archive
488   *  location for this instance.
489   *
490   * @param  filename  Zip Filename to send
491   */
492  private void deliverZipToArchiveLocation(String filename)
493  {
494    deliveryInProcess_ = true;
495    String shortname = filename.substring(filename.lastIndexOf(File.separator) + 1);
496    if (archiveType_.equals("ftp"))
497    {
498      Ftp ftp = new Ftp();
499      try
500      {
501        /*
502         *  connect & login to host
503         */
504        ftp.connect(archiveServer_, Ftp.PORT);
505        ftp.login(archiveUser_, archivePass_);
506
507        String uploadPath = archivePath_ + "/" + shortname;
508
509        /*
510         *  destination  FtpFile remote file
511         */
512        logInfo("\n"+shortname +" --> Preparing to deliver to " + archiveServer_ + uploadPath);
513        System.out.println(shortname +
514              " --> Preparing to deliver to " + archiveServer_ + uploadPath);
515        //CoFile to = new FtpFile(uploadPath, ftp);
516        CoFile to = new FtpFile(archivePath_, shortname, ftp);
517
518        /*
519         *  source LocalFile
520         */
521        logInfo("\n"+shortname +" --> Preparing to deliver from " + filename);
522        System.out.println(shortname +
523              " --> Preparing to deliver from " + filename);
524        CoFile from = new LocalFile(filename);
525
526        /*
527         *  upload
528         */
529        if (CoLoad.copy(to, from))
530        {
531          logInfo("\n"+shortname +" --> Delivery Successful to " + archiveServer_ + uploadPath);
532          System.out.println(shortname +
533              " --> Delivery Successful to " + archiveServer_ + uploadPath);
534          // Now delete the local zip file
535          ((LocalFile) from).delete();
536        }
537
538        /*
539         *  upload LOGfile
540         */
541        try
542        {
543          uploadPath = archivePath_ + "/" + logOutFileName_;
544          to = new FtpFile(uploadPath, ftp);
545          from = new LocalFile(logOutFileName_);
546          if (CoLoad.copy(to, from))
547          {
548            logInfo("\n"+shortname +" --> Logfile Delivery Successful to " + archiveServer_ + uploadPath);
549            System.out.println(shortname +
550                " --> Logfile Delivery Successful to " + archiveServer_ + uploadPath);
551            ((LocalFile) from).delete();
552          }
553        }
554        catch (Exception e)
555        {
556          // Any exception here should not halt the uploads
557          System.out.println(e);
558          e.printStackTrace();
559          logInfo("\nERROR: "+e.getMessage()+"\n"+e.getStackTrace());
560        }
561
562        ftp.disconnect();
563      }
564      catch (IOException e)
565      {
566        System.out.println(e);
567        e.printStackTrace();
568        logInfo("\nERROR: "+e.getMessage()+"\n"+e.getStackTrace());
569        if (ftp!=null && ftp.isConnected())
570          ftp.disconnect();
571      }
572    }
573    else if (archiveType_.equals("local") && !zipDirectToLocalArchive_)
574    {
575      // move the zip file
576      if (Util.moveFile(filename, archivePath_))
577      {
578        System.out.println(shortname +
579            " Delivery Successful to " + archivePath_);
580      }
581      else
582      {
583        System.out.println(shortname +
584            "\nERROR: Delivery of " + shortname + " to folder "+
585            archivePath_+" failed.\n" +
586            "Please check permissions and settings.");
587      }
588
589      // move the logfile
590      if (Util.moveFile(logOutFileName_, archivePath_))
591      {
592        System.out.println(logOutFileName_ +
593            " Delivery Successful to " + archivePath_);
594      }
595      else
596      {
597        System.out.println(logOutFileName_ +
598            "\nERROR: Delivery of " + logOutFileName_ + " to folder "+
599            archivePath_+" failed.\n" +
600            "Please check permissions and settings.");
601      }
602    }
603    else if (archiveType_.equals("none"))
604    {
605      // do nothing
606      System.out.println(shortname +
607          " is now in current directory.");
608    }
609    deliveryInProcess_ = false;
610  }
611
612
613  /**
614   *  Reads the DEFAULT config XML file and parses the required data.
615   *
616   * @return    success or failure
617   */
618  private boolean readZipConfig()
619  {
620    return readZipConfig(CONFIG_FILENAME);
621  }
622
623
624  /**
625   *  Reads the passed in config XML file and parses the required data.
626   *
627   * @param  configFilename  Description of the Parameter
628   * @return                 success or failure
629   */
630  private boolean readZipConfig(String configFilename)
631  {
632    boolean retVal = true;
633
634    // validate input params
635    if (configFilename == null || configFilename.equals(""))
636    {
637      configFilename = CONFIG_FILENAME;
638    }
639
640    zipPlanDoc_ = readWithSAX(configFilename);
641
642    if (zipPlanDoc_ != null)
643    {
644      // Get the Zipfile name
645      outFileName_ = zipPlanDoc_.selectSingleNode("//EasyBakPlan/Archive").
646          valueOf("@filename");
647      datedArchive_ = zipPlanDoc_.selectSingleNode("//EasyBakPlan/Archive").
648          valueOf("@dated");
649      labelArchive_ = zipPlanDoc_.selectSingleNode("//EasyBakPlan/Archive").
650          valueOf("@serverLabel");
651
652      String tmpStr = zipPlanDoc_.selectSingleNode("//EasyBakPlan/Archive").
653            valueOf("@maxSize");
654      if (tmpStr != null && !tmpStr.equals(""))
655          maxZipSize_ = Long.parseLong(tmpStr);
656
657      // get the files to zip
658      List list = zipPlanDoc_.selectNodes("//EasyBakPlan/BackupFile");
659      for (Iterator iter = list.iterator(); iter.hasNext(); )
660      {
661        Node BackupFileNode = (Node) iter.next();
662        inFilenames_.add(BackupFileNode.valueOf("@filename"));
663      }
664      System.out.println("NumFiles =" + inFilenames_.size());
665
666      // get the files to EXCLUDE
667      list = zipPlanDoc_.selectNodes("//EasyBakPlan/ExcludedFile");
668      for (Iterator iter = list.iterator(); iter.hasNext(); )
669      {
670        Node ExcludedFileNode = (Node) iter.next();
671        excludes_.add(ExcludedFileNode.valueOf("@endsWith"));
672      }
673      System.out.println("NumFiles =" + inFilenames_.size());
674
675      // get the archive type and location
676      archiveType_ =
677          zipPlanDoc_.selectSingleNode("//EasyBakPlan/ArchiveLocation").
678          valueOf("@type");
679      if (!(archiveType_.toLowerCase().equals("ftp") ||
680          archiveType_.toLowerCase().equals("local") ||
681          archiveType_.toLowerCase().equals("none")))
682      {
683        retVal = false;
684        System.out.println("ERROR in EasyBak Config File: ArchiveLocation - " +
685            "type attribute MUST be one of 'ftp' or 'local' or 'none'.");
686        archiveType_ = "none";
687      }
688      System.out.println("archiveType_ =" + archiveType_);
689
690      if (archiveType_.toLowerCase().equals("ftp"))
691      {
692        archiveServer_ =
693            zipPlanDoc_.selectSingleNode("//EasyBakPlan/ArchiveLocation").
694            valueOf("@server");
695        System.out.println("ArchiveServer =" + archiveServer_);
696        archiveUser_ =
697            zipPlanDoc_.selectSingleNode("//EasyBakPlan/ArchiveLocation").
698            valueOf("@user");
699        System.out.println("archiveUser_ =" + archiveUser_);
700        archivePass_ =
701            zipPlanDoc_.selectSingleNode("//EasyBakPlan/ArchiveLocation").
702            valueOf("@pass");
703        System.out.println("archivePass_ = ****");
704      }
705      archivePath_ =
706          zipPlanDoc_.selectSingleNode("//EasyBakPlan/ArchiveLocation").
707          valueOf("@path");
708      System.out.println("archivePath_ =" + archivePath_);
709
710      // concatenate the FULL archive filename
711      if (labelArchive_ != null && !labelArchive_.equals("") &&
712          labelArchive_.toLowerCase().equals("true"))
713      {
714        try
715        {
716          InetAddress localHostAddr = InetAddress.getLocalHost();
717          if (localHostAddr != null)
718            outFileName_ += (outFileName_ != null && !outFileName_.equals("") ?
719                            "-":"") +
720                            localHostAddr.getHostName();
721        }
722        catch (UnknownHostException ex)
723        {
724          System.out.println(
725            "Cannot obtain LocalHostname - NOT labelling archive file." );
726        }
727      }
728      if (datedArchive_ != null && !datedArchive_.equals("") &&
729          datedArchive_.toLowerCase().equals("true"))
730        outFileName_ += (outFileName_ != null && !outFileName_.equals("") ?
731                         "-":"") + Util.createCurrentDateStamp();
732
733      if (archiveType_.toLowerCase().equals("local")&&zipDirectToLocalArchive_)
734      {
735        outFileName_ = archivePath_ + File.separator + outFileName_;
736      }
737
738      if (!outFileName_.toLowerCase().endsWith(".zip"))
739      {
740        outFileName_+=".zip";
741      }
742      System.out.println("Outname =" + outFileName_);
743
744    }
745    else
746      retVal = false;
747
748    return retVal;
749  }
750
751
752  /**
753   *  Reads the input config file with a SAX processor using dom4j.
754   *
755   * @param  filename  The XML config file to read
756   * @return           Description of the Return Value
757   */
758  private Document readWithSAX(String filename)
759  {
760    Document retVal = null;
761
762    try
763    {
764      retVal = (new SAXReader(validatingXsd_)).read(filename);
765    }
766    catch (DocumentException docEx)
767    {
768      System.out.println("\nERROR: The XML Config file "+filename+
769                         " cannot be read or validated.");
770      System.out.println("Please check file: "+ filename);
771    }
772    catch (Exception ex)
773    {
774      System.out.println("Something crapped Out:\n" + ex.getMessage());
775    }
776
777    return retVal;
778  }
779
780
781}