Package com.caucho.server.deploy

Source Code of com.caucho.server.deploy.ExpandDeployController

/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.server.deploy;

import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.caucho.config.types.FileSetType;
import com.caucho.loader.DynamicClassLoader;
import com.caucho.loader.Environment;
import com.caucho.server.repository.Repository;
import com.caucho.util.L10N;
import com.caucho.util.Log;
import com.caucho.vfs.Depend;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
import com.caucho.vfs.WriteStream;

/**
* A deployment entry that expands from an archive (Jar/Zip) file.
*/
abstract public class ExpandDeployController<I extends DeployInstance>
  extends DeployController<I> {
  private static final L10N L = new L10N(ExpandDeployController.class);
  private static final Logger log
    = Logger.getLogger(ExpandDeployController.class.getName());

  private Object _archiveExpandLock = new Object();

  private Path _rootDirectory;
  private Path _archivePath;

  private Repository _repository;
  private String _repositoryTag;
  private String _baseRepositoryTag;

  private FileSetType _expandCleanupFileSet;

  // classloader for the manifest entries
  private DynamicClassLoader _manifestLoader;
  private Manifest _manifest;

  protected ExpandDeployController(String id)
  {
    this(id, null, null);
  }

  protected ExpandDeployController(String id,
                                   ClassLoader loader,
                                   Path rootDirectory)
  {
    super(id, loader);

    if (rootDirectory == null)
      rootDirectory = Vfs.getPwd(getParentClassLoader());

    _rootDirectory = rootDirectory;
  }

  /**
   * Gets the root directory
   */
  public Path getRootDirectory()
  {
    return _rootDirectory;
  }

  /**
   * Sets the root directory
   */
  protected void setRootDirectory(Path rootDirectory)
  {
    _rootDirectory = rootDirectory;
  }

  /**
   * Gets the archive path.
   */
  public Path getArchivePath()
  {
    return _archivePath;
  }

  /**
   * Sets the archive path.
   */
  public void setArchivePath(Path path)
  {
    _archivePath = path;
  }

  /**
   * Returns the repository
   */
  public Repository getRepository()
  {
    return _repository;
  }

  /**
   * Sets the repository
   */
  public void setRepository(Repository repository)
  {
    _repository = repository;
  }

  /**
   * The repository tag
   */
  public String getRepositoryTag()
  {
    return _repositoryTag;
  }

  /**
   * The repository tag
   */
  public void setRepositoryTag(String tag)
  {
    _repositoryTag = tag;
  }

  /**
   * The base repository tag
   */
  public String getBaseRepositoryTag()
  {
    return _baseRepositoryTag;
  }

  /**
   * The base repository tag
   */
  public void setBaseRepositoryTag(String tag)
  {
    _baseRepositoryTag = tag;
  }

  /**
   * Returns the manifest.
   */
  public Manifest getManifest()
  {
    return _manifest;
  }

  /**
   * Returns the manifest as an attribute map
   */
  public Map<String,String> getManifestAttributes()
  {
    if (_manifest == null)
      return null;
   
    Map<String,String> map = new TreeMap<String,String>();

    Attributes attr = _manifest.getMainAttributes();

    if (attr != null) {
      for (Map.Entry entry : attr.entrySet()) {
  map.put(String.valueOf(entry.getKey()),
    String.valueOf(entry.getValue()));
      }
    }

    return map;
  }

  /**
   * Sets the manifest class loader.
   */
  public void setManifestClassLoader(DynamicClassLoader loader)
  {
    _manifestLoader = loader;
  }

  /**
   * Sets the archive auto-remove file set.
   */
  public void setExpandCleanupFileSet(FileSetType fileSet)
  {
    _expandCleanupFileSet = fileSet;
  }

  /**
   * Merges with the old controller.
   */
  @Override
  protected void mergeController(DeployController oldControllerV)
  {
    super.mergeController(oldControllerV);

    ExpandDeployController<I> oldController;
    oldController = (ExpandDeployController<I>) oldControllerV;

    if (oldController._expandCleanupFileSet != null)
      _expandCleanupFileSet = oldController._expandCleanupFileSet;

    if (oldController.getArchivePath() != null)
      setArchivePath(oldController.getArchivePath());

    if (oldController.getRepositoryTag() != null)
      setRepositoryTag(oldController.getRepositoryTag());
  }

  /**
   * Deploys the controller
   */
  public void deploy()
  {
    if (log.isLoggable(Level.FINER))
      log.finer(this + " deploying");
   
    try {
      expandArchive();
    } catch (Exception e) {
      // XXX: better exception
      throw new RuntimeException(e);
    }
  }

  /**
   * Expand an archive file.  The _archiveExpandLock must be obtained
   * before the expansion.
   */
  protected void expandArchive()
    throws IOException
  {
    synchronized (_archiveExpandLock) {
      if (expandRepositoryImpl()) {
      }
      else if (expandArchiveImpl()) {
      }
      else {
        try {
          Thread.sleep(2000);
        } catch (InterruptedException e) {
        }

  if (expandRepositoryImpl()) {
  }
  else
    expandArchiveImpl();
      }

      Path path = getRootDirectory().lookup("META-INF/MANIFEST.MF");
      if (path.canRead()) {
        ReadStream is = path.openRead();
        try {
          _manifest = new Manifest(is);
        } catch (IOException e) {
          log.warning(L.l("Manifest file cannot be read for '{0}'.\n{1}",
                          getRootDirectory(), e));

          log.log(Level.FINE, e.toString(), e);
        } finally {
          is.close();
        }
      }
    }
  }

  /**
   * Adds any class path from the manifest.
   */
  protected void addManifestClassPath()
    throws IOException
  {
    DynamicClassLoader loader = Environment.getDynamicClassLoader();
    if (loader == null)
      return;

    Manifest manifest = getManifest();

    if (manifest == null)
      return;

    Attributes main = manifest.getMainAttributes();

    if (main == null)
      return;

    String classPath = main.getValue("Class-Path");

    Path pwd = null;

    if (getArchivePath() != null)
      pwd = getArchivePath().getParent();
    else
      pwd = getRootDirectory();

    if (classPath == null) {
    }
    else if (_manifestLoader != null)
      _manifestLoader.addManifestClassPath(classPath, pwd);
    else
      loader.addManifestClassPath(classPath, pwd);
  }

  /**
   * Expand an archive.  The _archiveExpandLock must be obtained before the
   * expansion.
   */
  private boolean expandRepositoryImpl()
    throws IOException
  {
    try {
      if (_repository == null)
  return false;
     
      String tree = _repository.getTagRoot(getRepositoryTag());

      if (tree == null)
  return false;
     
      Path pwd = getRootDirectory();

      pwd.mkdirs();

      if (log.isLoggable(Level.FINE))
  log.fine(this + " expanding .git repository tag=" + getRepositoryTag()
     + " tree=" + tree + " -> root=" + getRootDirectory());

      _repository.expandToPath(pwd, tree);
 
      return true;
    } catch (IOException e) {
      log.log(Level.FINE, e.toString(), e);

      return false;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * Expand an archive.  The _archiveExpandLock must be obtained before the
   * expansion.
   */
  private boolean expandArchiveImpl()
    throws IOException
  {
    Path archivePath = getArchivePath();

    if (archivePath == null)
      return true;

    if (! archivePath.canRead())
      return true;

    Path expandDir = getRootDirectory();
    Path parent = expandDir.getParent();

    try {
      parent.mkdirs();
    } catch (Throwable e) {
    }

    Path dependPath = expandDir.lookup("META-INF/resin-war.digest");
    Depend depend = null;

    // XXX: change to a hash
    if (dependPath.canRead()) {
      ReadStream is = null;
      try {
        is = dependPath.openRead();

        String line = is.readLine();

        long digest;

        if (line != null) {
          digest = Long.parseLong(line.trim());

          depend = new Depend(archivePath, digest);

          if (! depend.isModified())
            return true;
        }
      } catch (Throwable e) {
        log.log(Level.FINE, e.toString(), e);
      } finally {
        if (is != null)
          is.close();
      }
    }

    if (depend == null)
      depend = new Depend(archivePath);

    try {
      if (log.isLoggable(Level.INFO))
        getLog().info("expanding " + archivePath + " to " + expandDir);

      removeExpandDirectory(expandDir);

      expandDir.mkdirs();

      ReadStream rs = archivePath.openRead();
      ZipInputStream zis = new ZipInputStream(rs);

      try {
        ZipEntry entry = zis.getNextEntry();

        byte []buffer = new byte[1024];

        while (entry != null) {
          String name = entry.getName();
          Path path = expandDir.lookup(name);

          if (entry.isDirectory())
            path.mkdirs();
          else {
            long entryLength = entry.getSize();
            long length = entryLength;

            // XXX: avoids unexpected end of ZLIB input stream.
            // XXX: This should be a really temp. workaround.
            int bufferLen = buffer.length;

            // XXX: avoids unexpected end of ZLIB input stream.
            if (length < 0) {
              // bufferLen = 1;
              length = Long.MAX_VALUE / 2;
            }
            else if (length < bufferLen) {
              bufferLen = (int) length;
            }

            long lastModified = entry.getTime();
            path.getParent().mkdirs();

            WriteStream os = path.openWrite();
            int len = 0;
            try {
              if (bufferLen == 1) {
                for (int ch = zis.read(); ch != -1; ch = zis.read())
                  os.write(ch);
              }
              else {
                while ((len = zis.read(buffer, 0, bufferLen)) > 0) {
                  os.write(buffer, 0, len);

                  // XXX: avoids unexpected end of ZLIB input stream.
                  /*
                  if (len < bufferLen) {
                    for (int ch = zis.read(); ch != -1; ch = zis.read())
                      os.write(ch);

                    break;
                  }
                  */

                  length -= len;

                  if (length < bufferLen) {
                    bufferLen = (int) length;
                  }
                }
              }
            } catch (IOException e) {
              Exception ex = new Exception("IOException when expanding entry "+entry+" in "+archivePath+", entry.length: "+entryLength+" entry.compressed: "+entry.getCompressedSize()+", bufferLen: "+bufferLen+", read len: "+len+", remaining: "+length,e);

              log.log(Level.FINE, ex.toString(), ex);
            } finally {
              os.close();
            }

            if (lastModified > 0)
              path.setLastModified(lastModified);
          }

          try {
            entry = zis.getNextEntry();
          } catch (IOException e) {
            log.log(Level.FINE, e.toString(), e);

            // XXX: avoids unexpected end of ZLIB input stream.
            break;
          }
        }
      } finally {
        try {
          zis.close();
        } catch (IOException e) {
        }

        rs.close();
      }
    } catch (IOException e) {
      log.log(Level.WARNING, e.toString(), e);
      // If the jar is incomplete, it should throw an exception here.
      return false;
    }

    try {
      dependPath.getParent().mkdirs();
      WriteStream os = dependPath.openWrite();

      os.println(depend.getDigest());

      os.close();
    } catch (Throwable e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    return true;
  }

  protected void addDependencies()
  {
    if (getArchivePath() != null)
      Environment.addDependency(getArchivePath());

    if (getRepository() != null && getRepositoryTag() != null) {
      String tag = getRepositoryTag();
      String value = getRepository().getTagRoot(tag);

      Environment.addDependency(new RepositoryDependency(tag, value));
    }
  }

  /**
   * Recursively remove all files in a directory.  Used for wars when
   * they change.
   *
   * @param path root directory to start removal
   */
  protected void removeExpandDirectory(Path path)
  {
    String prefix = path.getPath();

    if (! prefix.endsWith("/"))
      prefix = prefix + "/";

    removeExpandDirectory(path, prefix);
  }

  /**
   * Recursively remove all files in a directory.  Used for wars when
   * they change.
   *
   * @param dir root directory to start removal
   */
  protected void removeExpandDirectory(Path path, String prefix)
  {
    try {
      if (path.isDirectory()) {
        String []list = path.list();
        for (int i = 0; list != null && i < list.length; i++) {
          removeExpandDirectory(path.lookup(list[i]), prefix);
        }
      }

      removeExpandFile(path, prefix);
    } catch (Throwable e) {
      log.log(Level.WARNING, e.toString(), e);
    }
  }

  /**
   * Removes an expanded file.
   */
  protected void removeExpandFile(Path path, String prefix)
    throws IOException
  {
    if (_expandCleanupFileSet == null
  || _expandCleanupFileSet.isMatch(path, prefix)) {
      path.remove();
    }
  }

  /**
   * Returns the hash code.
   */
  public int hashCode()
  {
    return getId().hashCode();
  }

  /**
   * Returns equality.
   */
  public boolean equals(Object o)
  {
    // server/125g
    if (o == null || ! getClass().equals(o.getClass()))
      return false;

    DeployController controller = (DeployController) o;

    // XXX: s/b getRootDirectory?
    return getId().equals(controller.getId());
  }
}
TOP

Related Classes of com.caucho.server.deploy.ExpandDeployController

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.