Import of the IEC 60870 component

This change adds an IEC 60870 camel component based on the
IEC 60870 implementation of Eclipse NeoSCADA.

Signed-off-by: Jens Reimann <jreim...@redhat.com>


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/eb4f6059
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/eb4f6059
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/eb4f6059

Branch: refs/heads/master
Commit: eb4f6059cdfa5e363afdc57d258ea51775a59b88
Parents: 239fde8
Author: Jens Reimann <jreim...@redhat.com>
Authored: Wed Aug 9 14:32:26 2017 +0200
Committer: Andrea Cosentino <anco...@gmail.com>
Committed: Thu Aug 10 12:20:18 2017 +0200

----------------------------------------------------------------------
 components/camel-iec60870/pom.xml               | 146 ++++++++++++
 .../main/docs/iec60870-client-component.adoc    | 127 +++++++++++
 .../main/docs/iec60870-server-component.adoc    | 111 +++++++++
 .../iec60870/AbstractConnectionMultiplexor.java |  88 ++++++++
 .../iec60870/AbstractIecComponent.java          | 174 +++++++++++++++
 .../component/iec60870/AbstractIecEndpoint.java | 109 +++++++++
 .../camel/component/iec60870/BaseOptions.java   | 223 +++++++++++++++++++
 .../camel/component/iec60870/ConnectionId.java  |  94 ++++++++
 .../camel/component/iec60870/Constants.java     |  25 +++
 .../iec60870/DiscardAckChannelHandler.java      |  54 +++++
 .../component/iec60870/DiscardAckModule.java    |  44 ++++
 .../camel/component/iec60870/ObjectAddress.java | 114 ++++++++++
 .../iec60870/client/ClientComponent.java        |  78 +++++++
 .../iec60870/client/ClientConnection.java       | 132 +++++++++++
 .../client/ClientConnectionMultiplexor.java     |  43 ++++
 .../iec60870/client/ClientConsumer.java         |  79 +++++++
 .../iec60870/client/ClientEndpoint.java         |  46 ++++
 .../iec60870/client/ClientOptions.java          |  91 ++++++++
 .../iec60870/client/ClientProducer.java         |  89 ++++++++
 .../iec60870/server/ServerComponent.java        |  83 +++++++
 .../server/ServerConnectionMultiplexor.java     |  43 ++++
 .../iec60870/server/ServerConsumer.java         | 110 +++++++++
 .../iec60870/server/ServerEndpoint.java         |  60 +++++
 .../iec60870/server/ServerInstance.java         | 190 ++++++++++++++++
 .../iec60870/server/ServerOptions.java          | 152 +++++++++++++
 .../iec60870/server/ServerProducer.java         |  68 ++++++
 .../src/main/resources/META-INF/LICENSE.txt     | 203 +++++++++++++++++
 .../src/main/resources/META-INF/NOTICE.txt      |  11 +
 .../org/apache/camel/component/iec60870-client  |  18 ++
 .../org/apache/camel/component/iec60870-server  |  18 ++
 .../component/iec60870/ConnectionIdTest.java    |  71 ++++++
 .../component/iec60870/ConnectionTest.java      | 171 ++++++++++++++
 .../apache/camel/component/iec60870/Ports.java  |  32 +++
 .../iec60870/testing/ExampleApplication1.java   |  76 +++++++
 .../src/test/resources/log4j2.properties        |  28 +++
 components/readme.adoc                          |   4 +-
 parent/pom.xml                                  |   6 +
 .../camel-iec60870-starter/pom.xml              |  53 +++++
 .../ClientComponentAutoConfiguration.java       | 128 +++++++++++
 .../ClientComponentConfiguration.java           |  87 ++++++++
 .../ServerComponentAutoConfiguration.java       | 128 +++++++++++
 .../ServerComponentConfiguration.java           | 136 +++++++++++
 .../src/main/resources/META-INF/LICENSE.txt     | 203 +++++++++++++++++
 .../src/main/resources/META-INF/NOTICE.txt      |  11 +
 .../main/resources/META-INF/spring.factories    |  21 ++
 .../src/main/resources/META-INF/spring.provides |  17 ++
 .../spring-boot/components-starter/pom.xml      |   1 +
 47 files changed, 3995 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-iec60870/pom.xml 
