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 2020/06/17 15:45:51 UTC
[karaf-decanter] branch master updated: [KARAF-5910] Improvements
on Rest collector and appender (supporting basic authentication,
custom headers, verb, ...)
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 6781fc6 [KARAF-5910] Improvements on Rest collector and appender (supporting basic authentication, custom headers, verb, ...)
new 2a4ff7f Merge pull request #188 from jbonofre/KARAF-5910
6781fc6 is described below
commit 6781fc67a5b0bae2273c7229eaac9f732d4d4e98
Author: jbonofre <jb...@apache.org>
AuthorDate: Wed Jun 17 16:23:32 2020 +0200
[KARAF-5910] Improvements on Rest collector and appender (supporting basic authentication, custom headers, verb, ...)
---
appender/rest/pom.xml | 32 +++++++-
.../org.apache.karaf.decanter.appender.rest.cfg | 8 ++
.../karaf/decanter/appender/rest/RestAppender.java | 42 ++++++++--
.../decanter/appender/rest/RestAppenderTest.java | 93 ++++++++++++----------
.../karaf/decanter/appender}/rest/TestService.java | 28 ++++---
.../org.apache.karaf.decanter.collector.rest.cfg | 7 ++
.../decanter/collector/rest/RestCollector.java | 44 +++++++---
.../decanter/collector/rest/RestCollectorTest.java | 75 +++++++++++++++++
.../karaf/decanter/collector/rest/TestService.java | 26 ++++--
manual/src/main/asciidoc/user-guide/appenders.adoc | 45 ++++++++++-
.../src/main/asciidoc/user-guide/collectors.adoc | 31 ++++++++
11 files changed, 348 insertions(+), 83 deletions(-)
diff --git a/appender/rest/pom.xml b/appender/rest/pom.xml
index 650ec1f..cd71553 100644
--- a/appender/rest/pom.xml
+++ b/appender/rest/pom.xml
@@ -33,6 +33,10 @@
<packaging>bundle</packaging>
<name>Apache Karaf :: Decanter :: Appender :: REST</name>
+ <properties>
+ <cxf.version>3.3.6</cxf.version>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.apache.karaf.decanter</groupId>
@@ -42,15 +46,35 @@
<groupId>org.apache.karaf.decanter.appender</groupId>
<artifactId>org.apache.karaf.decanter.appender.utils</artifactId>
</dependency>
+
+ <!-- test -->
<dependency>
<groupId>org.apache.karaf.decanter.marshaller</groupId>
<artifactId>org.apache.karaf.decanter.marshaller.json</artifactId>
- <scope>test</scope>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-mapper</artifactId>
+ <version>1.2.7</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.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-jdk14</artifactId>
- <version>1.7.21</version>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ <version>1.7.21</version>
</dependency>
</dependencies>
diff --git a/appender/rest/src/main/cfg/org.apache.karaf.decanter.appender.rest.cfg b/appender/rest/src/main/cfg/org.apache.karaf.decanter.appender.rest.cfg
index 369c1aa..7ec9174 100644
--- a/appender/rest/src/main/cfg/org.apache.karaf.decanter.appender.rest.cfg
+++ b/appender/rest/src/main/cfg/org.apache.karaf.decanter.appender.rest.cfg
@@ -24,5 +24,13 @@
# Mandatory URI where the REST appender connects to
uri=
+#request.method=POST (the REST verb)
+#user= (for basic authentication)
+#password= (for basic authentication)
+#content.type=application/json (the message content type sent)
+#charset=utf-8 (the message charset)
+#header.foo= (HTTP header prefixed with header.)
+#payload.header= (if set the Decanter collected data is sent as HTTP header instead of body)
+
# Marshaller to use (json is recommended)
marshaller.target=(dataFormat=json)
diff --git a/appender/rest/src/main/java/org/apache/karaf/decanter/appender/rest/RestAppender.java b/appender/rest/src/main/java/org/apache/karaf/decanter/appender/rest/RestAppender.java
index 6130bbe..0dfffe2 100644
--- a/appender/rest/src/main/java/org/apache/karaf/decanter/appender/rest/RestAppender.java
+++ b/appender/rest/src/main/java/org/apache/karaf/decanter/appender/rest/RestAppender.java
@@ -16,12 +16,16 @@
*/
package org.apache.karaf.decanter.appender.rest;
+import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.Dictionary;
+import java.util.Enumeration;
import org.apache.karaf.decanter.api.marshaller.Marshaller;
import org.apache.karaf.decanter.appender.utils.EventFilter;
@@ -84,12 +88,38 @@ public class RestAppender implements EventHandler {
HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
- connection.setRequestMethod("POST");
- connection.setRequestProperty("Content-Type", "application/json");
- connection.setRequestProperty("charset", "utf-8");
- OutputStream out = connection.getOutputStream();
- marshaller.marshal(event, out);
- out.close();
+ String user = config.get("user") != null ? (String) config.get("user") : null;
+ String password = config.get("password") != null ? (String) config.get("password") : null;
+ if (user != null) {
+ String authentication = user + ":" + password;
+ byte[] encodedAuthentication = Base64.getEncoder().encode(authentication.getBytes(StandardCharsets.UTF_8));
+ String authenticationHeader = "Basic " + new String(encodedAuthentication);
+ connection.setRequestProperty("Authorization", authenticationHeader);
+ }
+ String requestMethod = config.get("request.method") != null ? (String) config.get("request.method") : "POST";
+ connection.setRequestMethod(requestMethod);
+ String contentType = config.get("content.type") != null ? (String) config.get("content.type") : "application/json";
+ connection.setRequestProperty("Content-Type", contentType);
+ String charset = config.get("charset") != null ? (String) config.get("charset") : "utf-8";
+ connection.setRequestProperty("charset", charset);
+ Enumeration<String> keys = config.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ if (key.startsWith("header.")) {
+ connection.setRequestProperty(key.substring("header.".length()), (String) config.get(key));
+ }
+ }
+ String payloadHeader = config.get("payload.header") != null ? (String) config.get("payload.header") : null;
+ if (payloadHeader != null) {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ marshaller.marshal(event, out);
+ connection.setRequestProperty(payloadHeader, out.toString());
+ }
+ } else {
+ try (OutputStream out = connection.getOutputStream()) {
+ marshaller.marshal(event, out);
+ }
+ }
InputStream is = connection.getInputStream();
is.read();
is.close();
diff --git a/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/RestAppenderTest.java b/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/RestAppenderTest.java
index 904d174..a232ef8 100644
--- a/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/RestAppenderTest.java
+++ b/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/RestAppenderTest.java
@@ -26,72 +26,77 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.karaf.decanter.api.marshaller.Marshaller;
import org.apache.karaf.decanter.marshaller.json.JsonMarshaller;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.*;
import org.osgi.service.event.Event;
public class RestAppenderTest {
private static final int NUM_MESSAGES = 100000;
+ private Server cxfServer;
+ private TestService testService;
+
+ @Before
+ public void setup() throws Exception {
+ JAXRSServerFactoryBean jaxrsServerFactoryBean = new JAXRSServerFactoryBean();
+ testService = new TestService();
+ jaxrsServerFactoryBean.setAddress("http://localhost:9091/test");
+ jaxrsServerFactoryBean.setServiceBean(testService);
+ cxfServer = jaxrsServerFactoryBean.create();
+ cxfServer.start();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ cxfServer.stop();
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testEmptyURI() throws URISyntaxException {
RestAppender appender = new RestAppender();
Dictionary<String, Object> config = new Hashtable<>();
appender.activate(config);
}
-
- @Ignore
- @Test
- public void testSend() throws URISyntaxException, InterruptedException {
- RestAppender appender = createAppender();
- sendMessage(appender);
- }
- @Ignore
@Test
- public void testPerformance() throws URISyntaxException, InterruptedException {
- RestAppender appender = createAppender();
- sendMessages(appender);
- long start = System.currentTimeMillis();
- sendMessages(appender);
- long end = System.currentTimeMillis();
- System.out.println(NUM_MESSAGES * 1000 / (end-start));
- }
-
- private RestAppender createAppender() throws URISyntaxException {
+ public void testPost() throws URISyntaxException {
RestAppender appender = new RestAppender();
- Marshaller marshaller = new JsonMarshaller();
- appender.marshaller = marshaller;
Dictionary<String, Object> config = new Hashtable<>();
- config.put("uri", "http://localhost:8181/decanter/collect");
+ config.put("uri", "http://localhost:9091/test/echo");
+ appender.marshaller = new JsonMarshaller();
appender.activate(config);
- return appender;
- }
- private void sendMessages(final RestAppender appender) throws InterruptedException {
- ExecutorService executor = Executors.newFixedThreadPool(20);
- for (int c=0; c<NUM_MESSAGES; c++) {
- executor.submit(new Callable<Void>() {
-
- @Override
- public Void call() throws Exception {
- sendMessage(appender);
- return null;
- }
- });
-
- }
- executor.shutdown();
- executor.awaitTermination(100, TimeUnit.SECONDS);
+ HashMap<String, Object> data = new HashMap<>();
+ data.put("foo", "bar");
+ Event event = new Event("post", data);
+
+ appender.handleEvent(event);
+
+ Assert.assertEquals(1, testService.postMessages.size());
+ Assert.assertTrue(testService.postMessages.get(0).contains("\"foo\":\"bar\""));
}
- private void sendMessage(RestAppender appender) {
- Map<String, Object> props = new HashMap<>();
- props.put("key1", "value1");
- Event event = new Event("decanter/collect", props);
+ @Test
+ public void testPut() throws URISyntaxException {
+ RestAppender appender = new RestAppender();
+ Dictionary<String, Object> config= new Hashtable<>();
+ config.put("uri", "http://localhost:9091/test/echo");
+ config.put("request.method", "PUT");
+ appender.marshaller = new JsonMarshaller();
+ appender.activate(config);
+
+ HashMap<String, Object> data = new HashMap<>();
+ data.put("foo", "bar");
+ Event event = new Event("put", data);
+
appender.handleEvent(event);
+
+ Assert.assertEquals(1, testService.putMessages.size());
+ Assert.assertTrue(testService.putMessages.get(0).contains("\"foo\":\"bar\""));
}
+
}
diff --git a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java b/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/TestService.java
similarity index 62%
copy from collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java
copy to appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/TestService.java
index 6e6cc3c..dfb84f3 100644
--- a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java
+++ b/appender/rest/src/test/java/org/apache/karaf/decanter/appender/rest/TestService.java
@@ -14,25 +14,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.decanter.collector.rest;
+package org.apache.karaf.decanter.appender.rest;
-import javax.ws.rs.GET;
+import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import java.util.ArrayList;
+import java.util.List;
@Path("/")
public class TestService {
- @GET
+ public List<String> postMessages = new ArrayList<>();
+ public List<String> putMessages = new ArrayList<>();
+
+ @POST
+ @Consumes("application/json")
@Path("/echo")
- public String echo() {
- return "hello world";
+ public String echoPost(String message) {
+ postMessages.add(message);
+ return message;
}
- @POST
- @Path("/submit")
- public void submit(String foo) {
- // nothing to do
+ @PUT
+ @Consumes("application/json")
+ @Path("/echo")
+ public String echoPut(String message) {
+ putMessages.add(message);
+ return message;
}
}
diff --git a/collector/rest/src/main/cfg/org.apache.karaf.decanter.collector.rest.cfg b/collector/rest/src/main/cfg/org.apache.karaf.decanter.collector.rest.cfg
index e0c5dca..f0e4c0f 100644
--- a/collector/rest/src/main/cfg/org.apache.karaf.decanter.collector.rest.cfg
+++ b/collector/rest/src/main/cfg/org.apache.karaf.decanter.collector.rest.cfg
@@ -24,5 +24,12 @@
url=http://localhost:8080
paths=metrics
+#request.method=GET (possible values are GET, POST, PUT, DELETE, default is GET)
+#request=foo (request payload)
+#header.foo=bar (header passed, prefixed with header.)
+#user=user (used for basic authentication)
+#password=password (used for basic authentication)
+#topic=decanter/collector/rest (Decanter dispatcher topic name to use)
+
# Unmarshaller to use
unmarshaller.target=(dataFormat=json)
diff --git a/collector/rest/src/main/java/org/apache/karaf/decanter/collector/rest/RestCollector.java b/collector/rest/src/main/java/org/apache/karaf/decanter/collector/rest/RestCollector.java
index 4aa9064..7c06825 100644
--- a/collector/rest/src/main/java/org/apache/karaf/decanter/collector/rest/RestCollector.java
+++ b/collector/rest/src/main/java/org/apache/karaf/decanter/collector/rest/RestCollector.java
@@ -16,13 +16,13 @@
*/
package org.apache.karaf.decanter.collector.rest;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
import org.apache.karaf.decanter.api.marshaller.Unmarshaller;
import org.apache.karaf.decanter.collector.utils.PropertiesPreparator;
@@ -60,6 +60,10 @@ public class RestCollector implements Runnable {
private URL url;
private String[] paths;
private String topic;
+ private String requestMethod;
+ private String request;
+ private String user;
+ private String password;
private Dictionary<String, Object> config;
@Activate
@@ -72,6 +76,10 @@ public class RestCollector implements Runnable {
this.url = new URL(getProperty(config, "url", "http://localhost:8181"));
this.paths = getProperty(config, "paths", "").split(",");
this.topic = getProperty(config, "topic", "decanter/collect/rest");
+ this.requestMethod = getProperty(config, "request.method", "GET");
+ this.user = getProperty(config, "user", null);
+ this.password = getProperty(config, "password", null);
+ this.request = getProperty(config, "request", null);
}
private String getProperty(Dictionary<String, Object> properties, String key, String defaultValue) {
@@ -94,19 +102,33 @@ public class RestCollector implements Runnable {
Map<String, Object> data = new HashMap<>();
try {
connection = (HttpURLConnection) urlWithPath.openConnection();
+ if (user != null) {
+ String authentication = user + ":" + password;
+ byte[] encodedAuthentication = Base64.getEncoder().encode(authentication.getBytes(StandardCharsets.UTF_8));
+ String authenticationHeader = "Basic " + new String(encodedAuthentication);
+ connection.setRequestProperty("Authorization", authenticationHeader);
+ }
+ connection.setRequestMethod(requestMethod);
+ if ((requestMethod.equalsIgnoreCase("POST") || requestMethod.equalsIgnoreCase("PUT")) && request != null) {
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+ try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()))) {
+ writer.write(request);
+ }
+ }
+ Enumeration<String> keys = config.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ if (key.startsWith("header.")) {
+ connection.setRequestProperty(key.substring("header.".length()), (String) config.get(key));
+ }
+ }
data.putAll(unmarshaller.unmarshal(connection.getInputStream()));
data.put("http.response.code", connection.getResponseCode());
data.put("http.response.message", connection.getResponseMessage());
data.put("type", "rest");
data.put("url", urlWithPath);
- // custom fields
- Enumeration<String> keys = config.keys();
- while (keys.hasMoreElements()) {
- String key = keys.nextElement();
- data.put(key, config.get(key));
- }
-
PropertiesPreparator.prepare(data, config);
data.put("service.hostName", url.getHost());
diff --git a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/RestCollectorTest.java b/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/RestCollectorTest.java
index 69d5a3d..711ecc1 100644
--- a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/RestCollectorTest.java
+++ b/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/RestCollectorTest.java
@@ -80,4 +80,79 @@ public class RestCollectorTest {
Assert.assertEquals("hello world\n", event.getProperty("payload"));
}
+ @Test
+ public void testPost() throws Exception {
+ EventAdminMock eventAdminMock = new EventAdminMock();
+ RestCollector collector = new RestCollector();
+ Dictionary<String, Object> config = new Hashtable<>();
+ config.put("request.method", "POST");
+ config.put("request", "test");
+ config.put("url", "http://localhost:9090/test/submit");
+ collector.unmarshaller = new RawUnmarshaller();
+ collector.dispatcher = eventAdminMock;
+ collector.activate(config);
+ collector.run();
+
+ Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+ Event event = eventAdminMock.postedEvents.get(0);
+ Assert.assertEquals(200, event.getProperty("http.response.code"));
+ Assert.assertEquals("hello post test\n", event.getProperty("payload"));
+ }
+
+ @Test
+ public void testPut() throws Exception {
+ EventAdminMock eventAdminMock = new EventAdminMock();
+ RestCollector collector = new RestCollector();
+ Dictionary<String, Object> config = new Hashtable<>();
+ config.put("request.method", "PUT");
+ config.put("request", "test");
+ config.put("url", "http://localhost:9090/test/submit");
+ collector.unmarshaller = new RawUnmarshaller();
+ collector.dispatcher = eventAdminMock;
+ collector.activate(config);
+ collector.run();
+
+ Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+ Event event = eventAdminMock.postedEvents.get(0);
+ Assert.assertEquals(200, event.getProperty("http.response.code"));
+ Assert.assertEquals("hello put test\n", event.getProperty("payload"));
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ EventAdminMock eventAdminMock = new EventAdminMock();
+ RestCollector collector = new RestCollector();
+ Dictionary<String, Object> config = new Hashtable<>();
+ config.put("request.method", "DELETE");
+ config.put("url", "http://localhost:9090/test/delete");
+ collector.unmarshaller = new RawUnmarshaller();
+ collector.dispatcher = eventAdminMock;
+ collector.activate(config);
+ collector.run();
+
+ Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+ Event event = eventAdminMock.postedEvents.get(0);
+ Assert.assertEquals(200, event.getProperty("http.response.code"));
+ Assert.assertEquals("deleted\n", event.getProperty("payload"));
+ }
+
+ @Test
+ public void testHeader() throws Exception {
+ EventAdminMock eventAdminMock = new EventAdminMock();
+ RestCollector collector = new RestCollector();
+ Dictionary<String, Object> config = new Hashtable<>();
+ config.put("request.method", "POST");
+ config.put("header.foo", "test");
+ config.put("url", "http://localhost:9090/test/header");
+ collector.unmarshaller = new RawUnmarshaller();
+ collector.dispatcher = eventAdminMock;
+ collector.activate(config);
+ collector.run();
+
+ Assert.assertEquals(1, eventAdminMock.postedEvents.size());
+ Event event = eventAdminMock.postedEvents.get(0);
+ Assert.assertEquals(200, event.getProperty("http.response.code"));
+ Assert.assertEquals("hello header test\n", event.getProperty("payload"));
+ }
+
}
diff --git a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java b/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java
index 6e6cc3c..0068982 100644
--- a/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java
+++ b/collector/rest/src/test/java/org/apache/karaf/decanter/collector/rest/TestService.java
@@ -16,9 +16,7 @@
*/
package org.apache.karaf.decanter.collector.rest;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
+import javax.ws.rs.*;
@Path("/")
public class TestService {
@@ -31,8 +29,26 @@ public class TestService {
@POST
@Path("/submit")
- public void submit(String foo) {
- // nothing to do
+ public String submitPost(String foo) {
+ return "hello post " + foo;
+ }
+
+ @PUT
+ @Path("/submit")
+ public String submitPut(String foo) {
+ return "hello put " + foo;
+ }
+
+ @DELETE
+ @Path("/delete")
+ public String delete() {
+ return "deleted";
+ }
+
+ @POST
+ @Path("/header")
+ public String headerPost(@HeaderParam("foo") String foo) {
+ return "hello header " + foo;
}
}
diff --git a/manual/src/main/asciidoc/user-guide/appenders.adoc b/manual/src/main/asciidoc/user-guide/appenders.adoc
index 639ee30..6fc28aa 100644
--- a/manual/src/main/asciidoc/user-guide/appenders.adoc
+++ b/manual/src/main/asciidoc/user-guide/appenders.adoc
@@ -682,8 +682,6 @@ You can install a test database with Docker for dev:
docker run -d --name timescaledb -p 5432:5432 -e POSTGRES_PASSWORD=decanter -e POSTGRES_USER=decanter -e POSTGRES_DATABASE=decanter timescale/timescaledb
```
-===== TimescaleDB appender
-
The `decanter-appender-timescaledb` feature installs the TimescaleDB appender.
As TimescaleDB is a PostgreSQL database extension, the *timescaledb* feature will install all required features to configure
@@ -718,7 +716,7 @@ The table is simple and contains just two column:
** `content` as TEXT
* `marshaller.target` is the marshaller used to serialize data into the table.
-===== WebSocket Servlet
+==== WebSocket Servlet
The `decanter-appender-websocket-servlet` feature exposes a websocket on which clients can register. Then, Decanter will send the collected data to the connected clients.
@@ -753,7 +751,7 @@ curl --include \
http://localhost:8181/decanter-websocket
```
-===== Prometheus
+==== Prometheus
The `decanter-appender-prometheus` feature collects and exposes metrics on prometheus:
@@ -823,3 +821,42 @@ Import-Package: io.prometheus.client;version="[0.8,1)"
```
That's the only thing you need: your metrics will be available on the Decanter Prometheus servlet (again on `http://localhost:8181/decanter/prometheus` by default).
+
+==== Rest
+
+Decanter Rest appender send collected data to a remote REST service.
+
+The `decanter-appender-rest` feature installs the Rest appender:
+
+----
+karaf@root()> feature:install decanter-appender-rest
+----
+
+The feature also installs `etc/org.apache.karaf.decanter.appender.rest.cfg` configuration file:
+
+----
+###############################
+# Decanter Appender REST Configuration
+###############################
+
+# Mandatory URI where the REST appender connects to
+uri=
+
+#request.method=POST (the REST verb)
+#user= (for basic authentication)
+#password= (for basic authentication)
+#content.type=application/json (the message content type sent)
+#charset=utf-8 (the message charset)
+#header.foo= (HTTP header prefixed with header.)
+#payload.header= (if set the Decanter collected data is sent as HTTP header instead of body)
+
+# Marshaller to use (json is recommended)
+marshaller.target=(dataFormat=json)
+----
+
+* `uri` is mandatory and contains the location of the Rest service to call
+* `user` and `password` are used if the Rest service uses basic authentication
+* `content.type` is the message type sent to the Rest service (default is `application/json`)
+* `charset` is the message encoding (default is `utf-8`)
+* `header.` allows you to add any custom HTTP headers (parameters) to the request (prefixed by `header.`)
+* `payload.header` allows you to use a HTTP header to send the collected data instead of directly the "body".
\ No newline at end of file
diff --git a/manual/src/main/asciidoc/user-guide/collectors.adoc b/manual/src/main/asciidoc/user-guide/collectors.adoc
index f37dde3..ddc92c3 100644
--- a/manual/src/main/asciidoc/user-guide/collectors.adoc
+++ b/manual/src/main/asciidoc/user-guide/collectors.adoc
@@ -844,6 +844,37 @@ The `decanter-collector-rest-servlet` feature installs the collector:
karaf@root()> feature:install decanter-collector-rest-servlet
----
+==== REST
+
+The Decanter REST collector periodically requests a REST service and returns the result (with all HTTP details).
+
+The `decanter-collector-rest` feature installs the collector:
+
+----
+karaf@root()> feature:install decanter-collector-rest
+----
+
+This feature also installs `etc/org.apache.karaf.decanter.collector.rest.cfg` configuration file where you can setup the REST service request:
+
+----
+#
+# Decanter REST collector
+#
+
+url=http://localhost:8080
+paths=metrics
+
+#request.method=GET (possible values are GET, POST, PUT, DELETE, default is GET)
+#request=foo (request payload)
+#header.foo=bar (header passed, prefixed with header.)
+#user=user (used for basic authentication)
+#password=password (used for basic authentication)
+#topic=decanter/collector/rest (Decanter dispatcher topic name to use)
+
+# Unmarshaller to use
+unmarshaller.target=(dataFormat=json)
+----
+
==== SOAP
The Decanter SOAP collector periodically requests a SOAP service and returns the result (the SOAP Response, or error details if it failed).