Package com.alibaba.antx.expand

Source Code of com.alibaba.antx.expand.Expander$WarExpanderHandler

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.antx.expand;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import com.alibaba.antx.util.FileObject;
import com.alibaba.antx.util.FileUtil;
import com.alibaba.antx.util.ZipUtil;
import org.apache.commons.digester.Digester;
import org.xml.sax.SAXException;

/**
* 将ear文件展开的tag。
*
* @author Michael Zhou
*/
public class Expander {
    private final ExpanderLogger log;
    private boolean expandWar          = true;
    private boolean expandRar          = true;
    private boolean expandEjbjar       = false;
    private boolean overwrite          = false;
    private boolean keepRedundantFiles = false;
    private File srcfile;
    private File destdir;
    private Set  expandedFiles;

    public Expander(ExpanderLogger log) {
        this.log = log;
    }

    public boolean isExpandWar() {
        return expandWar;
    }

    public boolean isExpandRar() {
        return expandRar;
    }

    public boolean isExpandEjbjar() {
        return expandEjbjar;
    }

    public boolean isOverwrite() {
        return overwrite;
    }

    public boolean isKeepRedundantFiles() {
        return keepRedundantFiles;
    }

    public File getSourceFile() {
        return srcfile;
    }

    public File getDestDir() {
        return destdir;
    }

    public void setExpandEjbjar(boolean expandEjbjar) {
        this.expandEjbjar = expandEjbjar;
    }

    public void setExpandRar(boolean expandRar) {
        this.expandRar = expandRar;
    }

    public void setExpandWar(boolean expandWar) {
        this.expandWar = expandWar;
    }

    public void setDestdir(String destdir) {
        String basedir = new File("").getAbsolutePath();

        this.destdir = new File(FileUtil.getPathBasedOn(basedir, destdir));
    }

    public void setSrcfile(String srcfile) {
        String basedir = new File("").getAbsolutePath();

        this.srcfile = new File(FileUtil.getPathBasedOn(basedir, srcfile));
    }