b/components/camel-iec60870/pom.xml
new file mode 100644
index 0000000..6102479
--- /dev/null
+++ b/components/camel-iec60870/pom.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>components</artifactId>
+    <version>2.20.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-iec60870</artifactId>
+  <packaging>jar</packaging>
+  <name>Camel :: IEC 60870</name>
+  <description>Camel IEC 60870-5-104 support</description>
+
+  <properties>
+    <camel.osgi.export.pkg>
+      !*.internal.*,
+      org.apache.camel.component.iec60870.*
+    </camel.osgi.export.pkg>
+    <camel.osgi.export.service>
+      org.apache.camel.spi.ComponentResolver;component=iec60870-client,
+      org.apache.camel.spi.ComponentResolver;component=iec60870-server
+    </camel.osgi.export.service>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.eclipse.neoscada.protocols</groupId>
+      <artifactId>org.eclipse.neoscada.protocol.iec60870</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.neoscada.protocols</groupId>
+      <artifactId>org.eclipse.neoscada.protocol.iec60870.client</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.neoscada.protocols</groupId>
+      
<artifactId>org.eclipse.neoscada.protocol.iec60870.client.data</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.neoscada.protocols</groupId>
+      <artifactId>org.eclipse.neoscada.protocol.iec60870.server</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.neoscada.protocols</groupId>
+      
<artifactId>org.eclipse.neoscada.protocol.iec60870.server.data</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.neoscada.utils</groupId>
+      <artifactId>org.eclipse.scada.utils</artifactId>
+      <version>${neoscada-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+      <version>${netty-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-codec</artifactId>
+      <version>${netty-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-common</artifactId>
+      <version>${netty-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-handler</artifactId>
+      <version>${netty-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport</artifactId>
+      <version>${netty-version}</version>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/docs/iec60870-client-component.adoc
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/docs/iec60870-client-component.adoc 
b/components/camel-iec60870/src/main/docs/iec60870-client-component.adoc
new file mode 100644
index 0000000..60367ec
--- /dev/null
+++ b/components/camel-iec60870/src/main/docs/iec60870-client-component.adoc
@@ -0,0 +1,127 @@
+## IEC 60870 Client Component
+
+*Available as of Camel version 2.20*
+
+### IEC 60870-5-104 Client Component
+
+*Available as of Camel 2.20*
+
+The *IEC 60870-5-104 Client* component provides access to IEC 60870 servers 
using the
+http://eclipse.org/eclipsescada[Eclipse NeoSCADA™] implementation.
+
+*Java 8*: This component requires Java 8 at runtime. 
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-iec60870</artifactId>
+    <version>x.x.x</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+// component options: START
+The IEC 60870 Client component supports 2 options which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **defaultConnection Options** (common) | Default connection options |  | 
ClientOptions
+| **resolveProperty Placeholders** (advanced) | Whether the component should 
resolve property placeholders on itself when starting. Only properties which 
are of String type can use property placeholders. | true | boolean
+|=======================================================================
+// component options: END
+
+
+
+
+
+### URI format
+
+The URI syntax of the endpoint is: 
+
+[source]
+------------------------
+iec60870-client:host:port/00-01-02-03-04
+------------------------
+
+The information object address is encoded in the path in the syntax shows 
above. Please
+note that always the full, 5 octet address format is being used. Unused octets 
have to be filled
+with zero.
+
+### URI options
+
+
+// endpoint options: START
+The IEC 60870 Client endpoint is configured using URI syntax:
+
+    iec60870-client:endpointUri
+
+with the following path and query parameters:
+
+#### Path Parameters (1 parameters):
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **uriPath** | *Required* The object information address |  | ObjectAddress
+|=======================================================================
+
+#### Query Parameters (18 parameters):
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **dataModuleOptions** (common) | Data module options |  | DataModuleOptions
+| **protocolOptions** (common) | Protocol options |  | ProtocolOptions
+| **bridgeErrorHandler** (consumer) | Allows for bridging the consumer to the 
Camel routing Error Handler which mean any exceptions occurred while the 
consumer is trying to pickup incoming messages or the likes will now be 
processed as a message and handled by the routing Error Handler. By default the 
consumer will use the org.apache.camel.spi.ExceptionHandler to deal with 
exceptions that will be logged at WARN or ERROR level and ignored. | false | 
boolean
+| **exceptionHandler** (consumer) | To let the consumer use a custom 
ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this 
options is not in use. By default the consumer will deal with exceptions that 
will be logged at WARN or ERROR level and ignored. |  | ExceptionHandler
+| **exchangePattern** (consumer) | Sets the exchange pattern when the consumer 
creates an exchange. |  | ExchangePattern
+| **synchronous** (advanced) | Sets whether synchronous processing should be 
strictly used or Camel is allowed to use asynchronous processing (if 
supported). | false | boolean
+| **acknowledgeWindow** (connection) | Parameter W - Acknowledgment window. | 
10 | short
+| **adsuAddressType** (connection) | The common ASDU address size. May be 
either SIZE_1 or SIZE_2. |  | ASDUAddressType
+| **causeOfTransmissionType** (connection) | The cause of transmission type. 
May be either SIZE_1 or SIZE_2. |  | CauseOfTransmission Type
+| **informationObjectAddress Type** (connection) | The information address 
size. May be either SIZE_1 SIZE_2 or SIZE_3. |  | InformationObject AddressType
+| **maxUnacknowledged** (connection) | Parameter K - Maximum number of 
un-acknowledged messages. | 15 | short
+| **timeout1** (connection) | Timeout T1 in milliseconds. | 15000 | int
+| **timeout2** (connection) | Timeout T2 in milliseconds. | 10000 | int
+| **timeout3** (connection) | Timeout T3 in milliseconds. | 20000 | int
+| **ignoreBackgroundScan** (data) | Whether background scan transmissions 
should be ignored. | true | boolean
+| **ignoreDaylightSavingTime** (data) | Whether to ignore or respect DST | 
false | boolean
+| **timeZone** (data) | The timezone to use. May be any Java time zone string 
| UTC | TimeZone
+| **connectionId** (id) | An identifier grouping connection instances |  | 
String
+|=======================================================================
+// endpoint options: END
+
+
+
+A connection instance if identified by the host and port part of the URI, plus 
all parameters in the "id" group.
+If a new connection id is encountered the connection options will be evaluated 
and the connection instance
+is created with those options.
+
+
+NOTE: If two URIs specify the same connection (host, port, …) but different 
connection options, then it is
+undefined which of those connection options will be used. 
+
+
+The final connection options will be evaluated in the following order:
+
+* If present, the +connectionOptions+ parameter will be used
+* Otherwise the +defaultConnectionOptions+ instance is copied and customized 
in the following steps
+* Apply +protocolOptions+ if present
+* Apply +dataModuleOptions+ if present
+* Apply all explicit connection parameters (e.g. +timeZone+)
+
+
+
+### See Also
+
+* link:configuring-camel.html[Configuring Camel]
+* link:component.html[Component]
+* link:endpoint.html[Endpoint]
+* link:getting-started.html[Getting Started]

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/docs/iec60870-server-component.adoc
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/docs/iec60870-server-component.adoc 
b/components/camel-iec60870/src/main/docs/iec60870-server-component.adoc
new file mode 100644
index 0000000..338e8d7
--- /dev/null
+++ b/components/camel-iec60870/src/main/docs/iec60870-server-component.adoc
@@ -0,0 +1,111 @@
+## IEC 60870-5-104 server Component
+
+*Available as of Camel version 2.20*
+
+### IEC 60870-5-104 Server Component
+
+*Available as of Camel 2.20*
+
+The *IEC 60870-5-104 Server* component provides access to IEC 60870 servers 
using the
+http://eclipse.org/eclipsescada[Eclipse NeoSCADA™] implementation.
+
+*Java 8*: This component requires Java 8 at runtime. 
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+------------------------------------------------------------
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-iec60870</artifactId>
+    <version>x.x.x</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+------------------------------------------------------------
+
+
+// component options: START
+The IEC 60870-5-104 server component supports 2 options which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **defaultConnection Options** (common) | Default connection options |  | 
ServerOptions
+| **resolveProperty Placeholders** (advanced) | Whether the component should 
resolve property placeholders on itself when starting. Only properties which 
are of String type can use property placeholders. | true | boolean
+|=======================================================================
+// component options: END
+
+
+
+
+
+### URI format
+
+The URI syntax of the endpoint is: 
+
+[source]
+------------------------
+iec60870-server:host:port/00-01-02-03-04
+------------------------
+
+The information object address is encoded in the path in the syntax shows 
above. Please
+note that always the full, 5 octet address format is being used. Unused octets 
have to be filled
+with zero.
+
+### URI options
+
+
+
+
+// endpoint options: START
+The IEC 60870-5-104 server endpoint is configured using URI syntax:
+
+    iec60870-server:endpointUri
+
+with the following path and query parameters:
+
+#### Path Parameters (1 parameters):
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **uriPath** | *Required* The object information address |  | ObjectAddress
+|=======================================================================
+
+#### Query Parameters (19 parameters):
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|=======================================================================
+| Name | Description | Default | Type
+| **dataModuleOptions** (common) | Data module options |  | DataModuleOptions
+| **filterNonExecute** (common) | Filter out all requests which don't have the 
execute bit set | true | boolean
+| **protocolOptions** (common) | Protocol options |  | ProtocolOptions
+| **bridgeErrorHandler** (consumer) | Allows for bridging the consumer to the 
Camel routing Error Handler which mean any exceptions occurred while the 
consumer is trying to pickup incoming messages or the likes will now be 
processed as a message and handled by the routing Error Handler. By default the 
consumer will use the org.apache.camel.spi.ExceptionHandler to deal with 
exceptions that will be logged at WARN or ERROR level and ignored. | false | 
boolean
+| **exceptionHandler** (consumer) | To let the consumer use a custom 
ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this 
options is not in use. By default the consumer will deal with exceptions that 
will be logged at WARN or ERROR level and ignored. |  | ExceptionHandler
+| **exchangePattern** (consumer) | Sets the exchange pattern when the consumer 
creates an exchange. |  | ExchangePattern
+| **synchronous** (advanced) | Sets whether synchronous processing should be 
strictly used or Camel is allowed to use asynchronous processing (if 
supported). | false | boolean
+| **acknowledgeWindow** (connection) | Parameter W - Acknowledgment window. | 
10 | short
+| **adsuAddressType** (connection) | The common ASDU address size. May be 
either SIZE_1 or SIZE_2. |  | ASDUAddressType
+| **causeOfTransmissionType** (connection) | The cause of transmission type. 
May be either SIZE_1 or SIZE_2. |  | CauseOfTransmission Type
+| **informationObjectAddress Type** (connection) | The information address 
size. May be either SIZE_1 SIZE_2 or SIZE_3. |  | InformationObject AddressType
+| **maxUnacknowledged** (connection) | Parameter K - Maximum number of 
un-acknowledged messages. | 15 | short
+| **timeout1** (connection) | Timeout T1 in milliseconds. | 15000 | int
+| **timeout2** (connection) | Timeout T2 in milliseconds. | 10000 | int
+| **timeout3** (connection) | Timeout T3 in milliseconds. | 20000 | int
+| **ignoreBackgroundScan** (data) | Whether background scan transmissions 
should be ignored. | true | boolean
+| **ignoreDaylightSavingTime** (data) | Whether to ignore or respect DST | 
false | boolean
+| **timeZone** (data) | The timezone to use. May be any Java time zone string 
| UTC | TimeZone
+| **connectionId** (id) | An identifier grouping connection instances |  | 
String
+|=======================================================================
+// endpoint options: END
+
+
+### See Also
+
+* link:configuring-camel.html[Configuring Camel]
+* link:component.html[Component]
+* link:endpoint.html[Endpoint]
+* link:getting-started.html[Getting Started]

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractConnectionMultiplexor.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractConnectionMultiplexor.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractConnectionMultiplexor.java
new file mode 100644
index 0000000..18fa1bf
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractConnectionMultiplexor.java
@@ -0,0 +1,88 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractConnectionMultiplexor {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AbstractConnectionMultiplexor.class);
+
+    public interface Handle {
+        void unregister() throws Exception;
+    }
+
+    private final class HandleImplementation implements Handle {
+        @Override
+        public void unregister() throws Exception {
+            AbstractConnectionMultiplexor.this.unregister(this);
+        }
+    }
+
+    private final Set<HandleImplementation> handles = new 
CopyOnWriteArraySet<>();
+
+    public synchronized Handle register() throws Exception {
+        final HandleImplementation handle = new HandleImplementation();
+
+        final boolean needStart = this.handles.isEmpty();
+        this.handles.add(handle);
+
+        if (needStart) {
+            LOG.info("Calling performStart()");
+            performStart();
+        }
+
+        return handle;
+    }
+
+    private synchronized void unregister(final HandleImplementation handle) 
throws Exception {
+        if (!this.handles.remove(handle)) {
+            return;
+        }
+
+        if (this.handles.isEmpty()) {
+            LOG.info("Calling performStop()");
+            performStop();
+        }
+    }
+
+    public synchronized void dispose() {
+
+        LOG.info("Disposing");
+        if (this.handles.isEmpty()) {
+            LOG.debug("Disposing - not started");
+            return;
+        }
+
+        LOG.debug("Disposing - calling performStop()");
+
+        this.handles.clear();
+        try {
+            performStop();
+        } catch (final Exception e) {
+            throw new RuntimeException("Failed to stop on dispose", e);
+        }
+    }
+
+    protected abstract void performStart() throws Exception;
+
+    protected abstract void performStop() throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecComponent.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecComponent.java
new file mode 100644
index 0000000..93ca36c
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecComponent.java
@@ -0,0 +1,174 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.component.iec60870.client.ClientOptions;
+import org.apache.camel.impl.DefaultComponent;
+import org.eclipse.neoscada.protocol.iec60870.ProtocolOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractIecComponent<T1, T2 extends BaseOptions<T2>> 
extends DefaultComponent {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AbstractIecComponent.class);
+
+    private final Map<ConnectionId, T1> connections = new HashMap<>();
+
+    private final Class<T2> connectionOptionsClazz;
+
+    private T2 defaultConnectionOptions;
+
+    public AbstractIecComponent(final Class<T2> connectionOptionsClazz, final 
T2 defaultConnectionOptions, final Class<? extends Endpoint> endpointClass) {
+        this.connectionOptionsClazz = connectionOptionsClazz;
+        this.defaultConnectionOptions = defaultConnectionOptions;
+    }
+
+    public AbstractIecComponent(final Class<T2> connectionOptionsClazz, final 
T2 defaultConnectionOptions, final CamelContext context,
+                                final Class<? extends Endpoint> endpointClass) 
{
+        super(context);
+        this.connectionOptionsClazz = connectionOptionsClazz;
+        this.defaultConnectionOptions = defaultConnectionOptions;
+    }
+
+    protected abstract T1 createConnection(ConnectionId id, T2 options);
+
+    /**
+     * Default connection options
+     *
+     * @param defaultConnectionOptions the new default connection options, must
+     *            not be {@code null}
+     */
+    protected void setDefaultConnectionOptions(final T2 
defaultConnectionOptions) {
+        this.defaultConnectionOptions = 
requireNonNull(defaultConnectionOptions);
+    }
+
+    /**
+     * Get the default connection options
+     *
+     * @return the default connect options, never returns {@code null}
+     */
+    protected T2 getDefaultConnectionOptions() {
+        return this.defaultConnectionOptions;
+    }
+
+    @Override
+    protected Endpoint createEndpoint(final String uri, final String 
remaining, final Map<String, Object> parameters) throws Exception {
+
+        LOG.info("Create endpoint - uri: {}, remaining: {}, parameters: {}", 
uri, remaining, parameters);
+
+        final T1 connection = lookupConnection(uri, parameters);
+        final ObjectAddress address = parseAddress(uri);
+
+        return createEndpoint(uri, connection, address);
+    }
+
+    protected abstract Endpoint createEndpoint(String uri, T1 connection, 
ObjectAddress address);
+
+    protected T2 parseOptions(final ConnectionId id, final Map<String, Object> 
parameters) throws Exception {
+
+        // test for provided connection options
+
+        final Object connectionOptions = 
parameters.get(Constants.PARAM_CONNECTION_OPTIONS);
+        if (connectionOptions != null) {
+            try {
+                return this.connectionOptionsClazz.cast(connectionOptions);
+            } catch (final ClassCastException e) {
+                throw new IllegalArgumentException(String.format("'%s' must by 
of type %s", Constants.PARAM_CONNECTION_OPTIONS, 
ClientOptions.class.getName()), e);
+            }
+        }
+
+        // construct new default set
+
+        final T2 options = this.defaultConnectionOptions.copy();
+
+        // apply protocolOptions
+
+        if (parameters.get(Constants.PARAM_PROTOCOL_OPTIONS) instanceof 
ProtocolOptions) {
+            
options.setProtocolOptions((ProtocolOptions)parameters.get(Constants.PARAM_PROTOCOL_OPTIONS));
+        }
+
+        // apply dataModuleOptions
+
+        applyDataModuleOptions(options, parameters);
+
+        // apply parameters to connection options
+
+        setProperties(options, parameters);
+
+        // return result
+
+        return options;
+    }
+
+    protected abstract void applyDataModuleOptions(T2 options, Map<String, 
Object> parameters);
+
+    private T1 lookupConnection(final String fullUri, final Map<String, 
Object> parameters) throws Exception {
+
+        LOG.debug("parse connection - '{}'", fullUri);
+
+        if (fullUri == null || fullUri.isEmpty()) {
+            throw new IllegalArgumentException("Invalid URI: " + fullUri);
+        }
+
+        final ConnectionId id = parseConnectionId(fullUri, parameters);
+
+        LOG.debug("parse connection - fullUri: {} -> {}", fullUri, id);
+
+        synchronized (this) {
+            LOG.debug("Locating connection - {}", id);
+
+            T1 connection = this.connections.get(id);
+
+            LOG.debug("Result - {} -> {}", id, connection);
+
+            if (connection == null) {
+                final T2 options = parseOptions(id, parameters);
+                LOG.debug("Creating new connection: {}", options);
+
+                connection = createConnection(id, options);
+                this.connections.put(id, connection);
+            }
+            return connection;
+        }
+    }
+
+    private static ConnectionId parseConnectionId(final String fullUri, final 
Map<String, Object> parameters) {
+        final URI uri = URI.create(fullUri);
+
+        final Object connectionId = parameters.get("connectionId");
+
+        return new ConnectionId(uri.getHost(), uri.getPort(), connectionId 
instanceof String ? (String)connectionId : null);
+    }
+
+    private static ObjectAddress parseAddress(final String fullUri) {
+        final URI uri = URI.create(fullUri);
+
+        String path = uri.getPath();
+        path = path.replaceAll("^\\/+", "");
+
+        return ObjectAddress.valueOf(path);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecEndpoint.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecEndpoint.java
new file mode 100644
index 0000000..98fd48c
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/AbstractIecEndpoint.java
@@ -0,0 +1,109 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.util.Objects.requireNonNull;
+
+import 
org.apache.camel.component.iec60870.AbstractConnectionMultiplexor.Handle;
+import org.apache.camel.component.iec60870.client.ClientOptions;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.impl.DefaultEndpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.eclipse.neoscada.protocol.iec60870.ProtocolOptions;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataModuleOptions;
+
+public abstract class AbstractIecEndpoint<T extends 
AbstractConnectionMultiplexor> extends DefaultEndpoint {
+
+    /**
+     * The object information address
+     */
+    @UriPath(name = "uriPath")
+    @Metadata(required = "true")
+    private final ObjectAddress address;
+
+    // dummy for doc generation
+    /**
+     * A full set of connection options
+     */
+    @UriParam
+    private ClientOptions connectionOptions;
+
+    // dummy for doc generation
+    /**
+     * A set of protocol options
+     */
+    @UriParam
+    private ProtocolOptions protocolOptions;
+
+    // dummy for doc generation
+    /**
+     * A set of data module options
+     */
+    @UriParam
+    private DataModuleOptions dataModuleOptions;
+
+    // dummy for doc generation
+    /**
+     * An identifier grouping connection instances
+     */
+    @UriParam(label = "id")
+    private String connectionId;
+
+    private final T connection;
+
+    private final AtomicReference<Handle> connectionHandle = new 
AtomicReference<>();
+
+    public AbstractIecEndpoint(final String uri, final DefaultComponent 
component, final T connection, final ObjectAddress address) {
+        super(uri, component);
+
+        this.connection = requireNonNull(connection);
+        this.address = requireNonNull(address);
+    }
+
+    public ObjectAddress getAddress() {
+        return this.address;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+        this.connectionHandle.set(this.connection.register());
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        final Handle connectionHandle = this.connectionHandle.getAndSet(null);
+        if (connectionHandle != null) {
+            connectionHandle.unregister();
+        }
+        super.doStop();
+    }
+
+    protected T getConnection() {
+        return this.connection;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/BaseOptions.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/BaseOptions.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/BaseOptions.java
new file mode 100644
index 0000000..e7e63c3
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/BaseOptions.java
@@ -0,0 +1,223 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.Objects;
+import java.util.TimeZone;
+
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.eclipse.neoscada.protocol.iec60870.ASDUAddressType;
+import org.eclipse.neoscada.protocol.iec60870.CauseOfTransmissionType;
+import org.eclipse.neoscada.protocol.iec60870.InformationObjectAddressType;
+import org.eclipse.neoscada.protocol.iec60870.ProtocolOptions;
+
+@UriParams
+public abstract class BaseOptions<T extends BaseOptions<T>> {
+
+    /**
+     * Protocol options
+     */
+    @UriParam(javaType = "ProtocolOptions")
+    private ProtocolOptions.Builder protocolOptions;
+
+    // dummy for doc generation
+    /**
+     * The common ASDU address size.
+     * <p>
+     * May be either {@code SIZE_1} or {@code SIZE_2}.
+     * </p>
+     */
+    @UriParam(enums = "SIZE_1, SIZE_2", label = "connection")
+    private ASDUAddressType adsuAddressType;
+
+    // dummy for doc generation
+    /**
+     * The information address size.
+     * <p>
+     * May be either {@code SIZE_1}, {@code SIZE_2} or {@code SIZE_3}.
+     * </p>
+     */
+    @UriParam(enums = "SIZE_1, SIZE_2, SIZE_3", label = "connection")
+    private InformationObjectAddressType informationObjectAddressType;
+
+    // dummy for doc generation
+    /**
+     * The cause of transmission type.
+     * <p>
+     * May be either {@code SIZE_1} or {@code SIZE_2}.
+     * </p>
+     */
+    @UriParam(enums = "SIZE_1, SIZE_2", label = "connection")
+    private CauseOfTransmissionType causeOfTransmissionType;
+
+    // dummy for doc generation
+    /**
+     * The timezone to use.
+     * <p>
+     * May be any Java time zone string
+     * </p>
+     */
+    @UriParam(label = "data", defaultValue = "UTC")
+    private TimeZone timeZone;
+
+    // dummy for doc generation
+    /**
+     * Whether to ignore or respect DST
+     */
+    @UriParam(label = "data")
+    private boolean ignoreDaylightSavingTime;
+
+    // dummy for doc generation
+    /**
+     * Timeout T1 in milliseconds.
+     */
+    @UriParam(label = "connection", defaultValue = "15000")
+    private int timeout1;
+
+    // dummy for doc generation
+    /**
+     * Timeout T2 in milliseconds.
+     */
+    @UriParam(label = "connection", defaultValue = "10000")
+    private int timeout2;
+
+    // dummy for doc generation
+    /**
+     * Timeout T3 in milliseconds.
+     */
+    @UriParam(label = "connection", defaultValue = "20000")
+    private int timeout3;
+
+    // dummy for doc generation
+    /**
+     * Parameter "K" - Maximum number of un-acknowledged messages.
+     */
+    @UriParam(label = "connection", defaultValue = "15")
+    private short maxUnacknowledged;
+
+    // dummy for doc generation
+    /**
+     * Parameter "W" - Acknowledgment window.
+     */
+    @UriParam(label = "connection", defaultValue = "10")
+    private short acknowledgeWindow;
+
+    public BaseOptions() {
+        this.protocolOptions = new ProtocolOptions.Builder();
+    }
+
+    public BaseOptions(final ProtocolOptions protocolOptions) {
+        Objects.requireNonNull(protocolOptions);
+        this.protocolOptions = new ProtocolOptions.Builder(protocolOptions);
+    }
+
+    public void setProtocolOptions(final ProtocolOptions protocolOptions) {
+        Objects.requireNonNull(protocolOptions);
+
+        this.protocolOptions = new ProtocolOptions.Builder(protocolOptions);
+    }
+
+    public ProtocolOptions getProtocolOptions() {
+        return this.protocolOptions.build();
+    }
+
+    public abstract T copy();
+
+    // wrapper methods - ProtocolOptions
+
+    public int getTimeout1() {
+        return this.protocolOptions.getTimeout1();
+    }
+
+    public void setTimeout1(final int timeout1) {
+        this.protocolOptions.setTimeout1(timeout1);
+    }
+
+    public int getTimeout2() {
+        return this.protocolOptions.getTimeout2();
+    }
+
+    public void setTimeout2(final int timeout2) {
+        this.protocolOptions.setTimeout2(timeout2);
+    }
+
+    public int getTimeout3() {
+        return this.protocolOptions.getTimeout3();
+    }
+
+    public void setTimeout3(final int timeout3) {
+        this.protocolOptions.setTimeout3(timeout3);
+    }
+
+    public short getAcknowledgeWindow() {
+        return this.protocolOptions.getAcknowledgeWindow();
+    }
+
+    public void setAcknowledgeWindow(final short acknowledgeWindow) {
+        this.protocolOptions.setAcknowledgeWindow(acknowledgeWindow);
+    }
+
+    public short getMaxUnacknowledged() {
+        return this.protocolOptions.getMaxUnacknowledged();
+    }
+
+    public void setMaxUnacknowledged(final short maxUnacknowledged) {
+        this.protocolOptions.setMaxUnacknowledged(maxUnacknowledged);
+    }
+
+    public ASDUAddressType getAdsuAddressType() {
+        return this.protocolOptions.getAdsuAddressType();
+    }
+
+    public void setAdsuAddressType(final ASDUAddressType adsuAddressType) {
+        this.protocolOptions.setAdsuAddressType(adsuAddressType);
+    }
+
+    public InformationObjectAddressType getInformationObjectAddressType() {
+        return this.protocolOptions.getInformationObjectAddressType();
+    }
+
+    public void setInformationObjectAddressType(final 
InformationObjectAddressType informationObjectAddressType) {
+        
this.protocolOptions.setInformationObjectAddressType(informationObjectAddressType);
+    }
+
+    public CauseOfTransmissionType getCauseOfTransmissionType() {
+        return this.protocolOptions.getCauseOfTransmissionType();
+    }
+
+    public void setCauseOfTransmissionType(final CauseOfTransmissionType 
causeOfTransmissionType) {
+        
this.protocolOptions.setCauseOfTransmissionType(causeOfTransmissionType);
+    }
+
+    public TimeZone getTimeZone() {
+        return this.protocolOptions.getTimeZone();
+    }
+
+    public void setTimeZone(final TimeZone timeZone) {
+        this.protocolOptions.setTimeZone(timeZone);
+    }
+
+    public void setIgnoreDaylightSavingTime(final boolean 
ignoreDaylightSavingTime) {
+        
this.protocolOptions.setIgnoreDaylightSavingTime(ignoreDaylightSavingTime);
+    }
+
+    public boolean isIgnoreDaylightSavingTime() {
+        return this.protocolOptions.isIgnoreDaylightSavingTime();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ConnectionId.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ConnectionId.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ConnectionId.java
new file mode 100644
index 0000000..66dfc07
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ConnectionId.java
@@ -0,0 +1,94 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.Objects;
+
+public class ConnectionId {
+    private final String host;
+
+    private final int port;
+
+    private final String connectionId;
+
+    public ConnectionId(final String host, final int port, final String 
connectionId) {
+        Objects.requireNonNull(host);
+
+        if (port <= 0) {
+            throw new IllegalArgumentException(String.format("Port must be 
greater than 0"));
+        }
+
+        this.host = host;
+        this.port = port;
+        this.connectionId = connectionId;
+    }
+
+    public String getHost() {
+        return this.host;
+    }
+
+    public int getPort() {
+        return this.port;
+    }
+
+    public String getConnectionId() {
+        return this.connectionId;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (this.connectionId == null ? 0 : 
this.connectionId.hashCode());
+        result = prime * result + (this.host == null ? 0 : 
this.host.hashCode());
+        result = prime * result + this.port;
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ConnectionId other = (ConnectionId)obj;
+        if (this.connectionId == null) {
+            if (other.connectionId != null) {
+                return false;
+            }
+        } else if (!this.connectionId.equals(other.connectionId)) {
+            return false;
+        }
+        if (this.host == null) {
+            if (other.host != null) {
+                return false;
+            }
+        } else if (!this.host.equals(other.host)) {
+            return false;
+        }
+        if (this.port != other.port) {
+            return false;
+        }
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/Constants.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/Constants.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/Constants.java
new file mode 100644
index 0000000..f2f5a60
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/Constants.java
@@ -0,0 +1,25 @@
+/**
+ * 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.camel.component.iec60870;
+
+public interface Constants {
+    String PARAM_DATA_MODULE_OPTIONS = "dataModuleOptions";
+
+    String PARAM_PROTOCOL_OPTIONS = "protocolOptions";
+
+    String PARAM_CONNECTION_OPTIONS = "connectionOptions";
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckChannelHandler.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckChannelHandler.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckChannelHandler.java
new file mode 100644
index 0000000..32cbd69
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckChannelHandler.java
@@ -0,0 +1,54 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.eclipse.neoscada.protocol.iec60870.asdu.message.AbstractMessage;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.Cause;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.StandardCause;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DiscardAckChannelHandler extends ChannelInboundHandlerAdapter {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DiscardAckChannelHandler.class);
+
+    private final Set<Cause> discards = new HashSet<>();
+
+    public DiscardAckChannelHandler() {
+        this.discards.add(StandardCause.ACTIVATION_CONFIRM);
+        this.discards.add(StandardCause.ACTIVATION_TERMINATION);
+        this.discards.add(StandardCause.DEACTIVATION_CONFIRM);
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, final Object msg) 
throws Exception {
+        if (msg instanceof AbstractMessage) {
+            final AbstractMessage amsg = (AbstractMessage)msg;
+            final Cause cause = 
amsg.getHeader().getCauseOfTransmission().getCause();
+            if (this.discards.contains(cause)) {
+                LOG.debug("Discarding: {}", cause);
+                return;
+            }
+        }
+        super.channelRead(ctx, msg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckModule.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckModule.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckModule.java
new file mode 100644
index 0000000..4fb9c6d
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/DiscardAckModule.java
@@ -0,0 +1,44 @@
+/**
+ * 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.camel.component.iec60870;
+
+import io.netty.channel.socket.SocketChannel;
+import org.eclipse.neoscada.protocol.iec60870.apci.MessageChannel;
+import org.eclipse.neoscada.protocol.iec60870.asdu.MessageManager;
+import org.eclipse.neoscada.protocol.iec60870.client.Client;
+import org.eclipse.neoscada.protocol.iec60870.client.ClientModule;
+import org.eclipse.neoscada.protocol.iec60870.server.Server;
+import org.eclipse.neoscada.protocol.iec60870.server.ServerModule;
+
+public class DiscardAckModule implements ClientModule, ServerModule {
+    @Override
+    public void initializeChannel(final SocketChannel channel, final 
MessageChannel messageChannel) {
+        channel.pipeline().addLast(new DiscardAckChannelHandler());
+    }
+
+    @Override
+    public void dispose() {
+    }
+
+    @Override
+    public void initializeClient(final Client client, final MessageManager 
manager) {
+    }
+
+    @Override
+    public void initializeServer(final Server server, final MessageManager 
manager) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ObjectAddress.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ObjectAddress.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ObjectAddress.java
new file mode 100644
index 0000000..232a506
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/ObjectAddress.java
@@ -0,0 +1,114 @@
+/**
+ * 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.camel.component.iec60870;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.ASDUAddress;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.types.InformationObjectAddress;
+
+public class ObjectAddress {
+    int[] address;
+
+    private ObjectAddress(final int[] address) {
+        this.address = address;
+    }
+
+    public ObjectAddress(final int a1, final int a2, final int a3, final int 
a4, final int a5) {
+        this.address = new int[] {a1, a2, a3, a4, a5};
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%02d-%02d-%02d-%02d-%02d", this.address[0], 
this.address[1], this.address[2], this.address[3], this.address[4]);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(this.address);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ObjectAddress other = (ObjectAddress)obj;
+        if (!Arrays.equals(this.address, other.address)) {
+            return false;
+        }
+        return true;
+    }
+
+    public static ObjectAddress valueOf(final ASDUAddress asduAddress, final 
InformationObjectAddress address) {
+        Objects.requireNonNull(asduAddress);
+        Objects.requireNonNull(address);
+
+        final int[] a = asduAddress.toArray();
+        final int[] b = address.toArray();
+
+        return new ObjectAddress(a[0], a[1], b[0], b[1], b[2]);
+    }
+
+    public static ObjectAddress valueOf(final String address) {
+        if (address == null || address.isEmpty()) {
+            return null;
+        }
+
+        final String[] toks = address.split("-");
+        if (toks.length != 5) {
+            throw new IllegalArgumentException("Invalid address. Must have 5 
octets.");
+        }
+
+        final int[] a = new int[toks.length];
+
+        for (int i = 0; i < toks.length; i++) {
+            final int v;
+            try {
+                v = Integer.parseInt(toks[i]);
+            } catch (final NumberFormatException e) {
+                throw new IllegalArgumentException("Address segment must be 
numeric", e);
+            }
+
+            if (v < 0 || v > 255) {
+                throw new IllegalArgumentException(String.format("Address 
segment must be an octet, between 0 and 255 (is %s)", v));
+            }
+
+            a[i] = v;
+        }
+
+        return new ObjectAddress(a);
+    }
+
+    public ASDUAddress getASDUAddress() {
+        return ASDUAddress.fromArray(new int[] {this.address[0], 
this.address[1]});
+    }
+
+    public InformationObjectAddress getInformationObjectAddress() {
+        return InformationObjectAddress.fromArray(new int[] {this.address[2], 
this.address[3], this.address[4]});
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientComponent.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientComponent.java
new file mode 100644
index 0000000..1609ed1
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientComponent.java
@@ -0,0 +1,78 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.component.iec60870.AbstractIecComponent;
+import org.apache.camel.component.iec60870.ConnectionId;
+import org.apache.camel.component.iec60870.Constants;
+import org.apache.camel.component.iec60870.ObjectAddress;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataModuleOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClientComponent extends 
AbstractIecComponent<ClientConnectionMultiplexor, ClientOptions> {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ClientComponent.class);
+
+    public ClientComponent(final CamelContext context) {
+        super(ClientOptions.class, new ClientOptions(), context, 
ClientEndpoint.class);
+    }
+
+    public ClientComponent() {
+        super(ClientOptions.class, new ClientOptions(), ClientEndpoint.class);
+    }
+
+    @Override
+    protected void applyDataModuleOptions(final ClientOptions options, final 
Map<String, Object> parameters) {
+        if (parameters.get(Constants.PARAM_DATA_MODULE_OPTIONS) instanceof 
DataModuleOptions) {
+            
options.setDataModuleOptions((DataModuleOptions)parameters.get(Constants.PARAM_DATA_MODULE_OPTIONS));
+        }
+    }
+
+    @Override
+    protected Endpoint createEndpoint(final String uri, final 
ClientConnectionMultiplexor connection, final ObjectAddress address) {
+        return new ClientEndpoint(uri, this, connection, address);
+    }
+
+    @Override
+    protected ClientConnectionMultiplexor createConnection(final ConnectionId 
id, final ClientOptions options) {
+        LOG.debug("Create new connection - id: {}", id);
+
+        return new ClientConnectionMultiplexor(new 
ClientConnection(id.getHost(), id.getPort(), options));
+    }
+
+    /**
+     * Default connection options
+     *
+     * @param defaultConnectionOptions the new default connection options, must
+     *            not be {@code null}
+     */
+    @Override
+    public void setDefaultConnectionOptions(final ClientOptions 
defaultConnectionOptions) {
+        super.setDefaultConnectionOptions(defaultConnectionOptions);
+    }
+
+    @Override
+    public ClientOptions getDefaultConnectionOptions() {
+        return super.getDefaultConnectionOptions();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnection.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnection.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnection.java
new file mode 100644
index 0000000..c4c47c7
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnection.java
@@ -0,0 +1,132 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.camel.component.iec60870.DiscardAckModule;
+import org.apache.camel.component.iec60870.ObjectAddress;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.ASDUAddress;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.types.InformationObjectAddress;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.types.QualifierOfInterrogation;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.Value;
+import org.eclipse.neoscada.protocol.iec60870.client.AutoConnectClient;
+import 
org.eclipse.neoscada.protocol.iec60870.client.AutoConnectClient.ModulesFactory;
+import org.eclipse.neoscada.protocol.iec60870.client.AutoConnectClient.State;
+import 
org.eclipse.neoscada.protocol.iec60870.client.AutoConnectClient.StateListener;
+import 
org.eclipse.neoscada.protocol.iec60870.client.data.AbstractDataProcessor;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataHandler;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataModule;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataModuleContext;
+
+public class ClientConnection {
+
+    @FunctionalInterface
+    public interface ValueListener {
+        void update(ObjectAddress address, Value<?> value);
+    }
+
+    private final StateListener stateListener = new StateListener() {
+
+        @Override
+        public void stateChanged(final State state, final Throwable e) {
+        }
+    };
+
+    private final DataHandler dataHandler = new AbstractDataProcessor() {
+
+        /**
+         * Called when the connection was established
+         */
+        @Override
+        public void activated(final DataModuleContext dataModuleContext, final 
ChannelHandlerContext ctx) {
+            dataModuleContext.requestStartData();
+            dataModuleContext.startInterrogation(ASDUAddress.BROADCAST, 
QualifierOfInterrogation.GLOBAL);
+        }
+
+        /**
+         * Called when the start data was accepted
+         */
+        @Override
+        public void started() {
+        }
+
+        /**
+         * Called when the connection broke
+         */
+        @Override
+        public void disconnected() {
+        }
+
+        @Override
+        protected void fireEntry(final ASDUAddress asduAddress, final 
InformationObjectAddress address, final Value<?> value) {
+            
ClientConnection.this.handleData(ObjectAddress.valueOf(asduAddress, address), 
value);
+        }
+    };
+
+    private final Map<ObjectAddress, Value<?>> lastValue = new HashMap<>();
+    private final Map<ObjectAddress, ValueListener> listeners = new 
HashMap<>();
+
+    private final String host;
+    private final int port;
+    private final ClientOptions options;
+
+    private AutoConnectClient client;
+
+    public ClientConnection(final String host, final int port, final 
ClientOptions options) {
+        this.host = host;
+        this.port = port;
+        this.options = options;
+    }
+
+    public void start() {
+        final DataModule dataModule = new DataModule(this.dataHandler, 
this.options.getDataModuleOptions());
+        final ModulesFactory factory = () -> Arrays.asList(dataModule, new 
DiscardAckModule());
+        this.client = new AutoConnectClient(this.host, this.port, 
this.options.getProtocolOptions(), factory, this.stateListener);
+    }
+
+    public void stop() throws Exception {
+        this.client.close();
+    }
+
+    protected synchronized void handleData(final ObjectAddress address, final 
Value<?> value) {
+        this.lastValue.put(address, value);
+        final ValueListener listener = this.listeners.get(address);
+        if (listener != null) {
+            listener.update(address, value);
+        }
+    }
+
+    public synchronized void setListener(final ObjectAddress address, final 
ValueListener listener) {
+        if (listener != null) {
+            this.listeners.put(address, listener);
+            final Value<?> last = this.lastValue.get(address);
+            if (last != null) {
+                listener.update(address, last);
+            }
+        } else {
+            this.listeners.remove(address);
+        }
+    }
+
+    public boolean executeCommand(final Object command) {
+        return this.client.writeCommand(command);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnectionMultiplexor.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnectionMultiplexor.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnectionMultiplexor.java
new file mode 100644
index 0000000..9805a0b
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConnectionMultiplexor.java
@@ -0,0 +1,43 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import org.apache.camel.component.iec60870.AbstractConnectionMultiplexor;
+
+public class ClientConnectionMultiplexor extends AbstractConnectionMultiplexor 
{
+
+    private final ClientConnection connection;
+
+    public ClientConnectionMultiplexor(final ClientConnection connection) {
+        this.connection = connection;
+    }
+
+    @Override
+    protected void performStart() throws Exception {
+        this.connection.start();
+    }
+
+    @Override
+    protected void performStop() throws Exception {
+        this.connection.stop();
+    }
+
+    public ClientConnection getConnection() {
+        return this.connection;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConsumer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConsumer.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConsumer.java
new file mode 100644
index 0000000..f947f29
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientConsumer.java
@@ -0,0 +1,79 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import java.time.Instant;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.component.iec60870.ObjectAddress;
+import org.apache.camel.impl.DefaultConsumer;
+import org.apache.camel.impl.DefaultMessage;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.Value;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClientConsumer extends DefaultConsumer {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ClientConsumer.class);
+
+    private final ClientConnection connection;
+    private final ClientEndpoint endpoint;
+
+    public ClientConsumer(final ClientEndpoint endpoint, final Processor 
processor, final ClientConnection connection) {
+        super(endpoint, processor);
+        this.endpoint = endpoint;
+        this.connection = connection;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+        this.connection.setListener(this.endpoint.getAddress(), 
this::updateValue);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        this.connection.setListener(this.endpoint.getAddress(), null);
+        super.doStop();
+    }
+
+    private void updateValue(final ObjectAddress address, final Value<?> 
value) {
+        // Note: we hold the sync lock for the connection
+        try {
+            final Exchange exchange = getEndpoint().createExchange();
+            exchange.setIn(mapMessage(value));
+            getAsyncProcessor().process(exchange);
+        } catch (final Exception e) {
+            LOG.debug("Failed to process message", e);
+        }
+    }
+
+    private Message mapMessage(final Value<?> value) {
+        final DefaultMessage message = new 
DefaultMessage(this.endpoint.getCamelContext());
+
+        message.setBody(value);
+
+        message.setHeader("value", value.getValue());
+        message.setHeader("timestamp", 
Instant.ofEpochMilli(value.getTimestamp()));
+        message.setHeader("quality", value.getQualityInformation());
+        message.setHeader("overflow", value.isOverflow());
+
+        return message;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientEndpoint.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientEndpoint.java
new file mode 100644
index 0000000..e554a4f
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientEndpoint.java
@@ -0,0 +1,46 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import static java.util.Objects.requireNonNull;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.component.iec60870.AbstractIecEndpoint;
+import org.apache.camel.component.iec60870.ObjectAddress;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.spi.UriEndpoint;
+
+@UriEndpoint(firstVersion = "2.20.0", scheme = "iec60870-client", syntax = 
"iec60870-client:endpointUri", title = "IEC 60870 Client", consumerClass = 
ClientConsumer.class, label = "iot")
+public class ClientEndpoint extends 
AbstractIecEndpoint<ClientConnectionMultiplexor> {
+
+    public ClientEndpoint(final String uri, final DefaultComponent component, 
final ClientConnectionMultiplexor connection, final ObjectAddress address) {
+        super(uri, component, requireNonNull(connection), address);
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        return new ClientProducer(this, getConnection().getConnection());
+    }
+
+    @Override
+    public Consumer createConsumer(final Processor processor) throws Exception 
{
+        return new ClientConsumer(this, processor, 
getConnection().getConnection());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientOptions.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientOptions.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientOptions.java
new file mode 100644
index 0000000..86cdb9e
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientOptions.java
@@ -0,0 +1,91 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import java.util.Objects;
+
+import org.apache.camel.component.iec60870.BaseOptions;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.eclipse.neoscada.protocol.iec60870.ProtocolOptions;
+import org.eclipse.neoscada.protocol.iec60870.client.data.DataModuleOptions;
+
+@UriParams
+public class ClientOptions extends BaseOptions<ClientOptions> {
+
+    /**
+     * Data module options
+     */
+    @UriParam(javaType = "DataModuleOptions")
+    private DataModuleOptions.Builder dataModuleOptions;
+
+    // dummy for doc generation
+    /**
+     * Whether background scan transmissions should be ignored.
+     */
+    @UriParam(label = "data", defaultValue = "true")
+    private boolean ignoreBackgroundScan;
+
+    public ClientOptions() {
+        this.dataModuleOptions = new DataModuleOptions.Builder();
+    }
+
+    public ClientOptions(final ClientOptions other) {
+        this(other.getProtocolOptions(), other.getDataModuleOptions());
+    }
+
+    public ClientOptions(final ProtocolOptions protocolOptions, final 
DataModuleOptions dataOptions) {
+        super(protocolOptions);
+
+        Objects.requireNonNull(dataOptions);
+
+        this.dataModuleOptions = new DataModuleOptions.Builder(dataOptions);
+    }
+
+    public void setDataModuleOptions(final DataModuleOptions 
dataModuleOptions) {
+        Objects.requireNonNull(dataModuleOptions);
+
+        this.dataModuleOptions = new 
DataModuleOptions.Builder(dataModuleOptions);
+    }
+
+    public DataModuleOptions getDataModuleOptions() {
+        return this.dataModuleOptions.build();
+    }
+
+    @Override
+    public ClientOptions copy() {
+        return new ClientOptions(this);
+    }
+
+    // wrapper methods - DataModuleOptions
+
+    public void setCauseSourceAddress(final Byte causeSourceAddress) {
+        this.dataModuleOptions.setCauseSourceAddress(causeSourceAddress);
+    }
+
+    public Byte getCauseSourceAddress() {
+        return this.dataModuleOptions.getCauseSourceAddress();
+    }
+
+    public void setIgnoreBackgroundScan(final boolean ignoreBackgroundScan) {
+        this.dataModuleOptions.setIgnoreBackgroundScan(ignoreBackgroundScan);
+    }
+
+    public boolean isIgnoreBackgroundScan() {
+        return this.dataModuleOptions.isIgnoreBackgroundScan();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/eb4f6059/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientProducer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientProducer.java
 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientProducer.java
new file mode 100644
index 0000000..de7c36d
--- /dev/null
+++ 
b/components/camel-iec60870/src/main/java/org/apache/camel/component/iec60870/client/ClientProducer.java
@@ -0,0 +1,89 @@
+/**
+ * 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.camel.component.iec60870.client;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.iec60870.ObjectAddress;
+import org.apache.camel.impl.DefaultProducer;
+import org.eclipse.neoscada.protocol.iec60870.asdu.ASDUHeader;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.message.SetPointCommandScaledValue;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.message.SetPointCommandShortFloatingPoint;
+import org.eclipse.neoscada.protocol.iec60870.asdu.message.SingleCommand;
+import org.eclipse.neoscada.protocol.iec60870.asdu.types.CauseOfTransmission;
+import 
org.eclipse.neoscada.protocol.iec60870.asdu.types.InformationObjectAddress;
+
+public class ClientProducer extends DefaultProducer {
+
+    private final ClientConnection connection;
+    private final ASDUHeader header;
+    private final InformationObjectAddress address;
+
+    public ClientProducer(final ClientEndpoint endpoint, final 
ClientConnection connection) {
+        super(endpoint);
+        this.connection = connection;
+
+        final ObjectAddress address = endpoint.getAddress();
+        this.header = new ASDUHeader(CauseOfTransmission.ACTIVATED, 
address.getASDUAddress());
+        this.address = address.getInformationObjectAddress();
+    }
+
+    @Override
+    public void process(final Exchange exchange) throws Exception {
+        final Object command = mapToCommand(exchange);
+
+        if (command != null) {
+            if (!this.connection.executeCommand(command)) {
+                throw new IllegalStateException("Failed to send command. Not 
connected.");
+            }
+        }
+    }
+
+    private Object mapToCommand(final Exchange exchange) {
+        final Object body = exchange.getIn().getBody();
+
+        if (body instanceof Float || body instanceof Double) {
+            return makeFloatCommand(((Number)body).floatValue());
+        }
+
+        if (body instanceof Boolean) {
+            return makeBooleanCommand((Boolean)body);
+        }
+
+        if (body instanceof Integer || body instanceof Short || body 
instanceof Byte || body instanceof Long) {
+            return makeIntCommand(((Number)body).longValue());
+        }
+
+        throw new IllegalArgumentException("Unable to map value to a command: 
" + body);
+    }
+
+    private Object makeBooleanCommand(final Boolean state) {
+        return new SingleCommand(this.header, this.address, state);
+    }
+
+    private Object makeIntCommand(final long value) {
+
+        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+            throw new IllegalArgumentException(String.format("Integer value is 
outside of range - min: %s, max: %s", Short.MIN_VALUE, Short.MAX_VALUE));
+        }
+
+        return new SetPointCommandScaledValue(this.header, this.address, 
(short)value);
+    }
+
+    private Object makeFloatCommand(final float value) {
+        return new SetPointCommandShortFloatingPoint(this.header, 
this.address, value);
+    }
+}

Reply via email to