blob: 16793a8639b19c0d2a13303d36993685574031cc [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", 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 names without prior written
* permission of the Apache Group.
*
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.ant.antcore.antlib;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.ant.antcore.xml.ParseContext;
import org.apache.ant.antcore.xml.XMLParseException;
import org.apache.ant.common.util.CircularDependencyChecker;
import org.apache.ant.common.util.CircularDependencyException;
import org.apache.ant.common.util.ExecutionException;
import org.apache.ant.init.InitConfig;
import org.apache.ant.init.InitUtils;
import org.apache.ant.init.LoaderUtils;
/**
* This class manages the configuration of Ant Libraries
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @created 29 January 2002
*/
public class AntLibManager {
/** The list of extensions which are examined for ant libraries */
public static final String[] ANTLIB_EXTENSIONS
= new String[]{".tsk", ".jar", ".zip"};
/** Flag which indicates whether non-file URLS are used */
private boolean remoteAllowed;
/**
* Constructor for the AntLibManager object
*
* @param remoteAllowed true if remote libraries can be used and
* configured
*/
public AntLibManager(boolean remoteAllowed) {
this.remoteAllowed = remoteAllowed;
}
/**
* Add all the Ant libraries that can be found at the given URL
*
* @param librarySpecs A map to which additional library specifications
* are added.
* @param libURL the URL from which Ant libraries are to be loaded
* @exception MalformedURLException if the URL for the individual
* library components cannot be formed
* @exception ExecutionException if the library specs cannot be parsed
*/
public void addAntLibraries(Map librarySpecs, URL libURL)
throws MalformedURLException, ExecutionException {
URL[] libURLs = LoaderUtils.getLocationURLs(libURL, libURL.toString(),
ANTLIB_EXTENSIONS);
if (libURLs == null) {
return;
}
// parse each task library to get its library definition
for (int i = 0; i < libURLs.length; ++i) {
URL antLibraryURL = new URL("jar:" + libURLs[i]
+ "!/META-INF/antlib.xml");
try {
AntLibrarySpec antLibrarySpec = parseLibraryDef(antLibraryURL);
if (antLibrarySpec != null) {
String libraryId = antLibrarySpec.getLibraryId();
if (librarySpecs.containsKey(libraryId)) {
AntLibrarySpec currentSpec
= (AntLibrarySpec)librarySpecs.get(libraryId);
throw new ExecutionException("Found more than one "
+ "copy of library with id = " + libraryId
+ " (" + libURLs[i] + ") + existing library at ("
+ currentSpec.getLibraryURL() + ")");
}
antLibrarySpec.setLibraryURL(libURLs[i]);
librarySpecs.put(libraryId, antLibrarySpec);
}
} catch (XMLParseException e) {
Throwable t = e.getCause();
// ignore file not found exceptions - means the
// jar does not provide META-INF/antlib.xml
if (!(t instanceof FileNotFoundException)) {
throw new ExecutionException("Unable to parse Ant library "
+ libURLs[i], e);
}
}
}
}
/**
* Configures the Ant Libraries. Configuration of an Ant Library
* involves resolving any dependencies between libraries and then
* creating the class loaders for the library
*
* @param librarySpecs the loaded specifications of the Ant libraries
* @param initConfig the Ant initialization configuration
* @param libraries the collection of libraries already configured
* @param libPathsMap a map of lists of library patsh fro each library
* @exception ExecutionException if a library cannot be configured from
* the given specification
*/
public void configLibraries(InitConfig initConfig, Map librarySpecs,
Map libraries, Map libPathsMap)
throws ExecutionException {
// check if any already defined
for (Iterator i = librarySpecs.keySet().iterator(); i.hasNext(); ) {
String libraryId = (String)i.next();
if (libraries.containsKey(libraryId)) {
AntLibrary currentVersion
= (AntLibrary)libraries.get(libraryId);
throw new ExecutionException("Ant Library \"" + libraryId
+ "\" is already loaded from "
+ currentVersion.getDefinitionURL());
}
}
CircularDependencyChecker configuring
= new CircularDependencyChecker("configuring Ant libraries");
for (Iterator i = librarySpecs.keySet().iterator(); i.hasNext(); ) {
String libraryId = (String)i.next();
if (!libraries.containsKey(libraryId)) {
configLibrary(initConfig, librarySpecs, libraryId,
configuring, libraries, libPathsMap);
}
}
}
/**
* Load either a set of libraries or a single library.
*
* @param libLocationURL URL where libraries can be found
* @param librarySpecs A collection of library specs which will be
* populated with the libraries found
* @exception ExecutionException if the libraries cannot be loaded
* @exception MalformedURLException if the library's location cannot be
* formed
*/
public void loadLibs(Map librarySpecs, URL libLocationURL)
throws ExecutionException, MalformedURLException {
if (!libLocationURL.getProtocol().equals("file")
&& !remoteAllowed) {
throw new ExecutionException("The config library "
+ "location \"" + libLocationURL
+ "\" cannot be used because config does "
+ "not allow remote libraries");
}
addAntLibraries(librarySpecs, libLocationURL);
}
/**
* Load either a set of libraries or a single library.
*
* @param libLocationString URL or file where libraries can be found
* @param librarySpecs A collection of library specs which will be
* populated with the libraries found
* @exception ExecutionException if the libraries cannot be loaded
* @exception MalformedURLException if the library's location cannot be
* formed
*/
public void loadLibs(Map librarySpecs, String libLocationString)
throws ExecutionException, MalformedURLException {
File libLocation = new File(libLocationString);
if (!libLocation.exists()) {
try {
loadLibs(librarySpecs, new URL(libLocationString));
} catch (MalformedURLException e) {
// XXX
}
} else {
addAntLibraries(librarySpecs, InitUtils.getFileURL(libLocation));
}
}
/**
* add a library path to the given library
*
* @param antLibrary the library to which the path is to be added
* @param path the path to be added
* @exception ExecutionException if remote paths are not allowed by
* configuration
*/
public void addLibPath(AntLibrary antLibrary, URL path)
throws ExecutionException {
if (!path.getProtocol().equals("file")
&& !remoteAllowed) {
throw new ExecutionException("Remote libpaths are not"
+ " allowed: " + path);
}
antLibrary.addLibraryURL(path);
}
/**
* Configure a library from a specification and the Ant init config.
*
* @param initConfig Ant's init config passed in from the front end.
* @param librarySpecs the library specs from which this library is to
* be configured.
* @param libraryId the global identifier for the library
* @param configuring A circualr dependency chcker for library
* dependencies.
* @param libraries the collection of libraries which have already been
* configured
* @param libPathsMap a map of lists of library patsh fro each library
* @exception ExecutionException if the library cannot be configured.
*/
private void configLibrary(InitConfig initConfig, Map librarySpecs,
String libraryId,
CircularDependencyChecker configuring,
Map libraries, Map libPathsMap)
throws ExecutionException {
try {
configuring.visitNode(libraryId);
AntLibrarySpec librarySpec
= (AntLibrarySpec)librarySpecs.get(libraryId);
String extendsId = librarySpec.getExtendsLibraryId();
if (extendsId != null) {
if (!libraries.containsKey(extendsId)) {
if (!librarySpecs.containsKey(extendsId)) {
throw new ExecutionException("Could not find library, "
+ extendsId + ", upon which library "
+ libraryId + " depends");
}
configLibrary(initConfig, librarySpecs, extendsId,
configuring, libraries, libPathsMap);
}
}
// now create the library for the specification
AntLibrary antLibrary = new AntLibrary(librarySpec);
// determine the URLs required for this task. These are the
// task URL itself, the XML parser URLs if required, the
// tools jar URL if required
List urlsList = new ArrayList();
if (librarySpec.getLibraryURL() != null) {
urlsList.add(librarySpec.getLibraryURL());
}
if (librarySpec.isToolsJarRequired()
&& initConfig.getToolsJarURL() != null) {
urlsList.add(initConfig.getToolsJarURL());
}
if (librarySpec.usesAntXML()) {
URL[] parserURLs = initConfig.getParserURLs();
for (int i = 0; i < parserURLs.length; ++i) {
urlsList.add(parserURLs[i]);
}
}
for (Iterator i = urlsList.iterator(); i.hasNext(); ) {
antLibrary.addLibraryURL((URL)i.next());
}
if (extendsId != null) {
AntLibrary extendsLibrary
= (AntLibrary)libraries.get(extendsId);
antLibrary.setExtendsLibrary(extendsLibrary);
}
antLibrary.setParentLoader(initConfig.getCommonLoader());
libraries.put(libraryId, antLibrary);
List libPaths = (List)libPathsMap.get(libraryId);
if (libPaths != null) {
for (Iterator j = libPaths.iterator(); j.hasNext(); ) {
URL pathURL = (URL)j.next();
addLibPath(antLibrary, pathURL);
}
}
configuring.leaveNode(libraryId);
} catch (CircularDependencyException e) {
throw new ExecutionException(e);
}
}
/**
* Read an Ant library definition from a URL
*
* @param antlibURL the URL of the library definition
* @return the AntLibrary specification read from the library XML
* definition
* @exception XMLParseException if the library cannot be parsed
*/
private AntLibrarySpec parseLibraryDef(URL antlibURL)
throws XMLParseException {
ParseContext context = new ParseContext();
AntLibHandler libHandler = new AntLibHandler();
context.parse(antlibURL, "antlib", libHandler);
return libHandler.getAntLibrarySpec();
}
}