You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by gd...@apache.org on 2001/07/01 05:31:22 UTC

cvs commit: xml-axis/java/test/encoding TestArrayListConversions.java PackageTests.java

gdaniels    01/06/30 20:31:21

  Modified:    java/src/org/apache/axis AxisEngine.java
               java/src/org/apache/axis/client ServiceClient.java
               java/src/org/apache/axis/encoding ArraySerializer.java
                        SOAPTypeMappingRegistry.java
               java/src/org/apache/axis/message MessageElement.java
               java/src/org/apache/axis/providers/java RPCProvider.java
               java/src/org/apache/axis/transport/local LocalSender.java
                        LocalTransport.java
               java/test/encoding PackageTests.java
  Added:       java/test/encoding TestArrayListConversions.java
  Log:
  * Add the ability to automatically do List -> Array -> List conversions.
  
    This involves two pieces.  First, the ArraySerializer can now happily
    serialize anything that implements List (Vector, LinkedList, etc).
    There is now also a check in SOAPTypeMappingRegistry for Lists in a
    similar vein to the one that was already there for arrays.
  
    LIMITATIONS: Right now we look at the first element in a List to
    determine the SOAP Array type.  This means we can't use Lists which
    contain multiple object types.  Long term we probably want to send
    "anyType" as the array type and explicitly xsi:type the elements.
  
    The second piece is in RPCProvider, which now attempts to do array ->
    List conversion if the first invoke() fails with an illegal argument
    exception.
  
    The end result of all this is that you can invoke a service with a
    LinkedList argument, have it get automatically serialized into a
    SOAP array, and then that array automatically gets converted into
    (say) a Vector on the other side so the desired function can be
    called.
  
    We might want a switch to turn this behavior on and off, and we should
    probably think all the way through the ramifications of matching
    methods this way (i.e. how can we decide between service(Vector) and
    service(LinkedList)?)
  
  * Add unit tests for the above.
  
  * Add a constructor for LocalTransport which lets you specify a particular
    AxisServer you'd like to use.  This allows dynamic configuration of an
    AxisServer for testing (see test.encoding.TestArrayListConversions for
    an example)
  
  * Remove a few debugging statements, and add a few.
  
  Revision  Changes    Path
  1.21      +1 -0      xml-axis/java/src/org/apache/axis/AxisEngine.java
  
  Index: AxisEngine.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/AxisEngine.java,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- AxisEngine.java	2001/06/29 13:14:30	1.20
  +++ AxisEngine.java	2001/07/01 03:30:37	1.21
  @@ -312,6 +312,7 @@
        */
       public void deployService(String key, SOAPService service)
       {
  +        Debug.Print(2, "Deploying service '" + key + "' into " + this);
           service.setName(key);
           service.setEngine(this);
           
  
  
  
  1.26      +0 -1      xml-axis/java/src/org/apache/axis/client/ServiceClient.java
  
  Index: ServiceClient.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/client/ServiceClient.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- ServiceClient.java	2001/06/29 20:34:16	1.25
  +++ ServiceClient.java	2001/07/01 03:30:42	1.26
  @@ -207,7 +207,6 @@
        */
       public static synchronized void addTransportPackage(String packageName)
       {
  -      System.out.println("Adding package " + packageName);
           String currentPackages = System.getProperty(TRANSPORT_PROPERTY);
           if (currentPackages == null) {
             currentPackages = "";
  
  
  
  1.6       +19 -4     xml-axis/java/src/org/apache/axis/encoding/ArraySerializer.java
  
  Index: ArraySerializer.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ArraySerializer.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ArraySerializer.java	2001/06/26 21:01:06	1.5
  +++ ArraySerializer.java	2001/07/01 03:30:48	1.6
  @@ -62,6 +62,7 @@
   import org.xml.sax.helpers.AttributesImpl;
   import java.lang.reflect.Array;
   import java.io.IOException;
  +import java.util.List;
   
   /** An ArraySerializer handles serializing and deserializing SOAP
    * arrays.
  @@ -192,17 +193,29 @@
               throw new IOException("Can't serialize null Arrays just yet...");
       
           Class cls = value.getClass();
  -        if (!cls.isArray())
  +        List list = null;
  +        
  +        if (!cls.isArray()) {
  +          if (!(value instanceof List)) {
               throw new IOException("Can't seialize a " + cls.getName() +
                                     " with the ArraySerializer!");
  +          }
  +          list = (List)value;
  +        }
  +        
  +        Class componentType;
  +        if (list == null) {
  +          componentType = cls.getComponentType();
  +        } else {
  +          componentType = list.get(0).getClass();
  +        }
           
  -        Class componentType = cls.getComponentType();
           QName componentQName = context.getQNameForClass(componentType);
           if (componentQName == null)
               throw new IOException("No mapped schema type for " + componentType.getName());
           String prefix = context.getPrefixForURI(componentQName.getNamespaceURI());
           String arrayType = prefix + ":" + componentQName.getLocalPart();
  -        int len = Array.getLength(value);
  +        int len = (list == null) ? Array.getLength(value) : list.size();
           
           arrayType += "[" + len + "]";
           
  @@ -224,7 +237,9 @@
           context.startElement(name, attrs);
           
           for (int index = 0; index < len; index++)
  -            context.serialize(new QName("","item"), null, Array.get(value, index));
  +            context.serialize(new QName("","item"), null,
  +                              (list == null) ? Array.get(value, index) :
  +                                              list.get(index));
           
           context.endElement();
       }
  
  
  
  1.16      +61 -1     xml-axis/java/src/org/apache/axis/encoding/SOAPTypeMappingRegistry.java
  
  Index: SOAPTypeMappingRegistry.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/SOAPTypeMappingRegistry.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- SOAPTypeMappingRegistry.java	2001/06/10 19:57:23	1.15
  +++ SOAPTypeMappingRegistry.java	2001/07/01 03:30:50	1.16
  @@ -1,3 +1,58 @@
  +/*
  + * The Apache Software License, Version 1.1
  + *
  + *
  + * Copyright (c) 2001 The Apache Software Foundation.  All rights
  + * reserved.
  + *
  + * Redistribution and use in source and binary forms, with or without
  + * modification, 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 "Axis" 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 (INCLUDING, 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.axis.encoding;
   
   import org.apache.axis.Constants;
  @@ -5,6 +60,7 @@
   import org.xml.sax.*;
   
   import java.util.Date;
  +import java.util.List;
   import java.math.BigDecimal;
   
   public class SOAPTypeMappingRegistry extends TypeMappingRegistry { 
  @@ -125,7 +181,8 @@
           Serializer ser = super.getSerializer(_class);
           
           if ((ser == null) && (_class != null) &&
  -            (_class.isArray())) {
  +            (_class.isArray() ||
  +             List.class.isAssignableFrom(_class))) {
               ser = arraySer;
           }
           
  @@ -136,6 +193,8 @@
           QName qName = super.getTypeQName(_class);
           if ((qName == null) && (_class != null)) {
               if (_class.isArray()) qName = SOAP_ARRAY;
  +            if (List.class.isAssignableFrom(_class))
  +              qName = SOAP_ARRAY;
               if (_class == boolean.class) qName = XSD_BOOLEAN;
               if (_class == double.class)  qName = XSD_DOUBLE;
               if (_class == float.class)   qName = XSD_FLOAT;
  @@ -157,6 +216,7 @@
           addSerializer(java.lang.Short.class, XSD_SHORT, se);
           addSerializer(java.util.Date.class, XSD_DATE, new DateSerializer());
           addSerializer(java.math.BigDecimal.class, XSD_DECIMAL, se);
  +        addSerializer(java.util.Vector.class, null, arraySer);
           
           addDeserializersFor(XSD_STRING, java.lang.String.class, new StringDeserializerFactory());    
           addDeserializersFor(XSD_BOOLEAN, java.lang.Boolean.class, new BooleanDeserializerFactory());
  
  
  
  1.23      +1 -1      xml-axis/java/src/org/apache/axis/message/MessageElement.java
  
  Index: MessageElement.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/MessageElement.java,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- MessageElement.java	2001/06/21 11:19:46	1.22
  +++ MessageElement.java	2001/07/01 03:30:56	1.23
  @@ -102,7 +102,7 @@
       {
           if (DEBUG_LOG) {
               System.out.println("New MessageElement named " + localPart);
  -            for (int i = 0; i < attributes.getLength(); i++) {
  +            for (int i = 0; attributes != null && i < attributes.getLength(); i++) {
                   System.out.println("  " + attributes.getQName(i) + " = '" + attributes.getValue(i) + "'");
               }
           }
  
  
  
  1.7       +57 -19    xml-axis/java/src/org/apache/axis/providers/java/RPCProvider.java
  
  Index: RPCProvider.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/providers/java/RPCProvider.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- RPCProvider.java	2001/06/25 14:30:55	1.6
  +++ RPCProvider.java	2001/07/01 03:31:01	1.7
  @@ -71,6 +71,41 @@
   public class RPCProvider extends JavaProvider {
       private static final boolean DEBUG_LOG = false;
       
  +    /** Utility function to convert an Object to some desired Class.
  +     * 
  +     * Right now this only works for arrays -> Lists, but it might be
  +     * expanded into a more general form (or pulled out into another
  +     * class) later.
  +     * 
  +     * @param arg the array to convert
  +     * @param destClass the actual class we want (must implement List)
  +     */
  +    private Object convert(Object arg, Class destClass)
  +    {
  +      // Right now only converts arrays -> Lists...
  +      if (!arg.getClass().isArray())
  +        return arg;
  +      
  +      Object [] argArray = (Object [])arg;
  +      
  +      if (List.class.isAssignableFrom(destClass)) {
  +        List list = null;
  +        try {
  +          list = (List)destClass.newInstance();
  +        } catch (Exception e) {
  +          // Couldn't build one for some reason... so forget it.
  +          return arg;
  +        }
  +        
  +        for (int j = 0; j < argArray.length; j++) {
  +          list.add(argArray[j]);
  +        }
  +        return list;
  +      }
  +      
  +      return arg;
  +    }
  +    
       public void processMessage (MessageContext msgContext,
                                   String clsName,
                                   String methodName,
  @@ -91,24 +126,6 @@
               
               RPCElement   body  = (RPCElement) bodies.get( bNum );
   
  -            /* This breaks JWS.  With JWS, the service name is JWSProcessor,
  -               but the URN is the actual service name.  hmmmmm.
  -            
  -            // validate that the incoming targetService is the same as the
  -            // namespace URI of the body... if not, someone spoofed the
  -            // SOAPAction header (or equivalent)
  -            if (body.getNamespaceURI() != null
  -                && !body.getNamespaceURI().equals(msgContext.getTargetService()))
  -            {
  -                throw new AxisFault( "AxisServer.error",
  -                                    "Incoming target service name doesn't match body namespace URI\n" +
  -                                        "Target service name=" + msgContext.getTargetService() + "\n" +
  -                                        "Body URI=" + body.getNamespaceURI(),
  -                                    null, null );  // should they??
  -            }
  -             */
  -            
  -            
               String       mName      = body.getMethodName();
               Vector       args       = body.getParams();
               Object[]     argValues  =  null ;
  @@ -196,7 +213,28 @@
               }
               */
   
  -            Object objRes = method.invoke( obj, argValues );
  +            Object objRes;
  +            try {
  +              objRes = method.invoke( obj, argValues );
  +            } catch (IllegalArgumentException e) {
  +              
  +              // Hm - maybe we can help this with a conversion or two...
  +              for (int i = 0; i < params.length; i++) {
  +                Object thisArg = argValues[i];
  +                if (!params[i].isAssignableFrom(thisArg.getClass())) {
  +                  // Attempt conversion for each non-assignable argument
  +                  Debug.Print(3, "Trying to convert " +
  +                                 thisArg.getClass().getName() +
  +                                 " to " + params[i].getName());
  +                  Object newArg = convert(thisArg, params[i]);
  +                  if (newArg != thisArg)
  +                    argValues[i] = newArg;
  +                }
  +              }
  +              
  +              // OK, now try again...
  +              objRes = method.invoke( obj, argValues );
  +            }
   
               /* Now put the result in the result SOAPEnvelope */
               /*************************************************/
  
  
  
  1.3       +18 -7     xml-axis/java/src/org/apache/axis/transport/local/LocalSender.java
  
  Index: LocalSender.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/transport/local/LocalSender.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- LocalSender.java	2001/06/27 16:18:44	1.2
  +++ LocalSender.java	2001/07/01 03:31:07	1.3
  @@ -84,17 +84,28 @@
     public void invoke(MessageContext clientContext) throws AxisFault {
       Debug.Print( 1, "Enter: LocalSender::invoke" );
   
  -    // This should have already been done, but it doesn't appear to be
  -    // something that can be relied on.  Oh, well...
  -    if (server == null) init();
  +    AxisServer targetServer = (AxisServer)clientContext.
  +                                     getProperty(LocalTransport.LOCAL_SERVER);
  +    Debug.Print(3, "LocalSender using server " + targetServer);
       
  +    if (targetServer == null) {
  +      // This should have already been done, but it doesn't appear to be
  +      // something that can be relied on.  Oh, well...
  +      if (server == null) init();
  +      targetServer = server;
  +    }
  +    
       // Define a new messageContext per request
  -    MessageContext serverContext = new MessageContext(server);
  +    MessageContext serverContext = new MessageContext(targetServer);
   
       // copy the request, and force its format to String in order to
       // exercise the serializers.
  -    serverContext.setRequestMessage(clientContext.getRequestMessage());
  -    serverContext.getRequestMessage().getAsString();
  +    String msgStr = clientContext.getRequestMessage().getAsString();
  +    
  +    Debug.Print(3, "LocalSender sending XML:");
  +    Debug.Print(3, msgStr);
  +
  +    serverContext.setRequestMessage(new Message(msgStr));
   
       // copy soap action if it is present
       String action = clientContext.getStrProp(HTTPConstants.MC_HTTP_SOAPACTION);
  @@ -118,7 +129,7 @@
       }
   
       // invoke the request
  -    server.invoke(serverContext);
  +    targetServer.invoke(serverContext);
   
       // copy back the response, and force its format to String in order to
       // exercise the deserializers.
  
  
  
  1.2       +36 -1     xml-axis/java/src/org/apache/axis/transport/local/LocalTransport.java
  
  Index: LocalTransport.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/transport/local/LocalTransport.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- LocalTransport.java	2001/06/29 19:59:09	1.1
  +++ LocalTransport.java	2001/07/01 03:31:09	1.2
  @@ -2,7 +2,7 @@
    * The Apache Software License, Version 1.1
    *
    *
  - * Copyright (c) 1999 The Apache Software Foundation.  All rights
  + * Copyright (c) 2001 The Apache Software Foundation.  All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
  @@ -59,8 +59,18 @@
   import org.apache.axis.utils.Debug ;
   import org.apache.axis.client.Transport;
   import org.apache.axis.client.ServiceClient;
  +import org.apache.axis.server.AxisServer;
   
   /**
  + * A Transport which will cause an invocation via a "local" AxisServer.
  + * 
  + * Serialization will still be tested, as the requests and responses
  + * pass through a String conversion (see LocalSender.java) - this is
  + * primarily for testing and debugging.
  + * 
  + * This transport will either allow the LocalSender to create its own
  + * AxisServer, or if you have one you've configured and wish to use,
  + * you may pass it in to the constructor here.
    *
    * @author Rob Jellinghaus (robj@unrealities.com)
    * @author Doug Davis (dug@us.ibm.com)
  @@ -68,6 +78,29 @@
    */
   public class LocalTransport extends Transport
   {
  +    public static final String LOCAL_SERVER = "LocalTransport.AxisServer";
  +    
  +    private AxisServer server;
  +    
  +    /** No-arg constructor, which will use an AxisServer constructed
  +     * by the LocalSender (see LocalSender.java).
  +     * 
  +     */
  +    public LocalTransport()
  +    {
  +    }
  +    
  +    /** Use this constructor if you have a particular server kicking
  +     * around (perhaps which you've already deployed useful stuff into)
  +     * which you'd like to use.
  +     * 
  +     * @param server an AxisServer which will bubble down to the LocalSender
  +     */
  +    public LocalTransport(AxisServer server)
  +    {
  +      this.server = server;
  +    }
  +    
       /**
        * Set up any transport-specific derived properties in the message context.
        * @param context the context to set up
  @@ -77,6 +110,8 @@
       public void setupMessageContext (MessageContext mc, ServiceClient serv, AxisEngine engine)
       {
           mc.setTransportName("local");
  +        if (server != null)
  +          mc.setProperty(LOCAL_SERVER, server);
       }
   }
   
  
  
  
  1.8       +1 -0      xml-axis/java/test/encoding/PackageTests.java
  
  Index: PackageTests.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/test/encoding/PackageTests.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- PackageTests.java	2001/06/15 00:20:02	1.7
  +++ PackageTests.java	2001/07/01 03:31:16	1.8
  @@ -27,6 +27,7 @@
           suite.addTestSuite(TestString.class);
           suite.addTestSuite(TestHrefs.class);
           suite.addTestSuite(TestBody.class);
  +        suite.addTestSuite(TestArrayListConversions.class);
   
           return suite;
       }
  
  
  
  1.1                  xml-axis/java/test/encoding/TestArrayListConversions.java
  
  Index: TestArrayListConversions.java
  ===================================================================
  package test.encoding;
  
  import org.apache.axis.*;
  import org.apache.axis.client.*;
  import org.apache.axis.handlers.soap.SOAPService;
  import org.apache.axis.registries.HandlerRegistry;
  import org.apache.axis.server.AxisServer;
  import org.apache.axis.transport.local.LocalTransport;
  
  import java.util.*;
  import junit.framework.TestCase;
  
  public class TestArrayListConversions extends TestCase {
    private static final String SERVICE_NAME = "TestArrayConversions";
    
    private ServiceClient client;
    
    public TestArrayListConversions()
    {
      super("service");
    }
    
    public TestArrayListConversions(String name)
    {
      super(name);
      init();
    }
    
    private static boolean equals(List list, Object obj)
    {
      if ((list == null) || (obj == null))
        return false;
      
      if (!obj.getClass().isArray()) return false;
      
      Object [] array = (Object [])obj;
      Iterator iter = list.iterator();
  
      for (int i=0; i < array.length; i++)
        if (!(array[i].equals(iter.next()))) return false;    return true;
    }
  
    public void init()
    {
      ServiceClient.initialize();
      AxisServer server = new AxisServer();
      HandlerRegistry hr = (HandlerRegistry) server.getHandlerRegistry();
      Handler disp = hr.find("RPCDispatcher");    SOAPService service = new SOAPService(disp, "RPCDispatcher");
      service.addOption("className", "test.encoding.TestArrayListConversions");
      
      server.deployService(SERVICE_NAME, service);
      
      client = new ServiceClient(new LocalTransport(server));
    }
    
    public void testVectorConversion() throws Exception
    {
      Vector v = new Vector();
      v.addElement("Hi there!");
      v.addElement("This'll be a SOAP Array and then a LinkedList!");
      Object ret = client.invoke(SERVICE_NAME, "echoLinkedList",
                                 new Object [] { v });
      if (!equals(v, ret)) assertEquals(v, ret);
    }
    
    public void testLinkedListConversion() throws Exception
    {
      LinkedList l = new LinkedList();
      l.add("Linked list item #1");
      l.add("Second linked list item");
      l.add("This will be a SOAP Array then a Vector!");
  
      Object ret = client.invoke(SERVICE_NAME, "echoVector",
                          new Object [] { l });
      if (!equals(l, ret)) assertEquals(l, ret);
    }
        
    public void testArrayConversion() throws Exception
    {
      Vector v = new Vector();
      v.addElement("Hi there!");
      v.addElement("This'll be a SOAP Array");
  
      Object ret = client.invoke(SERVICE_NAME, "echoArray",
                          new Object [] { v });
      if (!equals(v, ret)) assertEquals(v, ret);
    }
  
    public static void main(String [] args)
    {
      TestArrayListConversions tester =
                new TestArrayListConversions("TestArrayListConversions");
      try {
        tester.testArrayConversion();
        tester.testLinkedListConversion();
        tester.testVectorConversion();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    
    /****************************************************************
     * 
     * Service methods - this class is also deployed as an Axis RPC
     * service for convenience.  These guys just echo various things.
     * 
     */
    public LinkedList echoLinkedList(LinkedList l)
    {
      return l;
    }
    
    public Vector echoVector(Vector v)
    {
      return v;
    }
    
    public Object [] echoArray(Object [] array)
    {
      return array;
    }
  }