You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@maven.apache.org by "Brett Porter (JIRA)" <ji...@codehaus.org> on 2005/06/04 07:09:21 UTC
[jira] Updated: (MPCHANGELOG-17) ChangeLog efficiency patch
[ http://jira.codehaus.org/browse/MPCHANGELOG-17?page=all ]
Brett Porter updated MPCHANGELOG-17:
------------------------------------
Fix Version: (was: 1.8)
> ChangeLog efficiency patch
> --------------------------
>
> Key: MPCHANGELOG-17
> URL: http://jira.codehaus.org/browse/MPCHANGELOG-17
> Project: maven-changelog-plugin
> Type: Improvement
> Environment: n/a
> Reporter: Luke Taylor
> Priority: Minor
> Attachments: patch
>
>
> The following patch is intended to speed up the generation of changelog reports, by introducing
> two optional new features:
> o the re-use of an existing changelog.xml file if one exists from a previous run
> o the use of compression for cvs network traffic (makes a marked difference when bandwidth is limited).
> They are enabled by the properties "maven.changelog.incremental" and "maven.changelog.compression"
> respectively. Both have been defaulted to "false" for the time being, though it would probably
> be worth enabling compression in most cases.
> If the incremental flag is set, a previous changelog.xml will be loaded and parsed into a list
> of entries. These will be reused and the range of dates requested from cvs will be reduced based on
> the last date found in the file. Note that if maven.changelog.range has been changed to a larger value
> than the one used to generate the cached file, it isn't possible to detect this. So if you want to extend the
> range back in time, then the cached file should be removed (e.g. by 'maven clean').
> ChangeLog has been extended to add the functionality for parsing the xml file, retrieving the entries
> from it and for combining the old and newly downloaded entries.
> ChangeLogEntry has been changed to implement the Comparable interface. An equals and hashCode method have
> been added. CvsChangeLogParser has been modified to make use of these methods, rather than using a differently
> formatted key for map entries.
> CvsChangeLogGenerator optionally adds the "-z3" parameter to the command line if compression is enabled.
> Index: plugin.jelly
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/plugin.jelly,v
> retrieving revision 1.8
> diff -u -w -r1.8 plugin.jelly
> --- plugin.jelly 8 Jul 2003 11:01:07 -0000 1.8
> +++ plugin.jelly 11 Jul 2003 17:56:09 -0000
> @@ -58,6 +58,8 @@
> range="${maven.changelog.range}"
> repositoryConnection="${pom.repository.connection}"
> dateFormat="${maven.changelog.dateformat}"
> + incremental="${maven.changelog.incremental}"
> + useCompression="${maven.changelog.compression}"
> />
>
> <doc:jsl
> Index: plugin.properties
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/plugin.properties,v
> retrieving revision 1.1.1.1
> diff -u -w -r1.1.1.1 plugin.properties
> --- plugin.properties 24 Jan 2003 03:44:50 -0000 1.1.1.1
> +++ plugin.properties 11 Jul 2003 17:56:09 -0000
> @@ -10,4 +10,6 @@
> maven.docs.outputencoding = ISO-8859-1
>
> maven.changelog.range = 30
> +maven.changelog.incremental = false
> +maven.changelog.compression = false
> maven.changelog.factory = org.apache.maven.cvslib.CvsChangeLogFactory
> Index: project.xml
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/project.xml,v
> retrieving revision 1.12
> diff -u -w -r1.12 project.xml
> --- project.xml 1 Jul 2003 10:05:46 -0000 1.12
> +++ project.xml 11 Jul 2003 17:56:09 -0000
> @@ -57,6 +57,10 @@
> </developers>
> <dependencies>
> <dependency>
> + <id>dom4j</id>
> + <version>1.4</version>
> + </dependency>
> + <dependency>
> <id>ant</id>
> <version>1.5.1</version>
> <properties>
> Index: src/main/org/apache/maven/changelog/ChangeLog.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/main/org/apache/maven/changelog/ChangeLog.java,v
> retrieving revision 1.4
> diff -u -w -r1.4 ChangeLog.java
> --- src/main/org/apache/maven/changelog/ChangeLog.java 27 Feb 2003 10:30:33 -0000 1.4
> +++ src/main/org/apache/maven/changelog/ChangeLog.java 11 Jul 2003 17:56:10 -0000
> @@ -60,21 +60,36 @@
> import java.io.File;
> import java.io.FileNotFoundException;
> import java.io.FileOutputStream;
> +import java.io.FileInputStream;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> import java.io.IOException;
> import java.io.OutputStreamWriter;
> import java.io.PrintWriter;
> import java.io.UnsupportedEncodingException;
> import java.util.Arrays;
> +import java.util.ArrayList;
> +import java.util.Calendar;
> import java.util.Collection;
> +import java.util.Collections;
> +import java.util.SortedSet;
> +import java.util.TreeSet;
> import java.util.Iterator;
> import java.util.List;
> import java.util.Properties;
> +import java.text.ParseException;
> // commons imports
> import org.apache.commons.logging.Log;
> import org.apache.commons.logging.LogFactory;
> // maven imports
> import org.apache.maven.project.Developer;
>
> +// dom4j, for parsing existing changelog.xml
> +import org.dom4j.Document;
> +import org.dom4j.DocumentException;
> +import org.dom4j.Element;
> +import org.dom4j.io.SAXReader;
> +
> /**
> * Change log task. It uses a ChangeLogGenerator and ChangeLogParser to create
> * a Collection of ChangeLogEntry objects, which are used to produce an XML
> @@ -134,6 +149,15 @@
> private String outputEncoding;
>
> /**
> + * flag to indicate whether an attempt should be made to reuse existing changelog data
> + * (usually target/changelog.xml) rather than querying the server for the full log.
> + */
> + private boolean incremental = false;
> +
> + /** flag to indicate that the compression should be used for network traffic if supported (e.g. cvs -z3) */
> + private boolean useCompression = false;
> +
> + /**
> * Set the ChangeLogFactory class name. If this isn't set, the factory
> * defaults to Maven's build in CVS factory.
> *
> @@ -236,6 +260,34 @@
> }
>
> /**
> + * Returns the value of the incremental flag
> + * @return whether incremental changelog will be generated.
> + */
> + public boolean getIncremental()
> + {
> + return incremental;
> + }
> +
> + /**
> + * Set the incremental flag. If true then an existing
> + * @param incremental set to true if existing data should be used.
> + */
> + public void setIncremental(boolean incremental)
> + {
> + this.incremental = incremental;
> + }
> +
> + public boolean getUseCompression()
> + {
> + return useCompression;
> + }
> +
> + public void setUseCompression(boolean flag)
> + {
> + useCompression = flag;
> + }
> +
> + /**
> * Execute task.
> * @throws FileNotFoundException if {@link ChangeLog#base} doesn't exist
> * @throws IOException if there are problems running CVS
> @@ -249,13 +301,99 @@
> {
> throw new NullPointerException("output must be set");
> }
> + LOG.debug("Output file is " + output.getName() + " exists = "+ output.exists());
>
> + if(incremental && output.exists())
> + {
> + loadCachedEntries();
> + }
> generateEntries();
> - replaceAuthorIdWithName();
> createDocument();
> }
>
> /**
> + * Loads the existing entries which lie within the range setting.
> + */
> + private void loadCachedEntries()
> + {
> + if(range == null || range == null || range.length() == 0)
> + { // bail out: this should only happen if date ranges aren't supported by the VCS.
> + LOG.warn("incremental flag set for changelog, but no range specified");
> + return;
> + }
> + try
> + {
> + // assume the range is a number of days in the log (can it be anything else? L.T.)
> + int nDays = Integer.parseInt(range);
> + Calendar earliest = Calendar.getInstance();
> + earliest.clear(Calendar.HOUR_OF_DAY);
> + earliest.clear(Calendar.MINUTE);
> + earliest.add(Calendar.DATE, -nDays); // nDays ago
> + FileInputStream in = new FileInputStream(output);
> + List cachedEntries = parseXMLEntries(in, earliest);
> + in.close();
> + if(cachedEntries.size() > 0)
> + {
> + Calendar last = Calendar.getInstance();
> + Calendar to = (Calendar) last.clone();
> + last.setTime(((ChangeLogEntry)cachedEntries.get(0)).getDate());
> + to.add(Calendar.HOUR_OF_DAY, 24);
> + // recalculate the number of days needed based on the last entry in the cached log data
> + for(nDays = 0; last.before(to); nDays++) // increment until equal to "to" date
> + {
> + last.add(Calendar.DATE, 1);
> + }
> + range = Integer.toString(nDays);
> + LOG.info("Cached data will be used; resetting range to " + range + " days" );
> + setEntries(cachedEntries);
> + }
> + }
> + catch(Exception e)
> + {
> + LOG.warn("Exception reading existing file " + output.getName() + ": "+ e + ". Full changelog will be generated");
> + }
> + }
> +
> + /**
> + * Parses the XML representation of the changelog entries.
> + * @param in the stream to read from
> + * @param earliest the start of the range. Anything prior to this will be ignored.
> + * @return a list of ChangeLogEnty objects, with the latest entry at the start.
> + * @throws DocumentException
> + * @throws ParseException
> + */
> + private List parseXMLEntries(InputStream in, Calendar earliest) throws DocumentException, ParseException
> + {
> + SAXReader reader = new SAXReader();
> +
> + Document xml = reader.read( new InputStreamReader(in) );
> + Element root = xml.getRootElement();
> + Iterator eltIterator = root.elementIterator();
> + List cachedEntries = new ArrayList();
> + while(eltIterator.hasNext())
> + {
> + Element entryElt = (Element)eltIterator.next();
> + ChangeLogEntry entry = new ChangeLogEntry( entryElt.element("date").getText(),
> + entryElt.element("time").getText(),
> + entryElt.element("author").getText(),
> + entryElt.element("msg").getText() );
> + if(entry.getDate().before(earliest.getTime()))
> + continue;
> + Iterator fileEltIterator = entryElt.elementIterator("file");
> + while(fileEltIterator.hasNext())
> + {
> + Element fileElt = (Element)fileEltIterator.next();
> + ChangeLogFile clFile = new ChangeLogFile( fileElt.element("name").getText(),
> + fileElt.element("revision").getText());
> + entry.addFile(clFile);
> + }
> + cachedEntries.add(entry);
> + }
> + LOG.info("Loaded " + cachedEntries.size() + " cached changelog entries.");
> + return cachedEntries;
> + }
> +
> + /**
> * Create the change log entries.
> * @throws IOException if there is a problem creating the change log
> * entries.
> @@ -271,11 +409,21 @@
>
> try
> {
> - setEntries(generator.getEntries(parser));
> - if (LOG.isInfoEnabled()) {
> - LOG.info("ChangeLog found: " + getEntries().size()
> - + " entries");
> + Collection newEntries = generator.getEntries(parser);
> + // The name substitution must be done here, as it will already
> + // have been applied to any cached entries and the author names
> + // must match for successful equality tests during the merging of
> + // cached and newly downloaded entries.
> + replaceAuthorIdWithName(newEntries);
> + if(incremental)
> + {
> + mergeNewEntries(newEntries);
> }
> + else
> + {
> + setEntries(newEntries);
> + }
> + LOG.info("ChangeLog found: " + getEntries().size() + " entries");
> } catch (IOException e) {
> LOG.warn(e.getLocalizedMessage(), e);
> throw e;
> @@ -288,6 +436,35 @@
> }
>
> /**
> + * Merges newly downloaded changelog entries with
> + * previously the previously cached list. The latter will already
> + * be stored in the 'entries' member variable.
> + * @param newEntries
> + */
> + private void mergeNewEntries(Collection newEntries)
> + {
> + if(getEntries().isEmpty()) // there may have been a problem while loading the existing file
> + {
> + setEntries(newEntries);
> + }
> + else
> + {
> + // Build a set of the existing entries and add the elements from newEntries - the Set should take care
> + // of any duplicates.
> + SortedSet entrySet = new TreeSet(Collections.reverseOrder());
> + entrySet.addAll(getEntries());
> + Iterator newEntryIterator = newEntries.iterator();
> + while( newEntryIterator.hasNext() )
> + {
> + ChangeLogEntry cle = (ChangeLogEntry)newEntryIterator.next();
> + entrySet.add(cle);
> + }
> + setEntries(entrySet);
> + }
> + }
> +
> +
> + /**
> * Create a new instance of the ChangeLogFactory specified by the
> * <code>clFactory</code> member.
> *
> @@ -339,12 +516,13 @@
>
> /**
> * replace all known author's id's with their maven specified names
> + * @param entrySet the set of entries to apply perform the substitution on.
> */
> - private void replaceAuthorIdWithName()
> + private void replaceAuthorIdWithName(Collection entrySet)
> {
> Properties userList = getUserList();
> ChangeLogEntry entry = null;
> - for (Iterator i = getEntries().iterator(); i.hasNext();)
> + for (Iterator i = entrySet.iterator(); i.hasNext();)
> {
> entry = (ChangeLogEntry) i.next();
> if (userList.containsKey(entry.getAuthor()))
> Index: src/main/org/apache/maven/changelog/ChangeLogEntry.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/main/org/apache/maven/changelog/ChangeLogEntry.java,v
> retrieving revision 1.2
> diff -u -w -r1.2 ChangeLogEntry.java
> --- src/main/org/apache/maven/changelog/ChangeLogEntry.java 4 Jul 2003 16:24:46 -0000 1.2
> +++ src/main/org/apache/maven/changelog/ChangeLogEntry.java 11 Jul 2003 17:56:10 -0000
> @@ -70,7 +70,7 @@
> * @author <a href="mailto:dion@multitask.com.au">dIon Gillard</a>
> * @version $Id: ChangeLogEntry.java,v 1.2 2003/07/04 16:24:46 evenisse Exp $
> */
> -public class ChangeLogEntry
> +public class ChangeLogEntry implements Comparable
> {
> /**
> * Escaped <code><</code> entity
> @@ -110,12 +110,18 @@
> new SimpleDateFormat("HH:mm:ss");
>
> /**
> + * Formatter used by the constructor which takes
> + * date and time arguments matching the two formats above.
> + */
> + private static final SimpleDateFormat DATE_TIME_FORMAT =
> + new SimpleDateFormat("yyyy-MM-ddHH:mm:ss");
> + /**
> * Formatter used to parse CVS date/timestamp.
> */
> private static final SimpleDateFormat CVS_TIMESTAMP_FORMAT =
> new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
>
> - /** Date the changes were committed */
> + /** Date the changes were committed (containing both date and time information) */
> private Date date;
>
> /** User who made changes */
> @@ -142,6 +148,22 @@
> }
>
> /**
> + * Creates an entry from previously formatted date and time parameters.
> + * (e.g. from an output XML file).
> + * @param date an absolute date value (yyyy-MM-dd)
> + * @param time the time of day within that date (HH:mm:ss)
> + * @param author author's name
> + * @param comment the commit message for the entry
> + */
> + public ChangeLogEntry(String date, String time, String author, String comment) throws ParseException
> + {
> + Date dateTime = DATE_TIME_FORMAT.parse(date.trim() + time.trim());
> + setDate(dateTime);
> + setAuthor(author);
> + setComment(comment);
> + }
> +
> + /**
> * Constructor used when attributes aren't available until later
> */
> public ChangeLogEntry()
> @@ -285,7 +307,6 @@
> throw new IllegalArgumentException("I don't understand this date: "
> + date);
> }
> -
> }
>
> /**
> @@ -345,5 +366,46 @@
> }
> }
> return buffer.toString();
> + }
> +
> + /**
> + * Compares for equality based on date, author and message.
> + * Note that the changelog XML file which is written by the plugin
> + * has the user Ids converted to names, so a direct comparison with
> + * the server log entries will fail.
> + *
> + * @param o the object to compare
> + * @return true if the objects have the same date/time author and message.
> + */
> + public boolean equals(Object o)
> + {
> + if(!(o instanceof ChangeLogEntry))
> + return false;
> + return (compareTo(o) == 0);
> + }
> +
> + /**
> + * Implementation of {@link Comparable} interface.
> + * Uses {@link String} representations to do the comparison.
> + */
> + public int compareTo(Object o)
> + {
> + ChangeLogEntry other =(ChangeLogEntry)o;
> + return encodeAsString().compareTo(other.encodeAsString());
> + }
> +
> + public int hashCode()
> + {
> + return encodeAsString().hashCode();
> + }
> +
> + /**
> + * Convenience method for creating a string rep for hashcodes, comparators etc.
> + * (toString has already been written but is probably overkill for this).
> + * @return unique String representation of this object.
> + */
> + private String encodeAsString()
> + {
> + return DATE_TIME_FORMAT.format(getDate()) + getAuthor() + getComment();
> }
> }
> Index: src/main/org/apache/maven/cvslib/CvsChangeLogGenerator.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/main/org/apache/maven/cvslib/CvsChangeLogGenerator.java,v
> retrieving revision 1.6
> diff -u -w -r1.6 CvsChangeLogGenerator.java
> --- src/main/org/apache/maven/cvslib/CvsChangeLogGenerator.java 11 Apr 2003 18:53:19 -0000 1.6
> +++ src/main/org/apache/maven/cvslib/CvsChangeLogGenerator.java 11 Jul 2003 17:56:10 -0000
> @@ -113,6 +113,11 @@
> Commandline command = new Commandline();
>
> command.setExecutable("cvs");
> + // null check is needed as super.init(ChangeLog) may not have been called (as in the ExposeGenerator test class)
> + if(changeLogExecutor != null && changeLogExecutor.getUseCompression())
> + {
> + command.createArgument().setValue("-z3");
> + }
> command.createArgument().setValue("-d");
> // from format:
> // scm:cvs:pserver:anoncvs@cvs.apache.org:/home/cvspublic:jakarta-turbine-maven/src/plugins-build/changelog/
> @@ -181,10 +186,7 @@
> if (ioe.getMessage().indexOf("CreateProcess") != -1 || ioe.getMessage().indexOf("cvs: not found") != -1)
> {
> // can't find CVS on Win32 or Linux...
> - if (LOG.isWarnEnabled())
> - {
> LOG.warn("Unable to find cvs executable. " + "Changelog will be empty");
> - }
> }
> else
> {
> Index: src/main/org/apache/maven/cvslib/CvsChangeLogParser.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/main/org/apache/maven/cvslib/CvsChangeLogParser.java,v
> retrieving revision 1.1.1.1
> diff -u -w -r1.1.1.1 CvsChangeLogParser.java
> --- src/main/org/apache/maven/cvslib/CvsChangeLogParser.java 24 Jan 2003 03:44:53 -0000 1.1.1.1
> +++ src/main/org/apache/maven/cvslib/CvsChangeLogParser.java 11 Jul 2003 17:56:11 -0000
> @@ -81,13 +81,6 @@
> class CvsChangeLogParser implements ChangeLogParser
> {
> /**
> - * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest
> - * minute.
> - */
> - private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT =
> - new SimpleDateFormat("yyyyMMddHHmm");
> -
> - /**
> * rcs entries, in reverse (date, time, author, comment) order
> */
> private Map entries = new TreeMap(Collections.reverseOrder());
> @@ -203,17 +196,14 @@
> return;
> }
>
> - String key = ENTRY_KEY_TIMESTAMP_FORMAT.format(entry.getDate())
> - + entry.getAuthor() + entry.getComment();
> -
> - if (!entries.containsKey(key))
> + if (!entries.containsKey(entry))
> {
> entry.addFile(file);
> - entries.put(key, entry);
> + entries.put(entry, entry);
> }
> else
> {
> - ChangeLogEntry existingEntry = (ChangeLogEntry) entries.get(key);
> + ChangeLogEntry existingEntry = (ChangeLogEntry) entries.get(entry);
> existingEntry.addFile(file);
> }
> }
> Index: src/test/org/apache/maven/changelog/ChangeLogEntryTest.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/test/org/apache/maven/changelog/ChangeLogEntryTest.java,v
> retrieving revision 1.1.1.1
> diff -u -w -r1.1.1.1 ChangeLogEntryTest.java
> --- src/test/org/apache/maven/changelog/ChangeLogEntryTest.java 24 Jan 2003 03:44:56 -0000 1.1.1.1
> +++ src/test/org/apache/maven/changelog/ChangeLogEntryTest.java 11 Jul 2003 17:56:11 -0000
> @@ -241,7 +241,6 @@
> cal.getTime(), instance.getDate());
> }
>
> -
> /**
> * Test of getDateFormatted method
> */
> @@ -260,6 +259,25 @@
> instance.getTimeFormatted());
> }
>
> + /**
> + * Tests the constructor which takes ready-formatted date and time Strings.
> + * Then checks for equality with the original instance.
> + * @throws Exception java.text.ParseException if formats are incorrect
> + */
> + public void testDateTimeConstructorAndEquals() throws Exception
> + {
> + ChangeLogEntry instance2 = new ChangeLogEntry("2002-04-01", "00:00:00", "dion", "comment");
> + assertEquals("Instances not equal with same data,", instance, instance2 );
> + }
> +
> +
> + public void testComparable() throws Exception
> + {
> + ChangeLogEntry instance2 = new ChangeLogEntry("2002-04-01", "00:00:00", "dion", "comment");
> + assertTrue("Comparator failed", instance2.compareTo(instance) == 0);
> + instance2 = new ChangeLogEntry("2002-04-01", "00:00:05", "dion", "comment");
> + assertTrue("Comparator failed", instance2.compareTo(instance) > 0);
> + }
> // Add test methods here, they have to start with 'test' name.
> // for example:
> // public void testHello() {}
> Index: src/test/org/apache/maven/cvslib/CvsChangeLogGeneratorTest.java
> ===================================================================
> RCS file: /home/cvspublic/maven/src/plugins-build/changelog/src/test/org/apache/maven/cvslib/CvsChangeLogGeneratorTest.java,v
> retrieving revision 1.5
> diff -u -w -r1.5 CvsChangeLogGeneratorTest.java
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://jira.codehaus.org/secure/Administrators.jspa
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@maven.apache.org
For additional commands, e-mail: dev-help@maven.apache.org