Revision: 374
Author:   tfenne
Date:     2006-08-13 13:50:36 -0700 (Sun, 13 Aug 2006)
ViewCVS:  http://svn.sourceforge.net/stripes/?rev=374&view=rev

Log Message:
-----------
Adding a sort attribute to options-collection and options-enumeration. 
Refactored OptionsEnumerationTag to extend OptionsCollectionTag, and created a 
new BeanComparator for sorting beans based on properties.

Modified Paths:
--------------
    trunk/examples/Examples.iml
    trunk/examples/web/bugzooky/AddEditBug.jsp
    trunk/stripes/resources/stripes.tld
    trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsCollectionTag.java
    
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsEnumerationTag.java

Added Paths:
-----------
    trunk/stripes/src/net/sourceforge/stripes/util/bean/BeanComparator.java
    trunk/tests/src/net/sourceforge/stripes/util/bean/BeanComparatorTest.java
Modified: trunk/examples/Examples.iml
===================================================================
--- trunk/examples/Examples.iml 2006-08-12 14:17:48 UTC (rev 373)
+++ trunk/examples/Examples.iml 2006-08-13 20:50:36 UTC (rev 374)
@@ -76,11 +76,6 @@
     </containerElement>
     <containerElement type="library" level="module">
       <attribute name="method" value="1" />
-      <attribute name="URI" value="/WEB-INF/lib/ognl-2.6.7.jar" />
-      <url>jar://$MODULE_DIR$/../stripes/lib/deploy/ognl-2.6.7.jar!/</url>
-    </containerElement>
-    <containerElement type="library" level="module">
-      <attribute name="method" value="1" />
       <attribute name="URI" value="/WEB-INF/lib/log4j-1.2.9.jar" />
       <url>jar://$MODULE_DIR$/../stripes/lib/test/log4j-1.2.9.jar!/</url>
     </containerElement>

Modified: trunk/examples/web/bugzooky/AddEditBug.jsp
===================================================================
--- trunk/examples/web/bugzooky/AddEditBug.jsp  2006-08-12 14:17:48 UTC (rev 
373)
+++ trunk/examples/web/bugzooky/AddEditBug.jsp  2006-08-13 20:50:36 UTC (rev 
374)
@@ -39,7 +39,7 @@
                     <td>
                         <stripes:select name="bug.owner.id">
                             <stripes:options-collection 
collection="${personManager.allPeople}"
-                                                        label="username" 
value="id"/>
+                                                        label="username" 
value="id" sort="label"/>
                         </stripes:select>
                     </td>
                 </tr>

Modified: trunk/stripes/resources/stripes.tld
===================================================================
--- trunk/stripes/resources/stripes.tld 2006-08-12 14:17:48 UTC (rev 373)
+++ trunk/stripes/resources/stripes.tld 2006-08-13 20:50:36 UTC (rev 374)
@@ -1092,6 +1092,15 @@
         </attribute>
         <attribute>
             <description>
+                A comma separated list of bean properties by which the 
collection
+                should be sorted before rendering the options. Special values 
of
+                'label' and 'value' indicate that the options should be sorted 
by the
+                label and value respectively - even when using localized or 
derived values.
+            </description>
+            
<name>sort</name><required>false</required><rtexprvalue>true</rtexprvalue>
+        </attribute>
+        <attribute>
+            <description>
                 The collection of beans to use to generate options.  This 
value must resolve to
                 a Collection, and therefore it will most often be an EL 
expression
                 (or a scriptlet, if you must).
@@ -1180,7 +1189,17 @@
             </description>
             
<name>label</name><required>false</required><rtexprvalue>true</rtexprvalue>
         </attribute>
+        <attribute>
+            <description>
+                A comma separated list of bean properties by which the enum 
values
+                should be sorted before rendering the options. Special values 
of
+                'label' and 'value' indicate that the options should be sorted 
by the
+                label and value respectively - even when using localized or 
derived values.
+            </description>
+            
<name>sort</name><required>false</required><rtexprvalue>true</rtexprvalue>
+        </attribute>
 
+
         <!-- Start: Standard HTML attributes -->
         
<attribute><description>@accesskey@</description><name>accesskey</name><required>false</required><rtexprvalue>true</rtexprvalue></attribute>
         
<attribute><description>@class@</description><name>class</name><required>false</required><rtexprvalue>true</rtexprvalue></attribute>

Modified: 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsCollectionTag.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsCollectionTag.java    
    2006-08-12 14:17:48 UTC (rev 373)
