Package org.codehaus.loom.components.extensions.pkgmgr.impl

Source Code of org.codehaus.loom.components.extensions.pkgmgr.impl.DefaultExtensionManager

/* ====================================================================
* Loom Software License, version 1.1
*
* Copyright (c) 2003, Loom Group. 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. Neither the name of the Loom Group nor the name "Loom" nor
*    the names of its contributors may be used to endorse or promote
*    products derived from this software without specific prior
*    written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS 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 THE
* COPYRIGHT OWNER OR CONTRIBUTORS 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.
*
* ====================================================================
*
* Loom includes code from the Apache Software Foundation
*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1997-2003 The Apache Software Foundation. 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 end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*    "This product includes software developed by the
*    Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software
*    itself, if and wherever such third-party acknowledgments
*    normally appear.
*
* 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
*    must not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* 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 THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS 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.codehaus.loom.components.extensions.pkgmgr.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.codehaus.loom.components.extensions.pkgmgr.ExtensionManager;
import org.codehaus.loom.components.extensions.pkgmgr.OptionalPackage;
import org.codehaus.loom.extension.Extension;

/**
* <p>Interface used to contain "Optional Packages" (formerly known as "Standard
* Extensions"). It is assumed that each "Optional Package" is represented by a
* single file on the file system. This Repository searches a path to find the
* Optional Packages.</p>
*
* @author Peter Donald
* @version $Revision: 1.3 $ $Date: 2004/08/17 23:14:32 $
* @see OptionalPackage
* @see ExtensionManager
*/
public class DefaultExtensionManager
    implements ExtensionManager
{
    private static final boolean DEBUG = false;

    /** separator used to separate path elements in a string. */
    private static final String SEPARATOR = "|";

    /** Map between files and {@link OptionalPackage} objects. */
    private final Map m_packages = new HashMap();

    /** The set of directories in which to look for Optional Packages */
    private File[] m_path;

    /**
     * Flag set when it is necessary to scan paths to build "Optional Package"
     * list
     */
    private boolean m_needToScan;

    /**
     * Construct a package repository with no path specified.
     */
    public DefaultExtensionManager()
    {
        this( new File[ 0 ] );
    }

    /**
     * Construct a package repository with path.
     *
     * @param path The set of directories in which to look for Optional
     * Packages
     */
    public DefaultExtensionManager( final File[] path )
    {
        setPath( path );
    }

    /**
     * Return an array of path elements where each element in array represents a
     * directory in which the ExtensionManager will look for Extensions.
     *
     * @return the list of paths to search in
     */
    public File[] getPaths()
    {
        return m_path;
    }

    /**
     * Return all the {@link OptionalPackage}s that satisfy specified {@link
     * Extension}. It is expected that this {@link Extension} object will be one
     * retrieved via getLocalExtension() method. If the specified {@link
     * Extension} is not local then <code>null</code> is returned.
     *
     * @param extension the extension to search for
     * @return an array of optional packages that satisfy the extension (and the
     *         extensions dependencies)
     */
    public synchronized OptionalPackage[] getOptionalPackages(
        final Extension extension )
    {
        if( m_needToScan )
        {
            scanPath();
        }

        final ArrayList results = new ArrayList();
        final ArrayList candidates = (ArrayList)m_packages.get(
            extension.getExtensionName() );
        if( null != candidates )
        {
            final int size = candidates.size();
            for( int i = 0; i < size; i++ )
            {
                final OptionalPackage optionalPackage = (OptionalPackage)candidates.get(
                    i );
                final Extension[] extensions = optionalPackage.getAvailableExtensions();

                for( int j = 0; j < extensions.length; j++ )
                {
                    if( extensions[ j ].isCompatibleWith( extension ) )
                    {
                        results.add( optionalPackage );
                    }
                }
            }
        }

        final OptionalPackageComparator comparator =
            new OptionalPackageComparator( extension.getExtensionName() );
        Collections.sort( results, comparator );
        return (OptionalPackage[])results.toArray( new OptionalPackage[ 0 ] );
    }

    /**
     * Return all the OptionalPackages stored in ExtensionManager.
     *
     * @return all the OptionalPackages stored in ExtensionManager.
     */
    protected synchronized OptionalPackage[] getAllOptionalPackages()
    {
        //This is woefully inefficient - should rewrite it somehow
        final ArrayList packages = new ArrayList();
        final Iterator iterator = m_packages.values().iterator();
        while( iterator.hasNext() )
        {
            final ArrayList list = (ArrayList)iterator.next();
            final int size = list.size();
            for( int i = 0; i < size; i++ )
            {
                final OptionalPackage optionalPackage = (OptionalPackage)list.get(
                    i );
                if( !packages.contains( optionalPackage ) )
                {
                    packages.add( optionalPackage );
                }
            }
        }

        return (OptionalPackage[])packages.toArray(
            new OptionalPackage[ packages.size() ] );
    }

    /**
     * Add path elements to repository search path
     *
     * @param pathElements the path elements to add to repository search path
     */
    protected void addPathElements( final String[] pathElements )
    {
        final File[] path = toFiles( pathElements );
        addPathElements( path );
    }

    /**
     * Add path elements to repository search path
     *
     * @param path the path elements to add to repository search path
     */
    protected synchronized void addPathElements( final File[] path )
    {
        validatePath( path );
        final File[] files = resolvePath( path );
        m_path = mergePaths( files );
        m_needToScan = true;
    }

    /**
     * Add path elements to repository search path. Note that each path element
     * is separated by a '|' character.
     *
     * @param pathString the path elements to add to repository search path
     */
    protected void addPathElements( final String pathString )
    {
        final String[] pathElements = split( pathString, SEPARATOR );
        addPathElements( pathElements );
    }

    /**
     * Set the path for the Repository. Note thart each path element is
     * separated by a '|' character.
     *
     * @param pathString the list of directories in which to search
     */
    protected synchronized void setPath( final String pathString )
    {
        final String[] pathElements = split( pathString, SEPARATOR );
        setPath( pathElements );
    }

    /**
     * Set the path for the Repository.
     *
     * @param pathElements the list of directories in which to search
     */
    protected synchronized void setPath( final String[] pathElements )
    {
        final File[] path = toFiles( pathElements );
        setPath( path );
    }

    /**
     * Set the path for the Repository.
     *
     * @param path the list of directories in which to search
     */
    protected synchronized void setPath( final File[] path )
    {
        validatePath( path );
        m_path = resolvePath( path );
        m_needToScan = true;
    }

    /**
     * Scan the path for this repository and reload all the "Optional Packages"
     * found in the path. All of the old Extensions/Optional Packages will be
     * removed.
     */
    protected final synchronized void scanPath()
    {
        clearCache();

        for( int i = 0; i < m_path.length; i++ )
        {
            scanDirectory( m_path[ i ] );
        }
    }

    /**
     * Utility method to scan a directory for all jar fi;les in directory and
     * add them as OptionalPackages.
     *
     * @param directory the directory to scan
     */
    private synchronized void scanDirectory( final File directory )
    {
        final File[] files = directory.listFiles();
        for( int i = 0; i < files.length; i++ )
        {
            final File file = files[ i ];
            final String name = file.getName();

            if( !name.endsWith( ".jar" ) )
            {
                final String message =
                    "Skipping " + file + " as it does not end with '.jar'";
                debug( message );
                continue;
            }

            if( !file.isFile() )
            {
                final String message =
                    "Skipping " + file + " as it is not a file.";
                debug( message );
                continue;
            }

            if( !file.canRead() )
            {
                final String message =
                    "Skipping " + file + " as it is not readable.";
                debug( message );
                continue;
            }

            try
            {
                final OptionalPackage optionalPackage = getOptionalPackage(
                    file );
                cacheOptionalPackage( optionalPackage );
            }
            catch( final IOException ioe )
            {
                final String message =
                    "Skipping " +
                    file +
                    " as it could not be loaded " +
                    "due to " + ioe;
                debug( message );
            }
        }
    }

    /**
     * Clear internal cache of optional packages.
     */
    protected final synchronized void clearCache()
    {
        m_packages.clear();
        m_needToScan = true;
    }

    /**
     * Add OptionalPackage to internal cache of Optional Packages. Note that
     * this method is only protected so that unit tests can sub-class and add
     * entries to PackageRepository by calling this method.
     *
     * @param optionalPackage the OptionalPackage to be added to repository
     */
    protected final synchronized void cacheOptionalPackage(
        final OptionalPackage optionalPackage )
    {
        m_needToScan = false;

        // added to avoid out of bounds exception
        if( optionalPackage.getAvailableExtensions().length == 0 )
        {
            return;
        }

        final Extension extension = optionalPackage.getAvailableExtensions()[ 0 ];
        ArrayList candidates = (ArrayList)m_packages.get(
            extension.getExtensionName() );
        if( null == candidates )
        {
            candidates = new ArrayList();
            m_packages.put( extension.getExtensionName(), candidates );
        }

        candidates.add( optionalPackage );
    }

    /**
     * Construct an OptionalPackage out of the specified jar archive.
     *
     * @param archive the file object for Jar archive
     * @return the OptionalPackage constructed
     * @throws IOException if an error occurs
     */
    private OptionalPackage getOptionalPackage( final File archive )
        throws IOException
    {
        final File file = archive.getCanonicalFile();
        final JarFile jarFile = new JarFile( file );
        try
        {
            final Manifest manifest = jarFile.getManifest();
            if( null == manifest )
            {
                return null;
            }
            final Extension[] available = Extension.getAvailable( manifest );
            final Extension[] required = Extension.getRequired( manifest );

            return new OptionalPackage( file, available, required );
        }
        finally
        {
            jarFile.close();
        }
    }

    /**
     * Output a debug message for repository.
     *
     * @param message the debug message
     */
    protected void debug( final String message )
    {
        if( DEBUG )
        {
            System.out.println( message );
        }
    }

    /**
     * Get Canonical or failing that the absolute file for every specified
     * file.
     *
     * @param path the files that make up path
     * @return the resolved path
     */
    private File[] resolvePath( final File[] path )
    {
        final File[] resultPath = new File[ path.length ];
        for( int i = 0; i < path.length; i++ )
        {
            resultPath[ i ] = resolveFile( path[ i ] );
        }
        return resultPath;
    }

    /**
     * Get Canonical or failing that the absolute file for specified file.
     *
     * @param file the file
     * @return the resolved file
     */
    private File resolveFile( final File file )
    {
        try
        {
            return file.getCanonicalFile();
        }
        catch( IOException e )
        {
            return file.getAbsoluteFile();
        }
    }

    /**
     * Validate each element in path to make sure they are valid.
     *
     * @param path the path
     */
    private void validatePath( final File[] path )
    {
        if( null == path )
        {
            throw new NullPointerException( "path" );
        }

        for( int i = 0; i < path.length; i++ )
        {
            validatePathElement( path[ i ] );
        }
    }

    /**
     * Make sure specified path element is valid. The elements should exist and
     * should be a directory.
     *
     * @param file the path element
     */
    private void validatePathElement( final File file )
    {
        if( !file.exists() || !file.isDirectory() )
        {
            final String message = "path element " +
                file +
                " must exist and must be a directory";
            throw new IllegalArgumentException( message );
        }
    }

    /**
     * Merge the specified file list with existing path.
     *
     * @param files the files to merge
     * @return the merged path
     */
    private File[] mergePaths( final File[] files )
    {
        final File[] resultPath =
            new File[ m_path.length + files.length ];
        System.arraycopy( m_path, 0, resultPath, 0, m_path.length );
        System.arraycopy( files, m_path.length, resultPath, 0, files.length );
        return resultPath;
    }

    /**
     * Convert set of string elements into file objects
     *
     * @param pathElements the string path elements
     * @return the file array representing each element
     */
    private File[] toFiles( final String[] pathElements )
    {
        final File[] path = new File[ pathElements.length ];
        for( int i = 0; i < path.length; i++ )
        {
            path[ i ] = new File( pathElements[ i ] );
        }
        return path;
    }

    /**
     * Splits the string on every token into an array of strings.
     *
     * @param string the string
     * @param onToken the token
     * @return the resultant array
     */
    private static String[] split( final String string, final String onToken )
    {
        final StringTokenizer tokenizer = new StringTokenizer( string,
                                                               onToken );
        final String[] result = new String[ tokenizer.countTokens() ];

        for( int i = 0; i < result.length; i++ )
        {
            result[ i ] = tokenizer.nextToken();
        }

        return result;
    }
}
TOP

Related Classes of org.codehaus.loom.components.extensions.pkgmgr.impl.DefaultExtensionManager

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.