Beginnings of automated Java library publishing, dependency tracking and
downloading system. git-svn-id: https://samskivert.googlecode.com/svn/trunk@702 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- build configuration -->
|
||||
<project name="depends" default="compile" basedir=".">
|
||||
|
||||
<!-- things you may want to change -->
|
||||
<property name="build.compiler" value="jikes"/>
|
||||
<property name="build.optimize" value="on"/>
|
||||
|
||||
<!-- things you probably don't want to change -->
|
||||
<property name="app.name" value="depends"/>
|
||||
<property name="copyright.holder" value="Michael Bayne"/>
|
||||
<property name="deploy.dir" value="dist"/>
|
||||
<property name="dist.jar" value="${app.name}.jar"/>
|
||||
<property name="javadoc.dir" value="${deploy.dir}/docs"/>
|
||||
|
||||
<!-- things you definitely don't want to change -->
|
||||
<property name="doc.packages" value="com.samskivert.depends.*"/>
|
||||
<property name="doc.overview" value="com/samskivert/depends/overview.html"/>
|
||||
<property name="src.dir" value="src/java"/>
|
||||
|
||||
<!-- prepares the application directories -->
|
||||
<target name="prepare">
|
||||
<tstamp><format property="year" pattern="yyyy" /></tstamp>
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<mkdir dir="${deploy.dir}/classes"/>
|
||||
<mkdir dir="${javadoc.dir}"/>
|
||||
<copy todir="${deploy.dir}/classes">
|
||||
<fileset dir="${src.dir}" includes="**/*.properties"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- cleans out the installed application -->
|
||||
<target name="clean">
|
||||
<delete dir="${deploy.dir}"/>
|
||||
</target>
|
||||
|
||||
<!-- builds the java class files -->
|
||||
<target name="compile" depends="prepare">
|
||||
<javac srcdir="${src.dir}" destdir="${deploy.dir}/classes"
|
||||
debug="on" optimize="${build.optimize}" deprecation="off">
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- builds the javadoc documentation -->
|
||||
<target name="javadoc" depends="prepare">
|
||||
<javadoc sourcepath="${src.dir}"
|
||||
packagenames="${doc.packages}"
|
||||
windowtitle="${app.name} API"
|
||||
doctitle="${app.name} API"
|
||||
overview="${src.dir}/${doc.overview}"
|
||||
destdir="${javadoc.dir}"
|
||||
additionalparam="-breakiterator">
|
||||
<bottom>Copyright © 2000-${year} ${copyright.holder}.
|
||||
All Rights Reserved.</bottom>
|
||||
<link href="http://java.sun.com/j2se/1.4/docs/api/"/>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<!-- builds our distribution files -->
|
||||
<target name="dist" depends="prepare,compile">
|
||||
<jar destfile="${deploy.dir}/${dist.jar}"
|
||||
basedir="${deploy.dir}/classes"/>
|
||||
</target>
|
||||
|
||||
<!-- a target for rebuilding everything -->
|
||||
<target name="all" depends="clean,prepare,compile,javadoc,dist"/>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<depends>
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/commons-util.ldf"/>
|
||||
<library name="jakarta-commons-digester" version="1.2"
|
||||
url="http://jakarta.apache.org/commons/commons-digester.ldf"/>
|
||||
<!-- use local build until those kooks incorporate our patches
|
||||
<library name="samskivert" version="1.1+"
|
||||
url="http://www.waywardgeeks.org/code/samskivert/samskivert.ldf"/>
|
||||
-->
|
||||
<library name="samskivert" version="1.1+"
|
||||
url="file:/home/mdb/projects/samskivert/target/samskivert.ldf"/>
|
||||
</depends>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0"?>
|
||||
<library>
|
||||
<!-- the library identifier: case insensitive; matches ([-_A-Za-z]+) -->
|
||||
<name>samskivert</name>
|
||||
|
||||
<!-- is the inclusion of copyright necessary or desirable? -->
|
||||
<copyright>2000-2002, Michael Bayne</copyright>
|
||||
|
||||
<!-- contact information for this library (optional) -->
|
||||
<contact>mdb@samskivert.com</contact>
|
||||
|
||||
<!-- a web page that provides information about the library and the
|
||||
base URL from which other URLs are interpreted to be relative -->
|
||||
<url>http://www.waywardgeeks.org/code/samskivert/</url>
|
||||
|
||||
<!-- composed with <url> using the standard rules:
|
||||
<url> + samskivert.ldf =
|
||||
http://www.waywardgeeks.org/code/samskivert/samskivert.ldf
|
||||
<url> + /samskivert.ldf =
|
||||
http://www.waywardgeeks.org/samskivert.ldf
|
||||
<url> + http://foo.bar.com/samskivert.ldf =
|
||||
http://foo.bar.com/samskivert.ldf
|
||||
-->
|
||||
<descriptor>samskivert.ldf</descriptor>
|
||||
|
||||
<!-- enumerates the "supported" releases -->
|
||||
<releases>
|
||||
<release>
|
||||
<version>1.1</version>
|
||||
<date>2001-12-03</date>
|
||||
<!-- composed with <url> using the standard rules -->
|
||||
<jar>releases/samskivert-1.1.jar</jar>
|
||||
<!-- describes this release's dependencies -->
|
||||
<depends>
|
||||
<!-- version strings ending with + means automatically use the
|
||||
latest version; versions are ordered by release date -->
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/commons-util.ldf"/>
|
||||
<library name="jakarta-commons-digester" version="1.2"
|
||||
url="http://jakarta.apache.org/commons/commons-digester.ldf"/>
|
||||
</depends>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<version>1.01</version>
|
||||
<date>2001-08-13</date>
|
||||
<jar>releases/samskivert-1.01.jar</jar>
|
||||
<depends>
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/util/commons-util.ldf"/>
|
||||
</depends>
|
||||
</release>
|
||||
</releases>
|
||||
</library>
|
||||
@@ -0,0 +1,33 @@
|
||||
Dependency system design
|
||||
------------------------
|
||||
|
||||
- Two interfaces: Java object interface and ANT wrapper
|
||||
|
||||
- If possible, use no external libraries to avoid bootstrapping problems
|
||||
(can't use depends to download depends for depends):
|
||||
+ make use of built-in Java XML parsing services
|
||||
+ make use of built-in Java logging services
|
||||
+ will have to "depend" on ant
|
||||
|
||||
- Object interface available via the following classes:
|
||||
|
||||
public class Dependency
|
||||
{
|
||||
public String getLibraryName ();
|
||||
|
||||
public File getLocalJar ();
|
||||
|
||||
public boolean isUpToDate ();
|
||||
|
||||
public void bringUpToDate ();
|
||||
}
|
||||
|
||||
public class DependencyParser
|
||||
{
|
||||
public <Dependency>List parseDependencies (
|
||||
File definition, File repository);
|
||||
}
|
||||
|
||||
public class LibraryDescriptor
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
Dependency system notes
|
||||
-----------------------
|
||||
|
||||
- Will need tool (ant task) for "stamping" a jar file (inserting the
|
||||
library descriptor).
|
||||
|
||||
- Will want tool that dumps library descriptor information from an
|
||||
existing jar file (both in XML and formatted text).
|
||||
@@ -0,0 +1,177 @@
|
||||
Dependency system specification
|
||||
-------------------------------
|
||||
|
||||
- Libraries publish "library descriptor file" which is a simple XML file
|
||||
describing the library's dependencies, latest release information and
|
||||
where supported releases can be downloaded:
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<library>
|
||||
<!-- the library identifier: case insensitive; matches ([-_A-Za-z]+) -->
|
||||
<name>samskivert</name>
|
||||
|
||||
<!-- is the inclusion of copyright necessary or desirable? -->
|
||||
<copyright>2000-2002, Michael Bayne</copyright>
|
||||
|
||||
<!-- contact information for this library (optional) -->
|
||||
<contact>mdb@samskivert.com</contact>
|
||||
|
||||
<!-- a web page that provides information about the library and the
|
||||
base URL from which other URLs are interpreted to be relative -->
|
||||
<url>http://www.waywardgeeks.org/code/samskivert/</url>
|
||||
|
||||
<!-- composed with <url> using the standard rules:
|
||||
<url> + samskivert.ldf =
|
||||
http://www.waywardgeeks.org/code/samskivert/samskivert.ldf
|
||||
<url> + /samskivert.ldf =
|
||||
http://www.waywardgeeks.org/samskivert.ldf
|
||||
<url> + http://foo.bar.com/samskivert.ldf =
|
||||
http://foo.bar.com/samskivert.ldf
|
||||
-->
|
||||
<descriptor>samskivert.ldf</descriptor>
|
||||
|
||||
<!-- enumerates the "supported" releases -->
|
||||
<releases>
|
||||
<release>
|
||||
<version>1.1</version>
|
||||
<date>2001-12-03</date>
|
||||
<!-- composed with <url> using the standard rules -->
|
||||
<jar>releases/samskivert-1.1.jar</jar>
|
||||
<!-- describes this release's dependencies -->
|
||||
<depends>
|
||||
<!-- version strings ending with + means automatically use the
|
||||
latest version; versions are ordered by release date -->
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/commons-util.ldf"/>
|
||||
<library name="jakarta-commons-digester" version="1.2"
|
||||
url="http://jakarta.apache.org/commons/commons-digester.ldf"/>
|
||||
</depends>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<version>1.01</version>
|
||||
<date>2001-08-13</date>
|
||||
<jar>releases/samskivert-1.01.jar</jar>
|
||||
<depends>
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/util/commons-util.ldf"/>
|
||||
</depends>
|
||||
</release>
|
||||
</releases>
|
||||
</library>
|
||||
|
||||
This would be made available at:
|
||||
|
||||
http://www.waywardgeeks.org/code/samskivert/samskivert.ldf
|
||||
|
||||
Some sort of version information should be inserted into the JAR file so
|
||||
that the downloader doesn't have to maintain a cached copy of the LDF
|
||||
file it used to retrieve the JAR file. Perhaps an additional file
|
||||
included in the jar:
|
||||
|
||||
META-INF/RELEASE.LDF:
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<library>
|
||||
<name>samskivert</name>
|
||||
<copyright>2000-2002, Michael Bayne</copyright>
|
||||
<url>http://www.waywardgeeks.org/code/samskivert/</url>
|
||||
<descriptor>samskivert.ldf</descriptor>
|
||||
<release>
|
||||
<version>1.1</version>
|
||||
<date>2001-12-03</date>
|
||||
<jar>releases/samskivert-1.1.jar</jar>
|
||||
<depends>
|
||||
<library name="jakarta-commons-util" version="1.0+"
|
||||
url="http://jakarta.apache.org/commons/commons-util.ldf"/>
|
||||
<library name="jakarta-commons-digester" version="1.2"
|
||||
url="http://jakarta.apache.org/commons/commons-digester.ldf"/>
|
||||
</depends>
|
||||
</release>
|
||||
</library>
|
||||
|
||||
This would be automatically generated by the depends system and inserted
|
||||
into the jar file during the build process.
|
||||
|
||||
Perhaps it is desirable to support the publishing of multiple libraries
|
||||
in a single LDF file?
|
||||
|
||||
- Projects reference dependencies via an ANT front-end or by other means
|
||||
(eg. Maven). Perhaps using an external file:
|
||||
|
||||
depends.xml:
|
||||
|
||||
<depends>
|
||||
<library name="samskivert" version="1.1+"
|
||||
url="http://www.waywardgeeks.org/code/samskivert/samskivert.ldf"/>
|
||||
<library name="jakarta-commons-collections" version="1.2+"
|
||||
url="http://jakarta.apache.org/commons/commons-collections.ldf"/>
|
||||
</depends>
|
||||
|
||||
build.xml:
|
||||
|
||||
<!-- includes the appropriate library files in the classpath -->
|
||||
<target name="include-depends">
|
||||
<taskdef name="include" classname="...IncludeDependsTask"/>
|
||||
<include deps="depends.xml" refid="classpath" repository="libs/"/>
|
||||
</target>
|
||||
|
||||
<!-- checks for updates to the dependencies; reports newer versions,
|
||||
obsolescence, etc. -->
|
||||
<target name="check-depends">
|
||||
<taskdef name="check" classname="...CheckDependsTask"/>
|
||||
<check deps="depends.xml" repository="libs/"/>
|
||||
</target>
|
||||
|
||||
<!-- downloads any newer versions of dependencies for which we have
|
||||
indicated a desire to use the latest version -->
|
||||
<target name="update-depends">
|
||||
<taskdef name="update" classname="...UpdateDependsTask"/>
|
||||
<update deps="depends.xml" repository="libs/"/>
|
||||
</target>
|
||||
|
||||
Or perhaps the dependencies should be specified directly in ant's build
|
||||
file (which would probably involve more sophisticated extension of ant
|
||||
than just implementing a task). This latter approach would make it more
|
||||
difficult for external tools to inspect a project's dependencies, but
|
||||
fewer files is also good.
|
||||
|
||||
- A developer could override the standard library location and specify a
|
||||
dependency to a local build:
|
||||
|
||||
<depends>
|
||||
<library name="samskivert" version="1.1+"
|
||||
<!-- use local build until those kooks incorporate our patches
|
||||
url="http://www.waywardgeeks.org/code/samskivert/samskivert.ldf"/>
|
||||
-->
|
||||
url="file:/home/mdb/projects/samskivert/target/samskivert.ldf"/>
|
||||
</depends>
|
||||
|
||||
Because the build system would tag a library with a "release" date of
|
||||
the current date at the time of the build, it would pick up the local
|
||||
build so long as it was built more recently than any public build that
|
||||
might be referenced in the dependency hierarchy.
|
||||
|
||||
- KISS: place as little information as is necessary in the library
|
||||
descriptor files so as to minimize the possibility of that information
|
||||
becoming out of date.
|
||||
|
||||
- Facilitate build stability: don't automatically check for or download
|
||||
new libraries; provide an explicit mechanism for checking for updates
|
||||
(separate build directive), a means for specifying "use the latest
|
||||
version" and one for specifying "use a specific version".
|
||||
|
||||
- Facilitate the bleeding edge: a project should be able to include
|
||||
libraries from the web site where the most recent stable release is
|
||||
published, or build from CVS and reference their local build directly.
|
||||
|
||||
- Facilitate forward progress: report when a project depends on a version
|
||||
of a library that is no longer supported (meaning it is no longer listed
|
||||
in the library's descriptor).
|
||||
|
||||
- Facilitate dependency generation: check project's generated classfiles
|
||||
for references to classes not enumerated in dependencies hierarchy;
|
||||
report findings.
|
||||
|
||||
- Facilitate obsolete dependency removal: report dependencies that are not
|
||||
referenced by any class in the project.
|
||||
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// $Id: Dependency.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* Describes a particular library dependency.
|
||||
*/
|
||||
public class Dependency
|
||||
{
|
||||
/**
|
||||
* Constructs a dependency from its specification criterion.
|
||||
*/
|
||||
public Dependency (String libraryName,
|
||||
Version requiredVersion,
|
||||
URL descriptorURL)
|
||||
{
|
||||
_libraryName = libraryName;
|
||||
_requiredVersion = requiredVersion;
|
||||
_descriptorURL = descriptorURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs this dependency of the location of our local library
|
||||
* repository.
|
||||
*/
|
||||
public void setLocalRepository (File repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable library identifier.
|
||||
*/
|
||||
public String getLibraryName ()
|
||||
{
|
||||
return _libraryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the library version required by this dependency
|
||||
* specification.
|
||||
*/
|
||||
public Version getRequiredVersion ()
|
||||
{
|
||||
return _requiredVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the descriptor URL for the library referenced by this
|
||||
* dependency.
|
||||
*/
|
||||
public URL getDescriptorURL ()
|
||||
{
|
||||
return _descriptorURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of our local copy of the library referenced by
|
||||
* this dependency, or null if we have no local copy of the library.
|
||||
*
|
||||
* @exception IllegalStateException thrown if the dependency has not
|
||||
* yet been configured with the path to the local library repository.
|
||||
*/
|
||||
public Version getLocalVersion ()
|
||||
throws IOException, FormatException
|
||||
{
|
||||
if (_localVersion == null) {
|
||||
resolveLocalVersion();
|
||||
}
|
||||
return _localVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file object referencing our local copy of the library
|
||||
* referenced by this dependency, or null if the dependency hasn't
|
||||
* been resolved.
|
||||
*
|
||||
* @exception IllegalStateException thrown if the dependency has not
|
||||
* yet been configured with the path to the local library repository.
|
||||
*/
|
||||
public File getLocalJar ()
|
||||
{
|
||||
if (_repository == null) {
|
||||
String errmsg = "Dependency has not yet been configured " +
|
||||
"with path to local library repository.";
|
||||
throw new IllegalStateException(errmsg);
|
||||
}
|
||||
return new File(_repository, constructJarName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we have a local copy of the library referenced by
|
||||
* this dependency that satisfies the dependency's version constraints
|
||||
* <em>and</em> that we have local copies with satisfactory versions
|
||||
* of all libraries upon which this library depends, ad infinitum. If
|
||||
* we have no local copy of the library or the version of the local
|
||||
* copy is older than the version specified by the dependency or same
|
||||
* for any derived dependencies, this method returns false.
|
||||
*
|
||||
* <p> Note that this method caches its results once they are computed
|
||||
* in the expectation that it will be used in a "one shot" manner
|
||||
* (meaning the JVM that instantiates and uses this class is not long
|
||||
* lived). Once a dependency is resolved, it does not go back and
|
||||
* reresolve it, thus it could become out of date if the library (or
|
||||
* its dependencies) is updated between the time that this method is
|
||||
* called and the time that the calling JVM exits.
|
||||
*/
|
||||
public boolean isSatisified ()
|
||||
{
|
||||
// TBD
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we have a local copy of the library and it is the
|
||||
* most recent release of that library. This method results in an HTTP
|
||||
* request being issued to download the latest library descriptor for
|
||||
* the library
|
||||
*
|
||||
* <p> Note that this method caches its results once they are computed
|
||||
* in the expectation that it will be used in a "one shot" manner
|
||||
* (meaning the JVM that instantiates and uses this class is not long
|
||||
* lived). Once a dependency is resolved, it does not go back and
|
||||
* reresolve it, thus it could become out of date if the library (or
|
||||
* its dependencies) is updated between the time that this method is
|
||||
* called and the time that the calling JVM exits.
|
||||
*/
|
||||
public boolean isUpToDate ()
|
||||
throws IOException
|
||||
{
|
||||
// TBD
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the library descriptor for the library referenced by this
|
||||
* dependency and returns it. Note that once the descriptor has been
|
||||
* downloaded once, it is cached for the remainder of the lifetime of
|
||||
* the JVM, hiding updates that take place between the time that this
|
||||
* method is called and the time that the calling JVM exists. This is
|
||||
* considered to be more desirable than making an HTTP request every
|
||||
* time this method is called.
|
||||
*/
|
||||
public LibraryDescriptor getLibraryDescriptor ()
|
||||
throws IOException, FormatException
|
||||
{
|
||||
if (_remoteDescriptor != null) {
|
||||
return _remoteDescriptor;
|
||||
}
|
||||
|
||||
// download the descriptor data from the publishing site
|
||||
URLConnection uconn = _descriptorURL.openConnection();
|
||||
_remoteDescriptor = LibraryDescriptorParser.parseLibraryDescriptor(
|
||||
uconn.getInputStream());
|
||||
return _remoteDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the library descriptor for the library referenced by this
|
||||
* dependency and reports the most recent version available of said
|
||||
* library.
|
||||
*
|
||||
* @see #getLibraryDescriptor
|
||||
*/
|
||||
public Version getRemoteVersion ()
|
||||
throws IOException, FormatException
|
||||
{
|
||||
LibraryDescriptor descriptor = getLibraryDescriptor();
|
||||
return descriptor.getLatestRelease().getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[name=" + _libraryName + ", version=" + _requiredVersion +
|
||||
", url=" + _descriptorURL + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the file name of our local copy of the jar file.
|
||||
*/
|
||||
protected String constructJarName ()
|
||||
{
|
||||
return _libraryName + LIBRARY_SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up the library stamp from our local copy of the library and
|
||||
* extracts the version information from it.
|
||||
*/
|
||||
protected Version resolveLocalVersion ()
|
||||
throws IOException, FormatException
|
||||
{
|
||||
File jar = getLocalJar();
|
||||
// TBD
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The name of the library referenced by this dependency. */
|
||||
protected String _libraryName;
|
||||
|
||||
/** The version required by this dependency. */
|
||||
protected Version _requiredVersion;
|
||||
|
||||
/** The URL for the descriptor of the library referenced by this
|
||||
* dependency. */
|
||||
protected URL _descriptorURL;
|
||||
|
||||
/** The path to our local library repository. */
|
||||
protected File _repository;
|
||||
|
||||
/** The version of our local copy of the library or null if we have no
|
||||
* local copy. */
|
||||
protected Version _localVersion;
|
||||
|
||||
/** A parsed library descriptor, obtained from our local copy of the
|
||||
* library. */
|
||||
protected LibraryDescriptor _localDescriptor;
|
||||
|
||||
/** A parsed library descriptor, downloaded from the site that
|
||||
* publishes this library. */
|
||||
protected LibraryDescriptor _remoteDescriptor;
|
||||
|
||||
/** All libraries end with this suffix: <code>.jar</code> */
|
||||
protected static final String LIBRARY_SUFFIX = ".jar";
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// $Id: DependencyParser.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses XML dependency definitions.
|
||||
*/
|
||||
public class DependencyParser
|
||||
{
|
||||
/**
|
||||
* Parses the specified dependency definition file into a list of
|
||||
* {@link Dependency} instances.
|
||||
*/
|
||||
public static List parseDependencies (File definition)
|
||||
throws FormatException, IOException
|
||||
{
|
||||
try {
|
||||
ArrayList list = new ArrayList();
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser parser = factory.newSAXParser();
|
||||
DependencyHandler handler = new DependencyHandler(list);
|
||||
parser.parse(definition, handler);
|
||||
return list;
|
||||
|
||||
} catch (SAXException saxe) {
|
||||
throw new FormatException(saxe.getMessage());
|
||||
|
||||
} catch (ParserConfigurationException pce) {
|
||||
throw new FormatException(
|
||||
"Unable to load parser: " + pce.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
try {
|
||||
parseDependencies(new File(args[0]));
|
||||
} catch (Exception e) {
|
||||
Log.warning("Chokey choke-opolis", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to parse the dependency specification.
|
||||
*/
|
||||
protected static class DependencyHandler extends DefaultHandler
|
||||
{
|
||||
public DependencyHandler (List list)
|
||||
{
|
||||
_list = list;
|
||||
}
|
||||
|
||||
public void startElement (String namespaceURI, String localName,
|
||||
String qName, Attributes attrs)
|
||||
{
|
||||
if (qName.equals("library")) {
|
||||
String name = null;
|
||||
String version = null;
|
||||
String url = null;
|
||||
|
||||
for (int i = 0; i < attrs.getLength(); i++) {
|
||||
String attrName = attrs.getQName(i);
|
||||
if (attrName.equals("name")) {
|
||||
name = attrs.getValue(i);
|
||||
} else if (attrName.equals("version")) {
|
||||
version = attrs.getValue(i);
|
||||
} else if (attrName.equals("url")) {
|
||||
url = attrs.getValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!validateAttribute("name", name) ||
|
||||
!validateAttribute("version", version) ||
|
||||
!validateAttribute("url", url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Dependency dep = new Dependency(
|
||||
name, new Version(version), new URL(url));
|
||||
Log.info("Parsed dependency " + dep + ".");
|
||||
_list.add(dep);
|
||||
|
||||
} catch (MalformedURLException mue) {
|
||||
Log.warning("Skipping dependency with invalid 'url' " +
|
||||
"specification [url=" + url + "]: " + mue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean validateAttribute (String name, String value)
|
||||
{
|
||||
if (value == null || value.length() == 0) {
|
||||
Log.warning("Skipping dependency declaration that is " +
|
||||
"missing required attribute '" + name + "'.");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected List _list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// $Id: FormatException.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
/**
|
||||
* Used to communicate file format exceptions.
|
||||
*/
|
||||
public class FormatException extends Exception
|
||||
{
|
||||
public FormatException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// $Id: LibraryDescriptor.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describes a library.
|
||||
*/
|
||||
public class LibraryDescriptor
|
||||
{
|
||||
public String getLibraryName ()
|
||||
{
|
||||
return _libraryName;
|
||||
}
|
||||
|
||||
public String getCopyright ()
|
||||
{
|
||||
return _copyright;
|
||||
}
|
||||
|
||||
public String getContact ()
|
||||
{
|
||||
return _contact;
|
||||
}
|
||||
|
||||
public URL getLibraryURL ()
|
||||
{
|
||||
return _libraryURL;
|
||||
}
|
||||
|
||||
public URL getDescriptorURL ()
|
||||
{
|
||||
return _descriptorURL;
|
||||
}
|
||||
|
||||
public List getReleases ()
|
||||
{
|
||||
return _releases;
|
||||
}
|
||||
|
||||
public Release getLatestRelease ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String _libraryName;
|
||||
|
||||
protected String _copyright;
|
||||
|
||||
protected String _contact;
|
||||
|
||||
protected URL _libraryURL;
|
||||
|
||||
protected URL _descriptorURL;
|
||||
|
||||
protected List _releases;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// $Id: LibraryDescriptorParser.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
/**
|
||||
* Parses XML library descriptor definitions.
|
||||
*/
|
||||
public class LibraryDescriptorParser
|
||||
{
|
||||
/**
|
||||
* Parses the specified library descriptor file.
|
||||
*/
|
||||
public static LibraryDescriptor parseLibraryDescriptor (File descriptor)
|
||||
throws FormatException, IOException
|
||||
{
|
||||
return parseLibraryDescriptor(new FileInputStream(descriptor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the library descriptor definition provided via the supplied
|
||||
* input stream.
|
||||
*/
|
||||
public static LibraryDescriptor parseLibraryDescriptor (InputStream source)
|
||||
throws FormatException, IOException
|
||||
{
|
||||
try {
|
||||
LibraryDescriptor ld = new LibraryDescriptor();
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
SAXParser parser = factory.newSAXParser();
|
||||
DescriptorHandler handler = new DescriptorHandler(ld);
|
||||
parser.parse(source, handler);
|
||||
return ld;
|
||||
|
||||
} catch (SAXException saxe) {
|
||||
throw new FormatException(saxe.getMessage());
|
||||
|
||||
} catch (ParserConfigurationException pce) {
|
||||
throw new FormatException(
|
||||
"Unable to load parser: " + pce.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
try {
|
||||
parseLibraryDescriptor(new File(args[0]));
|
||||
} catch (Exception e) {
|
||||
Log.warning("Chokey choke-opolis", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to parse a library descriptor specification.
|
||||
*/
|
||||
protected static class DescriptorHandler extends DefaultHandler
|
||||
{
|
||||
public DescriptorHandler (LibraryDescriptor descriptor)
|
||||
{
|
||||
_descriptor = descriptor;
|
||||
}
|
||||
|
||||
public void startElement (String namespaceURI, String localName,
|
||||
String qName, Attributes attrs)
|
||||
{
|
||||
}
|
||||
|
||||
protected boolean validateAttribute (String name, String value)
|
||||
{
|
||||
if (value == null || value.length() == 0) {
|
||||
Log.warning("Skipping dependency declaration that is " +
|
||||
"missing required attribute '" + name + "'.");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected LibraryDescriptor _descriptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// $Id: Log.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Provides convenient access to our logging instance.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** A reference to our logger. */
|
||||
public static Logger log = Logger.getLogger("com.samskivert.depends");
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#fine(String)}.
|
||||
*/
|
||||
public static void fine (String message)
|
||||
{
|
||||
log.fine(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#info(String)}.
|
||||
*/
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#warning(String)}.
|
||||
*/
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#log} with level
|
||||
* <code>WARNING</code>.
|
||||
*/
|
||||
public static void warning (String message, Exception e)
|
||||
{
|
||||
log.log(Level.WARNING, message, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#severe(String)}.
|
||||
*/
|
||||
public static void severe (String message)
|
||||
{
|
||||
log.severe(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes through to {@link Logger#log} with level
|
||||
* <code>SEVERE</code>.
|
||||
*/
|
||||
public static void severe (String message, Exception e)
|
||||
{
|
||||
log.log(Level.SEVERE, message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id: Release.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describes a particular release of a library.
|
||||
*/
|
||||
public class Release
|
||||
{
|
||||
public Version getVersion ()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
public Date getDate ()
|
||||
{
|
||||
return _date;
|
||||
}
|
||||
|
||||
public URL getJarURL ()
|
||||
{
|
||||
return _jarURL;
|
||||
}
|
||||
|
||||
public List getDependencies ()
|
||||
{
|
||||
return _dependencies;
|
||||
}
|
||||
|
||||
protected Version _version;
|
||||
|
||||
protected Date _date;
|
||||
|
||||
protected URL _jarURL;
|
||||
|
||||
protected List _dependencies;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// $Id: ReleaseDescriptor.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
/**
|
||||
* Does something extraordinary.
|
||||
*/
|
||||
public class ReleaseDescriptor
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id: Version.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends;
|
||||
|
||||
/**
|
||||
* Represents a library version. Version strings are not constrained to a
|
||||
* particular format, but must meet a couple of criterion: first, they
|
||||
* must contain only valid filename characters as they will be used to
|
||||
* create the filename of our local copy of the library's jar file.
|
||||
* Second, if a dependency's version specification ends with a
|
||||
* '<code>+</code>', the dependency is considered satisfied by any library
|
||||
* release newer than the specified version. Thus, if a version string
|
||||
* ends in plus, wackiness ensues. Fortunately, we can issue a warning if
|
||||
* anyone tries to stamp a library with a descriptor that declares its
|
||||
* version to end with a plus.
|
||||
*/
|
||||
public class Version
|
||||
{
|
||||
/**
|
||||
* Constructs a version instance from the supplied version
|
||||
* specification string.
|
||||
*/
|
||||
public Version (String version)
|
||||
{
|
||||
_version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this version specification allows replacement by
|
||||
* newer versions, false otherwise.
|
||||
*/
|
||||
public boolean openEnded ()
|
||||
{
|
||||
return _version.endsWith("+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
/** Our version specification string. */
|
||||
protected String _version;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// $Id: CheckDependsTask.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends.tasks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
|
||||
import com.samskivert.depends.Dependency;
|
||||
|
||||
/**
|
||||
* An ant task for checking that a set of dependencies are satisfied by
|
||||
* the jar files that currently exist in our local library repository.
|
||||
*/
|
||||
public class CheckDependsTask extends DependsTask
|
||||
{
|
||||
/**
|
||||
* Performs the actual work of the task.
|
||||
*/
|
||||
public void execute ()
|
||||
throws BuildException
|
||||
{
|
||||
// load and resolve our dependencies
|
||||
List depends = loadDependencies();
|
||||
resolveDependencies(depends);
|
||||
|
||||
int dcount = depends.size();
|
||||
for (int i = 0; i < dcount; i++) {
|
||||
Dependency dep = (Dependency)depends.get(i);
|
||||
try {
|
||||
if (!dep.isUpToDate()) {
|
||||
System.out.println("Library '" + dep.getLibraryName() +
|
||||
"' has newer version " +
|
||||
"[have=" + dep.getLocalVersion() +
|
||||
", latest=" + dep.getRemoteVersion() +
|
||||
"].");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error checking dependency " +
|
||||
"[dep=" + dep + ", error=" + e + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// $Id: DependsTask.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends.tasks;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import com.samskivert.depends.Dependency;
|
||||
import com.samskivert.depends.DependencyParser;
|
||||
|
||||
/**
|
||||
* A base class that handles functionality common to all dependency
|
||||
* related ant tasks.
|
||||
*/
|
||||
public abstract class DependsTask extends Task
|
||||
{
|
||||
/**
|
||||
* Called by ant to set the 'deps' attribute.
|
||||
*/
|
||||
public void setDeps (File deps)
|
||||
{
|
||||
_deps = deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by ant to set the 'repository' attribute.
|
||||
*/
|
||||
public void setRepository (File repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a build exception using the supplied error message if the
|
||||
* supplied value is null.
|
||||
*/
|
||||
protected void ensureSet (Object value, String errmsg)
|
||||
throws BuildException
|
||||
{
|
||||
if (value == null) {
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up a set of dependencies from the dependency file specified
|
||||
* via the 'deps' task attribute.
|
||||
*/
|
||||
protected List loadDependencies ()
|
||||
throws BuildException
|
||||
{
|
||||
ensureSet(_deps, "Missing 'deps' attribute which should " +
|
||||
"reference the dependency definition file.");
|
||||
try {
|
||||
return DependencyParser.parseDependencies(_deps);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Error parsing dependency " +
|
||||
"definition file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the supplied set of dependencies with the local library
|
||||
* repository specified via the 'repository' task attribute.
|
||||
*/
|
||||
protected void resolveDependencies (List depends)
|
||||
throws BuildException
|
||||
{
|
||||
// make sure the repository directory was specified...
|
||||
ensureSet(_repository, "Missing 'repository' attribute which should " +
|
||||
"reference the local library repository directory.");
|
||||
|
||||
// ...exists...
|
||||
if (!_repository.exists()) {
|
||||
String errmsg = "Local library repository directory '" +
|
||||
_repository.getPath() + "' does not exist.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
|
||||
// ...and is actually a directory
|
||||
if (!_repository.isDirectory()) {
|
||||
String errmsg = "Specified local library repository '" +
|
||||
_repository.getPath() + "' is not a directory.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
|
||||
int dcount = depends.size();
|
||||
for (int i = 0; i < dcount; i++) {
|
||||
Dependency dep = (Dependency)depends.get(i);
|
||||
dep.setLocalRepository(_repository);
|
||||
}
|
||||
}
|
||||
|
||||
/** The dependency definition file. */
|
||||
protected File _deps;
|
||||
|
||||
/** The local library repository directory. */
|
||||
protected File _repository;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// $Id: IncludeDependsTask.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends.tasks;
|
||||
|
||||
/**
|
||||
* Does something extraordinary.
|
||||
*/
|
||||
public class IncludeDependsTask
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// $Id: UpdateDependsTask.java,v 1.1 2002/04/11 07:23:59 mdb Exp $
|
||||
|
||||
package com.samskivert.depends.tasks;
|
||||
|
||||
/**
|
||||
* Does something extraordinary.
|
||||
*/
|
||||
public class UpdateDependsTask
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user