Package org.jasen.update

Source Code of org.jasen.update.JasenAutoUpdater

/*
* @(#)JasenAutoUpdater.java  5/01/2005
*
* Copyright (c) 2005 jASEN.org
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*   1. Redistributions of source code must retain the above copyright notice,
*      this list of conditions and the following disclaimer.
*
*   2. Redistributions in binary form must reproduce the above copyright
*      notice, this list of conditions and the following disclaimer in
*      the documentation and/or other materials provided with the distribution.
*
*   3. The names of the authors may not be used to endorse or promote products
*      derived from this software without specific prior written permission.
*
*   4. Any modification or additions to the software must be contributed back
*      to the project.
*
*   5. Any investigation or reverse engineering of source code or binary to
*      enable emails to bypass the filters, and hence inflict spam and or viruses
*      onto users who use or do not use jASEN could subject the perpetrator to
*      criminal and or civil liability.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JASEN.ORG,
* OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.jasen.update;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.Vector;
import java.util.zip.ZipFile;

import org.apache.log4j.Logger;
import org.jasen.JasenScanner;
import org.jasen.interfaces.AutoUpdateExecutor;
import org.jasen.io.ByteArrayReader;
import org.jasen.thread.StoppableThread;
import org.jasen.util.FileUtils;
import org.jasen.util.IOUtils;
import org.jasen.util.WebUtils;
import org.xml.sax.SAXException;

/**
* <p>
* The auto updater is a thread which continuously runs and at regular intervals checks the update site for engine updates.
* </p>
* <p>
* If new updates are found, they are downloaded and upon successful download the engine is signalled to
* block any further scan requests until the updates have been loaded.
* </p>
* @author Jason Polites
*/
public final class JasenAutoUpdater extends StoppableThread {

    static final Logger logger = Logger.getLogger(JasenAutoUpdater.class);

    public static final String UPDATE_PATH = "updates/";
    public static final String UPDATE_LIB_PATH = UPDATE_PATH + "lib/";
    public static final String PARCEL_CACHE = UPDATE_PATH + "jasen-update.dat_DO_NOT_DELETE";

    private JasenAutoUpdateManager manager;

    private long lastUpdateTime = -1L;

    private JasenAutoUpdateParcelDigester digester;

    private volatile boolean running = false;
    private volatile boolean neverStart = false;
    private volatile boolean stopped = false;
    private volatile boolean forcedUpdate = false;
    private volatile boolean idle = true;

    /**
     *
     */
    public JasenAutoUpdater(JasenAutoUpdateManager manager) {
        super();
        this.manager = manager;
    }

    /**
     * @param name
     */
    public JasenAutoUpdater(JasenAutoUpdateManager manager, String name) {
        super(name);
        this.manager = manager;
    }

    public void run() {

        logger.debug("AutoUpdater starting...");

        if(!neverStart) {

            running = true;

            long nextUpdateTime;
            long currentTime;

            digester = new JasenAutoUpdateParcelDigester();
            digester.init();

            // Make sure we have an update path
            File updatePath = new File(UPDATE_PATH);
            updatePath.mkdirs();

            while(running && !neverStart) {

                logger.debug("AutoUpdater running");

                idle = false;

                synchronized(this) {

                    logger.debug("AutoUpdater in check cycle");

                  currentTime = System.currentTimeMillis();

                  if(forcedUpdate) {
                      forcedUpdate = false;
                      lastUpdateTime = currentTime;
                      checkForUpdates();
                  }
                  else if(lastUpdateTime == -1 && manager.getConfiguration().isCheckOnStartup()) {
                      lastUpdateTime = currentTime;
                      checkForUpdates();
                  }
                  else if(lastUpdateTime != -1) {
                      nextUpdateTime = lastUpdateTime + (manager.getConfiguration().getFrequency() * 60000L); // milliseconds

                      if(nextUpdateTime <= currentTime) {
                          lastUpdateTime = currentTime;
                          checkForUpdates();
                      }
                  }
                  else
                  {
                      lastUpdateTime = currentTime;
                  }

                  logger.debug("Auto updater is running");

                  // Wait for timeout or stop signal
                  if(running) {
                      try {
                          idle = true;
                          logger.debug("AutoUpdater waiting for notification");
                          wait(manager.getConfiguration().getFrequency() * 60000L);
                          logger.debug("AutoUpdater stopped waiting");
                          idle = false;
                      }
                      catch (InterruptedException ignore) {}
                  }
              }
            }
        }

        logger.debug("AutoUpdater stopped");

        stopped = true;

        // Make sure nobody is waiting for us to stop
        synchronized(this) {
            notifyAll();
        }
    }

