qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ritch...@apache.org
Subject svn commit: r891340 - in /qpid/branches/0.5.x-dev: ./ qpid/java/ qpid/java/broker/etc/log4j.xml qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
Date Wed, 16 Dec 2009 17:33:34 GMT
Author: ritchiem
Date: Wed Dec 16 17:33:33 2009
New Revision: 891340

URL: http://svn.apache.org/viewvc?rev=891340&view=rev
Log:
Merged from trunk r891322:891332

QPID-2274 : Addressed initial issues with Async compressing deleting log files
QPID-2275: Update so that CountDirection +ve,-ve work correctly in conjunction with exiting
log files and MaxSizeRollBackups
Adding .n to the end of the name and calculating the .n value based on existing log files.
QPID-2275 : Update to address CountDirection 0 increments when MaxFileSize kicks in before
DatePattern.
Now it is possible to specify a DatePattern with small units (seconds/minutes) and not lose
log file date when the MaxFileSize causes the file to roll over
QPID-2155 : Update addressing issues with compression and maintaining backup counts.
Because a compressed file is renamed the .n value is not recognised so this update makes it
check for the COMPRESSION_EXTENSION (.gz)
QPID-2155 : Added NPE checks for path errors, updated to check the backup location for existing
backup files to initialise the .n count
QPID-2155 : Updated log4j file with more sensible default values for the QpidRollingFileAppender
appender.
Also added more local documentation explaining the more obscure params


Modified:
    qpid/branches/0.5.x-dev/   (props changed)
    qpid/branches/0.5.x-dev/qpid/java/   (props changed)
    qpid/branches/0.5.x-dev/qpid/java/broker/etc/log4j.xml
    qpid/branches/0.5.x-dev/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java

Propchange: qpid/branches/0.5.x-dev/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Dec 16 17:33:33 2009
@@ -1 +1 @@
-/qpid/trunk:887948,887950-887951,887994,888345
+/qpid/trunk:887948,887950-887951,887994,888345,889645,891323-891332

Propchange: qpid/branches/0.5.x-dev/qpid/java/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Dec 16 17:33:33 2009
@@ -1,2 +1,2 @@
 /qpid/branches/java-broker-0-10/qpid/java:829414,829575
-/qpid/trunk/qpid/java:835115,884634-884635,884838,885765,887948,887950-887952,887994,888246,888248,888250,888345,888348,889645
+/qpid/trunk/qpid/java:835115,884634-884635,884838,885765,887948,887950-887952,887994,888246,888248,888250,888345,888348,889645,891323-891332

Modified: qpid/branches/0.5.x-dev/qpid/java/broker/etc/log4j.xml
URL: http://svn.apache.org/viewvc/qpid/branches/0.5.x-dev/qpid/java/broker/etc/log4j.xml?rev=891340&r1=891339&r2=891340&view=diff
==============================================================================
--- qpid/branches/0.5.x-dev/qpid/java/broker/etc/log4j.xml (original)
+++ qpid/branches/0.5.x-dev/qpid/java/broker/etc/log4j.xml Wed Dec 16 17:33:33 2009
@@ -22,30 +22,43 @@
 
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="null" threshold="null">
     <appender class="org.apache.log4j.QpidCompositeRollingAppender" name="ArchivingFileAppender">
-        <!-- Ensure that logs allways have the dateFormat set-->
-        <param name="StaticLogFileName" value="false"/>
-        <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
-        <param name="Append" value="false"/>
+        <!-- Ensure that logs allways have the dateFormat set Default: TRUE-->
+        <param name="StaticLogFileName" value="true"/>
+	<param name="file" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
+	<!-- Style of rolling to use, by:
+	        File Size (1), 
+		Date(2), 
+		Both(3) - DEFAULT
+	   When Date (or Both) is enabled then the value of DatePattern will determine
+	   when the new file is made. e.g. a DatePattern of "'.'yyyy-MM-dd-HH-mm"
+	   which includes minutes will cause a new backup file to be made every minute.
+	 -->
+	<param name="RollingStyle" value="1"/>
         <!-- Change the direction so newer files have bigger numbers -->
