You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2019/02/26 08:16:53 UTC

[karaf-decanter] branch master updated: [KARAF-6118] Add SOAP collector

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf-decanter.git


The following commit(s) were added to refs/heads/master by this push:
     new ff11e9b  [KARAF-6118] Add SOAP collector
     new f0146f0  Merge pull request #72 from jbonofre/KARAF-6118
ff11e9b is described below

commit ff11e9b04b0a61cbf49fc39b8a7f965d878efdd4
Author: Jean-Baptiste Onofré <jb...@nanthrax.net>
AuthorDate: Sun Feb 17 22:05:47 2019 +0100

    [KARAF-6118] Add SOAP collector
---
 assembly/src/main/feature/feature.xml              |  11 ++
 collector/pom.xml                                  |   1 +
 collector/soap/pom.xml                             | 114 +++++++++++++++++
 .../org.apache.karaf.decanter.collector.soap.cfg   |  27 ++++
 .../decanter/collector/soap/SoapCollector.java     | 141 +++++++++++++++++++++
 .../decanter/collector/soap/EventAdminMock.java    |  40 ++++++
 .../decanter/collector/soap/SoapCollectorTest.java | 130 +++++++++++++++++++
 .../karaf/decanter/collector/soap/TestService.java |  26 ++++
 .../decanter/collector/soap/TestServiceImpl.java   |  29 +++++
 .../src/main/asciidoc/user-guide/collectors.adoc   |  27 ++++
 10 files changed, 546 insertions(+)

diff --git a/assembly/src/main/feature/feature.xml b/assembly/src/main/feature/feature.xml
index af953e6..fd4969d 100644
--- a/assembly/src/main/feature/feature.xml
+++ b/assembly/src/main/feature/feature.xml
@@ -107,6 +107,17 @@
         <configfile finalname="/etc/org.apache.karaf.decanter.collector.rest-1.cfg">mvn:org.apache.karaf.decanter.collector/org.apache.karaf.decanter.collector.rest/${project.version}/cfg</configfile>
     </feature>
 
+    <feature name="decanter-collector-soap-core" version="${project.version}" description="Karaf Decanter SOAP Collector Core">
+        <feature>decanter-common</feature>
+        <feature>scheduler</feature>
+        <bundle>mvn:org.apache.karaf.decanter.collector/org.apache.karaf.decanter.collector.soap/${project.version}</bundle>
+    </feature>
+
+    <feature name="decanter-collector-soap" version="${project.version}" description="Karaf Decanter SOAP Collector">
+        <feature>decanter-collector-soap-core</feature>
+        <configfile finalname="/etc/org.apache.karaf.decanter.collector.soap-1.cfg">mvn:org.apache.karaf.decanter.collector/org.apache.karaf.decanter.collector.soap/${project.version}/cfg</configfile>
+    </feature>
+
     <feature name="decanter-collector-system" version="${project.version}" description="Karaf Decanter OS Collector">
         <feature>decanter-common</feature>
         <feature>scheduler</feature>
diff --git a/collector/pom.xml b/collector/pom.xml
index 9841651..fe92393 100644
--- a/collector/pom.xml
+++ b/collector/pom.xml
@@ -48,6 +48,7 @@
         <module>process</module>
         <module>rest</module>
         <module>rest-servlet</module>
+        <module>soap</module>
         <module>system</module>
         <module>socket</module>
         <module>jdbc</module>