    private boolean checkForUpdates() {

        logger.debug("Checking for updates...");

        // Look for the update parcel
        // Because this is small, just write it to a String
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        JasenAutoUpdateParcel parcel = null;
        JasenAutoUpdateParcel oldParcel = null;
        URL parcelUrl;

        boolean updated = false;
        boolean updateRequired = false;

        try {
            parcelUrl = prepareItemURL(manager.getConfiguration().getUpdateURL(), manager.getConfiguration().getParcel());

            WebUtils.get(parcelUrl, bout, manager.getConfiguration().getReadBuffer(),manager.getConfiguration().getReadTimeout());

            // Use the digester to parse the file
            ByteArrayReader reader = new ByteArrayReader(bout.toByteArray());
            parcel = (JasenAutoUpdateParcel)digester.parse(reader);

            // Check the current parcel against the last recorded parcel
            oldParcel = loadLastParcel();

            if(oldParcel == null || (oldParcel.getUpdateDate() == null || !oldParcel.getUpdateDate().equals(parcel.getUpdateDate()))) {
                // We are ok to update...
                updateRequired = true;

                // Notify the manager
                logger.debug("Updates are required, notifying the manager");

                manager.notifyUpdateRequired(parcel);

                update(parcel);

                // And record the update
                saveCurrentParcel(parcel);

                updated = true;
            }
            else
            {
                logger.debug("No updates required");
            }
        }
        catch (IOException e) {
           
            if(e instanceof FileNotFoundException) {
                logger.debug("No updates available");
            }
            else
            {
                manager.getErrorHandler().handleException(e);
            }
        }
        catch (SAXException e) {
            manager.getErrorHandler().handleException(e);
        }
        catch (ClassNotFoundException e) {
            manager.getErrorHandler().handleException(e);
        }
        finally {
            // Notify completed
            //if(updateRequired) {
            logger.debug("Updates completed, notifying the manager");

                // Create a report
                JasenAutoUpdateReport report = new JasenAutoUpdateReport();
                report.setUpdated(updated);
                report.setUpdateParcel(parcel);

                if(parcel != null && parcel.getWebUpdateRequired() != null) {
                    report.setWebUpdateRequired(new Boolean(parcel.getWebUpdateRequired()).booleanValue());
                    report.setWebUpdateUrl(parcel.getWebUpdateUrl());
                }

                report.setEngineRestart(updated);

                manager.notifyUpdateComplete(report);
               
                logger.debug("Manager notified");
            //}
        }

        return updated;
    }

    private void update(JasenAutoUpdateParcel parcel) throws IOException {

        logger.debug("Updates found, downloading...");

        URL updateUrl = prepareItemURL(manager.getConfiguration().getUpdateURL(), parcel.getArchiveName());

        // This time, pipe to a file
        File update = new File(UPDATE_PATH + parcel.getArchiveName());
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(update);
            WebUtils.get(updateUrl, out, manager.getConfiguration().getReadBuffer(),manager.getConfiguration().getReadTimeout(), manager);
        }
        finally {
            if(out != null) {
                try {
                    out.close();
                } catch (Exception ignore) {}
            }
        }

        // Now, the update should be a zip file
        ZipFile zip = null;
        Vector tmpFiles = null;
        InputStream zin = null;
        OutputStream fout = null;

