Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Becker2013-09-06 11:27:00 +0000
committerThomas Becker2013-09-06 11:27:00 +0000
commit30487639a7d915e1cc25a99b148fef8e21f95509 (patch)
treee355db580207f41133b148d50c6826a52ede0ab3
parent3d7c1324d9324f8ede761dde51c039ad15a6c774 (diff)
parent1f765bf9ae124eb10562b9de85d3dc5dfdc916f1 (diff)
downloadorg.eclipse.jetty.project-30487639a7d915e1cc25a99b148fef8e21f95509.tar.gz
org.eclipse.jetty.project-30487639a7d915e1cc25a99b148fef8e21f95509.tar.xz
org.eclipse.jetty.project-30487639a7d915e1cc25a99b148fef8e21f95509.zip
Merge commit '1f765bf9ae124eb10562b9de85d3dc5dfdc916f1' into jetty-9.1
-rw-r--r--jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java35
-rw-r--r--jetty-spring/src/test/java/org/eclipse/jetty/spring/SpringXmlConfigurationTest.java77
-rw-r--r--jetty-xml/src/main/java/org/eclipse/jetty/xml/ConfigurationProcessor.java3
-rw-r--r--jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java81
-rw-r--r--jetty-xml/src/test/java/org/eclipse/jetty/xml/TestConfiguration.java24
-rw-r--r--jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java22
-rw-r--r--jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml10
7 files changed, 172 insertions, 80 deletions
diff --git a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
index b95da72300..89108bc73d 100644
--- a/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
+++ b/jetty-spring/src/main/java/org/eclipse/jetty/spring/SpringConfigurationProcessor.java
@@ -30,6 +30,9 @@ import org.eclipse.jetty.xml.ConfigurationProcessor;
import org.eclipse.jetty.xml.ConfigurationProcessorFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.eclipse.jetty.xml.XmlParser;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
@@ -59,17 +62,15 @@ public class SpringConfigurationProcessor implements ConfigurationProcessor
{
private static final Logger LOG = Log.getLogger(SpringConfigurationProcessor.class);
- private Map<String, Object> _idMap;
- private Map<String, String> _propertyMap;
+ private XmlConfiguration _configuration;
private XmlBeanFactory _beanFactory;
private String _main;
- public void init(URL url, XmlParser.Node config, Map<String, Object> idMap, Map<String, String> properties)
+ public void init(URL url, XmlParser.Node config, XmlConfiguration configuration)
{
try
{
- _idMap = idMap;
- _propertyMap = properties;
+ _configuration = configuration;
Resource resource = url != null
? new UrlResource(url)
@@ -78,7 +79,14 @@ public class SpringConfigurationProcessor implements ConfigurationProcessor
"<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"http://www.springframework.org/dtd/spring-beans.dtd\">" +
config).getBytes("UTF-8"));
- _beanFactory = new XmlBeanFactory(resource);
+ _beanFactory = new XmlBeanFactory(resource){
+ @Override
+ protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
+ {
+ _configuration.initializeDefaults(bw.getWrappedInstance());
+ super.applyPropertyValues(beanName, mbd, bw, pvs);
+ }
+ };
}
catch (Exception e)
{
@@ -105,7 +113,7 @@ public class SpringConfigurationProcessor implements ConfigurationProcessor
private void doConfigure()
{
- _beanFactory.registerSingleton("properties", _propertyMap);
+ _beanFactory.registerSingleton("properties", _configuration.getProperties());
// Look for the main bean;
for (String bean : _beanFactory.getBeanDefinitionNames())
@@ -122,25 +130,26 @@ public class SpringConfigurationProcessor implements ConfigurationProcessor
_main = _beanFactory.getBeanDefinitionNames()[0];
// Register id beans as singletons
- LOG.debug("idMap {}", _idMap);
- for (String id : _idMap.keySet())
+ Map<String, Object> idMap = _configuration.getIdMap();
+ LOG.debug("idMap {}", idMap);
+ for (String id : idMap.keySet())
{
LOG.debug("register {}", id);
- _beanFactory.registerSingleton(id, _idMap.get(id));
+ _beanFactory.registerSingleton(id, idMap.get(id));
}
// Apply configuration to existing singletons
- for (String id : _idMap.keySet())
+ for (String id : idMap.keySet())
{
if (_beanFactory.containsBeanDefinition(id))
{
LOG.debug("reconfigure {}", id);
- _beanFactory.configureBean(_idMap.get(id), id);
+ _beanFactory.configureBean(idMap.get(id), id);
}
}
// Extract id's for next time.
for (String id : _beanFactory.getSingletonNames())
- _idMap.put(id, _beanFactory.getBean(id));
+ idMap.put(id, _beanFactory.getBean(id));
}
}
diff --git a/jetty-spring/src/test/java/org/eclipse/jetty/spring/SpringXmlConfigurationTest.java b/jetty-spring/src/test/java/org/eclipse/jetty/spring/SpringXmlConfigurationTest.java
index 7c712ccb01..13a333d2eb 100644
--- a/jetty-spring/src/test/java/org/eclipse/jetty/spring/SpringXmlConfigurationTest.java
+++ b/jetty-spring/src/test/java/org/eclipse/jetty/spring/SpringXmlConfigurationTest.java
@@ -21,16 +21,18 @@ package org.eclipse.jetty.spring;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.xml.XmlConfiguration;
-import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
public class SpringXmlConfigurationTest
{
protected String _configure="org/eclipse/jetty/spring/configure.xml";
@@ -48,7 +50,7 @@ public class SpringXmlConfigurationTest
if (matcher.matches())
{
String minor = matcher.group(1);
- Assume.assumeTrue(Integer.parseInt(minor) > 5);
+ assumeTrue(Integer.parseInt(minor) > 5);
}
}
@@ -74,54 +76,71 @@ public class SpringXmlConfigurationTest
tc=(TestConfiguration)configuration.configure(tc);
- Assert.assertEquals("preconfig", tc.getTestString0());
- Assert.assertEquals(42, tc.getTestInt0());
- Assert.assertEquals("SetValue", tc.getTestString1());
- Assert.assertEquals(1, tc.getTestInt1());
+ assertEquals("preconfig", tc.getTestString0());
+ assertEquals(42, tc.getTestInt0());
+ assertEquals("SetValue", tc.getTestString1());
+ assertEquals(1, tc.getTestInt1());
- Assert.assertEquals("nested", tc.getNested().getTestString0());
- Assert.assertEquals("nested", tc.getNested().getTestString1());
- Assert.assertEquals("default", tc.getNested().getNested().getTestString0());
- Assert.assertEquals("deep", tc.getNested().getNested().getTestString1());
+ assertEquals("nested", tc.getNested().getTestString0());
+ assertEquals("nested", tc.getNested().getTestString1());
+ assertEquals("default", tc.getNested().getNested().getTestString0());
+ assertEquals("deep", tc.getNested().getNested().getTestString1());
- Assert.assertEquals("deep", ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestString1());
- Assert.assertEquals(2, ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestInt2());
+ assertEquals("deep", ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestString1());
+ assertEquals(2, ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestInt2());
- Assert.assertEquals("xxx", tc.getTestString2());
+ assertEquals("xxx", tc.getTestString2());
}
@Test
public void testNewObject() throws Exception
{
+ final String newDefaultValue = "NEW DEFAULT";
TestConfiguration.VALUE=71;
URL url = SpringXmlConfigurationTest.class.getClassLoader().getResource(_configure);
- XmlConfiguration configuration = new XmlConfiguration(url);
-
- Map<String,String> properties = new HashMap<>();
+ final AtomicInteger count = new AtomicInteger(0);
+ XmlConfiguration configuration = new XmlConfiguration(url)
+ {
+ @Override
+ public void initializeDefaults(Object object)
+ {
+ super.initializeDefaults(object);
+ if (object instanceof TestConfiguration)
+ {
+ count.incrementAndGet();
+ ((TestConfiguration)object).setTestString0(newDefaultValue);
+ ((TestConfiguration)object).setTestString1("WILL BE OVERRIDDEN");
+ }
+ }
+ };
+
+ Map<String,String> properties = new HashMap<String,String>();
properties.put("test", "xxx");
TestConfiguration nested = new TestConfiguration();
nested.setTestString0("nested");
- configuration.getIdMap().put("nested",nested);
+ configuration.getIdMap().put("nested", nested);
configuration.getProperties().putAll(properties);
TestConfiguration tc = (TestConfiguration)configuration.configure();
- Assert.assertEquals("default", tc.getTestString0());
- Assert.assertEquals(-1, tc.getTestInt0());
- Assert.assertEquals("SetValue", tc.getTestString1());
- Assert.assertEquals(1, tc.getTestInt1());
+ assertEquals(3,count.get());
+
+ assertEquals(newDefaultValue, tc.getTestString0());
+ assertEquals(-1, tc.getTestInt0());
+ assertEquals("SetValue", tc.getTestString1());
+ assertEquals(1, tc.getTestInt1());
- Assert.assertEquals("nested", tc.getNested().getTestString0());
- Assert.assertEquals("nested", tc.getNested().getTestString1());
- Assert.assertEquals("default", tc.getNested().getNested().getTestString0());
- Assert.assertEquals("deep", tc.getNested().getNested().getTestString1());
+ assertEquals(newDefaultValue, tc.getNested().getTestString0());
+ assertEquals("nested", tc.getNested().getTestString1());
+ assertEquals(newDefaultValue, tc.getNested().getNested().getTestString0());
+ assertEquals("deep", tc.getNested().getNested().getTestString1());
- Assert.assertEquals("deep", ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestString1());
- Assert.assertEquals(2, ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestInt2());
+ assertEquals("deep", ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestString1());
+ assertEquals(2, ((TestConfiguration)configuration.getIdMap().get("nestedDeep")).getTestInt2());
- Assert.assertEquals("xxx", tc.getTestString2());
+ assertEquals("xxx", tc.getTestString2());
}
@Test
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/ConfigurationProcessor.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/ConfigurationProcessor.java
index b179da4a2e..c6f9151030 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/ConfigurationProcessor.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/ConfigurationProcessor.java
@@ -19,7 +19,6 @@
package org.eclipse.jetty.xml;
import java.net.URL;
-import java.util.Map;
/**
* A ConfigurationProcessor for non XmlConfiguration format files.
@@ -32,7 +31,7 @@ import java.util.Map;
*/
public interface ConfigurationProcessor
{
- public void init(URL url, XmlParser.Node config, Map<String, Object> idMap, Map<String, String> properties);
+ public void init(URL url, XmlParser.Node root, XmlConfiguration configuration);
public Object configure( Object obj) throws Exception;
public Object configure() throws Exception;
diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
index 27b13932af..3874da811a 100644
--- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
+++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java
@@ -201,7 +201,7 @@ public class XmlConfiguration
{
throw new IllegalArgumentException("Unknown XML tag:"+config.getTag());
}
- _processor.init(_url,config,_idMap, _propertyMap);
+ _processor.init(_url,config,this);
}
/* ------------------------------------------------------------ */
@@ -261,52 +261,61 @@ public class XmlConfiguration
{
return _processor.configure();
}
+
+ /* ------------------------------------------------------------ */
+ /** Initialize a new Object defaults.
+ * <p>This method must be called by any {@link ConfigurationProcessor} when it
+ * creates a new instance of an object before configuring it, so that a derived
+ * XmlConfiguration class may inject default values.
+ * @param object the object to initialize defaults on
+ */
+ public void initializeDefaults(Object object)
+ {
+ }
private static class JettyXmlConfiguration implements ConfigurationProcessor
{
private String _url;
- private XmlParser.Node _config;
- private Map<String, Object> _idMap;
- private Map<String, String> _propertyMap;
+ XmlParser.Node _root;
+ XmlConfiguration _configuration;
- public void init(URL url, XmlParser.Node config, Map<String, Object> idMap, Map<String, String> properties)
+ public void init(URL url, XmlParser.Node root, XmlConfiguration configuration)
{
_url=url==null?null:url.toString();
- _config=config;
- _idMap=idMap;
- _propertyMap=properties;
+ _root=root;
+ _configuration=configuration;
}
public Object configure(Object obj) throws Exception
{
// Check the class of the object
- Class<?> oClass = nodeClass(_config);
+ Class<?> oClass = nodeClass(_root);
if (oClass != null && !oClass.isInstance(obj))
{
String loaders = (oClass.getClassLoader()==obj.getClass().getClassLoader())?"":"Object Class and type Class are from different loaders.";
throw new IllegalArgumentException("Object of class '"+obj.getClass().getCanonicalName()+"' is not of type '" + oClass.getCanonicalName()+"'. "+loaders+" in "+_url);
}
- configure(obj,_config,0);
+ configure(obj,_root,0);
return obj;
}
public Object configure() throws Exception
{
- Class<?> oClass = nodeClass(_config);
+ Class<?> oClass = nodeClass(_root);
- String id = _config.getAttribute("id");
- Object obj = id == null ? null : _idMap.get(id);
+ String id = _root.getAttribute("id");
+ Object obj = id == null?null:_configuration.getIdMap().get(id);
int index = 0;
if (obj == null && oClass != null)
{
- index = _config.size();
+ index = _root.size();
Map<String, Object> namedArgMap = new HashMap<>();
List<Object> arguments = new LinkedList<>();
- for (int i = 0; i < _config.size(); i++)
+ for (int i = 0; i < _root.size(); i++)
{
- Object o = _config.get(i);
+ Object o = _root.get(i);
if (o instanceof String)
{
continue;
@@ -340,8 +349,9 @@ public class XmlConfiguration
throw new IllegalStateException("No suitable constructor on " + oClass, x);
}
}
+ _configuration.initializeDefaults(obj);
- configure(obj, _config, index);
+ configure(obj, _root, index);
return obj;
}
@@ -367,7 +377,7 @@ public class XmlConfiguration
{
String id = cfg.getAttribute("id");
if (id != null)
- _idMap.put(id,obj);
+ _configuration.getIdMap().put(id,obj);
// Object already constructed so skip any arguments
for (; i < cfg.size(); i++)
@@ -558,6 +568,7 @@ public class XmlConfiguration
}
Constructor<?> cons = sClass.getConstructor(vClass);
arg[0] = cons.newInstance(arg);
+ _configuration.initializeDefaults(arg[0]);
set.invoke(obj,arg);
return;
}
@@ -664,7 +675,7 @@ public class XmlConfiguration
}
}
if (id != null)
- _idMap.put(id,obj);
+ _configuration.getIdMap().put(id,obj);
return obj;
}
@@ -718,7 +729,7 @@ public class XmlConfiguration
{
Object n= TypeUtil.call(oClass,method,obj,arg);
if (id != null)
- _idMap.put(id,n);
+ _configuration.getIdMap().put(id,n);
configure(n,node,argIndex);
return n;
}
@@ -799,7 +810,8 @@ public class XmlConfiguration
{
throw new IllegalStateException("No suitable constructor: " + node + " on " + obj);
}
-
+
+ _configuration.initializeDefaults(n);
configure(n,node,argIndex);
return n;
}
@@ -814,7 +826,7 @@ public class XmlConfiguration
String refid = node.getAttribute("refid");
if (refid==null)
refid = node.getAttribute("id");
- obj = _idMap.get(refid);
+ obj = _configuration.getIdMap().get(refid);
if (obj == null && node.size()>0)
throw new IllegalStateException("No object for refid=" + refid);
configure(obj,node,0);
@@ -862,12 +874,12 @@ public class XmlConfiguration
Object v = value(obj,item);
al = LazyList.add(al,(v == null && aClass.isPrimitive())?0:v);
if (nid != null)
- _idMap.put(nid,v);
+ _configuration.getIdMap().put(nid,v);
}
Object array = LazyList.toArray(al,aClass);
if (id != null)
- _idMap.put(id,array);
+ _configuration.getIdMap().put(id,array);
return array;
}
@@ -880,7 +892,7 @@ public class XmlConfiguration
Map<Object, Object> map = new HashMap<>();
if (id != null)
- _idMap.put(id,map);
+ _configuration.getIdMap().put(id,map);
for (Object o : node)
{
@@ -916,9 +928,9 @@ public class XmlConfiguration
map.put(k,v);
if (kid != null)
- _idMap.put(kid,k);
+ _configuration.getIdMap().put(kid,k);
if (vid != null)
- _idMap.put(vid,v);
+ _configuration.getIdMap().put(vid,v);
}
return map;
@@ -937,12 +949,13 @@ public class XmlConfiguration
String name = node.getAttribute("name");
String defaultValue = node.getAttribute("default");
Object prop;
- if (_propertyMap != null && _propertyMap.containsKey(name))
- prop = _propertyMap.get(name);
+ Map<String,String> property_map=_configuration.getProperties();
+ if (property_map != null && property_map.containsKey(name))
+ prop = property_map.get(name);
else
prop = defaultValue;
if (id != null)
- _idMap.put(id,prop);
+ _configuration.getIdMap().put(id,prop);
if (prop != null)
configure(prop,node,0);
return prop;
@@ -963,7 +976,7 @@ public class XmlConfiguration
String ref = node.getAttribute("ref");
if (ref != null)
{
- value = _idMap.get(ref);
+ value = _configuration.getIdMap().get(ref);
}
else
{
@@ -1197,10 +1210,10 @@ public class XmlConfiguration
}
// For all arguments, load properties
- for (int i = 0; i < args.length; i++)
+ for (String arg : args)
{
- if (args[i].toLowerCase(Locale.ENGLISH).endsWith(".properties"))
- properties.load(Resource.newResource(args[i]).getInputStream());
+ if (arg.toLowerCase(Locale.ENGLISH).endsWith(".properties"))
+ properties.load(Resource.newResource(arg).getInputStream());
}
// For all arguments, parse XMLs
diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/TestConfiguration.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/TestConfiguration.java
index 3fa46db0fa..37fa1785ae 100644
--- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/TestConfiguration.java
+++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/TestConfiguration.java
@@ -34,8 +34,11 @@ public class TestConfiguration extends HashMap<String,Object>
{
public static int VALUE=77;
+ public final Object ID = new Object();
+
public final String name;
public TestConfiguration nested;
+ public String testString="default";
public Object testObject;
public int testInt;
public URL url;
@@ -79,6 +82,25 @@ public class TestConfiguration extends HashMap<String,Object>
propValue=value;
}
+ public TestConfiguration getNested()
+ {
+ return nested;
+ }
+
+ public void setNested(TestConfiguration nested)
+ {
+ this.nested = nested;
+ }
+
+ public String getTestString()
+ {
+ return testString;
+ }
+
+ public void setTestString(String testString)
+ {
+ this.testString = testString;
+ }
public void call()
{
@@ -87,7 +109,6 @@ public class TestConfiguration extends HashMap<String,Object>
public TestConfiguration call(Boolean b)
{
- nested=new TestConfiguration("called-"+name);
nested.put("Arg",b);
return nested;
}
@@ -167,4 +188,5 @@ public class TestConfiguration extends HashMap<String,Object>
{
this.map = map;
}
+
}
diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
index f4b76f1fca..929a980e37 100644
--- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
+++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
@@ -128,10 +129,29 @@ public class XmlConfigurationTest
properties.put("whatever", "xxx");
URL url = XmlConfigurationTest.class.getClassLoader().getResource(_configure);
- XmlConfiguration configuration = new XmlConfiguration(url);
+ final AtomicInteger count = new AtomicInteger(0);
+ XmlConfiguration configuration = new XmlConfiguration(url)
+ {
+ @Override
+ public void initializeDefaults(Object object)
+ {
+ if (object instanceof TestConfiguration)
+ {
+ count.incrementAndGet();
+ ((TestConfiguration)object).setNested(null);
+ ((TestConfiguration)object).setTestString("NEW DEFAULT");
+ }
+ }
+ };
configuration.getProperties().putAll(properties);
TestConfiguration tc = (TestConfiguration)configuration.configure();
+ assertEquals(3,count.get());
+
+ assertEquals("NEW DEFAULT",tc.getTestString());
+ assertEquals("nested",tc.getNested().getTestString());
+ assertEquals("NEW DEFAULT",tc.getNested().getNested().getTestString());
+
assertEquals("Set String","SetValue",tc.testObject);
assertEquals("Set Type",2,tc.testInt);
diff --git a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml
index 709bd0d611..7fcccd2370 100644
--- a/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml
+++ b/jetty-xml/src/test/resources/org/eclipse/jetty/xml/configure.xml
@@ -84,6 +84,16 @@
<Put name="Float" type="Float">2.3</Put>
<Put name="Env"><Env name="HOME"/></Put>
+ <Set name="nested">
+ <New class="org.eclipse.jetty.xml.TestConfiguration">
+ <Set name="testString">nested</Set>
+ <Set name="nested">
+ <New class="org.eclipse.jetty.xml.TestConfiguration">
+ </New>
+ </Set>
+ </New>
+ </Set>
+
<Call name="call">
</Call>

Back to the top