avalon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From l...@apache.org
Subject cvs commit: avalon-excalibur/fortress/src/test/org/apache/avalon/fortress/util/dag/test VertexTestCase.java DirectedAcyclicGraphVerifierTestCase.java VertexTest.java
Date Wed, 28 May 2003 17:50:21 GMT
leif        2003/05/28 10:50:21

  Modified:    fortress/src/java/org/apache/avalon/fortress/impl
                        AbstractContainer.java
               fortress/src/java/org/apache/avalon/fortress/impl/lookup
                        FortressServiceSelector.java
               fortress/src/java/org/apache/avalon/fortress/util/dag
                        CyclicDependencyException.java
                        DirectedAcyclicGraphVerifier.java
               fortress/src/test/org/apache/avalon/fortress/util/dag/test
                        DirectedAcyclicGraphVerifierTestCase.java
  Added:       fortress/src/test/org/apache/avalon/fortress/util/dag/test
                        VertexTestCase.java
  Removed:     fortress/src/test/org/apache/avalon/fortress/util/dag/test
                        VertexTest.java
  Log:
  Rework the way the Directed Graph is validated and sorted so it works correctly.
  This new method also maintains all of the information necessary to be able to
  display a useful description of any cyclic loops in the graph if they are detected.
  
  Revision  Changes    Path
  1.30      +59 -15    avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/impl/AbstractContainer.java
  
  Index: AbstractContainer.java
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/impl/AbstractContainer.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- AbstractContainer.java	27 May 2003 18:39:46 -0000	1.29
  +++ AbstractContainer.java	28 May 2003 17:50:20 -0000	1.30
  @@ -655,14 +655,25 @@
   
       private void verifyComponents() throws CyclicDependencyException
       {
  -        List verteces = new ArrayList(m_components.size());
  +        Map vertexMap = new HashMap();
  +        List vertices = new ArrayList( m_components.size() );
           Iterator it = m_components.iterator();
  -
  +        
           while(it.hasNext())
           {
               ComponentHandlerEntry entry = (ComponentHandlerEntry) it.next();
  -            Vertex v = new Vertex(entry.getHandler());
  -            MetaInfoEntry meta = m_metaManager.getMetaInfoForClassname(entry.getMetaData().getClassname());
  +            ComponentHandlerMetaData metaData = entry.getMetaData();
  +            
  +            String name = metaData.getName();
  +            Vertex v = (Vertex)vertexMap.get( name );
  +            if ( v == null )
  +            {
  +                v = new Vertex( name, entry.getHandler() );
  +                vertexMap.put( name, v );
  +                vertices.add( v );
  +            }
  +            
  +            MetaInfoEntry meta = m_metaManager.getMetaInfoForClassname(metaData.getClassname());
   
               Iterator dit = meta.getDependencies().iterator();
               while(dit.hasNext())
  @@ -672,21 +683,42 @@
                   while(mdit.hasNext())
                   {
                       Map.Entry depEntry = (Map.Entry)mdit.next();
  -
  -                    if ( ! depEntry.getKey().equals(DEFAULT_ENTRY) ||
  -                         ! depEntry.getKey().equals(SELECTOR_ENTRY))
  +                    
  +                    // If this key is neither the DEFAULT_ENTRY or the SELECTOR_ENTRY then
we
  +                    //  want to add a dependency vertex.
  +                    if ( ! ( depEntry.getKey().equals( DEFAULT_ENTRY ) ||
  +                        depEntry.getKey().equals( SELECTOR_ENTRY ) ) )
                       {
  -                        v.addDependency(new Vertex(depEntry.getValue()));
  +                        String dName = depEntry.getKey().toString();
  +                        Vertex dv = (Vertex)vertexMap.get( dName );
  +                        if ( dv == null )
  +                        {
  +                            dv = new Vertex( dName, depEntry.getValue() );
  +                            vertexMap.put( dName, dv );
  +                            vertices.add( dv );
  +                        }
  +                        v.addDependency( dv );
                       }
                   }
               }
  -
  -            verteces.add(v);
           }
  -
  -        DirectedAcyclicGraphVerifier.topologicalSort(verteces);
  -        Collections.reverse(verteces);
  -        m_shutDownOrder = verteces;
  +        
  +        DirectedAcyclicGraphVerifier.topologicalSort(vertices);
  +        
  +        if ( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug( "Component initialization order:" );
  +            int i = 1;
  +            for ( Iterator iter = vertices.iterator(); iter.hasNext(); i++ )
  +            {
  +                Vertex v = (Vertex)iter.next();
  +                getLogger().debug( "  #" + i + " (" + v.getOrder() + ") : " + v.getName()
);
  +            }
  +        }
  +        
  +        Collections.reverse(vertices);
  +        
  +        m_shutDownOrder = vertices;
       }
   
       /**
  @@ -694,6 +726,18 @@
        */
       public void dispose()
       {
  +        
  +        if ( getLogger().isDebugEnabled() )
  +        {
  +            getLogger().debug( "Component shutdown order:" );
  +            int i = 1;
  +            for ( Iterator iter = m_shutDownOrder.iterator(); iter.hasNext(); i++ )
  +            {
  +                Vertex v = (Vertex)iter.next();
  +                getLogger().debug( "  #" + i + " (" + v.getOrder() + ") : " + v.getName()
);
  +            }
  +        }
  +        
           final Iterator i = m_shutDownOrder.iterator();
           while ( i.hasNext() )
           {
  
  
  
  1.14      +6 -1      avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/impl/lookup/FortressServiceSelector.java
  
  Index: FortressServiceSelector.java
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/impl/lookup/FortressServiceSelector.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- FortressServiceSelector.java	14 May 2003 15:54:46 -0000	1.13
  +++ FortressServiceSelector.java	28 May 2003 17:50:20 -0000	1.14
  @@ -151,4 +151,9 @@
           }
           return handler;
       }
  +    
  +    public String getKey()
  +    {
  +        return m_key;
  +    }
   }
  
  
  
  1.2       +2 -1      avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/util/dag/CyclicDependencyException.java
  
  Index: CyclicDependencyException.java
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/util/dag/CyclicDependencyException.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- CyclicDependencyException.java	15 May 2003 17:10:28 -0000	1.1
  +++ CyclicDependencyException.java	28 May 2003 17:50:20 -0000	1.2
  @@ -57,7 +57,8 @@
    */
   public class CyclicDependencyException extends Exception
   {
  -    public CyclicDependencyException()
  +    public CyclicDependencyException( String message )
       {
  +        super( message );
       }
   }
  
  
  
  1.5       +70 -66    avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/util/dag/DirectedAcyclicGraphVerifier.java
  
  Index: DirectedAcyclicGraphVerifier.java
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/fortress/src/java/org/apache/avalon/fortress/util/dag/DirectedAcyclicGraphVerifier.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- DirectedAcyclicGraphVerifier.java	28 May 2003 16:11:03 -0000	1.4
  +++ DirectedAcyclicGraphVerifier.java	28 May 2003 17:50:20 -0000	1.5
  @@ -53,8 +53,8 @@
   
   /**
    * DirectedAcyclicGraphVerifier provides methods to verify that any set of
  - * verteces has no cycles.  A Directed Acyclic Graph is a "graph" or set of
  - * verteces where all connections between each vertex goes in a particular
  + * vertices has no cycles.  A Directed Acyclic Graph is a "graph" or set of
  + * vertices where all connections between each vertex goes in a particular
    * direction and there are no cycles or loops.  It is used to track dependencies
    * and ansure that dependencies can be loaded and unloaded in the proper order.
    *
  @@ -67,110 +67,114 @@
        * Verify that a vertex and its set of dependencies have no cycles.
        *
        * @param vertex  The vertex we want to test.
  +     *
        * @throws CyclicDependencyException  if there is a cycle.
        */
  -    public static void verify( Vertex vertex ) throws CyclicDependencyException
  +    public static void verify( Vertex vertex )
  +        throws CyclicDependencyException
       {
  -        List list = new ArrayList(1);
  -        list.add(vertex);
  -
  -        addDependencies( list, vertex );
  -
  -        topologicalSort(list);
  +        // We need a list of vertices that contains the entire graph, so build it.
  +        List vertices = new ArrayList();
  +        addDependencies( vertex, vertices );
  +        
  +        verify( vertices );
       }
  -
  -    private static void addDependencies( final List list, final Vertex vertex )
  +    
  +    /**
  +     * Recursively add a vertex and all of its dependencies to a list of
  +     *  vertices
  +     *
  +     * @param vertex Vertex to be added.
  +     * @param vertices Existing list of vertices.
  +     */
  +    private static void addDependencies( final Vertex vertex, final List vertices )
       {
  -        Iterator deps = vertex.getDependencies().iterator();
  -        while(deps.hasNext())
  +        if ( !vertices.contains( vertex ) )
           {
  -            Vertex v = (Vertex)deps.next();
  -
  -            if ( ! list.contains(v) )
  +            vertices.add( vertex );
  +            
  +            for ( Iterator iter = vertex.getDependencies().iterator(); iter.hasNext();
)
               {
  -                list.add(v);
  -                addDependencies(list, v);
  +                addDependencies( (Vertex)iter.next(), vertices );
               }
           }
       }
   
       /**
  -     * Verify a set of verteces and all their dependencies have no cycles.
  +     * Verify a set of vertices and all their dependencies have no cycles.  All
  +     *  Vertices in the graph must exist in the list.
  +     *
  +     * @param vertices  The list of vertices we want to test.
        *
  -     * @param verteces  The list of verteces we want to test.
        * @throws CyclicDependencyException  if there is a cycle.
        */
  -    public static void verify( List verteces ) throws CyclicDependencyException
  +    public static void verify( List vertices )
  +        throws CyclicDependencyException
       {
  -        topologicalSort(verteces);
  +        // Reset the orders of all the vertices.
  +        resetVertices( vertices );
  +        
  +        // Assert that all vertices are in the vertices list and resolve each of their
orders.
  +        Iterator it = vertices.iterator();
  +        while ( it.hasNext() )
  +        {
  +            Vertex v = (Vertex) it.next();
  +            
  +            // Make sure that any dependencies are also in the vertices list.  This adds
  +            //  a little bit to the load, but we don't test this and the test would have
  +            //  failed, this would lead to some very hard to track down problems elsewhere.
  +            Iterator dit = v.getDependencies().iterator();
  +            while( dit.hasNext() )
  +            {
  +                Vertex dv = (Vertex) dit.next();
  +                if ( !vertices.contains( dv ) )
  +                {
  +                    throw new IllegalStateException( "A dependent vertex (" + dv.getName()
+ ") of "
  +                        + "vertex (" + v.getName() + ") was not included in the vertices
list." );
  +                }
  +            }
  +            
  +            v.resolveOrder();
  +        }
       }
   
       /**
  -     * Sort a set of verteces so that no dependency is before its vertex.  If
  +     * Sort a set of vertices so that no dependency is before its vertex.  If
        * we have a vertex named "Parent" and one named "Child" that is listed as
        * a dependency of "Parent", we want to ensure that "Child" always comes
        * after "Parent".  As long as there are no cycles in the list, we can sort
  -     * any number of verteces that may or may not be related.
  +     * any number of vertices that may or may not be related.  Both "Parent"
  +     * and "Child" must exist in the vertices list, but "Child" will also be
  +     * referenced as a dependency of "Parent".
        *
        * <p>
        *   <b>Implementation Detail:</b> This particular algorithm is a more
        *   efficient variation of the typical Topological Sort algorithm.  It uses
        *   a Queue (Linked List) to ensure that each edge (connection between
  -     *   two verteces) or vertex is checked only once.  The efficiency is
  +     *   two vertices) or vertex is checked only once.  The efficiency is
        *   O = (|V| + |E|).
        * </p>
        *
  -     * @param verteces
  +     * @param vertices
        * @throws CyclicDependencyException
        */
  -    public static void topologicalSort( final List verteces ) throws CyclicDependencyException
  +    public static void topologicalSort( final List vertices ) throws CyclicDependencyException
       {
  -        resetVerteces( verteces );
  -        int counter = 0;
  -        final LinkedList queue = new LinkedList();
  -
  -        Iterator it = verteces.iterator();
  -        while ( it.hasNext() )
  -        {
  -            Vertex v = (Vertex) it.next();
  -
  -            if ( v.getIndegrees() == 0 )
  -            {
  -                queue.addFirst( v );
  -            }
  -        }
  -
  -        while ( !queue.isEmpty() )
  -        {
  -            Vertex v = (Vertex) queue.removeLast();
  -            v.setOrder( counter );
  -            counter++;
  -
  -            Iterator deps = v.getDependencies().iterator();
  -            while ( deps.hasNext() )
  -            {
  -                Vertex w = (Vertex) deps.next();
  -
  -                w.accountForIndegree();
  -                if ( w.getIndegrees() == 0 )
  -                {
  -                    queue.addFirst( w );
  -                }
  -            }
  -        }
  -
  -        if ( counter != verteces.size() ) throw new CyclicDependencyException();
  -
  -        Collections.sort( verteces );
  +        // Verify the graph and set the vertex orders in the process.
  +        verify( vertices );
  +        
  +        // We now that there are no cycles and that each of the vertices has an order
  +        //  that will allow them to be sorted.
  +        Collections.sort( vertices );
       }
   
       /**
  -     * Resets all the verteces so that the visitation flags and indegrees are
  +     * Resets all the vertices so that the visitation flags and indegrees are
        * reset to their start values.
        *
        * @param vertices
        */
  -    public static void resetVerteces( List vertices )
  +    public static void resetVertices( List vertices )
       {
           Iterator it = vertices.iterator();
           while ( it.hasNext() )
  
  
  
  1.4       +67 -33    avalon-excalibur/fortress/src/test/org/apache/avalon/fortress/util/dag/test/DirectedAcyclicGraphVerifierTestCase.java
  
  Index: DirectedAcyclicGraphVerifierTestCase.java
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/fortress/src/test/org/apache/avalon/fortress/util/dag/test/DirectedAcyclicGraphVerifierTestCase.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- DirectedAcyclicGraphVerifierTestCase.java	28 May 2003 16:18:50 -0000	1.3
  +++ DirectedAcyclicGraphVerifierTestCase.java	28 May 2003 17:50:20 -0000	1.4
  @@ -107,6 +107,45 @@
           }
       }
   
  +    /**
  +     * This test waas written to test the algorithm used to search for cycles.
  +     *  It makes sure that cycles that start a ways into the dependency tree
  +     *  are handled correctly.
  +     */
  +    public void testCycleTest() throws Exception
  +    {
  +        Vertex component1 = new Vertex( "Component1" );
  +        Vertex component2 = new Vertex( "Component2" );
  +        Vertex component3 = new Vertex( "Component3" );
  +        Vertex component4 = new Vertex( "Component4" );
  +        Vertex component5 = new Vertex( "Component5" );
  +        
  +        List vertices = new ArrayList( 5 );
  +        vertices.add( component1 );
  +        vertices.add( component2 );
  +        vertices.add( component3 );
  +        vertices.add( component4 );
  +        vertices.add( component5 );
  +        
  +        component1.addDependency( component2 );
  +        component2.addDependency( component3 );
  +            
  +        component3.addDependency( component4 );
  +        component4.addDependency( component5 );
  +        component5.addDependency( component3 ); // Cycle
  +        
  +        try
  +        {
  +            DirectedAcyclicGraphVerifier.topologicalSort( vertices );
  +            fail( "Did not detect the expected cyclic dependency" );
  +        }
  +        catch ( CyclicDependencyException cde )
  +        {
  +            //Success!
  +        }
  +    }
  +        
  +    
       public void testSortDAG() throws Exception
       {
           Vertex component1 = new Vertex( "Component1" );
  @@ -131,14 +170,13 @@
           vertices.add( component5 );
   
           DirectedAcyclicGraphVerifier.topologicalSort( vertices );
  -
  -        List verifyList = generateVerifyList( component1, component5, component2, component3,
component4 );
  -        verifyTopSort( vertices, verifyList );
  +        verifyGraphOrders( vertices );
  +        verifyListOrder( vertices );
   
           Collections.shuffle( vertices );
           DirectedAcyclicGraphVerifier.topologicalSort( vertices );
  -        verifyList = generateVerifyList( component1, component5, component2, component3,
component4 );
  -        verifyTopSort( vertices, verifyList );
  +        verifyGraphOrders( vertices );
  +        verifyListOrder( vertices );
   
           component4.addDependency( component1 );
           Collections.shuffle( vertices );
  @@ -153,38 +191,34 @@
               //Success!
           }
       }
  -
  -    private List generateVerifyList( Vertex component1, Vertex component5, Vertex component2,
Vertex component3, Vertex component4 )
  +    
  +    private void verifyGraphOrders( List vertices )
       {
  -        List verifyList = new ArrayList( 3 );
  -        List level1 = new ArrayList( 2 );
  -        level1.add( component1 );
  -        level1.add( component5 );
  -        verifyList.add( level1 );
  -        List level2 = new ArrayList( 2 );
  -        level2.add( component2 );
  -        level2.add( component3 );
  -        verifyList.add( level2 );
  -        List level3 = new ArrayList( 1 );
  -        level3.add( component4 );
  -        verifyList.add( level3 );
  -        return verifyList;
  -    }
  -
  -    private void verifyTopSort( List vertices, List verifyList )
  -    {
  -        List currList = null;
  -        Iterator it = vertices.iterator();
  -        while ( it.hasNext() )
  +        for ( Iterator iter = vertices.iterator(); iter.hasNext(); )
           {
  -            if ( null == currList || currList.isEmpty() )
  +            Vertex v = (Vertex)iter.next();
  +            
  +            // Make sure that the orders of all dependencies are less than
  +            //  the order of v.
  +            for ( Iterator iter2 = v.getDependencies().iterator(); iter2.hasNext(); )
               {
  -                currList = (List) verifyList.remove( 0 );
  +                Vertex dv = (Vertex)iter2.next();
  +                assertTrue( "The order of " + dv.getName() + " (" + dv.getOrder() + ")
should be "
  +                    + "less than the order of " + v.getName() + " (" + v.getOrder() + ")",
  +                    dv.getOrder() < v.getOrder() );
               }
  -
  -            Vertex v = (Vertex) it.next();
  -
  -            assertTrue( currList.remove( v ) );
  +        }
  +    }
  +    
  +    private void verifyListOrder( List vertices )
  +    {
  +        Vertex[] ary = new Vertex[vertices.size()];
  +        vertices.toArray( ary );
  +        for ( int i = 1; i < ary.length; i++ )
  +        {
  +            assertTrue( "The order of vertex #" + ( i - 1 ) + " (" + ary[i - 1].getOrder()
+ ") "
  +                + "should be <= the order of vertex #" + ( i ) + " (" + ary[i].getOrder()
+ ")",
  +                ary[i - 1].getOrder() <= ary[i].getOrder() );
           }
       }
   }
  
  
  
  1.1                  avalon-excalibur/fortress/src/test/org/apache/avalon/fortress/util/dag/test/VertexTestCase.java
  
  Index: VertexTestCase.java
  ===================================================================
  /*
  
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
  
   Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
  
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
  
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
  
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
  
   4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"
      must not be used to endorse or promote products derived from this  software
      without  prior written permission. For written permission, please contact
      apache@apache.org.
  
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
  
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation. For more  information on the
   Apache Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.avalon.fortress.util.dag.test;
  
  import java.util.List;
  
  import junit.framework.TestCase;
  
  import org.apache.avalon.fortress.util.dag.CyclicDependencyException;
  import org.apache.avalon.fortress.util.dag.Vertex;
  
  /**
   * VertexTest does XYZ
   *
   * @author <a href="bloritsch.at.d-haven.org">Berin Loritsch</a>
   * @version CVS $ Revision: 1.1 $
   */
  public class VertexTestCase extends TestCase
  {
      public VertexTestCase( String name )
      {
          super( name );
      }
  
      public void setUp()
      {
      }
  
      public void tearDown()
      {
      }
  
      public void testSortMethods()
      {
          Vertex v = new Vertex( "Root" );
          List deps = v.getDependencies();
          assertNotNull( deps );
          assertEquals( 0, deps.size() );
          assertEquals( "Root", v.getNode() );
          assertEquals( "Root", v.getName() );
          assertEquals( 0, v.getOrder() );
  
          Vertex w = new Vertex( "Child" );
          v.addDependency( w );
          deps = v.getDependencies();
          assertNotNull( deps );
          assertEquals( 1, deps.size() );
  
          v.reset();
          w.reset();
          
          try
          {
              v.resolveOrder();
              w.resolveOrder();
          }
          catch ( CyclicDependencyException e )
          {
              fail( "Unexpected cyclic exception: " + e );
          }
          
          assertEquals( 1, v.getOrder() );
          assertEquals( 0, w.getOrder() );
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org


Mime
View raw message