+++ 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsCollectionTag.java    
    2006-08-13 20:50:36 UTC (rev 374)
@@ -18,11 +18,15 @@
 import net.sourceforge.stripes.localization.LocalizationUtility;
 import net.sourceforge.stripes.util.bean.BeanUtil;
 import net.sourceforge.stripes.util.bean.ExpressionException;
+import net.sourceforge.stripes.util.bean.BeanComparator;
 
 import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.tagext.Tag;
 import java.util.Collection;
 import java.util.Locale;
+import java.util.Collections;
+import java.util.List;
+import java.util.LinkedList;
 
 /**
  * <p>Writes a set of [EMAIL PROTECTED] <option value="foo">bar</option>} tags 
to the page based on the
@@ -74,7 +78,25 @@
     private Collection collection;
     private String value;
     private String label;
+    private String sort;
 
+    /**
+     * A little container class that holds an entry in the collection of items 
being used
+     * to generate the options, along with the determined label and value 
(either from a
+     * property, or a localized value).
+     */
+    public static class Entry {
+        public Object bean, label, value;
+        Entry(Object bean, Object label, Object value) {
+            this.bean = bean;
+            this.label = label;
+            this.value = value;
+        }
+    }
+
+    /** Internal list of entires that is assembled from the items in the 
collection. */
+    private List<Entry> entries;
+
     /** Sets the collection that will be used to generate options. */
     public void setCollection(Collection collection) {
         this.collection = collection;
@@ -115,27 +137,50 @@
         return label;
     }
 
+    /**
+     * Sets a comma separated list of properties by which the beans in the 
collection will
+     * be sorted prior to rendering them as options.  'label' and 'value' are 
special case
+     * properties that are used to indicate the generated label and value of 
the option.
+     *
+     * @param sort the name of the attribute(s) used to sort the collection of 
options
+     */
+    public void setSort(String sort) {
+        this.sort = sort;
+    }
 
+    /** Gets the comma separated list of properties by which the collection is 
sorted. */
+    public String getSort() {
+        return sort;
+    }
+
     /**
-     * Iterates through the collection and uses an instance of InputOptionTag 
to generate each
-     * individual option with the correct state.  It is assumed that each 
element in the collection
+     * Adds an entry to the interal list of items being used to generate 
options.
+     * @param item the object represented by the option
+     * @param label the actual label for the option
+     * @param value the actual value for the option
+     */
+    protected void addEntry(Object item, Object label, Object value) {
+        if (this.entries == null) this.entries = new LinkedList<Entry>();
+        this.entries.add(new Entry(item, label, value));
+    }
+
+    /**
+     * Iterates through the collection and generates the list of Entry objects 
that can then
+     * be sorted and rendered into options. It is assumed that each element in 
the collection
      * has non-null values for the properties specified for generating the 
label and value.
      *
      * @return SKIP_BODY in all cases
      * @throws JspException if either the label or value attributes specify 
properties that are
-     *         not present on the beans in the collection, or output cannot be 
written.
+     *         not present on the beans in the collection
      */
     public int doStartTag() throws JspException {
         String labelProperty = getLabel();
         String valueProperty = getValue();
 
-        Locale locale = getPageContext().getRequest().getLocale();
-        InputOptionTag tag = new InputOptionTag();
-        tag.setParent(this);
-        tag.setPageContext(getPageContext());
-        tag.getAttributes().putAll(getAttributes());
 
         try {
+            Locale locale = getPageContext().getRequest().getLocale();
+
             for (Object item : this.collection) {
                 Class clazz = item.getClass();
 
@@ -155,12 +200,7 @@
                 }
                 if (localizedLabel != null) label = localizedLabel;
 
-                tag.setLabel(label.toString());
-                tag.setValue(value);
-                tag.doStartTag();
-                tag.doInitBody();
-                tag.doAfterBody();
-                tag.doEndTag();
+                addEntry(item, label, value);
             }
         }
         catch (ExpressionException ee) {
@@ -173,11 +213,43 @@
     }
 
     /**
-     * Does nothing.
+     * Optionally sorts the assembled entries and then renders them into a 
series of
+     * option tags using an instance of InputOptionTag to do the rendering 
work.
      *
      * @return EVAL_PAGE in all cases.
      */
     public int doEndTag() throws JspException {
+        // Determine if we're going to be sorting the collection
+        List<Entry> sortedEntries = new LinkedList<Entry>(this.entries);
+        if (this.sort != null) {
+            String[] props = this.sort.split(" *, *");
+            for (int i=0;i<props.length;++i) {
+                if (!props[i].equals("label") && !props[i].equals("value")) {
+                    props[i] = "bean." + props[i];
+                }
+            }
+
+            Collections.sort(sortedEntries,
+                             new 
BeanComparator(getPageContext().getRequest().getLocale(), props));
+        }
+
+        InputOptionTag tag = new InputOptionTag();
+        tag.setParent(this);
+        tag.setPageContext(getPageContext());
+        tag.getAttributes().putAll(getAttributes());
+
+        for (Entry entry : sortedEntries) {
+            tag.setLabel(entry.label.toString());
+            tag.setValue(value);
+            tag.doStartTag();
+            tag.doInitBody();
+            tag.doAfterBody();
+            tag.doEndTag();
+        }
+
+        // Clean up any temporary state
+        this.entries.clear();
+
         return EVAL_PAGE;
     }
 }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsEnumerationTag.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsEnumerationTag.java   
    2006-08-12 14:17:48 UTC (rev 373)
