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

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 13fc914  CAMEL-14344 camel-milo - Upgrade to newer milo version (#3539)
13fc914 is described below

commit 13fc9143ef6dfee9d8e1d297fd305c2a644594d4
Author: JiriOndrusek <jondr...@redhat.com>
AuthorDate: Wed Feb 5 04:37:20 2020 +0100

    CAMEL-14344 camel-milo - Upgrade to newer milo version (#3539)
---
 components/camel-milo/pom.xml                      |   6 +
 .../src/main/docs/milo-client-component.adoc       |   8 +-
 .../src/main/docs/milo-server-component.adoc       |   8 +-
 .../milo/client/MiloClientConfiguration.java       |   4 +-
 .../component/milo/client/MiloClientConsumer.java  |   1 -
 .../milo/client/internal/SubscriptionManager.java  |  22 +-
 .../component/milo/server/MiloServerComponent.java | 297 ++++++++++++++-------
 .../component/milo/server/internal/CallMethod.java |  73 +++++
 .../milo/server/internal/CamelNamespace.java       | 163 +++--------
 .../milo/server/internal/CamelServerItem.java      |   7 +-
 .../component/milo/AbstractMiloServerTest.java     |  27 +-
 .../milo/MonitorItemMultiConnectionsCertTest.java  |  12 +-
 .../milo/MonitorItemMultiConnectionsTest.java      |   6 +-
 .../camel/component/milo/MonitorItemTest.java      |   6 +-
 .../camel/component/milo/WriteClientTest.java      |   4 +-
 .../camel/component/milo/call/CallClientTest.java  | 135 +++++++---
 .../apache/camel/component/milo/call/MockCall.java |  71 -----
 .../camel/component/milo/call/MockCallMethod.java  |  77 ++++++
 .../component/milo/call/MockCamelNamespace.java    | 141 ++++++++++
 .../camel/component/milo/call/MockNamespace.java   | 176 ------------
 .../server/ServerSetCertificateManagerTest.java    |   2 +-
 .../camel-milo/src/test/resources/ca/cacert.pem    |  32 +++
 .../camel-milo/src/test/resources/cert/Makefile    |  16 --
 .../camel-milo/src/test/resources/cert/cert.ini    |  13 -
 .../camel-milo/src/test/resources/cert/cert.p12    | Bin 2461 -> 0 bytes
 .../src/test/resources/cert/certificate.crt        |  20 --
 .../src/test/resources/cert/certificate.der        | Bin 856 -> 0 bytes
 .../src/test/resources/cert/privateKey.key         |  28 --
 components/camel-milo/src/test/resources/keystore  | Bin 0 -> 3990 bytes
 .../camel-milo/src/test/resources/openssl-ca.cnf   |  80 ++++++
 .../src/test/resources/openssl-server.cnf          |  39 +++
 components/camel-milo/src/test/resources/run.sh    |  21 ++
 .../modules/ROOT/pages/milo-client-component.adoc  |   9 +-
 .../modules/ROOT/pages/milo-server-component.adoc  |   6 +-
 .../modules/ROOT/pages/camel-3x-upgrade-guide.adoc |  21 ++
 parent/pom.xml                                     |   5 +-
 .../karaf/features/src/main/resources/features.xml |  23 +-
 .../apache/camel/itest/karaf/CamelMiloTest.java    |  28 ++
 38 files changed, 951 insertions(+), 636 deletions(-)

diff --git a/components/camel-milo/pom.xml b/components/camel-milo/pom.xml
index 807ea5f..3149be6 100644
--- a/components/camel-milo/pom.xml
+++ b/components/camel-milo/pom.xml
@@ -55,6 +55,12 @@
             <version>${milo-version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${milo-guava-version}</version>
+        </dependency>
+
         <!-- testing -->
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git a/components/camel-milo/src/main/docs/milo-client-component.adoc 
b/components/camel-milo/src/main/docs/milo-client-component.adoc
index 705750a..d30640b 100644
--- a/components/camel-milo/src/main/docs/milo-client-component.adoc
+++ b/components/camel-milo/src/main/docs/milo-client-component.adoc
@@ -10,6 +10,8 @@
 The Milo Client component provides access to OPC UA servers using the
 http://eclipse.org/milo[Eclipse Milo™] implementation.
 
+*Java 9+*: This component requires Java 9+ at runtime.
+
 Maven users will need to add the following dependency to their `pom.xml`
 for this component:
 
@@ -52,13 +54,13 @@ The URI syntax of the endpoint is:
 
 [source]
 ------------------------
-milo-client:tcp://[user:password@]host:port/path/to/service?node=RAW(nsu=urn:foo:bar;s=item-1)
+milo-client:opc.tcp://[user:password@]host:port/path/to/service?node=RAW(nsu=urn:foo:bar;s=item-1)
 ------------------------
 
 If the server does not use a path, then it is possible to simply omit it:
 
 ------------------------
-milo-client:tcp://[user:password@]host:port?node=RAW(nsu=urn:foo:bar;s=item-1)
+milo-client:opc.tcp://[user:password@]host:port?node=RAW(nsu=urn:foo:bar;s=item-1)
 ------------------------
 
 If no user credentials are provided the client will switch to anonymous mode.
@@ -239,7 +241,7 @@ As the values generated by the syntax cannot be 
transparently encoded into a URI
 However Camel allows to wrap the actual value inside `RAW(…)`, which makes 
escaping unnecessary. For example:
 
 ------------------------
-milo-client:tcp://user:password@localhost:12345?node=RAW(nsu=http://foo.bar;s=foo/bar)
+milo-client:opc.tcp://user:password@localhost:12345?node=RAW(nsu=http://foo.bar;s=foo/bar)
 ------------------------
 
 === Method ID
diff --git a/components/camel-milo/src/main/docs/milo-server-component.adoc 
b/components/camel-milo/src/main/docs/milo-server-component.adoc
index d70a8ae..bf62602 100644
--- a/components/camel-milo/src/main/docs/milo-server-component.adoc
+++ b/components/camel-milo/src/main/docs/milo-server-component.adoc
@@ -10,7 +10,7 @@
 The Milo Server component provides an OPC UA server using the
 http://eclipse.org/milo[Eclipse Milo™] implementation.
 
-*Java 8*: This component requires Java 8 at runtime. 
+*Java 9+*: This component requires Java 9+ at runtime.
 
 Maven users will need to add the following dependency to their `pom.xml`
 for this component:
@@ -30,7 +30,7 @@ Value write requests from OPC UA Client will trigger messages 
which are sent int
 
 
 // component options: START
-The OPC UA Server component supports 22 options, which are listed below.
+The OPC UA Server component supports 20 options, which are listed below.
 
 
 
@@ -39,12 +39,10 @@ The OPC UA Server component supports 22 options, which are 
listed below.
 | Name | Description | Default | Type
 | *namespaceUri* (common) | The URI of the namespace, defaults to 
urn:org:apache:camel |  | String
 | *applicationName* (common) | The application name |  | String
+| *path* (common) | The path to be appended to the end of the endpoint url. 
(doesn't need to start with '/') |  | String
 | *applicationUri* (common) | The application URI |  | String
 | *productUri* (common) | The product URI |  | String
 | *bindPort* (common) | The TCP port the server binds to |  | int
-| *strictEndpointUrlsEnabled* (common) | Set whether strict endpoint URLs are 
enforced | false | boolean
-| *serverName* (common) | Server name |  | String
-| *hostname* (common) | Server hostname |  | String
 | *securityPolicies* (common) | Security policies |  | Set
 | *securityPoliciesById* (common) | Security policies by URI or name |  | 
Collection
 | *userAuthenticationCredentials* (common) | Set user password combinations in 
the form of user1:pwd1,user2:pwd2 Usernames and passwords will be URL decoded | 
 | String
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConfiguration.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConfiguration.java
index a532657..687a88a 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConfiguration.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConfiguration.java
@@ -353,12 +353,12 @@ public class MiloClientConfiguration implements Cloneable 
{
 
             String adding = null;
             try {
-                adding = SecurityPolicy.fromUri(policy).getSecurityPolicyUri();
+                adding = SecurityPolicy.fromUri(policy).getUri();
             } catch (Exception e) {
             }
             if (adding == null) {
                 try {
-                    adding = 
SecurityPolicy.valueOf(policy).getSecurityPolicyUri();
+                    adding = SecurityPolicy.valueOf(policy).getUri();
                 } catch (Exception e) {
                 }
             }
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConsumer.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConsumer.java
index 515fa4b..03023da 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConsumer.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/MiloClientConsumer.java
@@ -28,7 +28,6 @@ import 
org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 import static java.util.Objects.requireNonNull;
 
 public class MiloClientConsumer extends DefaultConsumer {
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/internal/SubscriptionManager.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/internal/SubscriptionManager.java
index 0a13dbd..786854f 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/internal/SubscriptionManager.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/client/internal/SubscriptionManager.java
@@ -45,7 +45,7 @@ import 
org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
 import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
 import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
 import 
org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscriptionManager.SubscriptionListener;
-import org.eclipse.milo.opcua.stack.client.UaTcpStackClient;
+import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
 import org.eclipse.milo.opcua.stack.core.AttributeId;
 import org.eclipse.milo.opcua.stack.core.Identifiers;
 import org.eclipse.milo.opcua.stack.core.StatusCodes;
@@ -427,10 +427,18 @@ public class SubscriptionManager {
 
         // eval enpoint
 
-        final String discoveryUri = getEndpointDiscoveryUri();
+        String discoveryUri = getEndpointDiscoveryUri();
+
+        final URI uri = URI.create(getEndpointDiscoveryUri());
+
+        //milo library doesn't allow user info as a part of the uri, it has to 
be removed before sending to milo
+        final String user = uri.getUserInfo();
+        if (user != null && !user.isEmpty()) {
+            discoveryUri = discoveryUri.replaceFirst(user + "@", "");
+        }
         LOG.debug("Discovering endpoints from: {}", discoveryUri);
 
-        final EndpointDescription endpoint = 
UaTcpStackClient.getEndpoints(discoveryUri).thenApply(endpoints -> {
+        final EndpointDescription endpoint = 
DiscoveryClient.getEndpoints(discoveryUri).thenApply(endpoints -> {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Found enpoints:");
                 for (final EndpointDescription ep : endpoints) {
@@ -447,13 +455,9 @@ public class SubscriptionManager {
 
         LOG.debug("Selected endpoint: {}", endpoint);
 
-        final URI uri = URI.create(getEndpointDiscoveryUri());
-
         // set identity providers
-
         final List<IdentityProvider> providers = new LinkedList<>();
 
-        final String user = uri.getUserInfo();
         if (user != null && !user.isEmpty()) {
             final String[] creds = user.split(":", 2);
             if (creds != null && creds.length == 2) {
@@ -470,7 +474,7 @@ public class SubscriptionManager {
 
         // create client
 
-        final OpcUaClient client = new OpcUaClient(cfg.build());
+        final OpcUaClient client = OpcUaClient.create(cfg.build());
         client.connect().get();
 
         try {
@@ -530,7 +534,7 @@ public class SubscriptionManager {
         }
     }
 
-    private EndpointDescription findEndpoint(final EndpointDescription[] 
endpoints) throws URISyntaxException {
+    private EndpointDescription findEndpoint(final List<EndpointDescription> 
endpoints) throws URISyntaxException {
 
         final Predicate<String> allowed;
         final Set<String> uris = 
this.configuration.getAllowedSecurityPolicies();
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/MiloServerComponent.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/MiloServerComponent.java
index ccbbebf..3e8ce5a 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/MiloServerComponent.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/MiloServerComponent.java
@@ -23,12 +23,12 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.security.KeyPair;
 import java.security.cert.X509Certificate;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -47,23 +47,28 @@ import 
org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfigBuilder;
 import org.eclipse.milo.opcua.sdk.server.identity.AnonymousIdentityValidator;
 import org.eclipse.milo.opcua.sdk.server.identity.IdentityValidator;
 import org.eclipse.milo.opcua.sdk.server.identity.UsernameIdentityValidator;
+import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
 import org.eclipse.milo.opcua.stack.core.StatusCodes;
 import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.application.CertificateManager;
-import org.eclipse.milo.opcua.stack.core.application.CertificateValidator;
-import org.eclipse.milo.opcua.stack.core.application.DefaultCertificateManager;
-import 
org.eclipse.milo.opcua.stack.core.application.DefaultCertificateValidator;
+import org.eclipse.milo.opcua.stack.core.security.CertificateManager;
+import org.eclipse.milo.opcua.stack.core.security.CertificateValidator;
+import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateManager;
+import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateValidator;
+import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager;
 import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
+import org.eclipse.milo.opcua.stack.core.transport.TransportProfile;
 import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
 import org.eclipse.milo.opcua.stack.core.types.enumerated.UserTokenType;
 import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo;
 import org.eclipse.milo.opcua.stack.core.types.structured.UserTokenPolicy;
+import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
-import static java.util.Collections.singletonList;
 import static 
org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS;
+import static 
org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME;
+import static 
org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_X509;
 
 /**
  * OPC UA Server based component
@@ -75,54 +80,25 @@ public class MiloServerComponent extends DefaultComponent {
     private static final Logger LOG = 
LoggerFactory.getLogger(MiloServerComponent.class);
 
     private static final String URL_CHARSET = "UTF-8";
-    private static final OpcUaServerConfig DEFAULT_SERVER_CONFIG;
-
-    static {
-        final OpcUaServerConfigBuilder cfg = OpcUaServerConfig.builder();
-
-        cfg.setCertificateManager(new DefaultCertificateManager());
-        cfg.setCertificateValidator(DenyAllCertificateValidator.INSTANCE);
-        cfg.setSecurityPolicies(EnumSet.allOf(SecurityPolicy.class));
-        cfg.setApplicationName(LocalizedText.english("Apache Camel Milo 
Server"));
-        cfg.setApplicationUri("urn:org:apache:camel:milo:server");
-        cfg.setProductUri("urn:org:apache:camel:milo");
-
-        if 
(Boolean.getBoolean("org.apache.camel.milo.server.default.enableAnonymous")) {
-            
cfg.setUserTokenPolicies(singletonList(USER_TOKEN_POLICY_ANONYMOUS));
-            cfg.setIdentityValidator(AnonymousIdentityValidator.INSTANCE);
-        }
-
-        DEFAULT_SERVER_CONFIG = cfg.build();
-    }
 
-    private static final class DenyAllCertificateValidator implements 
CertificateValidator {
-        public static final CertificateValidator INSTANCE = new 
DenyAllCertificateValidator();
-
-        private DenyAllCertificateValidator() {
-        }
-
-        @Override
-        public void validate(final X509Certificate certificate) throws 
UaException {
-            throw new UaException(StatusCodes.Bad_CertificateUseNotAllowed);
-        }
-
-        @Override
-        public void verifyTrustChain(List<X509Certificate> certificateChain) 
throws UaException {
-            throw new UaException(StatusCodes.Bad_CertificateUseNotAllowed);
-        }
-    }
+    private int port;
 
     private String namespaceUri = DEFAULT_NAMESPACE_URI;
 
-    private final OpcUaServerConfigBuilder serverConfig;
+    private OpcUaServerConfigBuilder opcServerConfig;
 
     private OpcUaServer server;
+
     private CamelNamespace namespace;
 
     private final Map<String, MiloServerEndpoint> endpoints = new HashMap<>();
 
     private Boolean enableAnonymousAuthentication;
 
+    private CertificateManager certificateManager;
+
+    private Set<SecurityPolicy> securityPolicies;
+
     private Map<String, String> userMap;
 
     private String usernameSecurityPolicyUri = 
OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME.getSecurityPolicyUri();
@@ -133,19 +109,33 @@ public class MiloServerComponent extends DefaultComponent 
{
 
     private final List<Runnable> runOnStop = new LinkedList<>();
 
+    private X509Certificate certificate;
+
+    private String productUri;
+
+    private String applicationUri;
+
+    private String applicationName;
+
+    private String path;
+
+    private BuildInfo buildInfo;
+
     public MiloServerComponent() {
-        this(DEFAULT_SERVER_CONFIG);
+        this.opcServerConfig = null;
     }
 
     public MiloServerComponent(final OpcUaServerConfig serverConfig) {
-        this.serverConfig = OpcUaServerConfig.copy(serverConfig != null ? 
serverConfig : DEFAULT_SERVER_CONFIG);
+        this.opcServerConfig = OpcUaServerConfig.copy(serverConfig);
+
     }
 
     @Override
     protected void doStart() throws Exception {
         this.server = new OpcUaServer(buildServerConfig());
 
-        this.namespace = 
this.server.getNamespaceManager().registerAndAdd(this.namespaceUri, index -> 
new CamelNamespace(index, this.namespaceUri, this.server));
+        this.namespace = new CamelNamespace(this.namespaceUri, this.server);
+        this.namespace.startup();
 
         super.doStart();
         this.server.startup();
@@ -157,6 +147,7 @@ public class MiloServerComponent extends DefaultComponent {
      * @return the new server configuration, never returns {@code null}
      */
     private OpcUaServerConfig buildServerConfig() {
+        OpcUaServerConfigBuilder serverConfig = this.opcServerConfig  != null 
? this.opcServerConfig : createDefaultConfiguration();
 
         if (this.userMap != null || this.enableAnonymousAuthentication != 
null) {
             // set identity validator
@@ -170,7 +161,7 @@ public class MiloServerComponent extends DefaultComponent {
                 }
                 return pwd.equals(challenge.getPassword());
             });
-            this.serverConfig.setIdentityValidator(identityValidator);
+            serverConfig.setIdentityValidator(identityValidator);
 
             // add token policies
 
@@ -181,11 +172,9 @@ public class MiloServerComponent extends DefaultComponent {
             if (userMap != null) {
                 tokenPolicies.add(getUsernamePolicy());
             }
-            this.serverConfig.setUserTokenPolicies(tokenPolicies);
-        }
-
-        if (this.bindAddresses != null) {
-            this.serverConfig.setBindAddresses(new 
ArrayList<>(this.bindAddresses));
+            
serverConfig.setEndpoints(createEndpointConfigurations(tokenPolicies));
+        } else {
+            serverConfig.setEndpoints(createEndpointConfigurations(null, 
securityPolicies));
         }
 
         if (this.certificateValidator != null) {
@@ -201,12 +190,151 @@ public class MiloServerComponent extends 
DefaultComponent {
                     }
                 });
             }
-            this.serverConfig.setCertificateValidator(validator);
+            serverConfig.setCertificateValidator(validator);
         }
 
         // build final configuration
+        return serverConfig.build();
+    }
+
+    private OpcUaServerConfigBuilder createDefaultConfiguration() {
+        final OpcUaServerConfigBuilder cfg = OpcUaServerConfig.builder();
+
+        cfg.setCertificateManager(new DefaultCertificateManager());
+        cfg.setCertificateValidator(DenyAllCertificateValidator.INSTANCE);
+        cfg.setEndpoints(createEndpointConfigurations(null));
+        cfg.setApplicationName(LocalizedText.english(applicationName == null ? 
"Apache Camel Milo Server" : applicationName));
+        cfg.setApplicationUri("urn:org:apache:camel:milo:server");
+        cfg.setProductUri("urn:org:apache:camel:milo");
+        cfg.setCertificateManager(certificateManager);
+        if (productUri != null) {
+            cfg.setProductUri(productUri);
+        }
+        if (applicationUri != null) {
+            cfg.setApplicationUri(applicationUri);
+        }
+        if (buildInfo != null) {
+            cfg.setBuildInfo(buildInfo);
+        }
+
+        if 
(Boolean.getBoolean("org.apache.camel.milo.server.default.enableAnonymous")) {
+            cfg.setIdentityValidator(AnonymousIdentityValidator.INSTANCE);
+        }
+
+        return cfg;
+    }
+
+    private Set<EndpointConfiguration> 
createEndpointConfigurations(List<UserTokenPolicy> userTokenPolicies) {
+        return createEndpointConfigurations(userTokenPolicies, 
this.securityPolicies);
+    }
+
+    private Set<EndpointConfiguration> 
createEndpointConfigurations(List<UserTokenPolicy> userTokenPolicies, 
Set<SecurityPolicy> securityPolicies) {
+        Set<EndpointConfiguration> endpointConfigurations = new 
LinkedHashSet<>();
+
+        //if address is not defined, return empty set
+        if (bindAddresses == null) {
+            return Collections.emptySet();
+        }
+
+        for (String bindAddress : bindAddresses) {
+            Set<String> hostnames = new LinkedHashSet<>();
+            hostnames.add(HostnameUtil.getHostname());
+            hostnames.addAll(HostnameUtil.getHostnames(bindAddress));
+
+            boolean anonymous = (this.enableAnonymousAuthentication != null && 
this.enableAnonymousAuthentication)
+                    || 
Boolean.getBoolean("org.apache.camel.milo.server.default.enableAnonymous");
+
+            UserTokenPolicy[] tokenPolicies =
+                    userTokenPolicies != null ? userTokenPolicies.toArray(new 
UserTokenPolicy[userTokenPolicies.size()])
+                            : anonymous
+                                ? new UserTokenPolicy[] 
{USER_TOKEN_POLICY_ANONYMOUS, USER_TOKEN_POLICY_USERNAME, 
USER_TOKEN_POLICY_X509}
+                                : new UserTokenPolicy[] 
{USER_TOKEN_POLICY_USERNAME, USER_TOKEN_POLICY_X509};
+
+            for (String hostname : hostnames) {
+                EndpointConfiguration.Builder builder = 
EndpointConfiguration.newBuilder()
+                        .setBindAddress(bindAddress)
+                        .setHostname(hostname)
+                        .setCertificate(certificate)
+                        .setPath(this.path == null ? "" : this.path)
+                        .addTokenPolicies(tokenPolicies);
+
+
+                if (securityPolicies == null || 
securityPolicies.contains(SecurityPolicy.None)) {
+                    EndpointConfiguration.Builder noSecurityBuilder = 
builder.copy()
+                            .setSecurityPolicy(SecurityPolicy.None)
+                            .setSecurityMode(MessageSecurityMode.None);
+
+                    
endpointConfigurations.add(buildTcpEndpoint(noSecurityBuilder));
+                    
endpointConfigurations.add(buildHttpsEndpoint(noSecurityBuilder));
+                } else if 
(securityPolicies.contains(SecurityPolicy.Basic256Sha256)) {
+
+                    // TCP Basic256Sha256 / SignAndEncrypt
+                    endpointConfigurations.add(buildTcpEndpoint(
+                            builder.copy()
+                                    
.setSecurityPolicy(SecurityPolicy.Basic256Sha256)
+                                    
.setSecurityMode(MessageSecurityMode.SignAndEncrypt))
+                    );
+                } else if 
(securityPolicies.contains(SecurityPolicy.Basic256Sha256)) {
+                    // HTTPS Basic256Sha256 / Sign (SignAndEncrypt not allowed 
for HTTPS)
+                    endpointConfigurations.add(buildHttpsEndpoint(
+                            builder.copy()
+                                    
.setSecurityPolicy(SecurityPolicy.Basic256Sha256)
+                                    .setSecurityMode(MessageSecurityMode.Sign))
+                    );
+                }
+
+                /*
+                 * It's good practice to provide a discovery-specific endpoint 
with no security.
+                 * It's required practice if all regular endpoints have 
security configured.
+                 *
+                 * Usage of the  "/discovery" suffix is defined by OPC UA Part 
6:
+                 *
+                 * Each OPC UA Server Application implements the Discovery 
Service Set. If the OPC UA Server requires a
+                 * different address for this Endpoint it shall create the 
address by appending the path "/discovery" to
+                 * its base address.
+                 */
+                EndpointConfiguration.Builder discoveryBuilder = builder.copy()
+                        .setPath("/discovery")
+                        .setSecurityPolicy(SecurityPolicy.None)
+                        .setSecurityMode(MessageSecurityMode.None);
+
+                endpointConfigurations.add(buildTcpEndpoint(discoveryBuilder));
+                
endpointConfigurations.add(buildHttpsEndpoint(discoveryBuilder));
+            }
+        }
+
+        return endpointConfigurations;
+    }
 
-        return this.serverConfig.build();
+    private EndpointConfiguration 
buildTcpEndpoint(EndpointConfiguration.Builder base) {
+        return base.copy()
+                .setTransportProfile(TransportProfile.TCP_UASC_UABINARY)
+                .setBindPort(this.port)
+                .build();
+    }
+
+    private EndpointConfiguration 
buildHttpsEndpoint(EndpointConfiguration.Builder base) {
+        return base.copy()
+                .setTransportProfile(TransportProfile.HTTPS_UABINARY)
+                .setBindPort(this.port)
+                .build();
+    }
+
+    private static final class DenyAllCertificateValidator implements 
CertificateValidator {
+        public static final CertificateValidator INSTANCE = new 
DenyAllCertificateValidator();
+
+        private DenyAllCertificateValidator() {
+        }
+
+        @Override
+        public void validate(final X509Certificate certificate) throws 
UaException {
+            throw new UaException(StatusCodes.Bad_CertificateUseNotAllowed);
+        }
+
+        @Override
+        public void verifyTrustChain(List<X509Certificate> certificateChain) 
throws UaException {
+            throw new UaException(StatusCodes.Bad_CertificateUseNotAllowed);
+        }
     }
 
     /**
@@ -271,7 +399,15 @@ public class MiloServerComponent extends DefaultComponent {
      */
     public void setApplicationName(final String applicationName) {
         Objects.requireNonNull(applicationName);
-        
this.serverConfig.setApplicationName(LocalizedText.english(applicationName));
+        this.applicationName = applicationName;
+    }
+
+    /**
+     * The path to be appended to the end of the endpoint url. (doesn't need 
to start with '/')
+     */
+    public void setPath(final String path) {
+        Objects.requireNonNull(path);
+        this.path = path;
     }
 
     /**
@@ -279,7 +415,7 @@ public class MiloServerComponent extends DefaultComponent {
      */
     public void setApplicationUri(final String applicationUri) {
         Objects.requireNonNull(applicationUri);
-        this.serverConfig.setApplicationUri(applicationUri);
+        this.applicationUri = applicationUri;
     }
 
     /**
@@ -287,35 +423,14 @@ public class MiloServerComponent extends DefaultComponent 
{
      */
     public void setProductUri(final String productUri) {
         Objects.requireNonNull(productUri);
-        this.serverConfig.setProductUri(productUri);
+        this.productUri = productUri;
     }
 
     /**
      * The TCP port the server binds to
      */
     public void setBindPort(final int port) {
-        this.serverConfig.setBindPort(port);
-    }
-
-    /**
-     * Set whether strict endpoint URLs are enforced
-     */
-    public void setStrictEndpointUrlsEnabled(final boolean 
strictEndpointUrlsEnforced) {
-        
this.serverConfig.setStrictEndpointUrlsEnabled(strictEndpointUrlsEnforced);
-    }
-
-    /**
-     * Server name
-     */
-    public void setServerName(final String serverName) {
-        this.serverConfig.setServerName(serverName);
-    }
-
-    /**
-     * Server hostname
-     */
-    public void setHostname(final String hostname) {
-        this.serverConfig.setServerName(hostname);
+        this.port = port;
     }
 
     /**
@@ -323,9 +438,9 @@ public class MiloServerComponent extends DefaultComponent {
      */
     public void setSecurityPolicies(final Set<SecurityPolicy> 
securityPolicies) {
         if (securityPolicies == null || securityPolicies.isEmpty()) {
-            
this.serverConfig.setSecurityPolicies(EnumSet.noneOf(SecurityPolicy.class));
+            this.securityPolicies = EnumSet.noneOf(SecurityPolicy.class);
         } else {
-            
this.serverConfig.setSecurityPolicies(EnumSet.copyOf(securityPolicies));
+            this.securityPolicies = EnumSet.copyOf(securityPolicies);
         }
     }
 
@@ -342,7 +457,7 @@ public class MiloServerComponent extends DefaultComponent {
             }
         }
 
-        this.serverConfig.setSecurityPolicies(policies);
+        this.securityPolicies = policies;
     }
 
     /**
@@ -392,7 +507,7 @@ public class MiloServerComponent extends DefaultComponent {
      * Set the {@link UserTokenPolicy} used when
      */
     public void setUsernameSecurityPolicyUri(final SecurityPolicy 
usernameSecurityPolicy) {
-        this.usernameSecurityPolicyUri = 
usernameSecurityPolicy.getSecurityPolicyUri();
+        this.usernameSecurityPolicyUri = usernameSecurityPolicy.getUri();
     }
 
     /**
@@ -417,7 +532,7 @@ public class MiloServerComponent extends DefaultComponent {
      * Server build info
      */
     public void setBuildInfo(final BuildInfo buildInfo) {
-        this.serverConfig.setBuildInfo(buildInfo);
+        this.buildInfo = buildInfo;
     }
 
     /**
@@ -440,6 +555,7 @@ public class MiloServerComponent extends DefaultComponent {
      * Server certificate
      */
     public void setServerCertificate(final KeyPair keyPair, final 
X509Certificate certificate) {
+        this.certificate = certificate;
         setCertificateManager(new DefaultCertificateManager(keyPair, 
certificate));
     }
 
@@ -447,11 +563,7 @@ public class MiloServerComponent extends DefaultComponent {
      * Server certificate manager
      */
     public void setCertificateManager(final CertificateManager 
certificateManager) {
-        if (certificateManager != null) {
-            this.serverConfig.setCertificateManager(certificateManager);
-        } else {
-            this.serverConfig.setCertificateManager(new 
DefaultCertificateManager());
-        }
+        this.certificateManager = certificateManager != null ? 
certificateManager : new DefaultCertificateManager();
     }
 
     /**
@@ -465,6 +577,11 @@ public class MiloServerComponent extends DefaultComponent {
      * Validator for client certificates using default file based approach
      */
     public void setDefaultCertificateValidator(final File certificatesBaseDir) 
{
-        this.certificateValidator = () -> new 
DefaultCertificateValidator(certificatesBaseDir);
+        try {
+            DefaultTrustListManager trustListManager = new 
DefaultTrustListManager(certificatesBaseDir);
+            this.certificateValidator = () -> new 
DefaultCertificateValidator(trustListManager);
+        } catch (IOException e) {
+            LOG.error("Failed to construct certificateValidator.", e);
+        }
     }
 }
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CallMethod.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CallMethod.java
new file mode 100644
index 0000000..c190280
--- /dev/null
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CallMethod.java
@@ -0,0 +1,73 @@
+/*
+ * 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.milo.server.internal;
+
+import org.eclipse.milo.opcua.sdk.core.ValueRanks;
+import 
org.eclipse.milo.opcua.sdk.server.api.methods.AbstractMethodInvocationHandler;
+import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
+import org.eclipse.milo.opcua.stack.core.Identifiers;
+import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+import org.eclipse.milo.opcua.stack.core.types.structured.Argument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CallMethod extends AbstractMethodInvocationHandler {
+
+    public static final Argument IN = new Argument(
+        "in",
+        Identifiers.String,
+        ValueRanks.Scalar,
+        null,
+        new LocalizedText("A value.")
+    );
+
+    public static final Argument OUT = new Argument(
+        "out",
+        Identifiers.String,
+        ValueRanks.Scalar,
+        null,
+        new LocalizedText("A value.")
+    );
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    public CallMethod(UaMethodNode node) {
+        super(node);
+    }
+
+    @Override
+    public Argument[] getInputArguments() {
+        return new Argument[]{IN};
+    }
+
+    @Override
+    public Argument[] getOutputArguments() {
+        return new Argument[]{OUT};
+    }
+
+    @Override
+    protected Variant[] invoke(InvocationContext invocationContext, Variant[] 
inputValues) {
+        logger.debug("Invoking sqrt() method of objectId={}", 
invocationContext.getObjectId());
+
+        String in = (String) inputValues[0].getValue();
+
+        return new Variant[]{new Variant("out-" + in)};
+    }
+
+}
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelNamespace.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelNamespace.java
index 7fdd2a9..7a166cf 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelNamespace.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelNamespace.java
@@ -19,157 +19,78 @@ package org.apache.camel.component.milo.server.internal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.CompletableFuture;
 
-import com.google.common.collect.Lists;
-import org.apache.camel.component.milo.client.MiloClientConsumer;
 import org.eclipse.milo.opcua.sdk.core.Reference;
 import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
-import org.eclipse.milo.opcua.sdk.server.api.AccessContext;
 import org.eclipse.milo.opcua.sdk.server.api.DataItem;
+import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespace;
 import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
-import org.eclipse.milo.opcua.sdk.server.api.Namespace;
-import org.eclipse.milo.opcua.sdk.server.api.ServerNodeMap;
-import org.eclipse.milo.opcua.sdk.server.nodes.AttributeContext;
-import org.eclipse.milo.opcua.sdk.server.nodes.ServerNode;
 import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
 import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
 import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
 import org.eclipse.milo.opcua.stack.core.Identifiers;
-import org.eclipse.milo.opcua.stack.core.StatusCodes;
-import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
 import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
 import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
 import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
-import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
-import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
-import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
-import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-public class CamelNamespace implements Namespace {
+public class CamelNamespace extends ManagedNamespace {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(MiloClientConsumer.class);
-
-    private final UShort namespaceIndex;
-
-    private final String namespaceUri;
-
-    private final ServerNodeMap nodeManager;
     private final SubscriptionModel subscriptionModel;
 
-    private final UaFolderNode folder;
-    private final UaObjectNode itemsObject;
+    private UaObjectNode itemsObject;
+    private UaFolderNode folder;
+
 
     private final Map<String, CamelServerItem> itemMap = new HashMap<>();
 
-    public CamelNamespace(final UShort namespaceIndex, final String 
namespaceUri, final OpcUaServer server) {
-        this.namespaceIndex = namespaceIndex;
-        this.namespaceUri = namespaceUri;
+    public CamelNamespace(final String namespaceUri, final OpcUaServer server) 
{
+        super(server, namespaceUri);
 
-        this.nodeManager = server.getNodeMap();
         this.subscriptionModel = new SubscriptionModel(server, this);
-
-        // create structure
-
-        {
-            final NodeId nodeId = new NodeId(namespaceIndex, "camel");
-            final QualifiedName name = new QualifiedName(namespaceIndex, 
"camel");
-            final LocalizedText displayName = LocalizedText.english("Camel");
-
-            this.folder = new UaFolderNode(this.nodeManager, nodeId, name, 
displayName);
-            this.nodeManager.addNode(this.folder);
-        }
-
-        {
-            final NodeId nodeId = new NodeId(namespaceIndex, "items");
-            final QualifiedName name = new QualifiedName(namespaceIndex, 
"items");
-            final LocalizedText displayName = LocalizedText.english("Items");
-            this.itemsObject = new UaObjectNode(this.nodeManager, nodeId, 
name, displayName);
-            this.folder.addComponent(this.itemsObject);
-        }
-
-        // register reference to structure
-
-        try {
-            server.getUaNamespace().addReference(Identifiers.ObjectsFolder, 
Identifiers.Organizes, true, this.folder.getNodeId().expanded(), 
NodeClass.Object);
-        } catch (final UaException e) {
-            throw new RuntimeException("Failed to register folder", e);
-        }
     }
 
     @Override
-    public UShort getNamespaceIndex() {
-        return this.namespaceIndex;
-    }
-
-    @Override
-    public String getNamespaceUri() {
-        return this.namespaceUri;
-    }
-
-    @Override
-    public CompletableFuture<List<Reference>> browse(final AccessContext 
context, final NodeId nodeId) {
-        final ServerNode node = this.nodeManager.get(nodeId);
-
-        if (node != null) {
-            return CompletableFuture.completedFuture(node.getReferences());
-        } else {
-            final CompletableFuture<List<Reference>> f = new 
CompletableFuture<>();
-            f.completeExceptionally(new 
UaException(StatusCodes.Bad_NodeIdUnknown));
-            return f;
-        }
-    }
-
-    @Override
-    public void read(final ReadContext context, final Double maxAge, final 
TimestampsToReturn timestamps, final List<ReadValueId> readValueIds) {
-        final List<DataValue> results = 
Lists.newArrayListWithCapacity(readValueIds.size());
-
-        for (final ReadValueId id : readValueIds) {
-            final ServerNode node = this.nodeManager.get(id.getNodeId());
-
-            final DataValue value;
-
-            if (node != null) {
-                value = node.readAttribute(new AttributeContext(context), 
id.getAttributeId(), timestamps, id.getIndexRange(), null);
-            } else {
-                value = new DataValue(StatusCodes.Bad_NodeIdUnknown);
-            }
+    protected void onStartup() {
+        super.onStartup();
+        // create structure
 
-            results.add(value);
-        }
+        final NodeId nodeId = newNodeId("camel");
+        final QualifiedName name = newQualifiedName("camel");
+        final LocalizedText displayName = LocalizedText.english("Camel");
 
-        context.complete(results);
-    }
+        this.folder = new UaFolderNode(getNodeContext(), nodeId, name, 
displayName);
+        getNodeManager().addNode(this.folder);
 
-    @Override
-    public void write(final WriteContext context, final List<WriteValue> 
writeValues) {
-        final List<StatusCode> results = 
Lists.newArrayListWithCapacity(writeValues.size());
+        final NodeId nodeId2 = newNodeId("items");
+        final QualifiedName name2 = newQualifiedName("items");
+        final LocalizedText displayName2 = LocalizedText.english("Items");
 
-        for (final WriteValue writeValue : writeValues) {
-            try {
-                final ServerNode node = 
this.nodeManager.getNode(writeValue.getNodeId()).orElseThrow(() -> new 
UaException(StatusCodes.Bad_NodeIdUnknown));
+        this.itemsObject = UaObjectNode.builder(getNodeContext())
+                .setNodeId(nodeId2)
+                .setBrowseName(name2)
+                .setDisplayName(displayName2)
+                .setTypeDefinition(Identifiers.FolderType)
+                .build();
+        this.folder.addComponent(this.itemsObject);
+        this.itemsObject.addComponent(this.folder);
+        this.getNodeManager().addNode(this.itemsObject);
 
-                node.writeAttribute(new AttributeContext(context), 
writeValue.getAttributeId(), writeValue.getValue(), writeValue.getIndexRange());
 
-                if (LOG.isTraceEnabled()) {
-                    final Variant variant = writeValue.getValue().getValue();
-                    final Object o = variant != null ? variant.getValue() : 
null;
-                    LOG.trace("Wrote value={} to attributeId={} of {}", o, 
writeValue.getAttributeId(), writeValue.getNodeId());
-                }
-
-                results.add(StatusCode.GOOD);
-            } catch (final UaException e) {
-                results.add(e.getStatusCode());
-            }
-        }
+        // register reference to structure
 
-        context.complete(results);
+        folder.addReference(new Reference(
+                folder.getNodeId(),
+                Identifiers.Organizes,
+                Identifiers.ObjectsFolder.expanded(),
+                false
+        ));
+
+        itemsObject.addReference(new Reference(
+                nodeId,
+                Identifiers.HasComponent,
+                Identifiers.ObjectNode.expanded(),
+                Reference.Direction.INVERSE
+        ));
     }
 
     @Override
@@ -196,7 +117,7 @@ public class CamelNamespace implements Namespace {
         synchronized (this) {
             CamelServerItem item = this.itemMap.get(itemId);
             if (item == null) {
-                item = new CamelServerItem(itemId, this.nodeManager, 
this.namespaceIndex, this.itemsObject);
+                item = new CamelServerItem(itemId, getNodeContext(), 
getNamespaceIndex(), this.itemsObject);
                 this.itemMap.put(itemId, item);
             }
             return item;
diff --git 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelServerItem.java
 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelServerItem.java
index c1db1eb..fa74c02 100644
--- 
a/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelServerItem.java
+++ 
b/components/camel-milo/src/main/java/org/apache/camel/component/milo/server/internal/CamelServerItem.java
@@ -23,7 +23,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.function.Consumer;
 
 import org.eclipse.milo.opcua.sdk.core.AccessLevel;
-import org.eclipse.milo.opcua.sdk.server.api.ServerNodeMap;
+import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext;
 import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
 import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
 import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
@@ -49,7 +49,7 @@ public class CamelServerItem {
     private final Set<Consumer<DataValue>> listeners = new 
CopyOnWriteArraySet<>();
     private DataValue value = new DataValue(StatusCode.BAD);
 
-    public CamelServerItem(final String itemId, final ServerNodeMap 
nodeManager, final UShort namespaceIndex, final UaObjectNode baseNode) {
+    public CamelServerItem(final String itemId, final UaNodeContext 
nodeContext, final UShort namespaceIndex, final UaObjectNode baseNode) {
 
         this.itemId = itemId;
         this.baseNode = baseNode;
@@ -60,7 +60,7 @@ public class CamelServerItem {
 
         // create variable node
 
-        this.item = new UaVariableNode(nodeManager, nodeId, qname, 
displayName) {
+        this.item = new UaVariableNode(nodeContext, nodeId, qname, 
displayName) {
 
             @Override
             public synchronized DataValue getValue() {
@@ -79,6 +79,7 @@ public class CamelServerItem {
         
this.item.setUserAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)));
 
         baseNode.addComponent(this.item);
+        nodeContext.getNodeManager().addNode(this.item);
     }
 
     public void dispose() {
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/AbstractMiloServerTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/AbstractMiloServerTest.java
index 35902b0..ab12493 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/AbstractMiloServerTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/AbstractMiloServerTest.java
@@ -27,6 +27,7 @@ import org.apache.camel.test.AvailablePortFinder;
 import org.apache.camel.test.junit4.CamelTestSupport;
 import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
 import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.junit.Assume;
 
 public abstract class AbstractMiloServerTest extends CamelTestSupport {
 
@@ -34,6 +35,7 @@ public abstract class AbstractMiloServerTest extends 
CamelTestSupport {
 
     @Override
     protected void doPreSetup() throws Exception {
+        Assume.assumeTrue("Requires java 9+", isJavaVersionSatisfied(9));
         super.doPreSetup();
         this.serverPort = AvailablePortFinder.getNextAvailable();
     }
@@ -112,9 +114,10 @@ public abstract class AbstractMiloServerTest extends 
CamelTestSupport {
         try {
 
             final KeyStoreLoader loader = new KeyStoreLoader();
-            loader.setUrl("file:src/test/resources/cert/cert.p12");
-            loader.setKeyStorePassword("pwd1");
-            loader.setKeyPassword("pwd1");
+            loader.setUrl("file:src/test/resources/keystore");
+            loader.setKeyStorePassword("testtest");
+
+            loader.setKeyPassword("test");
             return loader.load();
         } catch (final GeneralSecurityException | IOException e) {
             throw new RuntimeException(e);
@@ -122,4 +125,22 @@ public abstract class AbstractMiloServerTest extends 
CamelTestSupport {
 
     }
 
+    /**
+     * Return true, if java version (defined by method 
getRequiredJavaVersion()) is satisfied.
+     * Works for java versions 9+
+     */
+    boolean isJavaVersionSatisfied(int requiredVersion) {
+        String version = System.getProperty("java.version");
+        if (!version.startsWith("1.")) {
+            int dot = version.indexOf(".");
+            if (dot != -1) {
+                version = version.substring(0, dot);
+            }
+            if (Integer.parseInt(version) >= requiredVersion) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsCertTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsCertTest.java
index 1c10522..5b3bcfcf 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsCertTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsCertTest.java
@@ -43,18 +43,18 @@ public class MonitorItemMultiConnectionsCertTest extends 
AbstractMiloServerTest
     private static final String MILO_SERVER_ITEM_1 = "milo-server:myitem1";
 
     // with key
-    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:tcp://foo:bar@localhost:@@port@@?node="
+    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:opc.tcp://foo:bar@localhost:@@port@@?node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
-                                                        + 
"&keyStoreUrl=file:src/test/resources/cert/cert.p12&keyStorePassword=pwd1&keyPassword=pwd1"
+                                                        + 
"&keyStoreUrl=file:src/test/resources/keystore&keyStorePassword=testtest&keyPassword=test&keyAlias=test"
                                                         + 
"&discoveryEndpointSuffix=/discovery&overrideHost=true";
 
     // with wrong password
-    private static final String MILO_CLIENT_ITEM_C2_1 = 
"milo-client:tcp://foo:bar2@localhost:@@port@@?node="
+    private static final String MILO_CLIENT_ITEM_C2_1 = 
"milo-client:opc.tcp://foo:bar2@localhost:@@port@@?node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
                                                         + 
"&discoveryEndpointSuffix=/discovery&overrideHost=true";
 
     // without key, clientId=1
-    private static final String MILO_CLIENT_ITEM_C3_1 = 
"milo-client:tcp://foo:bar@localhost:@@port@@?clientId=1&node="
+    private static final String MILO_CLIENT_ITEM_C3_1 = 
"milo-client:opc.tcp://foo:bar@localhost:@@port@@?clientId=1&node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
                                                         + 
"&discoveryEndpointSuffix=/discovery&overrideHost=true";
 
@@ -79,10 +79,10 @@ public class MonitorItemMultiConnectionsCertTest extends 
AbstractMiloServerTest
         super.configureMiloServer(server);
 
         final Path baseDir = Paths.get("target/testing/cert/default");
-        final Path trusted = baseDir.resolve("trusted");
+        final Path trusted = baseDir.resolve("trusted/certs");
 
         Files.createDirectories(trusted);
-        Files.copy(Paths.get("src/test/resources/cert/certificate.der"), 
trusted.resolve("certificate.der"), REPLACE_EXISTING);
+        Files.copy(Paths.get("src/test/resources/ca/cacert.pem"), 
trusted.resolve("cacert.pem"), REPLACE_EXISTING);
 
         server.setServerCertificate(loadDefaultTestKey());
         server.setDefaultCertificateValidator(baseDir.toFile());
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsTest.java
index dcccaf9..116cf98 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemMultiConnectionsTest.java
@@ -34,13 +34,13 @@ public class MonitorItemMultiConnectionsTest extends 
AbstractMiloServerTest {
 
     private static final String MILO_SERVER_ITEM_1 = "milo-server:myitem1";
 
-    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:tcp://foo:bar@localhost:@@port@@?node="
+    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:opc.tcp://foo:bar@localhost:@@port@@?node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1") + 
"&overrideHost=true";
 
-    private static final String MILO_CLIENT_ITEM_C2_1 = 
"milo-client:tcp://foo:bar2@localhost:@@port@@?node="
+    private static final String MILO_CLIENT_ITEM_C2_1 = 
"milo-client:opc.tcp://foo:bar2@localhost:@@port@@?node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1") + 
"&overrideHost=true";
 
-    private static final String MILO_CLIENT_ITEM_C3_1 = 
"milo-client:tcp://foo2:bar@localhost:@@port@@?node="
+    private static final String MILO_CLIENT_ITEM_C3_1 = 
"milo-client:opc.tcp://foo2:bar@localhost:@@port@@?node="
                                                         + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1") + 
"&overrideHost=true";
 
     private static final String MOCK_TEST_1 = "mock:test1";
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemTest.java
index b4d9b3e..3b3ba6d 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/MonitorItemTest.java
@@ -34,9 +34,9 @@ public class MonitorItemTest extends AbstractMiloServerTest {
 
     private static final String MILO_SERVER_ITEM_1 = "milo-server:myitem1";
 
-    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:tcp://foo:bar@localhost:@@port@@?node="
-                                                        + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
-                                                        + 
"&allowedSecurityPolicies=None&overrideHost=true";
+    private static final String MILO_CLIENT_ITEM_C1_1 = 
"milo-client:opc.tcp://foo:bar@localhost:@@port@@?node="
+                                                    + 
NodeIds.nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
+                                                    + 
"&allowedSecurityPolicies=None&overrideHost=true";
 
     private static final String MOCK_TEST_1 = "mock:test1";
 
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/WriteClientTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/WriteClientTest.java
index 43214b3..9a0942c 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/WriteClientTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/WriteClientTest.java
@@ -41,8 +41,8 @@ public class WriteClientTest extends AbstractMiloServerTest {
     private static final String MILO_SERVER_ITEM_1 = "milo-server:myitem1";
     private static final String MILO_SERVER_ITEM_2 = "milo-server:myitem2";
 
-    private static final String MILO_CLIENT_BASE_C1 = 
"milo-client:tcp://foo:bar@localhost:@@port@@";
-    private static final String MILO_CLIENT_BASE_C2 = 
"milo-client:tcp://foo2:bar2@localhost:@@port@@";
+    private static final String MILO_CLIENT_BASE_C1 = 
"milo-client:opc.tcp://foo:bar@localhost:@@port@@";
+    private static final String MILO_CLIENT_BASE_C2 = 
"milo-client:opc.tcp://foo2:bar2@localhost:@@port@@";
 
     private static final String MILO_CLIENT_ITEM_C1_1 = MILO_CLIENT_BASE_C1 + 
"?node=" + nodeValue(MiloServerComponent.DEFAULT_NAMESPACE_URI, "items-myitem1")
                                                         + "&overrideHost=true";
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/CallClientTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/CallClientTest.java
index 56ef9ee..db70850 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/CallClientTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/CallClientTest.java
@@ -18,29 +18,34 @@ package org.apache.camel.component.milo.call;
 
 import java.util.Arrays;
 import java.util.EnumSet;
-import java.util.LinkedList;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.camel.Produce;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.milo.AbstractMiloServerTest;
-import org.apache.camel.component.milo.call.MockCall.Call1;
+import org.apache.camel.component.milo.NodeIds;
 import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
 import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig;
 import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfigBuilder;
-import org.eclipse.milo.opcua.sdk.server.identity.AnonymousIdentityValidator;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
-import org.eclipse.milo.opcua.stack.core.application.DefaultCertificateManager;
-import 
org.eclipse.milo.opcua.stack.core.application.InsecureCertificateValidator;
+import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
+import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateManager;
+import org.eclipse.milo.opcua.stack.core.security.InsecureCertificateValidator;
 import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
-import org.junit.After;
+import org.eclipse.milo.opcua.stack.core.transport.TransportProfile;
+import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
+import org.eclipse.milo.opcua.stack.core.types.structured.UserTokenPolicy;
+import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
 import static org.apache.camel.component.milo.NodeIds.nodeValue;
+import static 
org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS;
 
 /**
  * Unit tests for calling from the client side
@@ -49,17 +54,27 @@ public class CallClientTest extends AbstractMiloServerTest {
 
     private static final String DIRECT_START_1 = "direct:start1";
 
-    private static final String MILO_CLIENT_BASE_C1 = 
"milo-client:tcp://localhost:@@port@@";
+    private static final String MILO_CLIENT_BASE_C1 = 
"milo-client:opc.tcp://localhost:@@port@@";
 
-    private static final String MILO_CLIENT_ITEM_C1_1 = MILO_CLIENT_BASE_C1 + 
"?node=" + nodeValue(MockNamespace.URI, MockNamespace.FOLDER_ID) + "&method="
-                                                        + 
nodeValue(MockNamespace.URI, "id1") + "&overrideHost=true";
-
-    @Produce(DIRECT_START_1)
-    protected ProducerTemplate producer1;
+    private static final String MILO_CLIENT_ITEM_C1_1 = MILO_CLIENT_BASE_C1 + 
"?node=" + NodeIds.nodeValue(MockCamelNamespace.URI, 
MockCamelNamespace.FOLDER_ID)
+                                                        + "&method=" + 
nodeValue(MockCamelNamespace.URI, MockCamelNamespace.CALL_ID) + 
"&overrideHost=true";
 
     private OpcUaServer server;
+    private MockCamelNamespace namespace;
+    private MockCallMethod callMethod;
 
-    private Call1 call1;
+    @Produce(DIRECT_START_1)
+    private ProducerTemplate producer1;
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(DIRECT_START_1).to(resolve(MILO_CLIENT_ITEM_C1_1));
+            }
+        };
+    }
 
     @Override
     protected boolean isAddServer() {
@@ -68,43 +83,75 @@ public class CallClientTest extends AbstractMiloServerTest {
 
     @Before
     public void start() throws Exception {
-        final OpcUaServerConfigBuilder config = new OpcUaServerConfigBuilder();
-        config.setBindAddresses(Arrays.asList("localhost"));
-        config.setBindPort(getServerPort());
-        config.setIdentityValidator(AnonymousIdentityValidator.INSTANCE);
-        
config.setUserTokenPolicies(Arrays.asList(OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS));
-        config.setSecurityPolicies(EnumSet.of(SecurityPolicy.None));
-        config.setCertificateManager(new DefaultCertificateManager());
-        config.setCertificateValidator(new InsecureCertificateValidator());
+        final OpcUaServerConfigBuilder cfg = OpcUaServerConfig.builder();
+
+        cfg.setCertificateManager(new DefaultCertificateManager());
+        
cfg.setEndpoints(createEndpointConfigurations(Arrays.asList(OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS),
 EnumSet.of(SecurityPolicy.None)));
+        cfg.setApplicationName(LocalizedText.english("Apache Camel Milo 
Server"));
+        cfg.setApplicationUri("urn:mock:namespace");
+        cfg.setProductUri("urn:org:apache:camel:milo");
+        cfg.setCertificateManager(new DefaultCertificateManager());
+        cfg.setCertificateValidator(new InsecureCertificateValidator());
+
+        this.server = new OpcUaServer(cfg.build());
 
-        this.server = new OpcUaServer(config.build());
+        this.namespace = new MockCamelNamespace(this.server, node -> 
callMethod = new MockCallMethod(node));
+        this.namespace.startup();
+        this.server.startup().get();
+    }
 
-        this.call1 = new MockCall.Call1();
+    private Set<EndpointConfiguration> 
createEndpointConfigurations(List<UserTokenPolicy> userTokenPolicies, 
Set<SecurityPolicy> securityPolicies) {
+        Set<EndpointConfiguration> endpointConfigurations = new 
LinkedHashSet<>();
 
-        this.server.getNamespaceManager().registerAndAdd(MockNamespace.URI, 
index -> {
+        String bindAddress = "0.0.0.0";
+        Set<String> hostnames = new LinkedHashSet<>();
+        hostnames.add(HostnameUtil.getHostname());
+        hostnames.addAll(HostnameUtil.getHostnames(bindAddress));
 
-            final List<UaMethodNode> methods = new LinkedList<>();
-            methods.add(MockCall.fromNode(index, this.server.getNodeMap(), 
"id1", "name1", this.call1));
+        UserTokenPolicy[] tokenPolicies = new UserTokenPolicy[] 
{USER_TOKEN_POLICY_ANONYMOUS};
 
-            return new MockNamespace(index, this.server, methods);
-        });
+        for (String hostname : hostnames) {
+            EndpointConfiguration.Builder builder = 
EndpointConfiguration.newBuilder()
+                    .setBindAddress(bindAddress)
+                    .setHostname(hostname)
+                    .setCertificate(() -> null)
+                    .addTokenPolicies(tokenPolicies);
 
-        this.server.startup().get();
-    }
 
-    @After
-    public void stop() {
-        this.server.shutdown();
-    }
+            if (securityPolicies == null || 
securityPolicies.contains(SecurityPolicy.None)) {
+                EndpointConfiguration.Builder noSecurityBuilder = 
builder.copy()
+                        .setSecurityPolicy(SecurityPolicy.None)
+                        .setSecurityMode(MessageSecurityMode.None);
 
-    @Override
-    protected RoutesBuilder createRouteBuilder() throws Exception {
-        return new RouteBuilder() {
-            @Override
-            public void configure() throws Exception {
-                from(DIRECT_START_1).to(resolve(MILO_CLIENT_ITEM_C1_1));
+                
endpointConfigurations.add(buildTcpEndpoint(noSecurityBuilder));
             }
-        };
+            /*
+             * It's good practice to provide a discovery-specific endpoint 
with no security.
+             * It's required practice if all regular endpoints have security 
configured.
+             *
+             * Usage of the  "/discovery" suffix is defined by OPC UA Part 6:
+             *
+             * Each OPC UA Server Application implements the Discovery Service 
Set. If the OPC UA Server requires a
+             * different address for this Endpoint it shall create the address 
by appending the path "/discovery" to
+             * its base address.
+             */
+
+            EndpointConfiguration.Builder discoveryBuilder = builder.copy()
+                    .setPath("/discovery")
+                    .setSecurityPolicy(SecurityPolicy.None)
+                    .setSecurityMode(MessageSecurityMode.None);
+
+            endpointConfigurations.add(buildTcpEndpoint(discoveryBuilder));
+        }
+
+        return endpointConfigurations;
+    }
+
+    private EndpointConfiguration 
buildTcpEndpoint(EndpointConfiguration.Builder base) {
+        return base.copy()
+                .setTransportProfile(TransportProfile.TCP_UASC_UABINARY)
+                .setBindPort(getServerPort())
+                .build();
     }
 
     @Test
@@ -115,8 +162,8 @@ public class CallClientTest extends AbstractMiloServerTest {
         doCall(this.producer1, "bar");
 
         // assert
-
-        Assert.assertArrayEquals(new Object[] {"foo", "bar"}, 
this.call1.calls.toArray());
+        Assert.assertNotNull(this.callMethod);
+        Assert.assertArrayEquals(new Object[] {"out-foo", "out-bar"}, 
this.callMethod.getCalls().toArray());
     }
 
     private static void doCall(final ProducerTemplate producerTemplate, final 
Object input) {
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCall.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCall.java
deleted file mode 100644
index 51408c2..0000000
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCall.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.milo.call;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import org.eclipse.milo.opcua.sdk.server.annotations.UaInputArgument;
-import org.eclipse.milo.opcua.sdk.server.annotations.UaMethod;
-import org.eclipse.milo.opcua.sdk.server.annotations.UaOutputArgument;
-import org.eclipse.milo.opcua.sdk.server.api.ServerNodeMap;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
-import org.eclipse.milo.opcua.sdk.server.util.AnnotationBasedInvocationHandler;
-import 
org.eclipse.milo.opcua.sdk.server.util.AnnotationBasedInvocationHandler.InvocationContext;
-import 
org.eclipse.milo.opcua.sdk.server.util.AnnotationBasedInvocationHandler.Out;
-import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
-import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
-
-import static 
org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText.english;
-
-public final class MockCall {
-
-    private MockCall() {
-    }
-
-    // in: str1[String], out: out1[String]
-    public static class Call1 {
-
-        public List<String> calls = new LinkedList<>();
-
-        @UaMethod
-        public void call(final InvocationContext context, 
@UaInputArgument(name = "in1")
-        final String in1, @UaOutputArgument(name = "out1")
-        final Out<String> out1) {
-            this.calls.add(in1);
-            out1.set("out-" + in1);
-        }
-    }
-
-    public static UaMethodNode fromNode(final UShort index, final 
ServerNodeMap nodeMap, final String nodeId, final String name, final Object 
methodObject) {
-
-        try {
-            final UaMethodNode method = new UaMethodNode(nodeMap, new 
NodeId(index, nodeId), new QualifiedName(index, name), english(name), 
english(nodeId), UInteger.MIN,
-                                                         UInteger.MIN, true, 
true);
-
-            final AnnotationBasedInvocationHandler handler = 
AnnotationBasedInvocationHandler.fromAnnotatedObject(nodeMap, methodObject);
-            method.setInputArguments(handler.getInputArguments());
-            method.setOutputArguments(handler.getOutputArguments());
-            method.setInvocationHandler(handler);
-            return method;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCallMethod.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCallMethod.java
new file mode 100644
index 0000000..fe52c8c
--- /dev/null
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCallMethod.java
@@ -0,0 +1,77 @@
+/*
+ * 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.milo.call;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.milo.opcua.sdk.core.ValueRanks;
+import 
org.eclipse.milo.opcua.sdk.server.api.methods.AbstractMethodInvocationHandler;
+import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
+import org.eclipse.milo.opcua.stack.core.Identifiers;
+import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+import org.eclipse.milo.opcua.stack.core.types.structured.Argument;
+
+public class MockCallMethod extends AbstractMethodInvocationHandler {
+
+    public static final Argument IN = new Argument(
+        "in",
+        Identifiers.String,
+        ValueRanks.Scalar,
+        null,
+        new LocalizedText("A value.")
+    );
+
+    public static final Argument OUT = new Argument(
+        "out",
+        Identifiers.String,
+        ValueRanks.Scalar,
+        null,
+        new LocalizedText("A value.")
+    );
+
+    public List<String> calls = new LinkedList<>();
+
+    public MockCallMethod(UaMethodNode node) {
+        super(node);
+    }
+
+    @Override
+    public Argument[] getInputArguments() {
+        return new Argument[]{IN};
+    }
+
+    @Override
+    public Argument[] getOutputArguments() {
+        return new Argument[]{OUT};
+    }
+
+    @Override
+    protected Variant[] invoke(InvocationContext invocationContext, Variant[] 
inputValues) {
+        String in = (String) inputValues[0].getValue();
+
+        calls.add("out-" + in);
+
+        return new Variant[]{new Variant("out-" + in)};
+    }
+
+    public List<String> getCalls() {
+        return calls;
+    }
+}
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCamelNamespace.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCamelNamespace.java
new file mode 100644
index 0000000..3197485
--- /dev/null
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockCamelNamespace.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.milo.call;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.camel.component.milo.client.MiloClientConsumer;
+import org.apache.camel.component.milo.server.internal.CamelServerItem;
+import org.eclipse.milo.opcua.sdk.core.Reference;
+import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
+import org.eclipse.milo.opcua.sdk.server.api.DataItem;
+import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespace;
+import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
+import 
org.eclipse.milo.opcua.sdk.server.api.methods.AbstractMethodInvocationHandler;
+import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
+import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
+import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
+import org.eclipse.milo.opcua.stack.core.Identifiers;
+import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MockCamelNamespace extends ManagedNamespace {
+
+    public static final String URI = "urn:org:apache:camel:mock";
+    public static final int FOLDER_ID = 1;
+    public static final int CALL_ID = 2;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(MiloClientConsumer.class);
+
+    private final SubscriptionModel subscriptionModel;
+
+    private final Function<UaMethodNode, AbstractMethodInvocationHandler> 
callMethodCreator;
+
+    private UaFolderNode folder;
+
+    private final Map<String, CamelServerItem> itemMap = new HashMap<>();
+
+    public MockCamelNamespace(final OpcUaServer server, Function<UaMethodNode, 
AbstractMethodInvocationHandler>  callMethodCreator) {
+        super(server, URI);
+
+        this.subscriptionModel = new SubscriptionModel(server, this);
+        this.callMethodCreator = callMethodCreator;
+    }
+
+    @Override
+    protected void onStartup() {
+        super.onStartup();
+        // create structure
+
+        final NodeId nodeId = newNodeId(FOLDER_ID);
+        final QualifiedName name = newQualifiedName("camel");
+        final LocalizedText displayName = LocalizedText.english("Camel");
+
+        this.folder = new UaFolderNode(getNodeContext(), nodeId, name, 
displayName);
+        getNodeManager().addNode(this.folder);
+
+        // register reference to structure
+
+        folder.addReference(new Reference(
+                folder.getNodeId(),
+                Identifiers.Organizes,
+                Identifiers.ObjectsFolder.expanded(),
+                false
+        ));
+
+        addCallMethod(folder);
+    }
+
+    private void addCallMethod(UaFolderNode folderNode) {
+        UaMethodNode methodNode = UaMethodNode.builder(getNodeContext())
+                .setNodeId(new NodeId(getNamespaceIndex(), CALL_ID))
+                .setBrowseName(newQualifiedName("call"))
+                .setDisplayName(new LocalizedText(null, "call"))
+                .setDescription(
+                        LocalizedText.english("Returns the \"out-\"+entry 
parameter"))
+                .build();
+
+        AbstractMethodInvocationHandler callMethod = 
callMethodCreator.apply(methodNode);
+        methodNode.setProperty(UaMethodNode.InputArguments, 
callMethod.getInputArguments());
+        methodNode.setProperty(UaMethodNode.OutputArguments, 
callMethod.getOutputArguments());
+        methodNode.setInvocationHandler(callMethod);
+
+        getNodeManager().addNode(methodNode);
+
+        methodNode.addReference(new Reference(
+                methodNode.getNodeId(),
+                Identifiers.HasComponent,
+                folderNode.getNodeId().expanded(),
+                false
+        ));
+
+        methodNode.addReference(new Reference(
+                methodNode.getNodeId(),
+                Identifiers.HasComponent,
+                folderNode.getNodeId().expanded(),
+                folderNode.getNodeClass(),
+                false
+        ));
+    }
+
+
+    @Override
+    public void onDataItemsCreated(final List<DataItem> dataItems) {
+        this.subscriptionModel.onDataItemsCreated(dataItems);
+    }
+
+    @Override
+    public void onDataItemsModified(final List<DataItem> dataItems) {
+        this.subscriptionModel.onDataItemsModified(dataItems);
+    }
+
+    @Override
+    public void onDataItemsDeleted(final List<DataItem> dataItems) {
+        this.subscriptionModel.onDataItemsDeleted(dataItems);
+    }
+
+    @Override
+    public void onMonitoringModeChanged(final List<MonitoredItem> 
monitoredItems) {
+        this.subscriptionModel.onMonitoringModeChanged(monitoredItems);
+    }
+}
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockNamespace.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockNamespace.java
deleted file mode 100644
index 1acd8de..0000000
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/call/MockNamespace.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.milo.call;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-
-import org.eclipse.milo.opcua.sdk.core.Reference;
-import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
-import org.eclipse.milo.opcua.sdk.server.api.AccessContext;
-import org.eclipse.milo.opcua.sdk.server.api.DataItem;
-import org.eclipse.milo.opcua.sdk.server.api.MethodInvocationHandler;
-import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
-import org.eclipse.milo.opcua.sdk.server.api.Namespace;
-import org.eclipse.milo.opcua.sdk.server.api.ServerNodeMap;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.FolderNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.AttributeContext;
-import org.eclipse.milo.opcua.sdk.server.nodes.ServerNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
-import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
-import org.eclipse.milo.opcua.stack.core.Identifiers;
-import org.eclipse.milo.opcua.stack.core.StatusCodes;
-import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
-import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
-import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
-import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
-import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
-import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
-import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
-
-import static java.util.stream.Collectors.toList;
-
-public class MockNamespace implements Namespace {
-
-    public static final int FOLDER_ID = 1;
-
-    public static final String URI = "urn:mock:namespace";
-
-    private final UShort index;
-
-    private final ServerNodeMap nodeMap;
-    private final SubscriptionModel subscriptionModel;
-
-    public MockNamespace(final UShort index, final OpcUaServer server, 
List<UaMethodNode> methods) {
-        this.index = index;
-        this.nodeMap = server.getNodeMap();
-        this.subscriptionModel = new SubscriptionModel(server, this);
-
-        registerItems(methods);
-    }
-
-    private void registerItems(List<UaMethodNode> methods) {
-
-        // create a folder
-
-        final UaFolderNode folder = new UaFolderNode(this.nodeMap, new 
NodeId(this.index, FOLDER_ID), new QualifiedName(this.index, "FooBarFolder"),
-                                                     
LocalizedText.english("Foo Bar Folder"));
-
-        // add our folder to the objects folder
-
-        this.nodeMap.getNode(Identifiers.ObjectsFolder).ifPresent(node -> {
-            ((FolderNode)node).addComponent(folder);
-        });
-
-        // add method calls
-
-        methods.forEach(folder::addComponent);
-    }
-
-    // default method implementations follow
-
-    @Override
-    public void read(final ReadContext context, final Double maxAge, final 
TimestampsToReturn timestamps, final List<ReadValueId> readValueIds) {
-
-        final List<DataValue> results = new ArrayList<>(readValueIds.size());
-
-        for (final ReadValueId id : readValueIds) {
-            final ServerNode node = this.nodeMap.get(id.getNodeId());
-
-            final DataValue value = node != null ? node.readAttribute(new 
AttributeContext(context), id.getAttributeId()) : new 
DataValue(StatusCodes.Bad_NodeIdUnknown);
-
-            results.add(value);
-        }
-
-        // report back with result
-
-        context.complete(results);
-    }
-
-    @Override
-    public void write(final WriteContext context, final List<WriteValue> 
writeValues) {
-
-        final List<StatusCode> results = writeValues.stream().map(value -> {
-            if (this.nodeMap.containsKey(value.getNodeId())) {
-                return new StatusCode(StatusCodes.Bad_NotWritable);
-            } else {
-                return new StatusCode(StatusCodes.Bad_NodeIdUnknown);
-            }
-        }).collect(toList());
-
-        // report back with result
-
-        context.complete(results);
-    }
-
-    @Override
-    public CompletableFuture<List<Reference>> browse(final AccessContext 
context, final NodeId nodeId) {
-        final ServerNode node = this.nodeMap.get(nodeId);
-
-        if (node != null) {
-            return CompletableFuture.completedFuture(node.getReferences());
-        } else {
-            final CompletableFuture<List<Reference>> f = new 
CompletableFuture<>();
-            f.completeExceptionally(new 
UaException(StatusCodes.Bad_NodeIdUnknown));
-            return f;
-        }
-    }
-
-    @Override
-    public Optional<MethodInvocationHandler> getInvocationHandler(final NodeId 
methodId) {
-        return Optional.ofNullable(this.nodeMap.get(methodId)).filter(n -> n 
instanceof UaMethodNode).flatMap(n -> {
-            final UaMethodNode m = (UaMethodNode)n;
-            return m.getInvocationHandler();
-        });
-    }
-
-    @Override
-    public void onDataItemsCreated(final List<DataItem> dataItems) {
-        this.subscriptionModel.onDataItemsCreated(dataItems);
-    }
-
-    @Override
-    public void onDataItemsModified(final List<DataItem> dataItems) {
-        this.subscriptionModel.onDataItemsModified(dataItems);
-    }
-
-    @Override
-    public void onDataItemsDeleted(final List<DataItem> dataItems) {
-        this.subscriptionModel.onDataItemsDeleted(dataItems);
-    }
-
-    @Override
-    public void onMonitoringModeChanged(final List<MonitoredItem> 
monitoredItems) {
-        this.subscriptionModel.onMonitoringModeChanged(monitoredItems);
-    }
-
-    @Override
-    public UShort getNamespaceIndex() {
-        return this.index;
-    }
-
-    @Override
-    public String getNamespaceUri() {
-        return URI;
-    }
-}
diff --git 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/server/ServerSetCertificateManagerTest.java
 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/server/ServerSetCertificateManagerTest.java
index fe58263..f4ea7f4 100644
--- 
a/components/camel-milo/src/test/java/org/apache/camel/component/milo/server/ServerSetCertificateManagerTest.java
+++ 
b/components/camel-milo/src/test/java/org/apache/camel/component/milo/server/ServerSetCertificateManagerTest.java
@@ -38,7 +38,7 @@ public class ServerSetCertificateManagerTest extends 
AbstractMiloServerTest {
         final Path trusted = baseDir.resolve("trusted");
 
         Files.createDirectories(trusted);
-        Files.copy(Paths.get("src/test/resources/cert/certificate.der"), 
trusted.resolve("certificate.der"), REPLACE_EXISTING);
+        Files.copy(Paths.get("src/test/resources/ca/cacert.pem"), 
trusted.resolve("cacert.pem"), REPLACE_EXISTING);
 
         server.setServerCertificate(loadDefaultTestKey());
         server.setDefaultCertificateValidator(baseDir.toFile());
diff --git a/components/camel-milo/src/test/resources/ca/cacert.pem 
b/components/camel-milo/src/test/resources/ca/cacert.pem
new file mode 100644
index 0000000..f23452c
--- /dev/null
+++ b/components/camel-milo/src/test/resources/ca/cacert.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFeTCCA2GgAwIBAgIJAIoc1wRX1g4hMA0GCSqGSIb3DQEBCwUAMEsxCzAJBgNV
+BAYTAlJIMQswCQYDVQQIDAJSSDELMAkGA1UEBwwCUkgxEDAOBgNVBAoMB1Rlc3Qg
+Q0ExEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjAwMTI4MTYxNTA0WhcNMjAwMjI3MTYx
+NTA0WjBLMQswCQYDVQQGEwJSSDELMAkGA1UECAwCUkgxCzAJBgNVBAcMAlJIMRAw
+DgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAvk1N3IMWVqJIlMuFkD2XiPiEPk5m7ToriB0ThNyY
+IUKVayEoFOyU82ciycERJHVgo64BxQ3e4BqBakyarI4ssbiI6hm8onWzjCBPy5U+
+RQxK/oDoK3vn1uCQRKbdjkJvbbmbTVlWrpTGmNBflw+OlTGSswcWxc3q0KrKYmm0
+eGcvL6ZNy+ciTPXRyE7ccHR87yP+CIs8l4O4j5QavcIYH6PSHtMU6XUFIZGpj9vI
+6PbKu2MPK1+QRL+zevf5Q3l5tdjHavh0NDW8QcCa/te7qc7gQvYttFzYMuzvNqeZ
+OL3iCWJVKG5Atxo9JvSsb0IbDXqnGQrRqYoHwk64bT2Hs3G5pwBLSoO+HrKqfaS3
+c5KINGrfCVcfrKIHyA3l/RLIhlhzkxj1Mvq2sP8Jt0IfZNpXjBiXw6zH76Biozgq
+gdTNypOz3an1OkfQRcsXjjaI/EKLxcTAV5DQR2++OI6eFfalVjbdmWCvOBOXa3tN
+UtxACL/kNbjAegRzZTZyTy/uA1nj3E2FdDDGqUnxiNtXBVQ40s4xoi2w0sdWQ3ZL
+ntLQ3NhLJw8UCCUMMQZEwghAeyXGqS7GuAszoPDBzjkJyI456sR8E5mapjwwVbMz
+nCGfeEgNoRSMuAteJwS/nw2M9ApxgqYan+683cV2pIU8wGtkRzYxvRXMswKIBx83
+ksUCAwEAAaNgMF4wHQYDVR0OBBYEFOS5A19qy9nHzu0q5pXrpdT4vGXbMB8GA1Ud
+IwQYMBaAFOS5A19qy9nHzu0q5pXrpdT4vGXbMA8GA1UdEwEB/wQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQBr3dLjF8MLJTGW92UejISaCLft
+jqyXonrHLi3snrC2rW6OkGD35Mk+ylpuYHSzCl258P5W8J9A9snRgzL6pqXw2ZPB
+GLjGyge4PKvzH25O8kCgAuvyNBPPDKMdhhMzaL1qsr8zveUIYxMtJMVZuzpYFD8u
+2/MZQ6yf8PVVsnEPlrjg2ByUEiSBNAnmqC3zFnR90Zv2F8jQyhCZyZf/57lGDS4c
+5ap8mFhwxY6gZbzv/LW8r13hT7QbGkgl6yildg1/+Ozn8RkRoYNcZ6r7Nl0Z6TL3
+Vs4vab0cuketxIdpzld5FW3b+DBjzpR1pdzGtnm3ultSBHD75vxBeKI3//K1Ar1G
+U8NAuWIKEEOirhsm23XBowmwEcbKD8AlHjfZVDGs1kWvhdu09RY1BHYt0YBIbirE
+g+F537+MUr5l934eQxIpOiHyJmiqNnfC4l9e4tUIKfX64yWoqu5davEIBI0x9mKe
+Ug8uofPCm6U78S6jANauze6X9r8T8bDm56y9yztQXVf1mZxTgoHRspX6wt89YQVJ
+VZzrWyRsMvPDGWevH809CHJaOFMjS23ffF5KML9Gzf/XShRQWCNkE85T55W8FgS0
+Kt0m9ZRPWEGulbsO85ETjssatI1xKKwRNOQIWdk625sci42PSgK/q8Avufe0rRbH
+398+r4PDI8gaMQKG4A==
+-----END CERTIFICATE-----
diff --git a/components/camel-milo/src/test/resources/cert/Makefile 
b/components/camel-milo/src/test/resources/cert/Makefile
deleted file mode 100644
index b44c850..0000000
--- a/components/camel-milo/src/test/resources/cert/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-.PHONY: clean all show
-
-all:
-       openssl req -batch -x509 -sha256 -nodes -days 36500 \
-               -subj '/C=XX/L=End of the universe/O=Milliways' \
-               -config cert.ini \
-               -newkey rsa:2048 -keyout privateKey.key -out certificate.crt
-       openssl pkcs12 -password pass:pwd1 -export -out cert.p12 -inkey 
privateKey.key -in certificate.crt
-       openssl x509 -outform der -in certificate.crt -out certificate.der
-
-show:
-       openssl x509 -in certificate.crt -text -noout
-
-clean:
-       @-rm privateKey.key certificate.crt cert.p12 certificate.der
-
diff --git a/components/camel-milo/src/test/resources/cert/cert.ini 
b/components/camel-milo/src/test/resources/cert/cert.ini
deleted file mode 100644
index 7eaaed2..0000000
--- a/components/camel-milo/src/test/resources/cert/cert.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[req]
-x509_extensions = v3_req
-distinguished_name = req_distinguished_name
-
-[req_distinguished_name]
-
-[v3_req]
-basicConstraints = CA:FALSE
-keyUsage = digitalSignature, keyEncipherment
-subjectAltName = @alt_names
-
-[alt_names]
-URI.1 = http://camel.apache.org/EclipseMilo/Client
diff --git a/components/camel-milo/src/test/resources/cert/cert.p12 
b/components/camel-milo/src/test/resources/cert/cert.p12
deleted file mode 100644
index 8a2eed9..0000000
Binary files a/components/camel-milo/src/test/resources/cert/cert.p12 and 
/dev/null differ
diff --git a/components/camel-milo/src/test/resources/cert/certificate.crt 
b/components/camel-milo/src/test/resources/cert/certificate.crt
deleted file mode 100644
index 8b7c784..0000000
--- a/components/camel-milo/src/test/resources/cert/certificate.crt
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDVDCCAjygAwIBAgIJAKvxBeV3q1AsMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV
-BAYTAlhYMRwwGgYDVQQHExNFbmQgb2YgdGhlIHVuaXZlcnNlMRIwEAYDVQQKEwlN
-aWxsaXdheXMwIBcNMTYwNzIxMTMyODAwWhgPMjExNjA2MjcxMzI4MDBaMD8xCzAJ
-BgNVBAYTAlhYMRwwGgYDVQQHExNFbmQgb2YgdGhlIHVuaXZlcnNlMRIwEAYDVQQK
-EwlNaWxsaXdheXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDYISUG
-ggjH/3KuUHLjUvDWNomCDzMqTA4QrlK0cxt0VZq0DmzP1xEtAhXJ6eXVsBsfbvzW
-F/AK1otAP67wsGz52edbzchW0TVvKd3CAKBH8LFgycu7k6bYdc+0crgyxeM1NUmC
-x1nxbfTMWSC1CWOzjZXOVlqyUIHY8HJetPpJpS0GizAfa+eCHaRXOyC89dUX0cS8
-370NbcqfrYvbd8LOosHLYqJ/x7P7YIxOkw/wn5m2OAeSfItqfNCLbR2+oiCrLLv0
-MXQVCiiYfPLoEePFb288/AkgI/1go4Lsh6/vBnTU03r/YKM1QHVxygax3+I/9cXp
-wNmV8HyDNo5Lu98bAgMBAAGjUTBPMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMDUG
-A1UdEQQuMCyGKmh0dHA6Ly9jYW1lbC5hcGFjaGUub3JnL0VjbGlwc2VNaWxvL0Ns
-aWVudDANBgkqhkiG9w0BAQsFAAOCAQEAWplx3EQyl69Xrn73v55sPa8mlBKMjjJ3
-FmloVkUoYO8k8xciPDRHSVaeKYwU3dO4NBwPnbo0JMZWEaYr9SsVWbLRsfqu3pGt
-ScHp7n1GB8gkstoX3cuqzVF0UQCkSsfUNXGCfQVQbQwJg8hBU77WTflDbcrGMbxf
-PgTkwOv8qfNjPawu4j05/9SKoauKoNVQ1nHS7D3tkzoPxh+0efOhrhOPXtB/C9yH
-QKIMFzsh0uLlzNZiMURjTZo/asZ9RdCplUzd3ciQDZk6QxW8DIrOfySUMuWU1UDa
-1/qA0w7xg+1azBRl3oiUwNxOmHHVeWqonhbvYlG+GG/MjQHcg62NHg==
------END CERTIFICATE-----
diff --git a/components/camel-milo/src/test/resources/cert/certificate.der 
b/components/camel-milo/src/test/resources/cert/certificate.der
deleted file mode 100644
index 37abde7..0000000
Binary files a/components/camel-milo/src/test/resources/cert/certificate.der 
and /dev/null differ
diff --git a/components/camel-milo/src/test/resources/cert/privateKey.key 
b/components/camel-milo/src/test/resources/cert/privateKey.key
deleted file mode 100644
index 5900120..0000000
--- a/components/camel-milo/src/test/resources/cert/privateKey.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYISUGggjH/3Ku
-UHLjUvDWNomCDzMqTA4QrlK0cxt0VZq0DmzP1xEtAhXJ6eXVsBsfbvzWF/AK1otA
-P67wsGz52edbzchW0TVvKd3CAKBH8LFgycu7k6bYdc+0crgyxeM1NUmCx1nxbfTM
-WSC1CWOzjZXOVlqyUIHY8HJetPpJpS0GizAfa+eCHaRXOyC89dUX0cS8370Nbcqf
-rYvbd8LOosHLYqJ/x7P7YIxOkw/wn5m2OAeSfItqfNCLbR2+oiCrLLv0MXQVCiiY
-fPLoEePFb288/AkgI/1go4Lsh6/vBnTU03r/YKM1QHVxygax3+I/9cXpwNmV8HyD
-No5Lu98bAgMBAAECggEBAJdMLJUvtmH7axan7qVATKRIrV5EsbasYzQ+NFtqMQ/x
-VUkyx+1/SuDNEt+0Q1ah33rTwV9Ghp2vru+dJSQM/VyytAlKNzK/Zb6Z+klzEsEJ
-t8JfwaVgKW5imrJhlJzGdtWqpflNAKPIK5RZ2FGjbw4k0XgOb5NgVGW/fPDblFK0
-ar1Yc/FLWOYFWFfNoZhni+ZH4U2KmEjZlkIL+d4KgewrjSPJ9w6XJijxMD5eDc5+
-r1ZfaSz5RNM/5LWokqLajCuxBxohRXldgF3Y3UtuQipeok1RPmQb0UcPSz7zndHF
-BV90c3o09v0/aqp2USlzCABIHO0l5Qru9m5wtvI+L/ECgYEA+TaweFBy6vGLa1jU
-hr8vaED880KNu6bIfHfmT580cG8ZXdz/+JHup+lERQmjuEX9Jc6QO2AUVsn1bZ8P
-oxs/4kNKL3cYZ/r66cBSSu0xihXsFAvDm4rrwSI5IAzOioMxBVs65sicpaceunHP
-l+c7Ma2cEUnlwYVuRAXhy1jjVhcCgYEA3gPR6q1lYgYsm8alOUzy/Eexp8PMbpgE
-t6c5eyT4Swau9XkNxp5R/tLCMvuZxwplZauChpE7O1TTiu88vrqRVVl4fZVkIlnx
-/xzzQFuAny/Qx+FOU18gNhFCRn4aAs5k+wdOncCIonz3IhMcusruseorBw57DLil
-TNlLZtQPZZ0CgYEAtUiEHDEhNyiX63GFv7MpUCQeHPJn2X4cTvaFEZxU8AjRIgdW
-KEI3oes8nx/A+ZXn7O2S264rfWqR3rkbDeIPmY6rU1XF6jWW+hzNf/WE2NbTkU1x
-cB8hGa/EcD0ArZ97NFNFyIVb9eBYqPWLNgudcqjAY48m05w1NsQ0mNBDJucCgYAP
-JWWRxAiRmmg6rF+jPBurmFyHXHU66kYQHWlvfEMwIyGWf46wCScA4nH7Nmz0RkJK
-oFvEQG4xCwVvigiz3liB4Ru2PZXaPhajV99EebmZopJ0wGsuhuPUrHLACmRN4rTC
-52m2m2b25t2ZRoKEP8nu+1G6JoPAh2xHhN9/AWKXhQKBgQDFmIUZwFmb3+oYgEN5
-QVmiwHgy9+AX+atZ76N3oajF42w3SFTLO9yQ5dlb9nBfwzpraZn2T9fAyiP0a9iU
-A9Y5MUFWlqMfgeOK/HEprcA7/Wth8ou3iu6Ojn85Iy2VqmqA4OYUYQTsS6izus4W
-Eel2uEuwhoIseSX1F0tqMlwxVg==
------END PRIVATE KEY-----
diff --git a/components/camel-milo/src/test/resources/keystore 
b/components/camel-milo/src/test/resources/keystore
new file mode 100644
index 0000000..ef6d68f
Binary files /dev/null and b/components/camel-milo/src/test/resources/keystore 
differ
diff --git a/components/camel-milo/src/test/resources/openssl-ca.cnf 
b/components/camel-milo/src/test/resources/openssl-ca.cnf
new file mode 100644
index 0000000..bd29e0e
--- /dev/null
+++ b/components/camel-milo/src/test/resources/openssl-ca.cnf
@@ -0,0 +1,80 @@
+HOME            = .
+RANDFILE        = $ENV::HOME/.rnd
+
+####################################################################
+[ ca ]
+default_ca    = CA_default      # The default ca section
+
+[ CA_default ]
+
+default_days     = 1000         # How long to certify for
+default_crl_days = 30           # How long before next CRL
+default_md       = sha256       # Use public key default MD
+preserve         = no           # Keep passed DN ordering
+
+x509_extensions = ca_extensions # The extensions to add to the cert
+
+email_in_dn     = no            # Don't concat the email in the DN
+copy_extensions = copy          # Required to copy SANs from CSR to cert
+
+base_dir      = .
+certificate   = $base_dir/ca/cacert.pem   # The CA certifcate
+private_key   = $base_dir/ca/cakey.pem    # The CA private key
+new_certs_dir = $base_dir/cert              # Location for new certs after 
signing
+database      = $base_dir/ca/index.txt    # Database index file
+serial        = $base_dir/ca/serial.txt   # The current serial number
+
+unique_subject = no  # Set to 'no' to allow creation of
+                     # several certificates with same subject.
+
+####################################################################
+[ req ]
+default_bits       = 4096
+default_keyfile    = ca/cakey.pem
+distinguished_name = ca_distinguished_name
+x509_extensions    = ca_extensions
+string_mask        = utf8only
+
+####################################################################
+[ ca_distinguished_name ]
+countryName         = Country Name (2 letter code)
+countryName_default = RH
+
+stateOrProvinceName         = State or Province Name (full name)
+stateOrProvinceName_default = RH
+
+localityName                = Locality Name (eg, city)
+localityName_default        = RH
+
+organizationName            = Organization Name (eg, company)
+organizationName_default    = Test CA
+
+commonName         = Common Name (e.g. server FQDN or YOUR name)
+commonName_default = Test CA
+
+####################################################################
+[ ca_extensions ]
+
+subjectKeyIdentifier   = hash
+authorityKeyIdentifier = keyid:always, issuer
+basicConstraints       = critical, CA:true
+keyUsage               = keyCertSign, cRLSign
+
+####################################################################
+[ signing_policy ]
+countryName            = optional
+stateOrProvinceName    = optional
+localityName           = optional
+organizationName       = optional
+organizationalUnitName = optional
+commonName             = supplied
+emailAddress           = optional
+
+####################################################################
+[ signing_req ]
+subjectKeyIdentifier   = hash
+authorityKeyIdentifier = keyid,issuer
+basicConstraints       = CA:FALSE
+keyUsage               = digitalSignature, keyEncipherment
+
+#openssl pkcs12 -export -in servercert.pem -inkey serverkey.pem -certfile 
cacert.pem -name "test" -out keystore.p12 -passout pass:test
diff --git a/components/camel-milo/src/test/resources/openssl-server.cnf 
b/components/camel-milo/src/test/resources/openssl-server.cnf
new file mode 100644
index 0000000..dd3cd0e
--- /dev/null
+++ b/components/camel-milo/src/test/resources/openssl-server.cnf
@@ -0,0 +1,39 @@
+HOME            = .
+RANDFILE        = $ENV::HOME/.rnd
+
+####################################################################
+[ req ]
+default_bits       = 2048
+default_keyfile    = cert/serverkey.pem
+distinguished_name = server_distinguished_name
+req_extensions     = server_req_extensions
+string_mask        = utf8only
+
+####################################################################
+[ server_distinguished_name ]
+countryName         = Country Name (2 letter code)
+countryName_default = RH
+
+stateOrProvinceName         = State or Province Name (full name)
+stateOrProvinceName_default = RH
+
+localityName         = Locality Name (eg, city)
+localityName_default = RH
+
+commonName           = Common Name (e.g. server FQDN or YOUR name)
+commonName_default   = milo-test
+
+
+####################################################################
+[ server_req_extensions ]
+
+subjectKeyIdentifier = hash
+basicConstraints     = CA:FALSE
+keyUsage             = digitalSignature, keyEncipherment
+subjectAltName       = @alternate_names
+nsComment            = "OpenSSL Generated Certificate"
+
+####################################################################
+[ alternate_names ]
+
+URI.1  = http://camel.apache.org/EclipseMilo/Client
diff --git a/components/camel-milo/src/test/resources/run.sh 
b/components/camel-milo/src/test/resources/run.sh
new file mode 100755
index 0000000..a6d1620
--- /dev/null
+++ b/components/camel-milo/src/test/resources/run.sh
@@ -0,0 +1,21 @@
+# script which prepares all needed certificates and keystore
+# properties of certificates has to be confirmed
+# password to be filled in the last step has to be 'test'
+
+mkdir cert
+mkdir ca
+
+#generate ca certificate and key
+openssl req -x509 -config openssl-ca.cnf -newkey rsa:4096 -sha256 -nodes -out 
ca/cacert.pem -outform PEM
+
+#generate csr
+openssl req -config openssl-server.cnf -newkey rsa:2048 -sha256 -nodes -out 
cert/servercert.csr -outform PEM
+
+#sign
+touch ca/index.txt
+echo '01' > ca/serial.txt
+openssl ca -config openssl-ca.cnf -policy signing_policy -extensions 
signing_req -out cert/servercert.pem -infiles cert/servercert.csr
+
+#import into keystore
+openssl pkcs12 -export -in cert/servercert.pem -inkey cert/serverkey.pem 
-certfile ca/cacert.pem -name "test" -out keystore.p12 -passout pass:test
+keytool -importkeystore -srckeystore keystore.p12 -srcstoretype pkcs12 
-destkeystore keystore -deststoretype JKS -storepass testtest
diff --git a/docs/components/modules/ROOT/pages/milo-client-component.adoc 
b/docs/components/modules/ROOT/pages/milo-client-component.adoc
index 634f914..3517f7e 100644
--- a/docs/components/modules/ROOT/pages/milo-client-component.adoc
+++ b/docs/components/modules/ROOT/pages/milo-client-component.adoc
@@ -11,6 +11,9 @@
 The Milo Client component provides access to OPC UA servers using the
 http://eclipse.org/milo[Eclipse Milo™] implementation.
 
+*Important*: To successfully use certificates for secured communication,
+JCE Jurisdiction Policy File Default has to be *Unlimited* (which is by 
default since jdk 9+).
+
 Maven users will need to add the following dependency to their `pom.xml`
 for this component:
 
@@ -53,13 +56,13 @@ The URI syntax of the endpoint is:
 
 [source]
 ------------------------
-milo-client:tcp://[user:password@]host:port/path/to/service?node=RAW(nsu=urn:foo:bar;s=item-1)
+milo-client:opc.tcp://[user:password@]host:port/path/to/service?node=RAW(nsu=urn:foo:bar;s=item-1)
 ------------------------
 
 If the server does not use a path, then it is possible to simply omit it:
 
 ------------------------
-milo-client:tcp://[user:password@]host:port?node=RAW(nsu=urn:foo:bar;s=item-1)
+milo-client:opc.tcp://[user:password@]host:port?node=RAW(nsu=urn:foo:bar;s=item-1)
 ------------------------
 
 If no user credentials are provided the client will switch to anonymous mode.
@@ -240,7 +243,7 @@ As the values generated by the syntax cannot be 
transparently encoded into a URI
 However Camel allows to wrap the actual value inside `RAW(…)`, which makes 
escaping unnecessary. For example:
 
 ------------------------
-milo-client:tcp://user:password@localhost:12345?node=RAW(nsu=http://foo.bar;s=foo/bar)
+milo-client:opc.tcp://user:password@localhost:12345?node=RAW(nsu=http://foo.bar;s=foo/bar)
 ------------------------
 
 === Method ID
diff --git a/docs/components/modules/ROOT/pages/milo-server-component.adoc 
b/docs/components/modules/ROOT/pages/milo-server-component.adoc
index 861c31c..072675e 100644
--- a/docs/components/modules/ROOT/pages/milo-server-component.adoc
+++ b/docs/components/modules/ROOT/pages/milo-server-component.adoc
@@ -31,7 +31,7 @@ Value write requests from OPC UA Client will trigger messages 
which are sent int
 
 
 // component options: START
-The OPC UA Server component supports 22 options, which are listed below.
+The OPC UA Server component supports 20 options, which are listed below.
 
 
 
@@ -40,12 +40,10 @@ The OPC UA Server component supports 22 options, which are 
listed below.
 | Name | Description | Default | Type
 | *namespaceUri* (common) | The URI of the namespace, defaults to 
urn:org:apache:camel |  | String
 | *applicationName* (common) | The application name |  | String
+| *path* (common) | The path to be appended to the end of the endpoint url. 
(doesn't need to start with '/') |  | String
 | *applicationUri* (common) | The application URI |  | String
 | *productUri* (common) | The product URI |  | String
 | *bindPort* (common) | The TCP port the server binds to |  | int
-| *strictEndpointUrlsEnabled* (common) | Set whether strict endpoint URLs are 
enforced | false | boolean
-| *serverName* (common) | Server name |  | String
-| *hostname* (common) | Server hostname |  | String
 | *securityPolicies* (common) | Security policies |  | Set
 | *securityPoliciesById* (common) | Security policies by URI or name |  | 
Collection
 | *userAuthenticationCredentials* (common) | Set user password combinations in 
the form of user1:pwd1,user2:pwd2 Usernames and passwords will be URL decoded | 
 | String
diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide.adoc
index 33abb55..4f75f8b 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide.adoc
@@ -41,6 +41,27 @@ The `camel-irc` component has changed its endpoint syntax 
and removed option #ro
 irc:nick@host[:port]?[options]
 ----
 
+=== camel-milo
+
+The `camel-milo` client component has changed its endpoint syntax from 
`milo-client:tcp` to `milo-client:opc.tcp`.
+For example before
+
+[source,text]
+----
+milo-client:tcp://foo:bar@localhost:1234
+----
+
+Should be changed to
+----
+milo-client:opc.tcp://foo:bar@localhost:1234
+----
+
+The `camel-milo` server component requires Java 9 at runtime. 
+Property `strictEndpointUrlsEnabled` is no longer supported.
+Properties`hostName` and `serverName` are replaced by `path`.
+To successfully use certificates for secured communication, JCE Jurisdiction 
Policy File Default
+has to be *Unlimited* (which is by default since Java 9+).
+
 === camel-nats
 
 The `camel-nats` component has changed its endpoint syntax from `nats:servers` 
to `nats:topic`.
diff --git a/parent/pom.xml b/parent/pom.xml
index c86d053..1cb3e58 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -164,6 +164,8 @@
         <derby-version>10.14.2.0</derby-version>
         <digitalocean-api-client-version>2.17</digitalocean-api-client-version>
         
<digitalocean-api-client-bundle-version>2.17_1</digitalocean-api-client-bundle-version>
+        <digitalpetri-fsm-client>0.2</digitalpetri-fsm-client>
+        <digitalpetri-netty-client>0.3</digitalpetri-netty-client>
         <directory-watcher-version>0.9.9</directory-watcher-version>
         <disruptor-version>3.4.2</disruptor-version>
         <dnsjava-version>2.1.9</dnsjava-version>
@@ -458,7 +460,8 @@
         <microprofile-config-version>1.3</microprofile-config-version>
         <microprofile-metrics-version>2.2.1</microprofile-metrics-version>
         <microprofile-health-version>2.1</microprofile-health-version>
-        <milo-version>0.2.5</milo-version>
+        <milo-version>0.3.7</milo-version>
+        <milo-guava-version>26.0-jre</milo-guava-version>
         <mimepull-version>1.9.12</mimepull-version>
         <mina-version>2.1.3</mina-version>
         <minidns-version>0.3.4</minidns-version>
diff --git a/platforms/karaf/features/src/main/resources/features.xml 
b/platforms/karaf/features/src/main/resources/features.xml
index a183476..ae3b5e9 100644
--- a/platforms/karaf/features/src/main/resources/features.xml
+++ b/platforms/karaf/features/src/main/resources/features.xml
@@ -1750,19 +1750,26 @@
     <bundle dependency='true'>mvn:org.jooq/jool/${jool-version}</bundle>
     <bundle 
dependency='true'>mvn:com.google.code.findbugs/jsr305/${google-findbugs-jsr305-version}</bundle>
     <bundle 
dependency='true'>mvn:com.google.code.findbugs/annotations/${google-findbugs-annotations2-version}</bundle>
-    <bundle 
dependency='true'>mvn:io.netty/netty-common/${netty40-version}</bundle>
-    <bundle 
dependency='true'>mvn:io.netty/netty-buffer/${netty40-version}</bundle>
-    <bundle 
dependency='true'>mvn:io.netty/netty-handler/${netty40-version}</bundle>
-    <bundle 
dependency='true'>mvn:io.netty/netty-transport/${netty40-version}</bundle>
-    <bundle 
dependency='true'>mvn:io.netty/netty-codec/${netty40-version}</bundle>
-    <bundle 
dependency='true'>mvn:com.google.guava/guava/${google-guava-version}</bundle>
+    <bundle 
dependency='true'>mvn:org.bouncycastle/bcprov-jdk15on/${bouncycastle-version}</bundle>
+    <bundle 
dependency='true'>mvn:org.bouncycastle/bcpkix-jdk15on/${bouncycastle-version}</bundle>
     
+    <bundle 
dependency='true'>mvn:org.bouncycastle/bcmail-jdk15on/${bouncycastle-version}</bundle>
    
+    <bundle 
dependency='true'>mvn:io.netty/netty-common/${netty-version}</bundle>
+    <bundle 
dependency='true'>mvn:io.netty/netty-buffer/${netty-version}</bundle>
+    <bundle 
dependency='true'>mvn:io.netty/netty-resolver/${netty-version}</bundle>
+    <bundle 
dependency='true'>mvn:io.netty/netty-transport/${netty-version}</bundle>    
+    <bundle 
dependency='true'>mvn:io.netty/netty-handler/${netty-version}</bundle>    
+    <bundle 
dependency='true'>mvn:io.netty/netty-codec/${netty-version}</bundle>
+    <bundle 
dependency='true'>mvn:io.netty/netty-codec-http/${netty-version}</bundle>
+    <bundle 
dependency='true'>wrap:mvn:com.digitalpetri.fsm/strict-machine/${digitalpetri-fsm-client}</bundle>
+    <bundle 
dependency='true'>wrap:mvn:com.digitalpetri.netty/netty-channel-fsm/${digitalpetri-netty-client}</bundle>
+    <bundle 
dependency='true'>mvn:com.google.guava/guava/${milo-guava-version}</bundle>
     <bundle 
dependency='true'>mvn:org.eclipse.milo/stack-core/${milo-version}</bundle>
     <bundle 
dependency='true'>mvn:org.eclipse.milo/stack-server/${milo-version}</bundle>
     <bundle 
dependency='true'>mvn:org.eclipse.milo/stack-client/${milo-version}</bundle>
     <bundle 
dependency='true'>mvn:org.eclipse.milo/sdk-core/${milo-version}</bundle>
-    <bundle 
dependency='true'>mvn:org.eclipse.milo/sdk-server/${milo-version}</bundle>
-    <bundle 
dependency='true'>mvn:org.eclipse.milo/sdk-client/${milo-version}</bundle>
+    <bundle 
dependency='true'>mvn:org.eclipse.milo/sdk-server/${milo-version}</bundle>    
     <bundle 
dependency='true'>mvn:org.eclipse.milo/bsd-parser-core/${milo-version}</bundle>
+    <bundle 
dependency='true'>mvn:org.eclipse.milo/sdk-client/${milo-version}</bundle>
     <bundle 
dependency='true'>mvn:org.eclipse.milo/bsd-parser-gson/${milo-version}</bundle>
     <bundle>mvn:org.apache.camel/camel-milo/${project.version}</bundle>
   </feature>
diff --git 
a/tests/camel-itest-karaf/src/test/java/org/apache/camel/itest/karaf/CamelMiloTest.java
 
b/tests/camel-itest-karaf/src/test/java/org/apache/camel/itest/karaf/CamelMiloTest.java
index 60d881c..a66342c 100644
--- 
a/tests/camel-itest-karaf/src/test/java/org/apache/camel/itest/karaf/CamelMiloTest.java
+++ 
b/tests/camel-itest-karaf/src/test/java/org/apache/camel/itest/karaf/CamelMiloTest.java
@@ -16,12 +16,22 @@
  */
 package org.apache.camel.itest.karaf;
 
+import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.PaxExam;
 
 @RunWith(PaxExam.class)
 public class CamelMiloTest extends BaseKarafTest {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        Assume.assumeTrue("Requires java 9+", isJavaVersionSatisfied(9));
+    }
+
     @Test
     public void testClient() throws Exception {
         testComponent("milo", "milo-client");
@@ -30,4 +40,22 @@ public class CamelMiloTest extends BaseKarafTest {
     public void testServer() throws Exception {
         testComponent("milo", "milo-server");
     }
+
+    /**
+     * Return true, if java version (defined by method 
getRequiredJavaVersion()) is satisfied.
+     * Works for java versions 9+
+     */
+    boolean isJavaVersionSatisfied(int requiredVersion) {
+        String version = System.getProperty("java.version");
+        if (!version.startsWith("1.")) {
+            int dot = version.indexOf(".");
+            if (dot != -1) {
+                version = version.substring(0, dot);
+            }
+            if (Integer.parseInt(version) >= requiredVersion) {
+                return true;
+            }
+        }
+        return false;
+    }
 }

Reply via email to