org.eclipse.gemini.blueprint.test.internal.util.jar.JarCreator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gemini.blueprint.test.internal.util.jar.JarCreator.java

Source

/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.test.internal.util.jar;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.Manifest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.test.internal.util.jar.storage.MemoryStorage;
import org.eclipse.gemini.blueprint.test.internal.util.jar.storage.Storage;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StringUtils;

/**
 * Helper class for creating Jar files. Note that this class is stateful and the
 * same instance should not be reused.
 * 
 * @author Costin Leau
 * 
 */
public class JarCreator {

    private static final Log log = LogFactory.getLog(JarCreator.class);

    public static final String EVERYTHING_PATTERN = "/**/*";

    private static final String CLASS_EXT = ".class";

    private String[] contentPattern = new String[] { EVERYTHING_PATTERN };

    private ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();

    private Storage storage = new MemoryStorage();

    private String rootPath = determineRootPath();

    private boolean addFolders = true;

    /** collection of packages contained by this jar */
    private Collection containedPackages = new TreeSet();

    /**
     * Resources' root path (the root path does not become part of the jar).
     * 
     * @return the root path
     */
    public String determineRootPath() {
        return Thread.currentThread().getContextClassLoader().getResource(".").toString();
    }

    /**
     * Actual jar creation.
     * 
     * @param manifest to use
     * @param entries array of resource to include in the jar
     * @return the number of bytes written to the underlying stream.
     * 
     * @throws IOException
     */
    protected int addJarContent(Manifest manifest, Map entries) throws IOException {
        // load manifest
        // add it to the jar
        if (log.isTraceEnabled()) {
            if (manifest != null)
                log.trace("Adding MANIFEST.MF [" + manifest.getMainAttributes().entrySet() + "]");
            log.trace("Adding entries:");
            Set key = entries.keySet();
            for (Iterator iter = key.iterator(); iter.hasNext();) {
                log.trace(iter.next());
            }
        }

        return JarUtils.createJar(manifest, entries, storage.getOutputStream());
    }

    /**
     * Create a jar using the current settings and return a {@link Resource}
     * pointing to the jar.
     * 
     * @param manifest
     */
    public Resource createJar(Manifest manifest) {
        return createJar(manifest, resolveContent());
    }

    /**
     * Create a jar using the current settings and return a {@link Resource}
     * pointing to the jar.
     * 
     * @param manifest
     */
    public Resource createJar(Manifest manifest, Map content) {
        try {
            addJarContent(manifest, content);
            return storage.getResource();
        } catch (IOException ex) {
            throw (RuntimeException) new IllegalStateException("Cannot create jar").initCause(ex);
        }
    }

    /**
     * Small utility method used for determining the file name by striping the
     * root path from the file full path.
     * 
     * @param rootPath
     * @param resource
     * @return
     */
    private String determineRelativeName(String rootPath, Resource resource) {
        try {
            String path = StringUtils.cleanPath(resource.getURL().toExternalForm());
            return path.substring(path.indexOf(rootPath) + rootPath.length());
        } catch (IOException ex) {
            throw (RuntimeException) new IllegalArgumentException("illegal resource " + resource.toString())
                    .initCause(ex);
        }
    }

    /**
     * Transform the pattern and rootpath into actual resources.
     * 
     * @return
     * @throws Exception
     */
    private Resource[][] resolveResources() {
        ResourcePatternResolver resolver = getPatternResolver();

        String[] patterns = getContentPattern();
        Resource[][] resources = new Resource[patterns.length][];

        // transform Strings into Resources
        for (int i = 0; i < patterns.length; i++) {
            StringBuilder buffer = new StringBuilder(rootPath);

            // do checking on lost slashes
            if (!rootPath.endsWith(JarUtils.SLASH) && !patterns[i].startsWith(JarUtils.SLASH))
                buffer.append(JarUtils.SLASH);

            buffer.append(patterns[i]);
            try {
                resources[i] = resolver.getResources(buffer.toString());
            } catch (IOException ex) {
                IllegalStateException re = new IllegalStateException("cannot resolve pattern " + buffer.toString());
                re.initCause(ex);
                throw re;
            }
        }

        return resources;
    }