        try {
            zip = new ZipFile(update);

            // We want to simply extract each file defined in the parcel and write it to the relevant path
            if(parcel.getFiles() != null) {
                JasenAutoUpdateFile updateFile = null;
                Iterator i = parcel.getFiles().iterator();


                File outFile = null;
                File tmpFile = null;
                Vector oldFiles = null;

                while(i.hasNext()) {
                    updateFile = (JasenAutoUpdateFile)i.next();

                    try {
                        outFile = new File(updateFile.getJasenPath());

                        // If this file already exists, rename it until we are done
                        if(outFile.exists()) {
                            if(oldFiles == null) {
                                oldFiles = new Vector();
                                tmpFiles = new Vector();
                            }

                            // Create a temp file so we don't overwrite
                            tmpFile = new File(outFile.getAbsolutePath() + ".tmp");

                            tmpFiles.add(tmpFile);

                            // Copy original to the temp
                            FileUtils.copy(outFile, tmpFile);

                            // Save the path of the original for rollback
                            oldFiles.add(outFile.getAbsolutePath());
                        }

                        zin = zip.getInputStream(zip.getEntry(updateFile.getArchivePath()));
                        fout = new FileOutputStream(outFile);

                        IOUtils.pipe(zin, fout, 1024);
                    }
                    catch (Exception e) {
                        // We need to rollback the file replacements
                        if(oldFiles != null) {

                            if(fout != null) {
                                try {
                                    fout.close();
                                    fout = null;
                                } catch (Exception ignore) {}
                            }

                            if(zin != null) {
                                try {
                                    zin.close();
                                    zin = null;
                                } catch (Exception ignore) {}
                            }

                            String path = null;

                            for (int j = 0; j < oldFiles.size(); j++) {
                                try {

                                    path = (String)oldFiles.get(j);
                                    outFile = new File(path);

                                    // Get the tmp file
                                    tmpFile = new File(outFile.getAbsolutePath() + ".tmp");

                                    // If the replacment exists, delete it
                                    if(outFile.exists() && tmpFile.exists()) {
                                        outFile.delete();
                                    }

                                    // now move the tmp file back
                                    if(tmpFile.exists()) {
                                        tmpFile.renameTo(outFile);
                                    }
                                }
                                catch (Exception ignore) {
                                    manager.getErrorHandler().handleException(ignore);
                                }
                            }
                        }

                        // Now throw the error back up
                        if (e instanceof IOException) {
                            throw (IOException)e;
                        }
                        else {
                            throw new IOException(e.toString());
                        }
                    }
                    finally {
                        if(fout != null) {
                            try {
                                fout.close();
                            } catch (Exception ignore) {}
                        }

                        if(zin != null) {
                            try {
                                zin.close();
                            } catch (Exception ignore) {}
                        }
                    }
                }
            }

            //  Now, look for classes to execute and jars to load
            if(parcel.getJarName() != null) {
                // We have a jar to load!
                // Save the jar to the given path
                File jar = new File(parcel.getJarPath());
                File tmpJar = null;

                try {

                    if(jar.exists()) {
                        // create a copy until the end
                        tmpJar = new File(jar.getAbsolutePath() + ".tmp");

                        FileUtils.copy(jar, tmpJar);

                        if(tmpFiles == null) tmpFiles = new Vector();

                        tmpFiles.add(tmpJar);
                    }
                    else
                    {
                        jar.getParentFile().mkdirs();
                    }

                    // Now save the zipped jar to the jar path
                    zin = zip.getInputStream(zip.getEntry(parcel.getJarName()));
                    fout = new FileOutputStream(jar);
                    IOUtils.pipe(zin, fout, 1024);

                    // Now, we need to dynamically load the jar
                    URL[] jars = new URL[]{jar.toURL()};
                   
                    ClassLoader parent = JasenScanner.getInstance().getEngine().getContextClassLoader();
                   
                    if(parent == null) {
                        parent = this.getContextClassLoader();
                    }
                   
                    URLClassLoader loader = new URLClassLoader(jars, parent);
                   
                    // Set this as the current classloader for the engine
                    JasenScanner.getInstance().getEngine().setContextClassLoader(loader);
                   
                    // Now, if there is a class to execute, execute it...
                    if(parcel.getClassName() != null && parcel.getClassName().trim().length() > 0) {
                        AutoUpdateExecutor executor = (AutoUpdateExecutor)loader.loadClass(parcel.getClassName()).newInstance();
                        executor.execute();
                    }
                }
                catch (Exception e) {

                    //  Rollback
                    if(fout != null) {
                        try {
                            fout.close();
                            fout = null;
                        } catch (Exception ignore) {}
                    }

                    if(zin != null) {
                        try {
                            zin.close();
                            zin = null;
                        } catch (Exception ignore) {}
                    }

                    // Move the tmp jar back
                    if(tmpJar != null) {
                        if(jar.exists()) {
                            jar.delete();
                        }

                        tmpJar.renameTo(jar);
                    }
                   
                    e.printStackTrace();

                    // Now throw the error back up
                    if (e instanceof IOException) {
                        throw (IOException)e;
                    }
                    else {
                        throw new IOException(e.toString());
                    }
                }
                finally {
                    if(fout != null) {
                        try {
                            fout.close();
                        } catch (Exception ignore) {}
                    }

                    if(zin != null) {
                        try {
                            zin.close();
                        } catch (Exception ignore) {}
                    }
                }

                // Now, if we had a jar and were told NOT to retain it, then we need to delete it...
                if(jar != null && jar.exists()) {
                    if(parcel.getRetainJar().equalsIgnoreCase("false")) {
                        jar.delete();
                    }
                }
            }

            // Now we have copied all the files, delete all the tmp files...
            if(tmpFiles != null) {
                for (int j = 0; j < tmpFiles.size(); j++) {
                    ((File)tmpFiles.get(j)).delete();
                }
            }
        }
        finally {
            if(zip != null) {
                try {
                    zip.close();
                } catch (IOException ignore) {
                    manager.getErrorHandler().handleException(ignore);
                }
            }
        }