+++ 
trunk/stripes/src/net/sourceforge/stripes/tag/InputOptionsEnumerationTag.java   
    2006-08-13 20:50:36 UTC (rev 374)
@@ -21,7 +21,6 @@
 import net.sourceforge.stripes.util.bean.ExpressionException;
 
 import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.Tag;
 import java.util.Locale;
 
 /**
@@ -58,9 +57,8 @@
  *
  * @author Tim Fennell
  */
-public class InputOptionsEnumerationTag extends HtmlTagSupport implements Tag {
+public class InputOptionsEnumerationTag extends InputOptionsCollectionTag {
     private String className;
-    private String label;
 
     /** Sets the fully qualified name of an enumeration class. */
     public void setEnum(String name) {
@@ -72,16 +70,6 @@
         return this.className;
     }
 
-    /** Sets the name of the property that will be used to generate the 
option's label. */
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    /** Gets the name of the property that will be used to generate the 
option's label. */
-    public String getLabel() {
-        return this.label;
-    }
-
     /**
      * Attempts to instantiate the Class object representing the enum and 
fetch the values of the
      * enum.  Then generates an option per value using an instance of an 
InputOptionTag.
@@ -110,12 +98,6 @@
 
         Enum[] enums = clazz.getEnumConstants();
 
-        InputOptionTag tag = new InputOptionTag();
-        tag.setParent(this);
-        tag.setPageContext(getPageContext());
-        tag.getAttributes().putAll(getAttributes());
-        tag.getAttributes().remove("enum");
-
         try {
             Locale locale = getPageContext().getRequest().getLocale();
 
@@ -128,37 +110,23 @@
                                                                   
clazz.getPackage().getName(),
                                                                   locale);
                 if (label == null) {
-                    if (this.label != null) {
-                        label = BeanUtil.getPropertyValue(this.label, item);
+                    if (getLabel() != null) {
+                        label = BeanUtil.getPropertyValue(getLabel(), item);
                     }
                     else {
                         label = item.toString();
                     }
                 }
 
-                tag.setLabel(label.toString());
-                tag.setValue(value);
-                tag.doStartTag();
-                tag.doInitBody();
-                tag.doAfterBody();
-                tag.doEndTag();
+                addEntry(item, label, value);
             }
         }
         catch (ExpressionException ee) {
             throw new StripesJspException("A problem occurred generating an 
options-enumeration. " +
                 "Most likely either the class [" + getEnum() + "] is not an 
enum or, [" +
-                    this.label + "] is not a valid property of the enum.", ee);
+                    getLabel() + "] is not a valid property of the enum.", ee);
         }
 
         return SKIP_BODY;
     }
-
-    /**
-     * Does nothing.
-     * @return EVAL_PAGE in all cases.
-     */
-    public int doEndTag() throws JspException {
-        return EVAL_PAGE;
-    }
-
 }

Added: trunk/stripes/src/net/sourceforge/stripes/util/bean/BeanComparator.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/bean/BeanComparator.java     
                        (rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/util/bean/BeanComparator.java     
2006-08-13 20:50:36 UTC (rev 374)
@@ -0,0 +1,113 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.util.bean;
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.Locale;
+
+/**
+ * <p>A comparator which compares objects based on one or more bean 
properties. Nested properties
+ * are fully supported.  If a property is non-String and implements [EMAIL 
PROTECTED] Comparable} then the
+ * [EMAIL PROTECTED] compareTo()} method is delegated to.  Otherwise the 
property is converted to a String
+ * and a [EMAIL PROTECTED] Locale} aware [EMAIL PROTECTED] Collator} is used 
to to compare property values.</p>
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.5
+ */
+public class BeanComparator implements Comparator<Object> {
+    private Locale locale;
+    private Collator collator;
+    private String[] properties;
+    private PropertyExpression[] expressions;
+
+    /**
+     * Constructs a BeanComparator for comparing beans based on the supplied 
set of properties,
+     * using the default system Locale to collate Strings.
+     *
+     * @param properties one or more property names to be used, in order, to 
sort beans
+     */
+    public BeanComparator(String... properties) {
+        this(Locale.getDefault(), properties);
+    }
+
+    /**
+     * Constructs a BeanComparator for comparing beans based on the supplied 
set of properties,
+     * using the supplied Locale to collate Strings.
+     *
+     * @param locale the Locale to be used for collating Strings
+     * @param properties one or more property names to be used, in order, to 
sort beans
+     */
+    public BeanComparator(Locale locale, String... properties) {
+        this.locale = locale;
+        this.collator = Collator.getInstance(locale);
+        this.properties = properties;
+        this.expressions = new PropertyExpression[properties.length];
+
+        for (int i=0; i<properties.length; ++i) {
+            this.expressions[i] = 
PropertyExpression.getExpression(properties[i]);
+        }
+    }
+
+    /**
+     * <p>Compares two JavaBeans for order. Returns a negative integer, zero, 
or a positive
+     * integer as the first argument sorts earlier, equal to, or later than 
the second.</p>
+     *
+     * <p>Iterates through the properties supplied in the constructor 
comparing the values of
+     * each property for the two beans.  As soon as a property is found that 
supplied a non-equal
+     * ordering, the ordering is returned. If all properties are equal, will 
return 0.</p>
+     *
+     * @param o1 the first object to be compared, must not be null.
+     * @param o2 the second object to be compared, must not be null.
+     * @return a negative integer, zero, or a positive integer as the first 
argument is less than,
+     *         equal to, or greater than the second.
+     * @throws ClassCastException if the arguments' types, or the types of the 
properties,
+     *         prevent them from being compared by this Comparator.
+     */
+    public int compare(Object o1, Object o2) {
+        int retval = 0;
+        Collator collator = Collator.getInstance(this.locale);
+
+        for (PropertyExpression expression : this.expressions) {
+            PropertyExpressionEvaluation e1 = new 
PropertyExpressionEvaluation(expression, o1);
+            PropertyExpressionEvaluation e2 = new 
PropertyExpressionEvaluation(expression, o2);
+
+            Object prop1 = e1.getValue();
+            Object prop2 = e2.getValue();
+
+            if (prop1 == null && prop2 == null) {
+                retval = 0;
+            }
+            else if (prop1 == null) {
+                retval = 1;
+            }
+            else if (prop2 == null) {
+                retval = -1;
+            }
+            else if ( !(prop1 instanceof String) && prop1 instanceof 
Comparable) {
+                retval = ((Comparable) prop1).compareTo(prop2);
+            }
+            else {
+                String string1 = prop1.toString();
+                String string2 = prop2.toString();
+                retval = this.collator.compare(string1, string2);
+            }
+
+            if (retval != 0) break;
+        }
+
+        return retval;
+    }
+}

Added: trunk/tests/src/net/sourceforge/stripes/util/bean/BeanComparatorTest.java
===================================================================
--- trunk/tests/src/net/sourceforge/stripes/util/bean/BeanComparatorTest.java   
                        (rev 0)
+++ trunk/tests/src/net/sourceforge/stripes/util/bean/BeanComparatorTest.java   
2006-08-13 20:50:36 UTC (rev 374)
@@ -0,0 +1,126 @@
+package net.sourceforge.stripes.util.bean;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+import net.sourceforge.stripes.test.TestBean;
+import net.sourceforge.stripes.test.TestEnum;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Test cases for the BeanComparator class that sorts lists of JavaBeans based 
on
+ * their properties.
+ *
+ * @author Tim Fennell
+ */
+public class BeanComparatorTest {
+    @Test(groups="fast")
+    public void testSimplePropertySort() throws Exception {
+        List<TestBean> beans = new ArrayList<TestBean>();
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("hello");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("goodbye");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("whatever");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("huh?");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("no way!");
+
+        Collections.sort(beans, new BeanComparator("stringProperty"));
+        Assert.assertEquals(beans.get(0).getStringProperty(), "goodbye");
+        Assert.assertEquals(beans.get(1).getStringProperty(), "hello");
+        Assert.assertEquals(beans.get(2).getStringProperty(), "huh?");
+        Assert.assertEquals(beans.get(3).getStringProperty(), "no way!");
+        Assert.assertEquals(beans.get(4).getStringProperty(), "whatever");
+    }
+
+    @Test(groups="fast")
+    public void testSimpleMultiPropertySort() throws Exception {
+        List<TestBean> beans = new ArrayList<TestBean>();
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setLongProperty(2l);
+        beans.get(beans.size()-1).setStringProperty("hello");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setLongProperty(2l);
+        beans.get(beans.size()-1).setStringProperty("goodbye");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setLongProperty(1l);
+        beans.get(beans.size()-1).setStringProperty("whatever");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setLongProperty(1l);
+        beans.get(beans.size()-1).setStringProperty("huh?");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setLongProperty(3l);
+        beans.get(beans.size()-1).setStringProperty("no way!");
+
+        Collections.sort(beans, new BeanComparator("longProperty", 
"stringProperty"));
+        Assert.assertEquals(beans.get(0).getStringProperty(), "huh?");
+        Assert.assertEquals(beans.get(1).getStringProperty(), "whatever");
+        Assert.assertEquals(beans.get(2).getStringProperty(), "goodbye");
+        Assert.assertEquals(beans.get(3).getStringProperty(), "hello");
+        Assert.assertEquals(beans.get(4).getStringProperty(), "no way!");
+    }
+
+    @Test(groups="fast")
+    public void testNullPropertySort() throws Exception {
+        List<TestBean> beans = new ArrayList<TestBean>();
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("hello");
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty(null);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("whatever");
+
+        Collections.sort(beans, new BeanComparator("stringProperty"));
+        Assert.assertEquals(beans.get(0).getStringProperty(), "hello");
+        Assert.assertEquals(beans.get(1).getStringProperty(), "whatever");
+        Assert.assertEquals(beans.get(2).getStringProperty(), null);
+    }
+
+    @Test(groups="fast")
+    public void testNullPropertySort2() throws Exception {
+        List<TestBean> beans = new ArrayList<TestBean>();
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty(null);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty(null);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setStringProperty("whatever");
+
+        Collections.sort(beans, new BeanComparator("stringProperty"));
+        Assert.assertEquals(beans.get(0).getStringProperty(), "whatever");
+        Assert.assertEquals(beans.get(1).getStringProperty(), null);
+        Assert.assertEquals(beans.get(2).getStringProperty(), null);
+    }
+
+    @Test(groups="fast")
+    public void testNestedPropertySort() throws Exception {
+        List<TestBean> beans = new ArrayList<TestBean>();
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setNestedBean(new TestBean());
+        
beans.get(beans.size()-1).getNestedBean().setEnumProperty(TestEnum.Fourth);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setNestedBean(new TestBean());
+        
beans.get(beans.size()-1).getNestedBean().setEnumProperty(TestEnum.Second);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setNestedBean(new TestBean());
+        
beans.get(beans.size()-1).getNestedBean().setEnumProperty(TestEnum.Ninth);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setNestedBean(new TestBean());
+        
beans.get(beans.size()-1).getNestedBean().setEnumProperty(TestEnum.Eight);
+        beans.add(new TestBean());
+        beans.get(beans.size()-1).setNestedBean(new TestBean());
+        
beans.get(beans.size()-1).getNestedBean().setEnumProperty(TestEnum.First);
+
+        Collections.sort(beans, new BeanComparator("nestedBean.enumProperty"));
+        Assert.assertEquals(beans.get(0).getNestedBean().getEnumProperty(), 
TestEnum.First);
+        Assert.assertEquals(beans.get(1).getNestedBean().getEnumProperty(), 
TestEnum.Second);
+        Assert.assertEquals(beans.get(2).getNestedBean().getEnumProperty(), 
TestEnum.Fourth);
+        Assert.assertEquals(beans.get(3).getNestedBean().getEnumProperty(), 
TestEnum.Eight);
+        Assert.assertEquals(beans.get(4).getNestedBean().getEnumProperty(), 
TestEnum.Ninth);
+    }
+}


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to