diff --git a/collector/soap/pom.xml b/collector/soap/pom.xml
new file mode 100644
index 0000000..a02f369
--- /dev/null
+++ b/collector/soap/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf.decanter</groupId>
+        <artifactId>collector</artifactId>
+        <version>2.2.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.decanter.collector</groupId>
+    <artifactId>org.apache.karaf.decanter.collector.soap</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Decanter :: Collector :: SOAP</name>
+
+    <properties>
+        <cxf.version>3.2.6</cxf.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.karaf.decanter</groupId>
+            <artifactId>org.apache.karaf.decanter.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.decanter.collector</groupId>
+            <artifactId>org.apache.karaf.decanter.collector.utils</artifactId>
+        </dependency>
+
+        <!-- test -->
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-frontend-jaxws</artifactId>
+            <version>${cxf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-http-jetty</artifactId>
+            <version>${cxf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <inherited>true</inherited>
+                <extensions>true</extensions>
+                <configuration>
+                    <obrRepository>NONE</obrRepository>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Export-Package>!*</Export-Package>
+                        <Import-Package>*</Import-Package>
+                        <Private-Package>
+                            org.apache.karaf.decanter.collector.soap,
+                            org.apache.karaf.decanter.collector.utils
+                        </Private-Package>
+                        <_dsannotations>*</_dsannotations>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>attach-artifact</goal>
+                        </goals>
+                        <configuration>
+                            <artifacts>
+                                <artifact>
+                                    <file>src/main/cfg/org.apache.karaf.decanter.collector.soap.cfg</file>
+                                    <type>cfg</type>
+                                </artifact>
+                            </artifacts>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/collector/soap/src/main/cfg/org.apache.karaf.decanter.collector.soap.cfg b/collector/soap/src/main/cfg/org.apache.karaf.decanter.collector.soap.cfg
new file mode 100644
index 0000000..81d226d
--- /dev/null
+++ b/collector/soap/src/main/cfg/org.apache.karaf.decanter.collector.soap.cfg
@@ -0,0 +1,27 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+# Decanter SOAP collector
+#
+
+url=http://localhost:8080/cxf/service
+soap.request=
+#topic=decanter/collect/soap
+
diff --git a/collector/soap/src/main/java/org/apache/karaf/decanter/collector/soap/SoapCollector.java b/collector/soap/src/main/java/org/apache/karaf/decanter/collector/soap/SoapCollector.java
new file mode 100644
index 0000000..738b978
--- /dev/null
+++ b/collector/soap/src/main/java/org/apache/karaf/decanter/collector/soap/SoapCollector.java
@@ -0,0 +1,141 @@
+/*
+ * 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.karaf.decanter.collector.soap;
+
+import org.apache.karaf.decanter.collector.utils.PropertiesPreparator;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component(
+        service = Runnable.class,
+        name = "org.apache.karaf.decanter.collector.soap",
+        immediate = true,
+        property = {
+                "decanter.collector.name=soap",
+                "scheduler.period:Long=60",
+                "scheduler.concurrent:Boolean=false",
+                "scheduler.name=decanter-collector-soap"
+        }
+)
+public class SoapCollector implements Runnable {
+
+    @Reference
+    public EventAdmin dispatcher;
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(SoapCollector.class);
+
+    private URL url;
+    private String soapRequest;
+    private String topic;
+    private Dictionary<String, Object> config;
+
+    @Activate
+    public void activate(ComponentContext componentContext) throws Exception {
+        activate(componentContext.getProperties());
+    }
+
+    public void activate(Dictionary<String, Object> config) throws MalformedURLException {
+        this.config = config;
+        if (config.get("url") == null) {
+            throw new IllegalArgumentException("url property is mandatory");
+        }
+        this.topic = "decanter/collect/soap";
+        if (config.get("topic") != null) {
+            this.topic = (String) config.get("topic");
+        }
+        url = new URL((String) config.get("url"));
+        if (config.get("soap.request") == null) {
+            throw new IllegalStateException("soap.request property is mandatory");
+        }
+        soapRequest = (String) config.get("soap.request");
+    }
+
+    @Override
+    public void run() {
+        Map<String, Object> data = new HashMap<>();
+        data.put("soap.request", soapRequest);
+        data.put("url", url);
+        data.put("type", "soap");
+
+        // custom fields
+        Enumeration<String> keys = config.keys();
+        while (keys.hasMoreElements()) {
+            String key = keys.nextElement();
+            data.put(key, config.get(key));
+        }
+
+        try {
+            PropertiesPreparator.prepare(data, config);
+        } catch (Exception e) {
+            LOGGER.warn("Can't prepare properties", e);
+        }
+
+        HttpURLConnection connection = null;
+        try {
+            connection = (HttpURLConnection) url.openConnection();
+            connection.setRequestMethod("POST");
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setRequestProperty("Content-Type", "text/xml");
+            connection.setRequestProperty("Accept", "text/xml");
+            try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) {
+                writer.write(soapRequest);
+                writer.flush();
+                data.put("http.response.code", connection.getResponseCode());
+                data.put("http.response.message", connection.getResponseMessage());
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+                    StringBuffer buffer = new StringBuffer();
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        buffer.append(line).append("\n");
+                    }
+                    data.put("soap.response", buffer.toString());
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Can't request SOAP service", e);
+            data.put("error", e.getClass().getName() + ": " + e.getMessage());
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+
+        dispatcher.postEvent(new Event(topic, data));
+    }
+
+}
diff --git a/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/EventAdminMock.java b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/EventAdminMock.java
new file mode 100644
index 0000000..8b9476d
--- /dev/null
+++ b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/EventAdminMock.java
@@ -0,0 +1,40 @@
+/*
+ * 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.karaf.decanter.collector.soap;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EventAdminMock implements EventAdmin {
+
+    public List<Event> postedEvents = new ArrayList<>();
+    public List<Event> sentEvents = new ArrayList<>();
+
+    @Override
+    public void postEvent(Event event) {
+        postedEvents.add(event);
+    }
+
+    @Override
+    public void sendEvent(Event event) {
+        sentEvents.add(event);
+    }
+
+}
diff --git a/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/SoapCollectorTest.java b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/SoapCollectorTest.java
new file mode 100644
index 0000000..b4267f0
--- /dev/null
+++ b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/SoapCollectorTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.karaf.decanter.collector.soap;
+
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.feature.LoggingFeature;
+import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.event.Event;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public class SoapCollectorTest {
+
+    private Server cxfServer;
+
+    @Before
+    public void setup() throws Exception {
+        JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
+
+        LoggingFeature loggingFeature = new LoggingFeature();
+        loggingFeature.setPrettyLogging(true);
+        factory.getFeatures().add(loggingFeature);
+
+        TestServiceImpl testService = new TestServiceImpl();
+        factory.setServiceBean(testService);
+        factory.setAddress("http://localhost:9090/test");
+        cxfServer = factory.create();
+        cxfServer.start();
+    }
+
+    @After
+    public void teardown() throws Exception {
+        cxfServer.stop();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBadConfiguration() throws Exception {
+        SoapCollector collector = new SoapCollector();
+        Dictionary<String, Object> config = new Hashtable<>();
+        collector.activate(config);
+    }
+
+    @Test
+    public void testWithInvalidRequest() throws Exception {
+        EventAdminMock eventAdminMock = new EventAdminMock();
+
+        SoapCollector collector = new SoapCollector();
+        Dictionary<String, Object> config = new Hashtable<>();
+        config.put("soap.request", "test");
+        config.put("url", "http://localhost:9090/test");
+        collector.dispatcher = eventAdminMock;
+        collector.activate(config);
+        collector.run();
+
+        Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+
+        Event event = eventAdminMock.postedEvents.get(0);
+
+        Assert.assertEquals(500, event.getProperty("http.response.code"));
+        Assert.assertTrue(((String) event.getProperty("error")).contains("java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:9090/test"));
+    }
+
+    @Test
+    public void testWithBadUrl() throws Exception {
+        EventAdminMock eventAdminMock = new EventAdminMock();
+
+        SoapCollector collector = new SoapCollector();
+        Dictionary<String, Object> config = new Hashtable<>();
+        config.put("soap.request", "test");
+        config.put("url", "http://foo.bar/foo");
+        collector.dispatcher = eventAdminMock;
+        collector.activate(config);
+        collector.run();
+
+        Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+
+        Event event = eventAdminMock.postedEvents.get(0);
+        Assert.assertEquals("java.net.UnknownHostException: foo.bar", event.getProperty("error"));
+    }
+
+    @Test
+    public void testWithValidRequest() throws Exception {
+        EventAdminMock eventAdminMock = new EventAdminMock();
+
+        SoapCollector collector = new SoapCollector();
+        Dictionary<String, Object> config = new Hashtable<>();
+        config.put("url", "http://localhost:9090/test");
+        config.put("soap.request", "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:soap=\"http://soap.collector.decanter.karaf.apache.org/\">\n" +
+                "   <soapenv:Header/>\n" +
+                "   <soapenv:Body>\n" +
+                "      <soap:echo>\n" +
+                "         <!--Optional:-->\n" +
+                "         <arg0>This is a test</arg0>\n" +
+                "      </soap:echo>\n" +
+                "   </soapenv:Body>\n" +
+                "</soapenv:Envelope>");
+        collector.dispatcher = eventAdminMock;
+        collector.activate(config);
+        collector.run();
+
+        Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+
+        Event event = eventAdminMock.postedEvents.get(0);
+        Assert.assertNull(event.getProperty("error"));
+        Assert.assertEquals(200, event.getProperty("http.response.code"));
+        Assert.assertEquals("OK", event.getProperty("http.response.message"));
+        Assert.assertTrue(((String) event.getProperty("soap.response")).contains("hello This is a test"));
+    }
+
+}
diff --git a/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestService.java b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestService.java
new file mode 100644
index 0000000..94b880a
--- /dev/null
+++ b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.karaf.decanter.collector.soap;
+
+import javax.jws.WebService;
+
+@WebService
+public interface TestService {
+
+    String echo(String message);
+
+}
diff --git a/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestServiceImpl.java b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestServiceImpl.java
new file mode 100644
index 0000000..5f14b5b
--- /dev/null
+++ b/collector/soap/src/test/java/org/apache/karaf/decanter/collector/soap/TestServiceImpl.java
@@ -0,0 +1,29 @@
+/*
+ * 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.karaf.decanter.collector.soap;
+
+import javax.jws.WebService;
+
+@WebService(endpointInterface = "org.apache.karaf.decanter.collector.soap.TestService", serviceName = "TestService")
+public class TestServiceImpl implements TestService {
+
+    @Override
+    public String echo(String message) {
+        return "hello " + message;
+    }
+
+}
diff --git a/manual/src/main/asciidoc/user-guide/collectors.adoc b/manual/src/main/asciidoc/user-guide/collectors.adoc
index f1bbbe7..f10b6a9 100644
--- a/manual/src/main/asciidoc/user-guide/collectors.adoc
+++ b/manual/src/main/asciidoc/user-guide/collectors.adoc
@@ -779,6 +779,33 @@ The `decanter-collector-rest-servlet` feature installs the collector:
 karaf@root()> feature:install decanter-collector-rest-servlet
 ----
 
+==== SOAP
+
+The Decanter SOAP collector periodically requests a SOAP service and returns the result (the SOAP Response, or error details if it failed).
+
+The `decanter-collector-soap` feature installs the collector:
+
+----
+karaf@root()> feature:install decanter-collector-soap
+----
+
+This feature also installs `etc/org.apache.karaf.decanter.collector.soap.cfg` configuration file where you can setup the URL of the service and the SOAP request to use:
+
+----
+#
+# Decanter SOAP collector
+#
+
+url=http://localhost:8080/cxf/service
+soap.request=
+----
+
+The collector send several collected properties to the dispatcher, especially:
+
+* `soap.response` property contains the actual SOAP response
+* `error` is only populated when the service request failed, containing the error detail
+* `http.response.code` contains the HTTP status code of the service request
+
 ==== Dropwizard Metrics
 
 The Decanter Dropwizard Metrics collector get a `MetricSet` OSGi service and periodically get the metrics in the set.