    /**
     * 设置覆盖选项。
     *
     * @param overwrite 如果目标目录中的文件比zip文件中的项要新,是否覆盖之
     */
    public void setOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
    }

    /**
     * 设置是否保持多余的文件。
     *
     * @param keepRedundantFiles 如果目标目录中有多余的文件,是否保持而不删除
     */
    public void setKeepRedundantFiles(boolean keepRedundantFiles) {
        this.keepRedundantFiles = keepRedundantFiles;
    }

    private void init() {
        // srcfile
        if (srcfile == null) {
            throw new ExpanderException("missing source file to expand");
        }

        srcfile = srcfile.getAbsoluteFile();

        if (!srcfile.exists() || !srcfile.isFile()) {
            throw new ExpanderException(srcfile + " does not exist or is not a file");
        }

        // destdir
        if (destdir == null) {
            String dirname = srcfile.getName();
            int index = dirname.lastIndexOf(".");

            if (index < 0) {
                dirname += ".expanded";
            } else {
                dirname = dirname.substring(0, index);
            }

            destdir = new File(srcfile.getParentFile(), dirname);
        }

        destdir = destdir.getAbsoluteFile();

        if (destdir.exists() && !destdir.isDirectory()) {
            throw new ExpanderException(destdir + " is not a directory");
        }

        destdir.mkdirs();

        if (!destdir.exists()) {
            throw new ExpanderException("could not create directory: " + destdir);
        }
    }

    /** 执行展开操作。 */
    public void expand() {
        init();

        log.info("Expanding: " + srcfile + "\n       To: " + destdir.getAbsolutePath());

        // 清除文件列表
        expandedFiles = new HashSet();

        // 开始展开
        InputStream istream = null;

        try {
            istream = new BufferedInputStream(new FileInputStream(srcfile), 8192);
            getExpanderHandler(srcfile.toURI().toURL()).expand(istream, destdir);
            removeRedundantFiles(destdir);

            log.info("done.");
        } catch (IOException e) {
            throw new ExpanderException(e);
        } finally {
            if (istream != null) {
                try {
                    istream.close();
                } catch (IOException e) {
                }
            }

            expandedFiles = null;
        }
    }

    /**
     * 删除多余的文件。
     *
     * @param todir     展开到此目录
     * @param zipStream 压缩流
     * @param zipEntry  zip结点
     * @param url       递归展开的url前缀
     * @param output    XML输出
     * @throws IOException 读写文件失败,或Zip格式错误
     */
    protected void removeRedundantFiles(File fileOrDir) throws IOException {
        if (isKeepRedundantFiles()) {
            return;
        }

        if (!fileOrDir.exists()) {
            return;
        }

        // 如果是文件,并且在expandedFiles中不存在该文件,则删除之
        if (!fileOrDir.isDirectory()) {
            if (!expandedFiles.contains(fileOrDir.getCanonicalPath())) {
                log.info("- " + getPathRelativeToDestdir(fileOrDir) + " - "
                         + (fileOrDir.delete() ? "deleted" : "can't delete"));
            }

            return;
        }

        // 深度优先地删除目录中的文件和子目录
        File[] files = fileOrDir.listFiles();

        for (File file : files) {
            removeRedundantFiles(file);
        }

        // 如果目录为空,并且在expandedFiles中不存在该目录,则删除目录本身
        if (!expandedFiles.contains(fileOrDir.getCanonicalPath()) && fileOrDir.delete()) {
            log.info("- " + getPathRelativeToDestdir(fileOrDir) + " - success");
        }
    }

    private ExpanderHandler getExpanderHandler(URL url) {
        String name = url.getFile();

        name = name.substring(name.lastIndexOf("/") + 1);

        if (name.endsWith(".war")) {
            return new WarExpanderHandler();
        } else {
            return new EarExpanderHandler(url);
        }
    }

    private String getPathRelativeToDestdir(File file) {
        return new FileObject(destdir).newFileObject(file).getRelativePath();
    }

    /** 处理不同类型的jar包的接口。 */
    private abstract class ExpanderHandler {
        /**
         * 展开ear文件到指定目录
         *
         * @param istream 输入流
         * @param todir   展开目录
         * @throws IOException 读写文件失败,或Zip格式错误
         */
        protected void expand(InputStream istream, File todir) throws IOException {
            ZipInputStream zipStream = null;

            try {
                zipStream = new ZipInputStream(istream);

                ZipEntry zipEntry = null;

                while ((zipEntry = zipStream.getNextEntry()) != null) {
                    extractFile(todir, zipStream, zipEntry, null);
                }
            } finally {
                if (zipStream != null) {
                    try {
                        zipStream.close();
                    } catch (IOException e) {
                    }
                }
            }
        }

        /**
         * 展开一个文件。
         *
         * @param todir     展开到此目录
         * @param zipStream 压缩流
         * @param zipEntry  zip结点
         * @param url       递归展开的url前缀
         * @throws IOException 读写文件失败,或Zip格式错误
         */
        protected void extractFile(File todir, InputStream zipStream, ZipEntry zipEntry, String url)
                throws IOException {
            String entryName = zipEntry.getName();
            Date entryDate = new Date(zipEntry.getTime());
            boolean isDirectory = zipEntry.isDirectory();
            File targetFile = FileUtil.getFile(todir, entryName);
            boolean expandFile = false;

            // 判断是否需要进一步展开
            if (!isDirectory && url == null) {
                expandFile = needToExpand(zipEntry.getName());
            }

            if (!expandFile && !overwrite && targetFile.exists() && targetFile.lastModified() >= entryDate.getTime()) {
                log.debug(". " + getPathRelativeToDestdir(targetFile) + " - up-to-date");
                expandedFiles.add(targetFile.getCanonicalPath());
                return;
            }

            if (isDirectory) {
                expandedFiles.add(targetFile.getCanonicalPath());
                targetFile.mkdirs();
            } else {
                File dir = targetFile.getParentFile();

                dir.mkdirs();

                // 如果是war或rar文件,则展开到同名的目录中
                if (expandFile) {
                    log.info("X " + getPathRelativeToDestdir(targetFile));

                    if (targetFile.exists() && !targetFile.isDirectory()) {
                        targetFile.delete();
                    }

                    targetFile.mkdirs();

                    if (!targetFile.exists() || !targetFile.isDirectory()) {
                        throw new ExpanderException("could not create directory: " + targetFile);
                    }

                    ZipInputStream zis = new ZipInputStream(zipStream);
                    ZipEntry subEntry = null;

                    while ((subEntry = zis.getNextEntry()) != null) {
                        extractFile(targetFile, zis, subEntry, entryName);
                    }
                } else {
                    log.debug("+ " + getPathRelativeToDestdir(targetFile));

                    if (targetFile.exists() && targetFile.isDirectory()) {
                        FileUtil.deleteDirectory(targetFile);
                    }

                    if (targetFile.exists() && !targetFile.isFile()) {
                        throw new ExpanderException("could not create file: " + targetFile + ", it's a directory");
                    }

                    byte[] buffer = new byte[8192];
                    int length = 0;
                    OutputStream ostream = null;

                    try {
                        expandedFiles.add(targetFile.getCanonicalPath());

                        ostream = new BufferedOutputStream(new FileOutputStream(targetFile), 8192);

                        while ((length = zipStream.read(buffer)) >= 0) {
                            ostream.write(buffer, 0, length);
                        }
                    } finally {
                        if (ostream != null) {
                            try {
                                ostream.close();
                            } catch (IOException e) {
                            }
                        }
                    }
                }
            }

            targetFile.setLastModified(entryDate.getTime());
        }

        /** 判断是否需要进一步展开。 */
        protected boolean needToExpand(String name) {
            if (expandWar && name.endsWith(".war")) {
                return true;
            }

            if (expandRar && name.endsWith(".rar")) {
                return true;
            }

            return false;
        }
    }

    private class WarExpanderHandler extends ExpanderHandler {
    }

    private class EarExpanderHandler extends ExpanderHandler {
        private URL earURL;
        private URL applicationXmlURL;
        private Set ejbJars;

        public EarExpanderHandler(URL earURL) {
            this.earURL = earURL;

            try {
                this.applicationXmlURL = ZipUtil.getJarURL(this.earURL, "META-INF/application.xml");
            } catch (MalformedURLException e) {
                IllegalArgumentException iae = new IllegalArgumentException();

                iae.initCause(e);
                throw iae;
            }

            ejbJars = new HashSet();

            if (expandEjbjar) {
                InputStream istream = null;

                try {
                    istream = applicationXmlURL.openStream();
                    log.info("  Loading: " + applicationXmlURL);
                    readApplicationXml(istream);
                } catch (Exception e) {
                    log.warn("   failed - EJB-jars will not be expanded");
                } finally {
                    if (istream != null) {
                        try {
                            istream.close();
                        } catch (IOException e) {
                        }
                    }
                }
            }
        }

        private void readApplicationXml(InputStream istream) throws IOException, SAXException {
            Digester digester = new Digester();

            digester.addCallMethod("application/module/ejb", "add", 1);
            digester.addCallParam("application/module/ejb", 0);

            digester.push(ejbJars);
            digester.parse(istream);
        }

        @Override
        protected boolean needToExpand(String name) {
            return super.needToExpand(name) || ejbJars.contains(name);
        }
    }
}
TOP

Related Classes of com.alibaba.antx.expand.Expander$WarExpanderHandler

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.