cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bimargul...@apache.org
Subject svn commit: r595519 - in /incubator/cxf/trunk/rt/javascript/src: main/java/org/apache/cxf/javascript/service/ test/java/org/apache/cxf/javascript/ test/java/org/apache/cxf/javascript/fortest/ test/java/org/apache/cxf/javascript/service/ test/resources/
Date Fri, 16 Nov 2007 02:04:46 GMT
Author: bimargulies
Date: Thu Nov 15 18:04:44 2007
New Revision: 595519

URL: http://svn.apache.org/viewvc?rev=595519&view=rev
Log:
Generate response parsing for the simplest case, and test it.

Added:
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrapped.java
      - copied, changed from r595489, incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrappedUnqualified.java
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/StringWrapper.java
Removed:
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrappedUnqualified.java
Modified:
    incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/Messages.properties
    incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/ServiceJavascriptBuilder.java
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JavascriptTestUtilities.java
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JsSimpleDomNode.java
    incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/service/DocLitWrappedTest.java
    incubator/cxf/trunk/rt/javascript/src/test/resources/serializationTestBeans.xml

Modified: incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/Messages.properties
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/Messages.properties?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/Messages.properties
(original)
+++ incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/Messages.properties
Thu Nov 15 18:04:44 2007
@@ -19,4 +19,5 @@
 #
 #
 NO_SOAP_BINDING=Service {1} has no SOAP binding.
-
+MULTIPLE_OUTPUTS=Operation {1} has more than one output part.
+RPC= Service {1} calls for unsupported RPC binding style.
\ No newline at end of file