-        <!-- So log.1 is written then log.2 etc This prevents a lot of file renames at
log rollover -->
-        <param name="CountDirection" value="1"/>
-        <!-- Use default 10MB -->
-        <!--param name="MaxFileSize" value="100000"/-->
+        <!-- 
+	 negative means backups become <latest>,.0,.1,2,...,n	 	
+	 0 means backup name is date stampted and follow Positive number if DataPattern clashes.
+	 Positive means backup becomes <lastest,n,n-1,n-2,..0
+	 
+	 Default is negative.
+	 -->	 	 
+        <param name="CountDirection" value="0"/>
+        <!-- Use default 1MB -->
+        <param name="MaxFileSize" value="1MB"/>
         <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm"/>
-        <!-- Unlimited number of backups -->
+        <!-- Unlimited number of backups : Default: 0, no backups, -1 infinite -->
         <param name="MaxSizeRollBackups" value="-1"/>
-        <!-- Compress(gzip) the backup files-->
+        <!-- Compress(gzip) the backup files default:FALSE-->
         <param name="CompressBackupFiles" value="true"/>
-        <!-- Compress the backup files using a second thread -->
+        <!-- Compress the backup files using a second thread  DEFAULT: FALSE-->
         <param name="CompressAsync" value="true"/>
-        <!-- Start at zero numbered files-->
-        <param name="ZeroBased" value="true"/>
-        <!-- Backup Location -->
-        <param name="backupFilesToPath" value="${QPID_WORK}/backup/log"/>
+        <!-- Backup Location : Default same dir as log file -->
+	<param name="backupFilesToPath" value="${QPID_WORK}/backup/log"/>
+
 
         <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