    /**
     * Resolve the jar content based on its path. Will return a map containing
     * the entries relative to the jar root path as keys and Spring Resource
     * pointing to the actual resources as values. It will also determine the
     * packages contained by this package.
     * 
     * @return
     */
    public Map resolveContent() {
        Resource[][] resources = resolveResources();

        URL rootURL;
        String rootP = getRootPath();
        try {
            rootURL = new URL(rootP);
        } catch (MalformedURLException ex) {
            throw (RuntimeException) new IllegalArgumentException("illegal root path given " + rootP).initCause(ex);
        }
        String rootPath = StringUtils.cleanPath(rootURL.getPath());

        // remove duplicates
        Map entries = new TreeMap();
        // save contained bundle packages
        containedPackages.clear();

        // empty stream used for folders
        Resource folderResource = new ByteArrayResource(new byte[0]);

        // add folder entries also
        for (int i = 0; i < resources.length; i++) {
            for (int j = 0; j < resources[i].length; j++) {
                String relativeName = determineRelativeName(rootPath, resources[i][j]);
                // be consistent when adding resources to jar
                if (!relativeName.startsWith("/"))
                    relativeName = "/" + relativeName;
                entries.put(relativeName, resources[i][j]);

                // look for class entries
                if (relativeName.endsWith(CLASS_EXT)) {

                    // determine package (exclude first char)
                    String clazzName = relativeName.substring(1, relativeName.length() - CLASS_EXT.length())
                            .replace('/', '.');
                    // remove class name
                    int index = clazzName.lastIndexOf('.');
                    if (index > 0)
                        clazzName = clazzName.substring(0, index);
                    // add it to the collection
                    containedPackages.add(clazzName);
                }

                String token = relativeName;
                // get folder and walk up to the root
                if (addFolders) {
                    // add META-INF
                    entries.put("/META-INF/", folderResource);
                    int slashIndex;
                    // stop at root folder
                    while ((slashIndex = token.lastIndexOf('/')) > 1) {
                        // add the folder with trailing /
                        entries.put(token.substring(0, slashIndex + 1), folderResource);
                        // walk the tree
                        token = token.substring(0, slashIndex);
                    }
                    // add root folder
                    //entries.put("/", folderResource);
                }
            }
        }

        if (log.isTraceEnabled())
            log.trace("The following packages were discovered in the bundle: " + containedPackages);

        return entries;
    }

    public Collection getContainedPackages() {
        return containedPackages;
    }

    /**
     * @return Returns the contentPattern.
     */
    public String[] getContentPattern() {
        return contentPattern;
    }

    /**
     * Pattern for content matching. Note that using {@link #EVERYTHING_PATTERN}
     * can become problematic on windows due to file system locking.
     * 
     * @param contentPattern The contentPattern to set.
     */
    public void setContentPattern(String[] contentPattern) {
        this.contentPattern = contentPattern;
    }

    /**
     * @return Returns the patternResolver.
     */
    public ResourcePatternResolver getPatternResolver() {
        return patternResolver;
    }

    /**
     * @param patternResolver The patternResolver to set.
     */
    public void setPatternResolver(ResourcePatternResolver patternResolver) {
        this.patternResolver = patternResolver;
    }

    /**
     * @return Returns the jarStorage.
     */
    public Storage getStorage() {
        return storage;
    }

    /**
     * @param jarStorage The jarStorage to set.
     */
    public void setStorage(Storage jarStorage) {
        this.storage = jarStorage;
    }

    /**
     * @param
     */
    public String getRootPath() {
        return rootPath;
    }

    /**
     * @param rootPath The rootPath to set.
     */
    public void setRootPath(String rootPath) {
        this.rootPath = rootPath;
    }

    /**
     * @return Returns the addFolders.
     */
    public boolean isAddFolders() {
        return addFolders;
    }

    /**
     * Whether the folders in which the files reside, should be added to the
     * archive. Default is true since otherwise, the archive will contains only
     * files and no folders.
     * 
     * @param addFolders The addFolders to set.
     */
    public void setAddFolders(boolean addFolders) {
        this.addFolders = addFolders;
    }
}