        // Now delete the update file itself
        if(update != null && update.exists()) {
            if(!update.delete()) {
                logger.warn("Failed to delete update file at " + update.getAbsolutePath());
            }
        }
    }


    private URL prepareItemURL(URL source, String item) throws MalformedURLException {
        URL url = null;

        String strUrl = source.toExternalForm();

        if(!strUrl.endsWith("/")) {
            strUrl += "/";
        }

        strUrl += item;

        url = new URL(strUrl);

        return url;
    }

    private JasenAutoUpdateParcel loadLastParcel() throws IOException, ClassNotFoundException {

        File parcelFile = new File(PARCEL_CACHE);
        JasenAutoUpdateParcel parcel = null;

        if(parcelFile.exists()) {
            ObjectInputStream in = null;

            try {
                in = new ObjectInputStream(new FileInputStream(parcelFile));

                if(in != null) {
                    parcel = (JasenAutoUpdateParcel)in.readObject();
                }
            }
      finally {
              if(in != null) {
                  try {
                      in.close();
                  } catch (Exception ignore) {}
              }
      }
        }

        return parcel;
    }

    private void saveCurrentParcel(JasenAutoUpdateParcel parcel) throws IOException {
        File parcelFile = new File(PARCEL_CACHE);

        ObjectOutputStream out = null;

        try {
            out = new ObjectOutputStream(new FileOutputStream(parcelFile));
            out.writeObject(parcel);
        }
        finally {
            if(out != null) {
                try {
                    out.close();
                } catch (Exception ignore) {}
            }
        }
    }

    public synchronized void finish() {

        logger.debug("AutoUpdater stopping...");

        neverStart = true;

        if(running) {
            running = false;
        }

        logger.debug("AutoUpdater stop command completed");

        notifyAll();

    }

    /**
     * Forces the auto updater to check for updates immediately
     *
     */
    synchronized boolean forceUpdate() {
       logger.debug("Update has been forced");
       if(idle) {
           forcedUpdate = true;
           notifyAll();
           return true;
       }
       else
       {
           return false;
       }


    }


    public boolean isRunning() {
        return running;
    }

    public boolean isIdle() {
        return idle;
    }

    public boolean isStopped() {
        return stopped;
    }
}
TOP

Related Classes of org.jasen.update.JasenAutoUpdater

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.