Modified: incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/ServiceJavascriptBuilder.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/ServiceJavascriptBuilder.java?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/ServiceJavascriptBuilder.java
(original)
+++ incubator/cxf/trunk/rt/javascript/src/main/java/org/apache/cxf/javascript/service/ServiceJavascriptBuilder.java
Thu Nov 15 18:04:44 2007
@@ -144,7 +144,9 @@
 
     @Override
     public void begin(OperationInfo op) {
-        assert !isRPC;
+        if (isRPC) {
+            unsupportedConstruct("RPC", op.getInterface().getName().toString());
+        }
         boolean isWrapped = op.isUnwrappedCapable();
         // we only process the wrapped operation, not the unwrapped alternative.
         if (op.isUnwrapped()) {
@@ -164,10 +166,6 @@
         String wrapperClassName = null;
         StringBuilder parameterList = new StringBuilder();
 
-        // the message content is a set of elements. Perhaps they come from the
-        // parts,
-        // or perhaps we invent them.
-        List<ElementAndNames> elements = new ArrayList<ElementAndNames>();
         List<MessagePartInfo> parts = null;
 
         if (inputMessage != null) {
@@ -198,42 +196,40 @@
         code.append(currentInterfaceClassName + ".prototype." + opFunctionName + " = " +
opGlobalFunctionName
                     + ";\n\n");
 
-        createInputSerializer(op, isWrapped, inputParameterNames, wrapperClassName, elements,
-                              parts);
+        createInputSerializer(op, isWrapped, inputParameterNames, wrapperClassName, parts);
+        
+        MessageInfo outputMessage = op.getOutput();
+        if (outputMessage != null) {
+            createResponseDeserializer(op, outputMessage.getMessageParts());
+        }
+    }
+    
+    private void createResponseDeserializer(OperationInfo op, List<MessagePartInfo>
parts) {
+        if (parts.size() != 1) {
+            unsupportedConstruct("MULTIPLE_OUTPUTS", op.getName().toString());
+        }
+        List<ElementAndNames> elements = new ArrayList<ElementAndNames>();
+        String functionName = nameManager.getJavascriptName(op.getName()) + "_deserializeResponse";
+        code.append("function " + functionName + "(cxfjsutils, partElement) {\n");
+        getElementsForParts(elements, parts);
+        ElementAndNames element = elements.get(0);
+        XmlSchemaComplexType type = (XmlSchemaComplexType)element.getElement().getSchemaType();
+        assert type != null;
+        String typeObjectName = nameManager.getJavascriptName(type);
+        utils.appendLine("var returnObject = " 
+                         + typeObjectName + "_deserialize (cxfjsutils, partElement);\n");
+        utils.appendLine("return returnObject;");
+        code.append("}\n");
     }
 
     private void createInputSerializer(OperationInfo op, boolean isWrapped,
                                        List<String> inputParameterNames, String wrapperClassName,
-                                       List<ElementAndNames> elements, List<MessagePartInfo>
parts) {
+                                       List<MessagePartInfo> parts) {
+        List<ElementAndNames> elements = new ArrayList<ElementAndNames>();
         String serializerFunctionName = nameManager.getJavascriptName(op.getName()) + "_serializeInput";
         
         code.append("function " + serializerFunctionName + "(args) {\n");
-        for (MessagePartInfo mpi : parts) {
-            XmlSchemaElement element;
-            if (mpi.isElement()) {
-                element = (XmlSchemaElement)mpi.getXmlSchema();
-                if (element == null) {
-                    element = XmlSchemaUtils.findElementByRefName(xmlSchemaCollection, mpi.getElementQName(),
-                                                                  serviceInfo.getTargetNamespace());
-                }
-            } else {
-                // there is still an element in there, but it's not a very
-                // interesting element
-                element = new XmlSchemaElement();
-                XmlSchemaElement dummyElement = (XmlSchemaElement)mpi.getXmlSchema();
-                element.setMaxOccurs(dummyElement.getMaxOccurs());
-                element.setMinOccurs(dummyElement.getMinOccurs());
-                element.setNillable(dummyElement.isNillable());
-                element.setSchemaType(xmlSchemaCollection.getTypeByQName(mpi.getTypeQName()));
-                element.setQName(mpi.getName());
-            }
-            assert element != null;
-            assert element.getQName() != null;
-            String partJavascriptVar = JavascriptUtils.javaScriptNameToken(element.getQName().getLocalPart());
-            String elementXmlRef = prefixAccumulator.xmlElementString(mpi.getConcreteName());
-
-            elements.add(new ElementAndNames(element, partJavascriptVar, elementXmlRef));
-        }
+        getElementsForParts(elements, parts);
 
         // if not wrapped, the param array matches up with the parts. If wrapped, the members
         // of it have to be packed into an object.
@@ -275,6 +271,40 @@
         code.append(currentInterfaceClassName + ".prototype.serializeInputMessage = " 
                     + serializerFunctionName
                     + ";\n\n");
+    }
+
+    private void getElementsForParts(List<ElementAndNames> elements, List<MessagePartInfo>
parts) {
+        for (MessagePartInfo mpi : parts) {
+            XmlSchemaElement element;
+            if (mpi.isElement()) {
+                element = (XmlSchemaElement)mpi.getXmlSchema();
+                if (element == null) {
+                    element = XmlSchemaUtils.findElementByRefName(xmlSchemaCollection, mpi.getElementQName(),
+                                                                  serviceInfo.getTargetNamespace());
+                }
+            } else {
+                // dkulp may have fixed the problem that caused me to write this code.
+                // aside from the fact that in the !isElement case (rpc) we have other work
to do.
+                LOG.severe("Missing element " 
+                           + mpi.getElementQName().toString() 
+                           + " in " + mpi.getName().toString());
+                // there is still an element in there, but it's not a very
+                // interesting element
+                element = new XmlSchemaElement();
+                XmlSchemaElement dummyElement = (XmlSchemaElement)mpi.getXmlSchema();
+                element.setMaxOccurs(dummyElement.getMaxOccurs());
+                element.setMinOccurs(dummyElement.getMinOccurs());
+                element.setNillable(dummyElement.isNillable());
+                element.setSchemaType(xmlSchemaCollection.getTypeByQName(mpi.getTypeQName()));
+                element.setQName(mpi.getName());
+            }
+            assert element != null;
+            assert element.getQName() != null;
+            String partJavascriptVar = JavascriptUtils.javaScriptNameToken(element.getQName().getLocalPart());
+            String elementXmlRef = prefixAccumulator.xmlElementString(mpi.getConcreteName());
+
+            elements.add(new ElementAndNames(element, partJavascriptVar, elementXmlRef));
+        }
     }
 
     private String setupWrapperElement(OperationInfo op, List<String> inputParameterNames,

Modified: incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JavascriptTestUtilities.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JavascriptTestUtilities.java?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JavascriptTestUtilities.java
(original)
+++ incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JavascriptTestUtilities.java
Thu Nov 15 18:04:44 2007
@@ -129,6 +129,10 @@
         return Context.javaToJS(value, rhinoScope);
     }
     
+    public Object rhinoNewObject(String constructorName) {
+        return rhinoContext.newObject(rhinoScope, constructorName);
+    }
+    
     public Object rhinoEvaluate(String jsExpression) {
         return rhinoContext.evaluateString(rhinoScope, jsExpression, "<testcase>",
1, null);
     }

Modified: incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JsSimpleDomNode.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JsSimpleDomNode.java?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JsSimpleDomNode.java
(original)
+++ incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/JsSimpleDomNode.java
Thu Nov 15 18:04:44 2007
@@ -27,6 +27,7 @@
 import org.w3c.dom.NodeList;
 
 import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
 
 /**
@@ -123,6 +124,13 @@
     }
 
     //CHECKSTYLE:ON
+    
+    public static JsSimpleDomNode wrapNode(Scriptable scope, Node node) {
+        Context cx = Context.enter();
+        JsSimpleDomNode newObject = (JsSimpleDomNode)cx.newObject(scope, "Node");
+        newObject.initialize(node, null);
+        return newObject;
+    }
     
     private JsSimpleDomNode newObject(Node node, JsSimpleDomNode prev) {
         Context cx = Context.enter();

Copied: incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrapped.java
(from r595489, incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrappedUnqualified.java)
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrapped.java?p2=incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrapped.java&p1=incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrappedUnqualified.java&r1=595489&r2=595519&rev=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrappedUnqualified.java
(original)
+++ incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/SimpleDocLitWrapped.java
Thu Nov 15 18:04:44 2007
@@ -19,26 +19,32 @@
 
 package org.apache.cxf.javascript.fortest;
 
+import javax.jws.WebMethod;
 import javax.jws.WebParam;
 import javax.jws.WebService;
 import javax.xml.ws.RequestWrapper;
+import javax.xml.ws.ResponseWrapper;
 
 /**
  * 
  */
 @WebService(targetNamespace = "uri:org.apache.cxf.javascript.fortest")
-public interface SimpleDocLitWrappedUnqualified {
+public interface SimpleDocLitWrapped {
     @RequestWrapper(className = "org.apache.cxf.javascript.fortest.BasicTypeFunctionReturnStringWrapper")
+    @ResponseWrapper(className = "org.apache.cxf.javascript.fortest.StringWrapper")
     String basicTypeFunctionReturnString(@WebParam(name = "s") String s, 
                                          @WebParam(name = "i") int i, 
                                          @WebParam(name = "l") long l, 
                                          @WebParam(name = "f") float f, 
                                          @WebParam(name = "d") double d);
+    @WebMethod
+    TestBean1 functionReturnTestBean1();
     int basicTypeFunctionReturnInt(@WebParam(name = "s") String s, 
                                    @WebParam(name = "i") int i, 
                                    @WebParam(name = "l") long l, 
                                    @WebParam(name = "f") float f, 
                                    @WebParam(name = "d") double d);
+    @WebMethod
     void beanFunction(@WebParam(name = "bean1") TestBean1 bean, 
                       @WebParam(name = "beanArray") TestBean1[] beans);
 }

Added: incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/StringWrapper.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/StringWrapper.java?rev=595519&view=auto
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/StringWrapper.java
(added)
+++ incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/fortest/StringWrapper.java
Thu Nov 15 18:04:44 2007
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.javascript.fortest;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * 
+ */
+@XmlRootElement(namespace = "uri:org.apache.cxf.javascript.testns")
+@XmlType(namespace = "uri:org.apache.cxf.javascript.testns")
+public class StringWrapper {
+    private String returnValue;
+
+    public String getReturnValue() {
+        return returnValue;
+    }
+
+    public void setReturnValue(String returnValue) {
+        this.returnValue = returnValue;
+    }
+
+}

Modified: incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/service/DocLitWrappedTest.java
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/service/DocLitWrappedTest.java?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/service/DocLitWrappedTest.java
(original)
+++ incubator/cxf/trunk/rt/javascript/src/test/java/org/apache/cxf/javascript/service/DocLitWrappedTest.java
Thu Nov 15 18:04:44 2007
@@ -26,19 +26,29 @@
 import java.util.logging.Logger;
 
 import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamReader;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.databinding.DataBinding;
 import org.apache.cxf.databinding.DataReader;
+import org.apache.cxf.databinding.DataWriter;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.javascript.BasicNameManager;
 import org.apache.cxf.javascript.JavascriptTestUtilities;
+import org.apache.cxf.javascript.JsSimpleDomNode;
 import org.apache.cxf.javascript.NameManager;
 import org.apache.cxf.javascript.NamespacePrefixAccumulator;
 import org.apache.cxf.javascript.fortest.BasicTypeFunctionReturnStringWrapper;
+import org.apache.cxf.javascript.fortest.StringWrapper;
 import org.apache.cxf.javascript.types.SchemaJavascriptBuilder;
 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
 import org.apache.cxf.service.model.MessageInfo;
@@ -48,9 +58,12 @@
 import org.apache.cxf.test.AbstractCXFSpringTest;
 import org.junit.Test;
 import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
 
 //@org.junit.Ignore
 public class DocLitWrappedTest extends AbstractCXFSpringTest {
+    private static final String WHAT_ROUGH_BEAST_ITS_HOUR_COME_AT_LAST = 
+        "What rough beast, its hour come at last, ...";
     private static final Logger LOG = LogUtils.getL7dLogger(DocLitWrappedTest.class);
     private static final String BASIC_TYPE_FUNCTION_RETURN_STRING_SERIALIZER_NAME 
         = "org_apache_cxf_javascript_fortest_basicTypeFunctionReturnString_serializeInput";
@@ -63,11 +76,17 @@
     private JaxWsProxyFactoryBean clientProxyFactory;
     private XMLInputFactory xmlInputFactory;
     private NamespacePrefixAccumulator prefixManager;
+    private DocumentBuilder documentBuilder;
 
     public DocLitWrappedTest() {
         testUtilities = new JavascriptTestUtilities(getClass());
         testUtilities.addDefaultNamespaces();
         xmlInputFactory = XMLInputFactory.newInstance();
+        try {
+            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @Override
@@ -119,6 +138,37 @@
         assertTrue(messageObject instanceof BasicTypeFunctionReturnStringWrapper);
         BasicTypeFunctionReturnStringWrapper wrapper = (BasicTypeFunctionReturnStringWrapper)messageObject;
         assertEquals(params[4], wrapper.getS());
+    }
+    
+    @Test
+    public void testResponseDeserialization() throws Exception {
+        setupClientAndRhino("simple-dlwu-proxy-factory");
+        DataBinding dataBinding = clientProxyFactory.getServiceFactory().getDataBinding();
+        assertNotNull(dataBinding);
+        StringWrapper responseObject = new StringWrapper();
+        responseObject.setReturnValue(WHAT_ROUGH_BEAST_ITS_HOUR_COME_AT_LAST);
+        DataWriter<Node> writer = dataBinding.createWriter(Node.class);
+        ServiceInfo serviceInfo = serviceInfos.get(0); // assume we only have one.
+        QName messageName = 
+            new QName("uri:org.apache.cxf.javascript.fortest", "basicTypeFunctionReturnString");
+        MessageInfo outputMessage = serviceInfo.getMessage(messageName);
+        assertNotNull(outputMessage);
+        MessagePartInfo part = outputMessage.getMessagePartByIndex(0); // has only one part.
+        Document document = documentBuilder.newDocument();
+        writer.write(responseObject, part, document);
+        Element messageElement = document.getDocumentElement();
+        Object jsUtils = testUtilities.rhinoNewObject("CxfApacheOrgUtil");
+        Object jsResult = testUtilities.rhinoCall("org_apache_cxf_javascript_fortest_"
+                                                  + "basicTypeFunctionReturnString_deserializeResponse",
+                                                  jsUtils,
+                                                  JsSimpleDomNode.wrapNode(testUtilities.getRhinoScope(),

+                                                                           messageElement));
+        assertNotNull(jsResult);
+        ScriptableObject jsResultObject = (ScriptableObject)jsResult;
+        Object returnValue = ScriptableObject.callMethod(jsResultObject, 
+                                                         "getReturnValue", 
+                                                          new Object[0]);
+        assertEquals(WHAT_ROUGH_BEAST_ITS_HOUR_COME_AT_LAST, returnValue);
     }
 
     private void setupClientAndRhino(String clientProxyFactoryBeanId) throws IOException
{

Modified: incubator/cxf/trunk/rt/javascript/src/test/resources/serializationTestBeans.xml
URL: http://svn.apache.org/viewvc/incubator/cxf/trunk/rt/javascript/src/test/resources/serializationTestBeans.xml?rev=595519&r1=595518&r2=595519&view=diff
==============================================================================
--- incubator/cxf/trunk/rt/javascript/src/test/resources/serializationTestBeans.xml (original)
+++ incubator/cxf/trunk/rt/javascript/src/test/resources/serializationTestBeans.xml Thu Nov
15 18:04:44 2007
@@ -33,11 +33,11 @@
 	
 	<!--  We use a client for the service for basic tests. We don't need a server at all.
-->
 	<bean id="simple-dlwu-proxy-factory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"
>
-    <property name="serviceClass" value="org.apache.cxf.javascript.fortest.SimpleDocLitWrappedUnqualified"/>
+    <property name="serviceClass" value="org.apache.cxf.javascript.fortest.SimpleDocLitWrapped"/>
     <property name="address" value="local://SimpleDocLitWrappedUnqualified"/>
   </bean>
    
-  <bean id="simple-dlwu-client" class="org.apache.cxf.javascript.fortest.SimpleDocLitWrappedUnqualified"
scope='prototype'
+  <bean id="simple-dlwu-client" class="org.apache.cxf.javascript.fortest.SimpleDocLitWrapped"
scope='prototype'
     factory-bean="simple-dlwu-proxy-factory" factory-method="create"/>
 	
 	<!-- Services. We use the JAXB test server which handles a lot of types. -->



Mime
View raw message