[jboss-cvs] jboss-seam/src/main/org/jboss/seam/servlet ...

Shane Bryzak Shane_Bryzak at symantec.com
Sun Feb 4 03:11:34 EST 2007


  User: sbryzak2
  Date: 07/02/04 03:11:34

  Modified:    src/main/org/jboss/seam/servlet   MultipartRequest.java
                        SeamMultipartFilter.java
  Log:
  JBSEAM-705, JBSEAM-706 file upload control improvements
  
  Revision  Changes    Path
  1.4       +375 -273  jboss-seam/src/main/org/jboss/seam/servlet/MultipartRequest.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: MultipartRequest.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/src/main/org/jboss/seam/servlet/MultipartRequest.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -b -r1.3 -r1.4
  --- MultipartRequest.java	30 Jan 2007 10:47:23 -0000	1.3
  +++ MultipartRequest.java	4 Feb 2007 08:11:34 -0000	1.4
  @@ -1,8 +1,15 @@
   package org.jboss.seam.servlet;
   
  +import java.io.ByteArrayInputStream;
  +import java.io.ByteArrayOutputStream;
  +import java.io.File;
  +import java.io.FileInputStream;
  +import java.io.FileNotFoundException;
  +import java.io.FileOutputStream;
   import java.io.IOException;
   import java.io.InputStream;
   import java.io.UnsupportedEncodingException;
  +import java.rmi.server.UID;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.Enumeration;
  @@ -24,31 +31,108 @@
   {
      public static final String WWW_FORM_URLENCODED_TYPE = "application/x-www-form-urlencoded";
      
  -   private class PartWrapper 
  +   private static final String PARAM_NAME = "name";
  +   private static final String PARAM_FILENAME = "filename";
  +   private static final String PARAM_CONTENT_TYPE = "Content-Type";
  +   
  +   private static final int BUFFER_SIZE = 2048;
  +   private static final int CHUNK_SIZE = 512;
  +   
  +   private boolean createTempFiles = false;
  +   
  +   private Map<String,Param> parameters = null;
  +   
  +   private enum ReadState { BOUNDARY, HEADERS, DATA }   
  +   
  +   private static final byte CR = 0x0d;
  +   private static final byte LF = 0x0a;   
  +   private static final byte[] CR_LF = {CR,LF};
  +         
  +   
  +   private abstract class Param
      {
  -      private Map<String,Object> params;
  -      private byte[] data;
  -      private String contentType;
  -      private String fileName;
  +      private String name;
         
  -      public PartWrapper()
  +      public Param(String name)
         {
  -         params = new HashMap<String,Object>();
  +         this.name = name;
         }
         
  -      public Map<String,Object> getParams()
  +      public String getName()
         {
  -         return params;
  +         return name;
         }
         
  -      public void setData(byte[] data)
  +      public abstract void appendData(byte[] data, int start, int length) 
  +         throws IOException;
  +   }
  +   
  +   private class ValueParam extends Param
  +   {
  +      private Object value = null;
  +      private ByteArrayOutputStream buf = new ByteArrayOutputStream();      
  +      
  +      public ValueParam(String name)
         {
  -         this.data = data;
  +         super(name);
         }
         
  -      public byte[] getData()
  +      @Override
  +      public void appendData(byte[] data, int start, int length)
  +         throws IOException
  +      {
  +         buf.write(data, start, length);
  +      }
  +      
  +      public void complete()
  +      {
  +         String val = new String(buf.toByteArray());
  +         if (value == null)
  +         {
  +            value = val;
  +         }
  +         else 
  +         {
  +            if (!(value instanceof List))
  +            {
  +               List<String> v = new ArrayList<String>();
  +               v.add((String) value);
  +               value = v;
  +            }
  +            
  +            ((List) value).add(val);
  +         }            
  +         buf.reset();
  +      }
  +      
  +      public Object getValue()
  +      {
  +         return value;
  +      }
  +   }
  +   
  +   private class FileParam extends Param
  +   {
  +      private String filename;
  +      private String contentType;
  +           
  +      private ByteArrayOutputStream bOut = null;
  +      private FileOutputStream fOut = null;
  +      private File tempFile = null;
  +      
  +      public FileParam(String name)
  +      {
  +         super(name);
  +      }      
  +      
  +      public String getFilename()
  +      {
  +         return filename;
  +      }
  +      
  +      public void setFilename(String filename)
         {
  -         return data;
  +         this.filename = filename;
         }
         
         public String getContentType()
  @@ -61,32 +145,97 @@
            this.contentType = contentType;
         }
         
  -      public String getFileName()
  +      public void createTempFile()
         {
  -         return fileName;
  +         try
  +         {
  +            tempFile = File.createTempFile(new UID().toString().replace(":", "-"), ".upload");
  +            tempFile.deleteOnExit();
  +            fOut = new FileOutputStream(tempFile);            
  +         }
  +         catch (IOException ex)
  +         {
  +            throw new RuntimeException("Could not create temporary file");
  +         }
         }
         
  -      public void setFileName(String fileName)
  +      @Override
  +      public void appendData(byte[] data, int start, int length)
  +         throws IOException
         {
  -         this.fileName = fileName;
  +         if (fOut != null)
  +         {
  +            fOut.write(data, start, length);
  +         }
  +         else
  +         {
  +            if (bOut == null) bOut = new ByteArrayOutputStream();
  +            bOut.write(data, start, length);
         }
      }
   
  -   private HttpServletRequest request;
  +      public byte[] getData()
  +      {
  +        if (bOut != null)
  +        {
  +           return bOut.toByteArray();
  +        }
  +        else if (tempFile != null)
  +        {
  +           if (tempFile.exists())
  +           {
  +              try
  +              {
  +                 FileInputStream fIn = new FileInputStream(tempFile);
  +                 ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  +                 byte[] buf = new byte[512];
  +                 int read = fIn.read(buf);
  +                 while (read != -1)
  +                 {
  +                    bOut.write(buf, 0, read);
  +                    read = fIn.read(buf);
  +                 }
  +                 bOut.flush();                 
  +                 return bOut.toByteArray();
  +              }
  +              catch (IOException ex) { /* too bad? */}
  +           }
  +        }
   
  -   private Map<String,PartWrapper> parameters;
  +        return null;
  +      }
   
  -   public MultipartRequest(HttpServletRequest request)
  +      public InputStream getInputStream()
  +      {
  +         if (bOut != null)
  +         {
  +            return new ByteArrayInputStream(bOut.toByteArray());
  +         }
  +         else
  +         {
  +            try
  +            {
  +               return new FileInputStream(tempFile);
  +            }
  +            catch (FileNotFoundException ex) { }
  +         }
  +         
  +         return null;
  +      }
  +   }
  +   
  +   private HttpServletRequest request;
  +
  +   public MultipartRequest(HttpServletRequest request, boolean createTempFiles)
      {
         super(request);
         this.request = request;
  +      this.createTempFiles = createTempFiles;
      }
   
      private void parseRequest()
      {     
  -      parameters = new HashMap<String,PartWrapper>();        
  -      
  -      boundaryMarker = getBoundary(request.getContentType());
  +      byte[] boundaryMarker = getBoundaryMarker(request.getContentType());
         if (boundaryMarker == null)
         {
            throw new RuntimeException("the request was rejected because "
  @@ -95,181 +244,147 @@
         
   //      String charEncoding = request.getCharacterEncoding();    
         
  +      parameters = new HashMap<String,Param>();      
  +      
         try
         {
  -         buffer = new byte[4096];
  -         bufferEnd = 0;
  -         input = request.getInputStream();
  +         byte[] buffer = new byte[BUFFER_SIZE];         
  +         Map<String,String> headers = new HashMap<String,String>();
                     
  -         pos = 0;
  +         ReadState readState = ReadState.BOUNDARY;
            
  -         fillBuffer();
  +         InputStream input = request.getInputStream();
  +         int read = input.read(buffer);
  +         int pos = 0;
            
  -         byte[] boundary = readNextBoundary();
  +         Param p = null;
   
  -         while (boundary != null || fillBuffer())
  +         while (read != -1)
            {
  -            if (boundary != null)
  -               parseBoundary(boundary);
  -            boundary = readNextBoundary();            
  -         }
  -      }
  -      catch (IOException ex)
  +            for (int i = 0; i < read; i++)
         {
  -         
  +               switch (readState)
  +               {
  +                  case BOUNDARY:
  +                  {
  +                     if (checkSequence(buffer, i, boundaryMarker) && checkSequence(buffer, i + 2, CR_LF))
  +                     {
  +                        readState = ReadState.HEADERS;
  +                        i += 2;
  +                        pos = i + 1;
         }
  +                     break;
      }
  -   
  -   private static final String FILE_CONTENT_TYPE = "Content-Type";
  -   private static final String FILE_NAME = "filename";
  -   
  -   private void parseBoundary(byte[] boundary)
  +                  case HEADERS:
      {
  -      PartWrapper entry = new PartWrapper();
  -      
  -      int start = 0;
  +                     if (checkSequence(buffer, i, CR_LF))
  +                     {
  +                        parseParams(new String(buffer, pos, i - pos - 1), ";", headers);
  +                        if (checkSequence(buffer, i + CR_LF.length, CR_LF))
  +                        {
  +                           readState = ReadState.DATA;
  +                           i += CR_LF.length;
  +                           pos = i + 1;
         
  -      for (int i = 0; i < boundary.length; i++)
  +                           String paramName = headers.get(PARAM_NAME);
  +                           if (paramName != null)
         {   
  -         if (checkSequence(boundary, i, CR_LF))
  +                              if (headers.containsKey(PARAM_FILENAME))
            {
  -            if (start < i - CR_LF.length)            
  +                                 FileParam fp = new FileParam(paramName);
  +                                 if (createTempFiles) fp.createTempFile();                                 
  +                                 fp.setContentType(headers.get(PARAM_CONTENT_TYPE));
  +                                 fp.setFilename(headers.get(PARAM_FILENAME));
  +                                 p = fp;                                 
  +                              }
  +                              else
               {
  -               String line = new String(boundary, start, i - CR_LF.length - start);            
  -               parseParams(line, ";", entry.getParams());
  -               start = i;
  +                                 p = new ValueParam(paramName);
  +                              }
  +                              
  +                              parameters.put(paramName, p);                              
               }
               
  -            if (checkSequence(boundary, i + CR_LF.length, CR_LF))
  +                           headers.clear();
  +                        }
  +                        else
               {
  -               start = i + CR_LF.length;
  -               break;
  +                           pos = i + 1;
               }            
            }         
  +                     break;                     
         }
  -      
  -      // Extract the content type and filename
  -      for (String key : entry.getParams().keySet())
  +                  case DATA:
         {
  -         Object val = entry.getParams().get(key);
  -         if (val instanceof String)
  +                     if (checkSequence(buffer, i - boundaryMarker.length - CR_LF.length, CR_LF) &&
  +                         checkSequence(buffer, i, boundaryMarker))
            {
  -            String s = (String) entry.getParams().get(key);
  -            if (s != null)
  +                        if (pos < i - boundaryMarker.length - CR_LF.length - 1)
               {        
  -               if (entry.getContentType() == null && FILE_CONTENT_TYPE.equalsIgnoreCase(key))
  -                  entry.setContentType(s);
  -               else if (entry.getFileName() == null && FILE_NAME.equalsIgnoreCase(key))
  -                  entry.setFileName(s);
  -            }
  -         }
  +                          p.appendData(buffer, pos, i - pos - boundaryMarker.length - CR_LF.length - 1);
         }
         
  -      byte[] data = new byte[boundary.length - start];
  -      System.arraycopy(boundary, start, data, 0, data.length);
  -      entry.setData(data);
  +                        if (p instanceof ValueParam) ((ValueParam) p).complete();
         
  -      if (entry.getParams().containsKey("name"))
  -         parameters.put((String) entry.getParams().get("name"), entry);
  +                        if (checkSequence(buffer, i + CR_LF.length, CR_LF))
  +                        {
  +                           i += CR_LF.length;
  +                           pos = i + 1;
      }
  -   
  -   /**
  -    * The boundary marker bytes.  Each part is separated by a boundary marker
  -    */
  -   private byte[] boundaryMarker; 
  -   
  -   /**
  -    * 
  -    */
  -   private InputStream input;
  -   
  -   /**
  -    * Read buffer
  -    */
  -   private byte[] buffer;
  -   
  -   /**
  -    * The read position in the buffer
  -    */
  -   private int pos;   
  -   
  -   /**
  -    * Total bytes read
  -    */
  -   private int totalRead;
  -   
  -   /**
  -    * The last written byte position in the buffer
  -    */
  -   private int bufferEnd;
  -   
  -   /**
  -    * Reads more data into the buffer if possible, shuffling the buffer
  -    * contents if required
  -    *
  -    */
  -   private boolean fillBuffer()
  -      throws IOException
  +                        else
      {
  -      if (totalRead >= request.getContentLength())
  -         return false;
  +                           pos = i;
  +                        }
         
  -      // If pos > 0, move the contents to the front of the buffer to make space
  -      if (pos > 0)
  +                        readState = ReadState.HEADERS;
  +                     }
  +                     else if (i > (pos + boundaryMarker.length + CHUNK_SIZE + CR_LF.length))
         {
  -         System.arraycopy(buffer, pos, buffer, 0, bufferEnd - pos);
  -         bufferEnd -= pos;
  -         pos = 0;
  +                        p.appendData(buffer, pos, CHUNK_SIZE);
  +                        pos += CHUNK_SIZE;
  +                     }
  +                     break;                     
  +                  }               
  +               }
         }
         
  -      // If the buffer is full, extend it
  -      if (pos == 0 && bufferEnd >= buffer.length - 1)
  +            if (pos < read)
         {
  -         byte[] newBuffer = new byte[buffer.length * 2];
  -         System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
  -         buffer = newBuffer;         
  +               // move the bytes that weren't read to the start of the buffer
  +               int bytesNotRead = read - pos;
  +               System.arraycopy(buffer, pos, buffer, 0, bytesNotRead);               
  +               read = input.read(buffer, bytesNotRead, buffer.length - bytesNotRead);
  +               read += bytesNotRead;
         }
  -      
  -      int read = input.read(buffer, bufferEnd, buffer.length - bufferEnd);
  -      
  -      if (read != -1)
  +            else
         {
  -         bufferEnd += read;
  -         totalRead += read;
  +               read = input.read(buffer);
         }
         
  -      return read != -1;
  +            pos = 0;                                    
  +         }
  +      }
  +      catch (IOException ex)
  +      {
  +         throw new RuntimeException("IO Error parsing multipart request", ex);
  +      }
      }
      
  -   private static final byte CR = 0x0d;
  -   private static final byte LF = 0x0a;   
  -   private static final byte[] CR_LF = {CR,LF};
  -      
  -   private byte[] readNextBoundary()
  -      throws IOException
  +   private byte[] getBoundaryMarker(String contentType)
      {
  +      Map<String, Object> params = parseParams(contentType, ";");
  +      String boundaryStr = (String) params.get("boundary");
   
  -      int boundaryStart = -1;
  +      if (boundaryStr == null) return null;
         
  -      for (int i = pos; i < bufferEnd; i++)
  -      {         
  -         if (boundaryStart == -1 && checkSequence(buffer, i, boundaryMarker) &&
  -                  checkSequence(buffer, i + CR_LF.length, CR_LF))
  +      try
            {
  -            // First boundary marker
  -            boundaryStart = i + CR_LF.length;
  +         return boundaryStr.getBytes("ISO-8859-1");
            }
  -         else if (boundaryStart != -1 && checkSequence(buffer, i, boundaryMarker)) 
  +      catch (UnsupportedEncodingException e)
            {
  -            // Second boundary marker
  -            byte[] boundary = new byte[i - boundaryMarker.length - boundaryStart - 4];
  -            System.arraycopy(buffer, boundaryStart, boundary, 0, boundary.length);
  -            pos = i - boundaryMarker.length;
  -            return boundary;
  -         }            
  +         return boundaryStr.getBytes();
         }
  -      
  -      return null;
      }
      
      /**
  @@ -288,34 +403,24 @@
         
         for (int i = 0; i < seq.length; i++)
         {
  -         if (data[pos - seq.length + i] != seq[i])
  +         if (data[(pos - seq.length) + i + 1] != seq[i])
               return false;
         }
         
         return true;
      }
      
  -   private byte[] getBoundary(String contentType)
  -   {
  -      Map<String, Object> params = parseParams(contentType, ";");
  -      String boundaryStr = (String) params.get("boundary");
  -
  -      if (boundaryStr == null) return null;
  +   private static final Pattern PARAM_VALUE_PATTERN = Pattern
  +            .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*([^\\s]+)\\s*$");
   
  -      try
  +   private Map parseParams(String paramStr, String separator)
         {
  -         return boundaryStr.getBytes("ISO-8859-1");
  -      }
  -      catch (UnsupportedEncodingException e)
  -      {
  -         return boundaryStr.getBytes();
  -      }
  +      Map<String,String> paramMap = new HashMap<String, String>();
  +      parseParams(paramStr, separator, paramMap);
  +      return paramMap;
      }
   
  -   private static final Pattern PARAM_VALUE_PATTERN = Pattern
  -            .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*([^\\s]+)\\s*$");
  -
  -   private void parseParams(String paramStr, String separator, Map<String,Object> target)
  +   private void parseParams(String paramStr, String separator, Map paramMap)
      {
         String[] parts = paramStr.split("[" + separator + "]");
   
  @@ -331,37 +436,16 @@
               if (value.startsWith("\"") && value.endsWith("\""))
                  value = value.substring(1, value.length() - 1);
               
  -            if (target.containsKey(key))
  -            {
  -               Object v = target.get(key);
  -               if (v instanceof List)
  -                  ((List) v).add(value);
  -               else if (v instanceof String)
  -               {
  -                  List<String> vals = new ArrayList<String>();
  -                  vals.add((String) v);
  -                  vals.add(value);
  -                  target.put(key, value);
  -               }
  -               else
  -               {
  -                  List vals = new ArrayList();
  -                  vals.add(v);
  -                  vals.add(value);
  -                  target.put(key, value);
  -               }
  -            }
  -            else
  -               target.put(key, value);
  +            paramMap.put(key, value);
            }
         }      
      }
      
  -   private Map<String, Object> parseParams(String paramStr, String separator)
  +   private Param getParam(String name)
      {
  -      Map<String, Object> target = new HashMap<String, Object>();
  -      parseParams(paramStr, separator, target);
  -      return target;      
  +      if (parameters == null) 
  +         parseRequest();
  +      return parameters.get(name);
      }
   
      @Override
  @@ -375,59 +459,72 @@
      
      public byte[] getFileBytes(String name)
      {
  -      if (parameters == null)
  -         parseRequest();
  +      Param p = getParam(name);
  +      return (p != null && p instanceof FileParam) ? 
  +               ((FileParam) p).getData() : null;
  +   }
         
  -      PartWrapper wrapper = parameters.get(name);
  -      return wrapper != null ? wrapper.getData() : null;
  +   public InputStream getFileInputStream(String name)
  +   {
  +      Param p = getParam(name);
  +      return (p != null && p instanceof FileParam) ? 
  +               ((FileParam) p).getInputStream() : null;      
      }
      
      public String getFileContentType(String name)
      {
  -      if (parameters == null)
  -         parseRequest();
  -      
  -      PartWrapper wrapper = parameters.get(name);      
  -      return wrapper != null ? wrapper.getContentType() : null;
  +      Param p = getParam(name);
  +      return (p != null && p instanceof FileParam) ? 
  +               ((FileParam) p).getContentType() : null;
      }
      
      public String getFileName(String name)
      {
  -      if (parameters == null)
  -         parseRequest();
  -      
  -      PartWrapper wrapper = parameters.get(name);
  -      return wrapper != null ? wrapper.getFileName() : null;
  +      Param p = getParam(name);    
  +      return (p != null && p instanceof FileParam) ? 
  +               ((FileParam) p).getContentType() : null;
      }   
      
      @Override
      public String getParameter(String name)
      {
  -      if (parameters == null) 
  -         parseRequest();
  -
  -      PartWrapper wrapper = parameters.get(name);
  -      return wrapper != null ? new String(wrapper.getData()) : super.getParameter(name);
  +      Param p = getParam(name);
  +      if (p != null && p instanceof ValueParam)
  +      {
  +         ValueParam vp = (ValueParam) p;
  +         if (vp.getValue() instanceof String) return (String) vp.getValue();
  +      }
  +      else
  +      {
  +         return super.getParameter(name);
  +      }
   
  -//      String[] values = (String[]) parameters.get(name);
  -//
  -//      if (values == null) 
  -//         return null;
  -//
  -//      return values[0];
  +      return null;
      }
   
      @Override
      public String[] getParameterValues(String name)
      {
  -      if (parameters == null) 
  -         parseRequest();
  -
  -//      return (String[]) parameters.get(name);
  -      
  -      PartWrapper wrapper = parameters.get(name);
  -      return wrapper != null ? new String[] { new String(parameters.get(name).getData()) } : 
  -         super.getParameterValues(name);
  +      Param p = getParam(name);
  +      if (p != null && p instanceof ValueParam)
  +      {
  +         ValueParam vp = (ValueParam) p;
  +         if (vp.getValue() instanceof List)
  +         {
  +            List vals = (List) vp.getValue();
  +            String[] values = new String[vals.size()];
  +            vals.toArray(values);
  +            return values;
  +         }
  +         else
  +         {
  +            return new String[] {(String) vp.getValue()};
  +         }
  +      }
  +      else
  +      {
  +         return super.getParameterValues(name);
  +      }
      }
   
      @Override
  @@ -436,21 +533,26 @@
         if (parameters == null)
            parseRequest();
   
  -      Map<String,Object> params = new HashMap<String,Object>();
  +      Map<String,Object> params = super.getParameterMap();
         
         for (String name : parameters.keySet())
         {
  -         PartWrapper w = parameters.get(name);         
  -         params.put(name, new String(w.getData()));
  +         Param p = parameters.get(name);
  +         if (p instanceof ValueParam)
  +         {
  +            ValueParam vp = (ValueParam) p;
  +            if (vp.getValue() instanceof String)
  +            {
  +               params.put(name, vp.getValue());               
  +            }
  +            else if (vp.getValue() instanceof List)
  +            {
  +               params.put(name, getParameterValues(name));
  +            }               
         }
  -      
  -      return params;
      }
   
  -   @Override
  -   public Object getAttribute(String string)
  -   {
  -      return super.getAttribute(string);
  +      return params;
      }
   
      @Override
  
  
  
  1.3       +17 -2     jboss-seam/src/main/org/jboss/seam/servlet/SeamMultipartFilter.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: SeamMultipartFilter.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/src/main/org/jboss/seam/servlet/SeamMultipartFilter.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- SeamMultipartFilter.java	4 Feb 2007 02:30:49 -0000	1.2
  +++ SeamMultipartFilter.java	4 Feb 2007 08:11:34 -0000	1.3
  @@ -3,6 +3,7 @@
   import java.io.IOException;
   
   import javax.servlet.FilterChain;
  +import javax.servlet.FilterConfig;
   import javax.servlet.ServletException;
   import javax.servlet.ServletRequest;
   import javax.servlet.ServletResponse;
  @@ -13,6 +14,20 @@
   {
      public static final String MULTIPART = "multipart/";   
      
  +   private boolean createTempFiles = false;
  +  
  +   @Override
  +   public void init(FilterConfig filterConfig) 
  +       throws ServletException
  +   {
  +      super.init(filterConfig);
  +      String param = filterConfig.getInitParameter("createTempFiles");
  +      if (param != null) 
  +      {
  +         createTempFiles = "true".equals(param);
  +      }
  +   }
  +   
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
               throws IOException, ServletException
      {
  @@ -26,7 +41,7 @@
   
         if (isMultipartRequest(httpRequest))
         {
  -         chain.doFilter(new MultipartRequest(httpRequest), response);
  +         chain.doFilter(new MultipartRequest(httpRequest, createTempFiles), response);
         }
         else
         {
  
  
  



More information about the jboss-cvs-commits mailing list