-        </layout>
+        </layout>`
     </appender>
 
     <appender class="org.apache.log4j.FileAppender" name="FileAppender">

Modified: qpid/branches/0.5.x-dev/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
URL: http://svn.apache.org/viewvc/qpid/branches/0.5.x-dev/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java?rev=891340&r1=891339&r2=891340&view=diff
==============================================================================
--- qpid/branches/0.5.x-dev/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
(original)
+++ qpid/branches/0.5.x-dev/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java
Wed Dec 16 17:33:33 2009
@@ -157,6 +157,7 @@
     protected String backupFilesToPath = null;
     private final ConcurrentLinkedQueue<CompressJob> _compress = new ConcurrentLinkedQueue<CompressJob>();
     private AtomicBoolean _compressing = new AtomicBoolean(false);
+    private static final String COMPRESS_EXTENSION = ".gz";
 
     /** The default constructor does nothing. */
     public QpidCompositeRollingAppender()
@@ -370,10 +371,6 @@
         if (!staticLogFileName)
         {
             scheduledFilename = fileName = fileName.trim() + sdf.format(now);
-            if (countDirection > 0)
-            {
-                scheduledFilename = fileName = fileName + '.' + (++curSizeRollBackups);
-            }
         }
 
         super.setFile(fileName, append, bufferedIO, bufferSize);
@@ -513,75 +510,10 @@
      */
     protected void existingInit()
     {
-
-        if (zeroBased)
-        {
-            curSizeRollBackups = -1;
-        }
-
         curTimeRollBackups = 0;
 
         // part A starts here
-        String filter;
-        if (staticLogFileName || !rollDate)
-        {
-            filter = baseFileName + ".*";
-        }
-        else
-        {
-            filter = scheduledFilename + ".*";
-        }
-
-        File f = new File(baseFileName);
-        f = f.getParentFile();
-        if (f == null)
-        {
-            f = new File(".");
-        }
-
-        LogLog.debug("Searching for existing files in: " + f);
-        String[] files = f.list();
-
-        if (files != null)
-        {
-            for (int i = 0; i < files.length; i++)
-            {
-                if (!files[i].startsWith(baseFileName))
-                {
-                    continue;
-                }
-
-                int index = files[i].lastIndexOf(".");
-
-                if (staticLogFileName)
-                {
-                    int endLength = files[i].length() - index;
-                    if ((baseFileName.length() + endLength) != files[i].length())
-                    {
-                        // file is probably scheduledFilename + .x so I don't care
-                        continue;
-                    }
-                }
-
-                try
-                {
-                    int backup = Integer.parseInt(files[i].substring(index + 1, files[i].length()));
-                    LogLog.debug("From file: " + files[i] + " -> " + backup);
-                    if (backup > curSizeRollBackups)
-                    {
-                        curSizeRollBackups = backup;
-                    }
-                }
-                catch (Exception e)
-                {
-                    // this happens when file.log -> file.log.yyyy-mm-dd which is normal
-                    // when staticLogFileName == false
-                    LogLog.debug("Encountered a backup file not ending in .x " + files[i]);
-                }
-            }
-        }
-
-        LogLog.debug("curSizeRollBackups starts at: " + curSizeRollBackups);
+        // This is now down at first log when curSizeRollBackup==0 see rollFile
         // part A ends here
 
         // part B not yet implemented
@@ -663,55 +595,11 @@
 
         this.closeFile(); // keep windows happy.
 
-        // delete the old stuff here
 
-        if (staticLogFileName)
-        {
-            /* Compute filename, but only if datePattern is specified */
-            if (datePattern == null)
-            {
-                errorHandler.error("Missing DatePattern option in rollOver().");
-
-                return;
-            }
-
-            // is the new file name equivalent to the 'current' one
-            // something has gone wrong if we hit this -- we should only
-            // roll over if the new file will be different from the old
-            String dateFormat = sdf.format(now);
-            if (scheduledFilename.equals(fileName + dateFormat))
-            {
-                errorHandler.error("Compare " + scheduledFilename + " : " + fileName + dateFormat);
-
-                return;
-            }
-
-            // close current file, and rename it to datedFilename
-            this.closeFile();
-
-            // we may have to roll over a large number of backups here
-            String from, to;
-            for (int i = 1; i <= curSizeRollBackups; i++)
-            {
-                from = fileName + '.' + i;
-                to = scheduledFilename + '.' + i;
-                rollFile(from, to, false);
-            }
-
-            rollFile(fileName, scheduledFilename, compress);
-        }
-        else
-        {
-            if (compress)
-            {
-                compress(fileName);
-            }
-        }
+        rollFile();
 
         try
         {
-            // This will also close the file. This is OK since multiple
-            // close operations are safe.
             curSizeRollBackups = 0; // We're cleared out the old date and are ready for the
new
 
             // new scheduled name
@@ -735,55 +623,46 @@
         {
             if (compress)
             {
-                LogLog.debug("Attempting to compress file with same output name.");
+                LogLog.error("Attempting to compress file with same output name.");
             }
 
             return;
         }
 
         File target = new File(to);
-        if (target.exists())
-        {
-            LogLog.debug("deleting existing target file: " + target);
-            target.delete();
-        }
 
         File file = new File(from);
-        if (compress)
+        // Perform Roll by renaming
+        if (!file.getPath().equals(target.getPath()))
         {
-            compress(file, target);
+            file.renameTo(target);
         }
-        else
+
+        // Compress file after it has been moved out the way... this is safe
+        // as it will gain a .gz ending and we can then safely delete this file
+        // as it will not be the statically named value.
+        if (compress)
         {
-            if (!file.getPath().equals(target.getPath()))
-            {
-                file.renameTo(target);
-            }
+            compress(target);
         }
 
         LogLog.debug(from + " -> " + to);
     }
 
-    protected void compress(String file)
-    {
-        File f = new File(file);
-        compress(f, f);
-    }
-
-    private void compress(File from, File target)
+    private void compress(File target)
     {
         if (compressAsync)
         {
             synchronized (_compress)
             {
-                _compress.offer(new CompressJob(from, target));
+                _compress.offer(new CompressJob(target, target));
             }
 
             startCompression();
         }
         else
         {
-            doCompress(from, target);
+            doCompress(target, target);
         }
     }
 
@@ -796,9 +675,9 @@
     }
 
     /** Delete's the specified file if it exists */
-    protected static void deleteFile(String fileName)
+    protected void deleteFile(String fileName)
     {
-        File file = new File(fileName);
+        File file = compress ? new File(fileName + COMPRESS_EXTENSION) : new File(fileName);
         if (file.exists())
         {
             file.delete();
@@ -837,69 +716,277 @@
         // If maxBackups <= 0, then there is no file renaming to be done.
         if (maxSizeRollBackups != 0)
         {
+            rollFile();
+        }
 
-            if (countDirection < 0)
+        try
+        {
+            // This will also close the file. This is OK since multiple
+            // close operations are safe.
+            this.setFile(baseFileName, false);
+        }
+        catch (IOException e)
+        {
+            LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+        }
+    }
+
+    /**
+     * Perform file Rollover ensuring the countDirection is applied along with
+     * the other options
+     */
+    private void rollFile()
+    {
+        LogLog.debug("CD="+countDirection+",start");
+        if (countDirection < 0)
+        {
+            // If we haven't rolled yet then validate we have the right value
+            // for curSizeRollBackups
+            if (curSizeRollBackups == 0)
+            {
+                //Validate curSizeRollBackups
+                curSizeRollBackups = countFileIndex(fileName);
+                // decrement to offset the later increment
+                curSizeRollBackups--;
+            }
+
+            // If we are not keeping an infinite set of backups the delete oldest
+            if (maxSizeRollBackups > 0)
             {
-                // Delete the oldest file, to keep Windows happy.
-                if (curSizeRollBackups == maxSizeRollBackups)
+                LogLog.debug("CD=-1,curSizeRollBackups:"+curSizeRollBackups);
+                LogLog.debug("CD=-1,maxSizeRollBackups:"+maxSizeRollBackups);
+
+                // Delete the oldest file.
+                // curSizeRollBackups is never -1 so infinite backups are ok here
+                if ((curSizeRollBackups - maxSizeRollBackups) >= 0)
                 {
-                    deleteFile(fileName + '.' + maxSizeRollBackups);
+                    //The oldest file is the one with the largest number
+                    // as the 0 is always fileName
+                    // which moves to fileName.1 etc.
+                    LogLog.debug("CD=-1,deleteFile:"+curSizeRollBackups);
+                    deleteFile(fileName + '.' + curSizeRollBackups);
+                    // decrement to offset the later increment
                     curSizeRollBackups--;
                 }
+            }
+            // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+            for (int i = curSizeRollBackups; i >= 1; i--)
+            {
+                String oldName = (fileName + "." + i);
+                String newName = (fileName + '.' + (i + 1));
 
-                // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
-                for (int i = curSizeRollBackups; i >= 1; i--)
+                // Ensure that when compressing we rename the compressed archives
+                if (compress)
+                {
+                    rollFile(oldName + COMPRESS_EXTENSION, newName + COMPRESS_EXTENSION,
false);
+                }
+                else
                 {
-                    rollFile((fileName + "." + i), (fileName + '.' + (i + 1)), false);
+                    rollFile(oldName, newName, false);
                 }
+            }
+
+            curSizeRollBackups++;
+            // Rename fileName to fileName.1
+            rollFile(fileName, fileName + ".1", compress);
+
+        } // REMOVE This code branching for Alexander Cerna's request
+        else if (countDirection == 0)
+        {
+            // rollFile based on date pattern
+            now.setTime(System.currentTimeMillis());
+            String newFile = fileName + sdf.format(now);
+            
+            // If we haven't rolled yet then validate we have the right value
+            // for curSizeRollBackups
+            if (curSizeRollBackups == 0)
+            {
+                //Validate curSizeRollBackups
+                curSizeRollBackups = countFileIndex(newFile);
+                // to balance the increment just coming up. as the count returns
+                // the next free number not the last used.
+                curSizeRollBackups--;
+            }
+
+            // If we are not keeping an infinite set of backups the delete oldest
+            if (maxSizeRollBackups > 0)
+            {
+                // Don't prune older files if they exist just go for the last
+                // one based on our maxSizeRollBackups. This means we may have
+                // more files left on disk that maxSizeRollBackups if this value
+                // is adjusted between runs but that is an acceptable state.
+                // Otherwise we would have to check on startup that we didn't
+                // have more than maxSizeRollBackups and prune then.
 
-                curSizeRollBackups++;
-                // Rename fileName to fileName.1
-                rollFile(fileName, fileName + ".1", compress);
-
-            } // REMOVE This code branching for Alexander Cerna's request
-            else if (countDirection == 0)
-            {
-                // rollFile based on date pattern
-                curSizeRollBackups++;
-                now.setTime(System.currentTimeMillis());
-                scheduledFilename = fileName + sdf.format(now);
-                rollFile(fileName, scheduledFilename, compress);
-            }
-            else
-            { // countDirection > 0
-                if ((curSizeRollBackups >= maxSizeRollBackups) && (maxSizeRollBackups
> 0))
+                if (((curSizeRollBackups - maxSizeRollBackups) >= 0))
                 {
-                    // delete the first and keep counting up.
+                    LogLog.debug("CD=0,curSizeRollBackups:"+curSizeRollBackups);
+                    LogLog.debug("CD=0,maxSizeRollBackups:"+maxSizeRollBackups);
+
+                    // delete the first and keep counting up.                           
                                                                   
                     int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1;
-                    deleteFile(fileName + '.' + oldestFileIndex);
+                    LogLog.debug("CD=0,deleteFile:"+oldestFileIndex);
+                    deleteFile(newFile + '.' + oldestFileIndex);
                 }
+            }
+
+
+            String finalName = newFile;
+
+            curSizeRollBackups++;             
+
+            // Add rollSize if it is > 0
+            if (curSizeRollBackups > 0 ) 
+            {
+                finalName = newFile + '.' + curSizeRollBackups;
+
+            }
+
+            rollFile(fileName, finalName, compress);
+        }
+        else
+        { // countDirection > 0
+            // If we haven't rolled yet then validate we have the right value
+            // for curSizeRollBackups
+            if (curSizeRollBackups == 0)
+            {
+                //Validate curSizeRollBackups
+                curSizeRollBackups = countFileIndex(fileName);
+                // to balance the increment just coming up. as the count returns
+                // the next free number not the last used.
+                curSizeRollBackups--;
+            }
+
+            // If we are not keeping an infinite set of backups the delete oldest
+            if (maxSizeRollBackups > 0)
+            {
+                LogLog.debug("CD=1,curSizeRollBackups:"+curSizeRollBackups);
+                LogLog.debug("CD=1,maxSizeRollBackups:"+maxSizeRollBackups);
+
+                // Don't prune older files if they exist just go for the last
+                // one based on our maxSizeRollBackups. This means we may have
+                // more files left on disk that maxSizeRollBackups if this value
+                // is adjusted between runs but that is an acceptable state.
+                // Otherwise we would have to check on startup that we didn't
+                // have more than maxSizeRollBackups and prune then.
 
-                if (staticLogFileName)
+                if (((curSizeRollBackups - maxSizeRollBackups) >= 0))
                 {
-                    curSizeRollBackups++;
-                    rollFile(fileName, fileName + '.' + curSizeRollBackups, compress);
+                    // delete the first and keep counting up.
+                    int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1;
+                    LogLog.debug("CD=1,deleteFile:"+oldestFileIndex);
+                    deleteFile(fileName + '.' + oldestFileIndex);
                 }
-                else
+            }
+
+
+            curSizeRollBackups++;
+
+            rollFile(fileName, fileName + '.' + curSizeRollBackups, compress);
+
+        }
+        LogLog.debug("CD="+countDirection+",done");
+    }
+
+
+    private int countFileIndex(String fileName)
+    {
+        return countFileIndex(fileName, true);
+    }
+    /**
+     * Use filename as a base name and find what count number we are up to by
+     * looking at the files in this format:
+     *
+     * <filename>.<count>[COMPRESS_EXTENSION]
+     *
+     * If a count value of 1 cannot be found then a directory listing is
+     * performed to try and identify if there is a valid value for <count>.
+     * 
+     *
+     * @param fileName the basefilename to use
+     * @param checkBackupLocation should backupFilesToPath location be checked for existing
backups
+     * @return int the next free index
+     */
+    private int countFileIndex(String fileName, boolean checkBackupLocation)
+    {
+        String testFileName;
+
+        // It is possible for index 1..n to be missing leaving n+1..n+1+m logs
+        // in this scenario we should still return n+1+m+1
+        int index=1;
+
+        testFileName = fileName + "." + index;
+
+        // Bail out early if there is a problem with the file
+        if (new File(testFileName) == null
+                || new File(testFileName + COMPRESS_EXTENSION) == null)
+
+        {
+            return index;
+        }
+
+        // Check that we do not have the 1..n missing scenario
+        if (!(new File(testFileName).exists()
+              || new File(testFileName + COMPRESS_EXTENSION).exists()))
+
+        {
+            int max=0;
+            String prunedFileName = new File(fileName).getName();
+
+            // Look through all files to find next index
+            if (new File(fileName).getParentFile() != null)
+            {
+                for (File file : new File(fileName).getParentFile().listFiles())
                 {
-                    if (compress)
+                    String name = file.getName();
+
+                    if (name.startsWith(prunedFileName) && !name.equals(prunedFileName))
                     {
-                        compress(fileName);
+                        String parsedCount = name.substring(prunedFileName.length() + 1);
+
+                        if (parsedCount.endsWith(COMPRESS_EXTENSION))
+                        {
+                            parsedCount = parsedCount.substring(0, parsedCount.indexOf(COMPRESS_EXTENSION));
+                        }
+
+                        try
+                        {
+                            max = Integer.parseInt(parsedCount);
+
+                            // if we got a good value then update our index value.
+                            if (max > index)
+                            {
+                                // +1 as we want to return the next free value.
+                                index = max + 1;
+                            }
+                        }
+                        catch (NumberFormatException nfe)
+                        {
+                            //ignore it assume file doesn't exist.
+                        }
                     }
                 }
             }
+
+            // Update testFileName 
+            testFileName = fileName + "." + index;
         }
 
-        try
+
+        while (new File(testFileName).exists()
+                || new File(testFileName + COMPRESS_EXTENSION).exists())
         {
-            // This will also close the file. This is OK since multiple
-            // close operations are safe.
-            this.setFile(baseFileName, false);
+            index++;
+            testFileName = fileName + "." + index;
         }
-        catch (IOException e)
+
+        if (checkBackupLocation && index == 1 && backupFilesToPath != null)
         {
-            LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+            LogLog.debug("Trying backup location:"+backupFilesToPath + System.getProperty("file.separator")
+ fileName);
+            return countFileIndex(backupFilesToPath + System.getProperty("file.separator")
+ new File(fileName).getName(), false);
         }
+
+        return index;
     }
 
     protected synchronized void doCompress(File from, File to)
@@ -907,11 +994,11 @@
         String toFile;
         if (backupFilesToPath == null)
         {
-            toFile = to.getPath() + ".gz";
+            toFile = to.getPath() + COMPRESS_EXTENSION;
         }
         else
         {
-            toFile = backupFilesToPath + System.getProperty("file.separator") + to.getName()
+ ".gz";
+            toFile = backupFilesToPath + System.getProperty("file.separator") + to.getName()
+ COMPRESS_EXTENSION;
         }
 
         File target = new File(toFile);



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org


Mime
View raw message