avalon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cziege...@apache.org
Subject cvs commit: jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml JaxpParser.java Parser.java XercesParser.java
Date Tue, 13 Nov 2001 12:34:56 GMT
cziegeler    01/11/13 04:34:56

  Modified:    src/scratchpad/org/apache/avalon/excalibur/xml
                        JaxpParser.java Parser.java XercesParser.java
  Added:       src/scratchpad/org/apache/avalon/excalibur/source
                        SourceResolverImpl.java URLSource.java
  Log:
  Started SourceResolverImpl and changed Parser to use ContentHandler instead of XMLConsumer
  
  Revision  Changes    Path
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/source/SourceResolverImpl.java
  
  Index: SourceResolverImpl.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.avalon.excalibur.source;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.ContextException;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.framework.logger.Loggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import java.io.File;
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  /**
   * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   * @version $Id: SourceResolverImpl.java,v 1.1 2001/11/13 12:34:56 cziegeler Exp $
   */
  public class SourceResolverImpl
  extends AbstractLoggable
  implements ThreadSafe, Configurable, Disposable, Composable, Contextualizable, SourceResolver
{
  
      /** The component manager */
      protected ComponentManager manager;
  
      /** The special Source factories */
      protected Map sourceFactories;
  
      /** The context */
      protected Context context;
  
      /**
       * The base URL
       */
      protected URL baseURL;
  
      /**
       * Configure the SourceFactories
       */
      public void configure(final Configuration conf)
      throws ConfigurationException {
          // set the base URL to the current directory
          try {
              this.baseURL = new File(System.getProperty("user.dir")).toURL();
          } catch (MalformedURLException mue) {
              throw new ConfigurationException("Malformed URL for user.dir");
          }
  
          // get the configured factories
          try {
              getLogger().debug("Getting the SourceFactories");
              HashMap factories = new HashMap();
              Configuration[] configs = conf.getChildren("protocol");
              SourceFactory sourceFactory = null;
              String protocol = null;
              for (int i = 0; i < configs.length; i++) {
                  protocol = configs[i].getAttribute("name");
                  if (factories.containsKey(protocol) == true) {
                      throw new ConfigurationException("SourceFactory defined twice for protocol:
" + protocol);
                  }
  
                  getLogger().debug("\tfor protocol: " + protocol + " " + configs[i].getAttribute("class"));
  
                  sourceFactory = (SourceFactory) Class.forName(configs[i].getAttribute("class")).newInstance();
                  this.init(sourceFactory);
                  factories.put(protocol, sourceFactory);
              }
              this.sourceFactories = java.util.Collections.synchronizedMap(factories);
          } catch (ConfigurationException e) {
              throw e;
          } catch (Exception e) {
              getLogger().error("Could not get SourceFactories", e);
              throw new ConfigurationException("Could not get parameters because: " +
                                             e.getMessage());
          }
      }
  
      /**
       * Get the context
       */
      public void contextualize(Context context)
      throws ContextException {
          this.context = context;
      }
  
      /**
       * Set the current <code>ComponentManager</code> instance used by this
       * <code>Composable</code>.
       */
      public void compose(ComponentManager manager)
      throws ComponentException {
          this.manager = manager;
      }
  
      /**
       * Dispose
       */
      public void dispose() {
          Iterator iter = this.sourceFactories.values().iterator();
          SourceFactory current;
          while (iter.hasNext() == true) {
              current = (SourceFactory) iter.next();
              this.deinit(current);
          }
          this.sourceFactories = null;
      }
  
      /**
       * Set the base URL. All relative references are resolved
       * according to this URL.
       */
      public void setBaseURL(URL base) {
          if (this.getLogger().isDebugEnabled() == true) {
              this.getLogger().debug("Changing baseURL to: " + base);
          }
          this.baseURL = base;
      }
  
      /**
       * Get the base URL
       */
      public URL getBaseURL() {
          return this.baseURL;
      }
  
      /**
       * Get a <code>Source</code> object.
       */
      public Source resolve(String location)
      throws MalformedURLException, IOException {
          return this.resolve(this.baseURL, location);
      }
  
      /**
       * Get a <code>Source</code> object.
       */
      public Source resolve(URL base, String location)
      throws MalformedURLException, IOException {
          this.getLogger().debug("Resolving '"+location+"' in context '" + base + "'");
          if (location == null) throw new MalformedURLException("Invalid System ID");
  
          // first step: create systemID
          String systemID;
          if (base == null) base = this.baseURL;
  
          if (location.length() == 0) {
              systemID = base.toExternalForm();
          } else if (location.indexOf(":") > 1) {
              systemID = location;
          } else if (location.charAt(0) == '/') {
              systemID = new StringBuffer(base.getProtocol())
                             .append(":").append(location).toString();
          // windows: absolute paths can start with drive letter
          } else if (location.length() > 1 && location.charAt(1) == ':') {
              systemID = new StringBuffer(base.getProtocol())
                             .append(":/").append(location).toString();
          } else {
              if (base.getProtocol().equals("file") == true) {
                  File temp = new File(base.toExternalForm().substring("file:".length()),
location);
                  String path = temp.getAbsolutePath();
                  // windows paths starts with drive letter
                  if (path.charAt(0) != File.separator.charAt(0)) {
                      systemID = "file:/" + path;
                  } else {
                      systemID = "file:" + path;
                  }
              } else {
                  systemID = new URL(base, location).toExternalForm();
              }
          }
          this.getLogger().debug("Resolved to systemID '"+systemID+"'");
  
          // search for a SourceFactory implementing the protocol
          final int protocolPos = systemID.indexOf(':');
          if ( protocolPos != -1 ) {
              final String protocol = systemID.substring(0, protocolPos);
              final SourceFactory factory = ( SourceFactory )this.sourceFactories.get( protocol
);
              if (factory != null) {
                  return factory.getSource( systemID );
              }
          }
  
  
          // no factory found, so usual url handling stuff...
          try {
              getLogger().debug("Making URL from " + systemID);
              return new URLSource(new URL(systemID), this.manager);
          } catch (MalformedURLException mue) {
              getLogger().debug("Making URL - MalformedURLException in getURL:" , mue);
              getLogger().debug("Making URL a File (assuming that it is full path):" + systemID);
              return new URLSource((new File(systemID)).toURL(), this.manager);
          }
      }
  
      /**
       * Init a source factory
       */
      private void init(SourceFactory factory)
      throws ContextException, ComponentException {
          if (factory instanceof Loggable) {
              ((Loggable) factory).setLogger(getLogger());
          }
          if (factory instanceof Contextualizable) {
              ((Contextualizable) factory).contextualize (this.context);
          }
          if (factory instanceof Composable) {
              ((Composable) factory).compose(this.manager);
          }
      }
  
      /**
       * Deinit a source factory
       */
      private void deinit(SourceFactory factory) {
          if (factory instanceof Disposable) {
              ((Disposable) factory).dispose();
          }
      }
  
  }
  
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/source/URLSource.java
  
  Index: URLSource.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.avalon.excalibur.source;
  
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.excalibur.xml.Parser;
  import org.apache.avalon.excalibur.xml.XMLConsumer;
  import org.apache.avalon.excalibur.xml.XMLizable;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.ext.LexicalHandler;
  
  import java.io.*;
  import java.lang.reflect.Method;
  import java.net.URL;
  import java.net.URLConnection;
  
  /**
   * Description of a source which is described by an URL.
   *
   * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/13 12:34:56 $
   */
  
  public final class URLSource implements ModifiableSource, XMLizable {
  
      /** Identifier for file urls */
      private final String FILE = "file:";
  
      /** The last modification date or 0 */
      private long lastModificationDate;
  
      /** The content length */
      private long contentLength;
  
      /** The system id */
      private String systemId;
  
      /** The URL of the source */
      private URL url;
  
      /** The connection for a real URL */
      private URLConnection connection;
  
      /** Is this a file or a "real" URL */
      private boolean isFile;
  
      /** Are we initialized? */
      private boolean gotInfos;
  
      /** The ComponentManager needed for streaming */
      private ComponentManager manager;
  
      /**
       * Construct a new object
       */
      public URLSource(URL url, ComponentManager manager)
      throws IOException {
          this.manager = manager;
          this.systemId = url.toExternalForm();
          this.isFile = systemId.startsWith(FILE);
          this.url = url;
          this.gotInfos = false;
      }
  
      /**
       * Get the last modification date and content length of the source.
       * Any exceptions are ignored.
       */
      private void getInfos() {
          if (this.gotInfos == false) {
              if (this.isFile == true) {
                  File file = new File(systemId.substring(FILE.length()));
                  this.lastModificationDate = file.lastModified();
                  this.contentLength = file.length();
              } else {
                  try {
                      if (this.connection == null) {
                          this.connection = this.url.openConnection();
                          String userInfo = this.getUserInfo();
                          if (this.url.getProtocol().startsWith("http") == true &&
userInfo != null) {
                              this.connection.setRequestProperty("Authorization","Basic "+this.encodeBASE64(userInfo));
                          }
                      }
                      this.lastModificationDate = this.connection.getLastModified();
                      this.contentLength = this.connection.getContentLength();
                  } catch (IOException ignore) {
                      this.lastModificationDate = 0;
                      this.contentLength = -1;
                  }
              }
              this.gotInfos = true;
          }
      }
  
      /**
       * Get the last modification date of the source or 0 if it
       * is not possible to determine the date.
       */
      public long getLastModified() {
          this.getInfos();
          return this.lastModificationDate;
      }
  
      /**
       * Get the content length of the source or -1 if it
       * is not possible to determine the length.
       */
      public long getContentLength() {
          this.getInfos();
          return this.contentLength;
      }
  
      /**
       * Return an <code>InputStream</code> object to read from the source.
       *
       * @throws ResourceNotFoundException if file not found or
       *         HTTP location does not exist.
       * @throws IOException if I/O error occured.
       */
      public InputStream getInputStream()
      throws IOException {
          this.getInfos();
          InputStream input = null;
          if (this.isFile == true) {
              input = new FileInputStream(this.systemId.substring(FILE.length()));
          } else {
              if (this.connection == null) {
                  this.connection = this.url.openConnection();
                  /* The following requires a jdk 1.3 */
                  String userInfo = this.getUserInfo();
                  if (this.url.getProtocol().startsWith("http") == true && userInfo
!= null) {
                      this.connection.setRequestProperty("Authorization","Basic "+encodeBASE64(userInfo));
                  }
              }
  
              input = this.connection.getInputStream();
              this.connection = null; // make sure a new connection is created next time
          }
          return input;
      }
  
      private static boolean checkedURLClass = false;
      private static boolean urlSupportsGetUserInfo = false;
      private static Method  urlGetUserInfo = null;
      private static Object[] emptyParams = new Object[0];
  
      /**
       * Check if the <code>URL</code> class supports the getUserInfo()
       * method which is introduced in jdk 1.3
       */
      private String getUserInfo() {
          if (URLSource.checkedURLClass == true) {
              if (URLSource.urlSupportsGetUserInfo == true) {
                  try {
                      return (String) URLSource.urlGetUserInfo.invoke(this.url, URLSource.emptyParams);
                  } catch (Exception e){
                      // ignore this anyway
                  }
              }
              return null;
          } else {
              // test if the url class supports the getUserInfo method
              try {
                  URLSource.urlGetUserInfo = URL.class.getMethod("getUserInfo", null);
                  String ui = (String)URLSource.urlGetUserInfo.invoke(this.url, URLSource.emptyParams);
                  URLSource.checkedURLClass = true;
                  URLSource.urlSupportsGetUserInfo = true;
                  return ui;
              } catch (Exception e){
              }
              URLSource.checkedURLClass = true;
              URLSource.urlSupportsGetUserInfo = false;
              URLSource.urlGetUserInfo = null;
              return null;
          }
      }
  
      /**
       * Return the unique identifer for this source
       */
      public String getSystemId() {
          return this.systemId;
      }
  
      /**
       * Refresh this object and update the last modified date
       * and content length.
       */
      public void refresh() {
          // reset connection
          this.connection = null;
          this.gotInfos = false;
      }
  
      /**
       * Return a new <code>InputSource</code> object
       *
       * @throws ResourceNotFoundException if file not found or
       *         HTTP location does not exist.
       * @throws IOException if I/O error occured.
       */
      public InputSource getInputSource()
      throws IOException {
          InputSource newObject = new InputSource(this.getInputStream());
          newObject.setSystemId(this.systemId);
          return newObject;
      }
  
       public static final char [ ] alphabet = {
         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
         'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
         'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
         'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
         'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
         'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
         'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
         '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63
  
       /**
        * BASE 64 encoding.
        * See also RFC 1421
        * @since 1.2
        */
       public static String encodeBASE64 ( String s ) {
           return encodeBASE64 ( s.getBytes ( ) );
       }
  
       /**
        * BASE 64 encoding.
        * See also RFC 1421
        * @since 1.2
        */
       public static String encodeBASE64 ( byte [ ] octetString ) {
           int bits24;
           int bits6;
  
           char [ ] out
           = new char [ ( ( octetString.length - 1 ) / 3 + 1 ) * 4 ];
  
           int outIndex = 0;
           int i = 0;
  
           while ( ( i + 3 ) <= octetString.length ) {
               // store the octets
               bits24 = ( octetString [ i++ ] & 0xFF ) << 16;
               bits24 |= ( octetString [ i++ ] & 0xFF ) << 8;
               bits24 |= ( octetString [ i++ ] & 0xFF ) << 0;
  
               bits6 = ( bits24 & 0x00FC0000 ) >> 18;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x0003F000 ) >> 12;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x00000FC0 ) >> 6;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x0000003F );
               out [ outIndex++ ] = alphabet [ bits6 ];
           }
  
           if ( octetString.length - i == 2 ) {
               // store the octets
               bits24 = ( octetString [ i ] & 0xFF ) << 16;
               bits24 |= ( octetString [ i + 1 ] & 0xFF ) << 8;
  
               bits6 = ( bits24 & 0x00FC0000 ) >> 18;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x0003F000 ) >> 12;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x00000FC0 ) >> 6;
               out [ outIndex++ ] = alphabet [ bits6 ];
  
               // padding
               out [ outIndex++ ] = '=';
           } else if ( octetString.length - i == 1 ) {
               // store the octets
               bits24 = ( octetString [ i ] & 0xFF ) << 16;
  
               bits6 = ( bits24 & 0x00FC0000 ) >> 18;
               out [ outIndex++ ] = alphabet [ bits6 ];
               bits6 = ( bits24 & 0x0003F000 ) >> 12;
               out [ outIndex++ ] = alphabet [ bits6 ];
  
               // padding
               out [ outIndex++ ] = '=';
               out [ outIndex++ ] = '=';
           }
  
           return new String ( out );
       }
  
      /**
       * Stream content to a content handler or to an XMLConsumer.
       *
       * @throws ResourceNotFoundException if file not found or
       *         HTTP location does not exist.
       * @throws SAXException if failed to parse source document.
       */
      public void toSAX(ContentHandler handler)
      throws SAXException
      {
          Parser parser = null;
          try {
              parser = (Parser)this.manager.lookup(Parser.ROLE);
  
              parser.parse(this.getInputSource(), handler);
          } catch (SAXException e) {
              // Preserve original exception
              throw e;
          } catch (Exception e){
              throw new SAXException("Exception during processing of "
                                            + this.systemId, e);
          } finally {
              if (parser != null) this.manager.release(parser);
          }
      }
  
      public void recycle()
      {
      }
  }
  
  
  
  1.2       +8 -4      jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/JaxpParser.java
  
  Index: JaxpParser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/JaxpParser.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JaxpParser.java	2001/11/13 11:03:58	1.1
  +++ JaxpParser.java	2001/11/13 12:34:56	1.2
  @@ -15,6 +15,7 @@
   import org.apache.avalon.framework.parameters.Parameters;
   import org.w3c.dom.Document;
   import org.xml.sax.*;
  +import org.xml.sax.ext.LexicalHandler;
   
   import javax.xml.parsers.*;
   import java.io.IOException;
  @@ -25,7 +26,7 @@
    *
    * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
    * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/13 11:03:58 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/13 12:34:56 $
    */
   public class JaxpParser
   extends AbstractLoggable
  @@ -52,7 +53,7 @@
           this.docfactory.setValidating(validate);
       }
   
  -    public void parse(InputSource in, XMLConsumer consumer)
  +    public void parse(InputSource in, ContentHandler consumer)
       throws SAXException, IOException {
           SAXParser parser = null;
   
  @@ -67,8 +68,11 @@
   
           reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
           try {
  -            reader.setProperty("http://xml.org/sax/properties/lexical-handler",
  -                        consumer);
  +            if (consumer instanceof XMLConsumer
  +                || consumer instanceof LexicalHandler) {
  +                reader.setProperty("http://xml.org/sax/properties/lexical-handler",
  +                        (LexicalHandler)consumer);
  +            }
           } catch (SAXException e) {
               getLogger().warn("SAX2 driver does not support property: "+
                                "'http://xml.org/sax/properties/lexical-handler'");
  
  
  
  1.2       +2 -2      jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/Parser.java
  
  Index: Parser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/Parser.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Parser.java	2001/11/13 11:03:58	1.1
  +++ Parser.java	2001/11/13 12:34:56	1.2
  @@ -19,13 +19,13 @@
   /**
    *
    * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/13 11:03:58 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/13 12:34:56 $
    */
   public interface Parser extends Component {
   
       String ROLE = "org.apache.avalon.excalibur.xml.Parser";
   
  -    void parse(InputSource in, XMLConsumer consumer)
  +    void parse(InputSource in, ContentHandler consumer)
       throws SAXException, IOException;
   
       Document parseDocument(InputSource in)
  
  
  
  1.2       +9 -5      jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/XercesParser.java
  
  Index: XercesParser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/xml/XercesParser.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- XercesParser.java	2001/11/13 11:03:58	1.1
  +++ XercesParser.java	2001/11/13 12:34:56	1.2
  @@ -12,18 +12,19 @@
   import org.apache.xerces.parsers.DOMParser;
   import org.apache.xerces.parsers.SAXParser;
   import org.w3c.dom.Document;
  +import org.xml.sax.ContentHandler;
   import org.xml.sax.ErrorHandler;
   import org.xml.sax.InputSource;
   import org.xml.sax.SAXException;
   import org.xml.sax.SAXParseException;
  -
  +import org.xml.sax.ext.LexicalHandler;
   import java.io.IOException;
   
   /**
    *
    * @author <a href="mailto:fumagalli@exoffice.com">Pierpaolo Fumagalli</a>
    *         (Apache Software Foundation, Exoffice Technologies)
  - * @version CVS $Revision: 1.1 $ $Date: 2001/11/13 11:03:58 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/13 12:34:56 $
    */
   public class XercesParser
   extends AbstractLoggable
  @@ -42,10 +43,13 @@
                             true);
       }
   
  -    public void parse(InputSource in, XMLConsumer consumer)
  +    public void parse(InputSource in, ContentHandler consumer)
       throws SAXException, IOException {
  -      this.parser.setProperty("http://xml.org/sax/properties/lexical-handler",
  -                              consumer);
  +        if (consumer instanceof XMLConsumer
  +            || consumer instanceof LexicalHandler) {
  +            this.parser.setProperty("http://xml.org/sax/properties/lexical-handler",
  +                              (LexicalHandler)consumer);
  +        }
           this.parser.setErrorHandler(this);
           this.parser.setContentHandler(consumer);
           this.parser.parse(in);
  
  
  

--
To unsubscribe, e-mail:   <mailto:avalon-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:avalon-dev-help@jakarta.apache.org>


Mime
View raw message