Details

    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Minor Minor
    • Resolution: Won't Fix
    • Affects Version/s: None
    • Fix Version/s: None
    • Labels:
      None
    • Environment:
      n/a
    • Number of attachments :
      1

      Description

      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

      1. patch
        21 kB
        Luke Taylor

        Activity

        Hide
        Luke Taylor added a comment -

        The patch file...

        Show
        Luke Taylor added a comment - The patch file...
        Hide
        Michael Osipov added a comment -

        Please refer to https://cwiki.apache.org/confluence/display/MAVEN/The+Great+JIRA+Cleanup+of+2014 if you're wondering why this issue was closed out.

        Show
        Michael Osipov added a comment - Please refer to https://cwiki.apache.org/confluence/display/MAVEN/The+Great+JIRA+Cleanup+of+2014 if you're wondering why this issue was closed out.

          People

          • Assignee:
            Unassigned
            Reporter:
            Luke Taylor
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: