diff --git a/LICENSE b/LICENSE
index 564d7622ee8..a168ddb2caf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -221,8 +221,3 @@ build-utils/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegen
is licensed under the Category A: "UNLICENSE" which is available here:
UNLICENSE file in the licenses directory.
-The files:
-plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java
-plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/KeyStoreLoader.java
-plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java
-are derivative works of the Eclipse Milo project and are licensed under the EPL 2.0 license.
diff --git a/NOTICE b/NOTICE
index 91529ec3249..fae53f7315b 100644
--- a/NOTICE
+++ b/NOTICE
@@ -11,5 +11,3 @@ The Netty project (https://netty.io/).
----------------------------------------------
-This product includes software developed at
-The Eclipse Milo project (https://projects.eclipse.org/projects/iot.milo/)
diff --git a/plc4j/integrations/apache-kafka/pom.xml b/plc4j/integrations/apache-kafka/pom.xml
index 7dc6405ab50..d836c4979fc 100644
--- a/plc4j/integrations/apache-kafka/pom.xml
+++ b/plc4j/integrations/apache-kafka/pom.xml
@@ -266,6 +266,11 @@
runtime
+
+ org.apache.commons
+ commons-lang3
+
+
+
org.bouncycastle
bcmail-jdk15on
@@ -208,7 +203,6 @@
org.slf4j
slf4j-simple
-
commons-io
commons-io
@@ -219,6 +213,11 @@
vavr
+
+ org.apache.commons
+ commons-lang3
+
+
@@ -278,7 +277,6 @@
org.slf4j:slf4j-simple
- org.bouncycastle:bcpkix-jdk15on
org.bouncycastle:bcmail-jdk15on
org.apache.plc4x:plc4j-driver-ab-eth
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/KeyStoreLoader.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/KeyStoreLoader.java
deleted file mode 100644
index 4e4d4a7688d..00000000000
--- a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/KeyStoreLoader.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2019 the Eclipse Milo Authors
- *
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-
-package org.apache.plc4x.java.opcuaserver;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.UUID;
-import java.util.regex.Pattern;
-
-import com.google.common.collect.Sets;
-import org.apache.plc4x.java.opcuaserver.configuration.Configuration;
-import org.apache.plc4x.java.opcuaserver.configuration.PasswordConfiguration;
-import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
-import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
-import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class KeyStoreLoader {
-
- private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
- "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
-
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- private final String certificateFileName = "plc4x-opcuaserver.pfx";
-
- private X509Certificate[] serverCertificateChain;
- private X509Certificate serverCertificate;
- private KeyPair serverKeyPair;
-
- private Configuration config;
- private PasswordConfiguration passwordConfig;
-
- public KeyStoreLoader(Configuration config, PasswordConfiguration passwordConfig, Boolean interactive) {
- this.config = config;
- this.passwordConfig = passwordConfig;
-
- File securityTempDir = new File(config.getDir(), "security");
- if (!securityTempDir.exists() && !securityTempDir.mkdirs()) {
- logger.error("Unable to create directory please confirm folder permissions on " + securityTempDir.toString());
- System.exit(1);
- }
- logger.info("security dir: {}", securityTempDir.getAbsolutePath());
-
- try {
- load(securityTempDir, interactive);
- } catch (Exception e) {
- logger.error("Error loading the key store " + e);
- System.exit(1);
- }
- }
-
- public KeyStoreLoader load(File baseDir, boolean interactive) throws Exception {
- KeyStore keyStore = KeyStore.getInstance("PKCS12");
-
- File serverKeyStore = baseDir.toPath().resolve(certificateFileName).toFile();
-
- if (!serverKeyStore.exists()) {
- if (!interactive) {
- logger.info("Please re-run with the -i switch to setup the security certificate key store");
- System.exit(1);
- }
-
- logger.info("Creating keystore at {}", serverKeyStore);
- keyStore.load(null, passwordConfig.getSecurityPassword().toCharArray());
-
- logger.info("Creating self signed certiciate {}", serverKeyStore);
- KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
-
- String applicationUri = "urn:eclipse:milo:plc4x:server" + UUID.randomUUID();
-
- SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
- .setCommonName(applicationUri)
- .setOrganization("org.apache")
- .setOrganizationalUnit("plc4x")
- .setLocalityName("Wakefield")
- .setStateName("MA")
- .setCountryCode("US")
- .setApplicationUri(applicationUri);
-
- // Get as many hostnames and IP addresses as we can listed in the certificate.
- Set hostnames = Sets.union(
- Sets.newHashSet(HostnameUtil.getHostname()),
- HostnameUtil.getHostnames("0.0.0.0", false)
- );
-
- logger.info("using IP address/hostnames {}", hostnames.toString());
-
- for (String hostname : hostnames) {
- if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
- builder.addIpAddress(hostname);
- } else {
- builder.addDnsName(hostname);
- }
- }
-
- X509Certificate certificate = builder.build();
-
- keyStore.setKeyEntry(config.getName(), keyPair.getPrivate(), passwordConfig.getSecurityPassword().toCharArray(), new X509Certificate[]{certificate});
- keyStore.store(new FileOutputStream(serverKeyStore), passwordConfig.getSecurityPassword().toCharArray());
-
- logger.info("Self signed certificate created. Replace {} and update config file passwords if not using a signed certificate.", serverKeyStore);
-
- } else {
- logger.info("Loading KeyStore at {}", serverKeyStore);
- keyStore.load(new FileInputStream(serverKeyStore), passwordConfig.getSecurityPassword().toCharArray());
- }
-
- Key serverPrivateKey = keyStore.getKey(config.getName(), passwordConfig.getSecurityPassword().toCharArray());
- if (serverPrivateKey instanceof PrivateKey) {
- serverCertificate = (X509Certificate) keyStore.getCertificate(config.getName());
-
- serverCertificateChain = Arrays.stream(keyStore.getCertificateChain(config.getName()))
- .map(X509Certificate.class::cast)
- .toArray(X509Certificate[]::new);
-
- PublicKey serverPublicKey = serverCertificate.getPublicKey();
- serverKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
- }
-
- return this;
- }
-
- X509Certificate getServerCertificate() {
- return serverCertificate;
- }
-
- public X509Certificate[] getServerCertificateChain() {
- return serverCertificateChain;
- }
-
- KeyPair getServerKeyPair() {
- return serverKeyPair;
- }
-
-}
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java
index e8c66d1f241..e3cc2b08e89 100644
--- a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java
@@ -1,27 +1,33 @@
/*
- * Copyright (c) 2019 the Eclipse Milo Authors
+ * 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
*
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * SPDX-License-Identifier: EPL-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.plc4x.java.opcuaserver;
-import java.io.File;
-import java.security.KeyPair;
-import java.security.Security;
+import java.io.*;
+import java.net.InetAddress;
+import java.security.*;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.io.IOException;
-import java.io.Console;
import java.nio.file.Path;
import java.nio.file.FileSystems;
@@ -29,13 +35,14 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import org.apache.plc4x.java.opcuaserver.context.CertificateKeyPair;
+import org.apache.plc4x.java.opcuaserver.context.CertificateGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
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.identity.CompositeValidator;
import org.eclipse.milo.opcua.sdk.server.identity.UsernameIdentityValidator;
import org.eclipse.milo.opcua.sdk.server.identity.X509IdentityValidator;
-import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
import org.eclipse.milo.opcua.stack.core.StatusCodes;
import org.eclipse.milo.opcua.stack.core.UaRuntimeException;
import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateManager;
@@ -47,15 +54,11 @@
import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo;
import org.eclipse.milo.opcua.stack.core.util.CertificateUtil;
-import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
-import org.eclipse.milo.opcua.stack.core.util.SelfSignedHttpsCertificateBuilder;
import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;
import org.eclipse.milo.opcua.stack.server.security.DefaultServerCertificateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static com.google.common.collect.Lists.newArrayList;
-import org.apache.commons.lang3.RandomStringUtils;
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;
@@ -194,40 +197,32 @@ private void readCommandLineArgs(String[] args) {
}
public static void main(String[] args) throws Exception {
- OPCUAServer server = new OPCUAServer(args);
- server.startup().get();
- final CompletableFuture future = new CompletableFuture<>();
- Runtime.getRuntime().addShutdownHook(new Thread(() -> future.complete(null)));
+ OPCUAServer serverInit = new OPCUAServer(args);
+ serverInit.getServer().startup().get();
+ CompletableFuture future = new CompletableFuture<>();
future.get();
}
private final OpcUaServer server;
private final Plc4xNamespace plc4xNamespace;
+ private final String certificateFileName = "plc4x-opcuaserver.pfx";
public OPCUAServer(String[] args) throws Exception {
readCommandLineArgs(args);
- KeyStoreLoader loader = new KeyStoreLoader(config, passwordConfig, cmd.hasOption("interactive"));
-
- DefaultCertificateManager certificateManager = new DefaultCertificateManager(
- loader.getServerKeyPair(),
- loader.getServerCertificateChain()
- );
+ File securityTempDir = new File(config.getDir(), "security");
+ if (!securityTempDir.exists() && !securityTempDir.mkdirs()) {
+ logger.error("Unable to create directory please confirm folder permissions on " + securityTempDir.toString());
+ System.exit(1);
+ }
+ logger.info("Security Directory is: {}", securityTempDir.getAbsolutePath()); //
File pkiDir = FileSystems.getDefault().getPath(config.getDir()).resolve("pki").toFile();
DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir);
- logger.info("pki dir: {}", pkiDir.getAbsolutePath());
-
- DefaultServerCertificateValidator certificateValidator =
- new DefaultServerCertificateValidator(trustListManager);
-
- KeyPair httpsKeyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
+ logger.info("Certificate directory is: {}, Please move certificates from the reject dir to the trusted directory to allow encrypted access", pkiDir.getAbsolutePath());
- SelfSignedHttpsCertificateBuilder httpsCertificateBuilder = new SelfSignedHttpsCertificateBuilder(httpsKeyPair);
- httpsCertificateBuilder.setCommonName(HostnameUtil.getHostname());
- HostnameUtil.getHostnames("0.0.0.0").forEach(httpsCertificateBuilder::addDnsName);
- X509Certificate httpsCertificate = httpsCertificateBuilder.build();
+ DefaultServerCertificateValidator certificateValidator = new DefaultServerCertificateValidator(trustListManager);
UsernameIdentityValidator identityValidator = new UsernameIdentityValidator(
true,
@@ -240,22 +235,113 @@ public OPCUAServer(String[] args) throws Exception {
}
);
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+
+ File serverKeyStore = securityTempDir.toPath().resolve(certificateFileName).toFile();
+
X509IdentityValidator x509IdentityValidator = new X509IdentityValidator(c -> true);
- // If you need to use multiple certificates you'll have to be smarter than this.
- X509Certificate certificate = certificateManager.getCertificates()
- .stream()
- .findFirst()
- .orElseThrow(() -> new UaRuntimeException(StatusCodes.Bad_ConfigurationError, "no certificate found"));
+ CertificateKeyPair certificate = null;
+ if (!serverKeyStore.exists()) {
+ if (!cmd.hasOption("interactive")) {
+ logger.info("Please re-run with the -i switch to setup the security certificate key store");
+ System.exit(1);
+ }
+ certificate = CertificateGenerator.generateCertificate();
+ logger.info("Creating new KeyStore at {}", serverKeyStore);
+ keyStore.load(null, passwordConfig.getSecurityPassword().toCharArray());
+ keyStore.setKeyEntry("plc4x-certificate-alias", certificate.getKeyPair().getPrivate(), passwordConfig.getSecurityPassword().toCharArray(), new X509Certificate[] { certificate.getCertificate() });
+ keyStore.store(new FileOutputStream(serverKeyStore), passwordConfig.getSecurityPassword().toCharArray());
+ } else {
+ logger.info("Loading KeyStore at {}", serverKeyStore);
+ keyStore.load(new FileInputStream(serverKeyStore), passwordConfig.getSecurityPassword().toCharArray());
+ String alias = keyStore.aliases().nextElement();
+ KeyPair kp = new KeyPair(keyStore.getCertificate(alias).getPublicKey(),
+ (PrivateKey) keyStore.getKey(alias, passwordConfig.getSecurityPassword().toCharArray()));
+ certificate = new CertificateKeyPair(kp,(X509Certificate) keyStore.getCertificate(alias));
+ }
- // The configured application URI must match the one in the certificate(s)
String applicationUri = CertificateUtil
- .getSanUri(certificate)
+ .getSanUri(certificate.getCertificate())
.orElseThrow(() -> new UaRuntimeException(
StatusCodes.Bad_ConfigurationError,
"certificate is missing the application URI"));
- Set endpointConfigurations = createEndpointConfigurations(certificate);
+ Set endpointConfigurations = new LinkedHashSet<>();
+
+ String hostname = InetAddress.getLocalHost().getHostName();
+
+ EndpointConfiguration.Builder builder = EndpointConfiguration.newBuilder()
+ .setBindAddress("0.0.0.0")
+ .setHostname(hostname)
+ .setPath("/plc4x")
+ .setCertificate(certificate.getCertificate())
+ .setBindPort(config.getTcpPort())
+ .setSecurityMode(MessageSecurityMode.None)
+ .addTokenPolicies(
+ USER_TOKEN_POLICY_ANONYMOUS,
+ USER_TOKEN_POLICY_USERNAME,
+ USER_TOKEN_POLICY_X509);
+
+ endpointConfigurations.add(
+ builder.copy()
+ .setSecurityPolicy(SecurityPolicy.Basic256Sha256)
+ .setSecurityMode(MessageSecurityMode.SignAndEncrypt)
+ .build()
+ );
+
+ endpointConfigurations.add(
+ builder.copy()
+ .setHostname("127.0.0.1")
+ .setSecurityPolicy(SecurityPolicy.Basic256Sha256)
+ .setSecurityMode(MessageSecurityMode.SignAndEncrypt)
+ .build()
+ );
+
+ EndpointConfiguration.Builder discoveryBuilder = builder.copy()
+ .setPath("/discovery")
+ .setSecurityPolicy(SecurityPolicy.None)
+ .setSecurityMode(MessageSecurityMode.None);
+
+ endpointConfigurations.add(discoveryBuilder.build());
+
+ EndpointConfiguration.Builder discoveryLocalBuilder = builder.copy()
+ .setPath("/discovery")
+ .setHostname("127.0.0.1")
+ .setSecurityPolicy(SecurityPolicy.None)
+ .setSecurityMode(MessageSecurityMode.None);
+
+ endpointConfigurations.add(discoveryLocalBuilder.build());
+
+ EndpointConfiguration.Builder discoveryLocalPlc4xBuilder = builder.copy()
+ .setPath("/plc4x/discovery")
+ .setHostname("127.0.0.1")
+ .setSecurityPolicy(SecurityPolicy.None)
+ .setSecurityMode(MessageSecurityMode.None);
+
+ endpointConfigurations.add(discoveryLocalPlc4xBuilder.build());
+
+ if (!config.getDisableInsecureEndpoint()) {
+ EndpointConfiguration.Builder noSecurityBuilder = builder.copy()
+ .setSecurityPolicy(SecurityPolicy.None)
+ .setTransportProfile(TransportProfile.TCP_UASC_UABINARY);
+ endpointConfigurations.add(noSecurityBuilder.build());
+ }
+
+ //Always add an unsecured endpoint to localhost, this is a work around for Milo throwing an exception if it isn't here.
+ EndpointConfiguration.Builder noSecurityBuilder = builder.copy()
+ .setSecurityPolicy(SecurityPolicy.None)
+ .setHostname("127.0.0.1")
+ .setTransportProfile(TransportProfile.TCP_UASC_UABINARY)
+ .setSecurityMode(MessageSecurityMode.None);
+ endpointConfigurations.add(noSecurityBuilder.build());
+
+ DefaultCertificateManager certificateManager = new DefaultCertificateManager(
+ certificate.getKeyPair(),
+ Arrays.stream(keyStore.getCertificateChain(keyStore.getCertificateAlias(certificate.getCertificate())))// Added so that existing certificates are loaded on startup
+ .map(X509Certificate.class::cast)
+ .toArray(X509Certificate[]::new)
+ );
OpcUaServerConfig serverConfig = OpcUaServerConfig.builder()
.setApplicationUri(applicationUri)
@@ -271,8 +357,6 @@ public OPCUAServer(String[] args) throws Exception {
.setCertificateManager(certificateManager)
.setTrustListManager(trustListManager)
.setCertificateValidator(certificateValidator)
- .setHttpsKeyPair(httpsKeyPair)
- .setHttpsCertificate(httpsCertificate)
.setIdentityValidator(new CompositeValidator(identityValidator, x509IdentityValidator))
.setProductUri("urn:eclipse:milo:plc4x:server")
.build();
@@ -283,90 +367,6 @@ public OPCUAServer(String[] args) throws Exception {
plc4xNamespace.startup();
}
- private Set createEndpointConfigurations(X509Certificate certificate) {
- Set endpointConfigurations = new LinkedHashSet<>();
-
- List bindAddresses = newArrayList();
- bindAddresses.add("0.0.0.0");
-
- List localAddresses = new ArrayList<>(bindAddresses);
-
- Set hostnames = new LinkedHashSet<>();
- hostnames.add(HostnameUtil.getHostname());
- hostnames.addAll(HostnameUtil.getHostnames("0.0.0.0"));
-
- for (String bindAddress : bindAddresses) {
- for (String hostname : hostnames) {
- EndpointConfiguration.Builder builder = EndpointConfiguration.newBuilder()
- .setBindAddress(bindAddress)
- .setHostname(hostname)
- .setPath("/plc4x")
- .setCertificate(certificate)
- .addTokenPolicies(
- USER_TOKEN_POLICY_ANONYMOUS,
- USER_TOKEN_POLICY_USERNAME,
- USER_TOKEN_POLICY_X509);
-
-
- if (!config.getDisableInsecureEndpoint()) {
- EndpointConfiguration.Builder noSecurityBuilder = builder.copy()
- .setSecurityPolicy(SecurityPolicy.None)
- .setSecurityMode(MessageSecurityMode.None);
- endpointConfigurations.add(buildTcpEndpoint(noSecurityBuilder));
- endpointConfigurations.add(buildHttpsEndpoint(noSecurityBuilder));
- } else {
- //Always add an unsecured endpoint to localhost, this is a work around for Milo throughing an exception if it isn't here.
- if (hostname.equals("127.0.0.1")) {
- EndpointConfiguration.Builder noSecurityBuilder = builder.copy()
- .setSecurityPolicy(SecurityPolicy.None)
- .setSecurityMode(MessageSecurityMode.None);
- endpointConfigurations.add(buildTcpEndpoint(noSecurityBuilder));
- endpointConfigurations.add(buildHttpsEndpoint(noSecurityBuilder));
- }
- }
-
- // TCP Basic256Sha256 / SignAndEncrypt
- endpointConfigurations.add(buildTcpEndpoint(
- builder.copy()
- .setSecurityPolicy(SecurityPolicy.Basic256Sha256)
- .setSecurityMode(MessageSecurityMode.SignAndEncrypt))
- );
-
- // HTTPS Basic256Sha256 / Sign (SignAndEncrypt not allowed for HTTPS)
- endpointConfigurations.add(buildHttpsEndpoint(
- builder.copy()
- .setSecurityPolicy(SecurityPolicy.Basic256Sha256)
- .setSecurityMode(MessageSecurityMode.Sign))
- );
-
- EndpointConfiguration.Builder discoveryBuilder = builder.copy()
- .setPath("/discovery")
- .setSecurityPolicy(SecurityPolicy.None)
- .setSecurityMode(MessageSecurityMode.None);
-
-
- endpointConfigurations.add(buildTcpEndpoint(discoveryBuilder));
- endpointConfigurations.add(buildHttpsEndpoint(discoveryBuilder));
- }
- }
-
- return endpointConfigurations;
- }
-
- private EndpointConfiguration buildTcpEndpoint(EndpointConfiguration.Builder base) {
- return base.copy()
- .setTransportProfile(TransportProfile.TCP_UASC_UABINARY)
- .setBindPort(config.getTcpPort())
- .build();
- }
-
- private EndpointConfiguration buildHttpsEndpoint(EndpointConfiguration.Builder base) {
- return base.copy()
- .setTransportProfile(TransportProfile.HTTPS_UABINARY)
- .setBindPort(config.getHttpPort())
- .build();
- }
-
public OpcUaServer getServer() {
return server;
}
@@ -377,7 +377,6 @@ public CompletableFuture startup() {
public CompletableFuture shutdown() {
plc4xNamespace.shutdown();
-
return server.shutdown();
}
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xCommunication.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xCommunication.java
index 9e32867f49e..6740d45e3ee 100644
--- a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xCommunication.java
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xCommunication.java
@@ -19,60 +19,19 @@
package org.apache.plc4x.java.opcuaserver.backend;
-
import java.lang.reflect.Array;
-import java.util.List;
import java.util.Arrays;
-import java.util.Random;
-import java.util.UUID;
-
-import org.eclipse.milo.opcua.sdk.core.AccessLevel;
-import org.eclipse.milo.opcua.sdk.core.Reference;
-import org.eclipse.milo.opcua.sdk.core.ValueRank;
-import org.eclipse.milo.opcua.sdk.core.ValueRanks;
-import org.eclipse.milo.opcua.sdk.server.Lifecycle;
-import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
+
+import org.eclipse.milo.opcua.sdk.server.AbstractLifecycle;
import org.eclipse.milo.opcua.sdk.server.api.DataItem;
-import org.eclipse.milo.opcua.sdk.server.api.DataTypeDictionaryManager;
-import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle;
-import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.BaseEventTypeNode;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.ServerTypeNode;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.variables.AnalogItemTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.filters.AttributeFilterContext;
-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.nodes.UaNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectTypeNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.factories.NodeFactory;
-import org.eclipse.milo.opcua.sdk.server.nodes.filters.AttributeFilters;
-import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
-import org.eclipse.milo.opcua.stack.core.AttributeId;
-import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.Identifiers;
-import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
-import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
-import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
-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.XmlElement;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.StructureType;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumDefinition;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumDescription;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumField;
-import org.eclipse.milo.opcua.stack.core.types.structured.Range;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureDefinition;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureDescription;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureField;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.ULong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -81,15 +40,14 @@
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
-import org.apache.plc4x.java.api.messages.PlcWriteResponse;
+
import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.api.value.PlcValue;
+
import org.apache.plc4x.java.utils.connectionpool.*;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+
import org.apache.plc4x.java.api.model.PlcField;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
@@ -98,26 +56,43 @@
import java.math.BigInteger;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ulong;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;
-public class Plc4xCommunication {
+
+public class Plc4xCommunication extends AbstractLifecycle {
private PlcDriverManager driverManager;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Integer DEFAULT_TIMEOUT = 1000000;
private final Integer DEFAULT_RETRY_BACKOFF = 5000;
+ private final DataValue BAD_RESPONSE = new DataValue(new Variant(null), StatusCode.BAD);
private Map failedConnectionList = new HashMap<>();
Map monitoredList = new HashMap<>();
public Plc4xCommunication () {
+
+ }
+
+ @Override
+ protected void onStartup() {
driverManager = new PooledPlcDriverManager();
}
+ @Override
+ protected void onShutdown() {
+ //Do Nothing
+ }
+
+ public PlcDriverManager getDriverManager() {
+ return driverManager;
+ }
+
+ public void setDriverManager(PlcDriverManager driverManager) {
+ this.driverManager = driverManager;
+ }
+
public PlcField getField(String tag, String connectionString) throws PlcConnectionException {
return driverManager.getDriver(connectionString).prepareField(tag);
}
@@ -196,92 +171,109 @@ public static NodeId getNodeId(String plcValue) {
public DataValue getValue(AttributeFilterContext.GetAttributeContext ctx, String tag, String connectionString) {
PlcConnection connection = null;
- DataValue resp = new DataValue(new Variant(null), StatusCode.BAD);
-
- //Check if we just polled the connection and it failed. Wait for the backoff counter to expire before we try again.
- if (failedConnectionList.containsKey(connectionString)) {
- if (System.currentTimeMillis() > failedConnectionList.get(connectionString) + DEFAULT_RETRY_BACKOFF) {
- failedConnectionList.remove(connectionString);
- } else {
- logger.debug("Waiting for back off timer - " + ((failedConnectionList.get(connectionString) + DEFAULT_RETRY_BACKOFF) - System.currentTimeMillis()) + " ms left");
- return resp;
- }
- }
-
- //Try to connect to PLC
try {
- connection = driverManager.getConnection(connectionString);
- logger.debug(connectionString + " Connected");
- } catch (PlcConnectionException e) {
- logger.error("Failed to connect to device, error raised - " + e);
- failedConnectionList.put(connectionString, System.currentTimeMillis());
- return resp;
- }
- if (!connection.getMetadata().canRead()) {
- logger.error("This connection doesn't support reading.");
+ //Check if we just polled the connection and it failed. Wait for the backoff counter to expire before we try again.
+ if (failedConnectionList.containsKey(connectionString)) {
+ if (System.currentTimeMillis() > failedConnectionList.get(connectionString) + DEFAULT_RETRY_BACKOFF) {
+ failedConnectionList.remove(connectionString);
+ } else {
+ logger.debug("Waiting for back off timer - " + ((failedConnectionList.get(connectionString) + DEFAULT_RETRY_BACKOFF) - System.currentTimeMillis()) + " ms left");
+ return BAD_RESPONSE;
+ }
+ }
+
+ //Try to connect to PLC
try {
- connection.close();
- } catch (Exception exception) {
- logger.warn("Closing connection failed with error - " + exception);
+ connection = driverManager.getConnection(connectionString);
+ logger.debug(connectionString + " Connected");
+ } catch (PlcConnectionException e) {
+ logger.error("Failed to connect to device, error raised - " + e);
+ failedConnectionList.put(connectionString, System.currentTimeMillis());
+ return BAD_RESPONSE;
}
- return resp;
- }
- long timeout = DEFAULT_TIMEOUT;
- if (monitoredList.containsKey(ctx.getNode().getNodeId())) {
- timeout = (long) monitoredList.get(ctx.getNode().getNodeId()).getSamplingInterval()*1000;
- }
+ if (!connection.getMetadata().canRead()) {
+ logger.error("This connection doesn't support reading.");
+ try {
+ connection.close();
+ } catch (Exception exception) {
+ logger.warn("Closing connection failed with error - " + exception);
+ }
+ return BAD_RESPONSE;
+ }
- // Create a new read request:
- // - Give the single item requested an alias name
- PlcReadRequest.Builder builder = connection.readRequestBuilder();
- builder.addItem("value-1", tag);
- PlcReadRequest readRequest = builder.build();
+ long timeout = DEFAULT_TIMEOUT;
+ if (monitoredList.containsKey(ctx.getNode().getNodeId())) {
+ timeout = (long) monitoredList.get(ctx.getNode().getNodeId()).getSamplingInterval() * 1000;
+ }
- PlcReadResponse response = null;
- try {
- response = readRequest.execute().get(timeout, TimeUnit.MICROSECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- logger.warn(e + " Occurred while reading value, using timeout of " + timeout/1000 + "ms");
+ // Create a new read request:
+ // - Give the single item requested an alias name
+ PlcReadRequest.Builder builder = connection.readRequestBuilder();
+ builder.addItem("value-1", tag);
+ PlcReadRequest readRequest = builder.build();
+
+ PlcReadResponse response = null;
try {
- connection.close();
- } catch (Exception exception) {
- logger.warn("Closing connection failed with error - " + exception);
+ response = readRequest.execute().get(timeout, TimeUnit.MICROSECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ logger.warn(e + " Occurred while reading value, using timeout of " + timeout / 1000 + "ms");
+ try {
+ connection.close();
+ } catch (Exception exception) {
+ logger.warn("Closing connection failed with error - " + exception);
+ }
+ return BAD_RESPONSE;
}
- return resp;
- }
-
- for (String fieldName : response.getFieldNames()) {
- if(response.getResponseCode(fieldName) == PlcResponseCode.OK) {
- int numValues = response.getNumberOfValues(fieldName);
- if(numValues == 1) {
- if (response.getObject(fieldName) instanceof BigInteger) {
- resp = new DataValue(new Variant(ulong((BigInteger) response.getObject(fieldName))), StatusCode.GOOD);
- } else {
- resp = new DataValue(new Variant(response.getObject(fieldName)), StatusCode.GOOD);
- }
- } else {
- Object array = Array.newInstance(response.getObject(fieldName, 0).getClass(), numValues);
- for (int i = 0; i < numValues; i++) {
- if (response.getObject(fieldName, i) instanceof BigInteger) {
- Array.set(array, i, ulong((BigInteger) response.getObject(fieldName, i)));
+ DataValue resp = BAD_RESPONSE;
+ for (String fieldName : response.getFieldNames()) {
+ if (response.getResponseCode(fieldName) == PlcResponseCode.OK) {
+ int numValues = response.getNumberOfValues(fieldName);
+ if (numValues == 1) {
+ if (response.getObject(fieldName) instanceof BigInteger) {
+ resp = new DataValue(new Variant(ulong((BigInteger) response.getObject(fieldName))), StatusCode.GOOD);
+ } else {
+ resp = new DataValue(new Variant(response.getObject(fieldName)), StatusCode.GOOD);
+ }
} else {
- Array.set(array, i, response.getObject(fieldName, i));
+ Object array = null;
+ if (response.getObject(fieldName, 0) instanceof BigInteger) {
+ array = Array.newInstance(ULong.class, numValues);
+ } else {
+ array = Array.newInstance(response.getObject(fieldName, 0).getClass(), numValues);
+ }
+ for (int i = 0; i < numValues; i++) {
+ if (response.getObject(fieldName, i) instanceof BigInteger) {
+ Array.set(array, i, ulong((BigInteger) response.getObject(fieldName, i)));
+ } else {
+ Array.set(array, i, response.getObject(fieldName, i));
+ }
+ }
+ resp = new DataValue(new Variant(array), StatusCode.GOOD);
}
}
- resp = new DataValue(new Variant(array), StatusCode.GOOD);
- }
- }
- }
+ }
- try {
- connection.close();
+ try {
+ connection.close();
+ } catch (Exception e) {
+ failedConnectionList.put(connectionString, System.currentTimeMillis());
+ logger.warn("Closing connection failed with error " + e);
+ }
+
+ return resp;
} catch (Exception e) {
- failedConnectionList.put(connectionString, System.currentTimeMillis());
- logger.warn("Closing connection failed with error " + e);
+ logger.warn("General error reading value " + e.getStackTrace()[0].toString());
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (Exception ex) {
+ //Do Nothing
+ }
+ }
+ return BAD_RESPONSE;
}
- return resp;
}
public void setValue(String tag, String value, String connectionString) {
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java
index 536022905cc..53c3be98d92 100644
--- a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java
@@ -1,150 +1,104 @@
/*
- * Copyright (c) 2019 the Eclipse Milo Authors
+ * 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
*
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * SPDX-License-Identifier: EPL-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.plc4x.java.opcuaserver.backend;
-import org.apache.plc4x.java.opcuaserver.*;
-
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
-import java.util.Random;
-import java.util.UUID;
import org.apache.plc4x.java.opcuaserver.configuration.Configuration;
import org.apache.plc4x.java.opcuaserver.configuration.DeviceConfiguration;
import org.apache.plc4x.java.opcuaserver.configuration.Tag;
+import org.apache.plc4x.java.utils.connectionpool.PooledPlcDriverManager;
import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.Reference;
import org.eclipse.milo.opcua.sdk.core.ValueRank;
-import org.eclipse.milo.opcua.sdk.core.ValueRanks;
-import org.eclipse.milo.opcua.sdk.server.Lifecycle;
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.DataTypeDictionaryManager;
import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle;
import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.BaseEventTypeNode;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.objects.ServerTypeNode;
-import org.eclipse.milo.opcua.sdk.server.model.nodes.variables.AnalogItemTypeNode;
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.nodes.UaNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectTypeNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
-import org.eclipse.milo.opcua.sdk.server.nodes.factories.NodeFactory;
import org.eclipse.milo.opcua.sdk.server.nodes.filters.AttributeFilters;
import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
-import org.eclipse.milo.opcua.stack.core.AttributeId;
-import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.Identifiers;
-import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
-import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
-import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
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.XmlElement;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.StructureType;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumDefinition;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumDescription;
-import org.eclipse.milo.opcua.stack.core.types.structured.EnumField;
-import org.eclipse.milo.opcua.stack.core.types.structured.Range;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureDefinition;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureDescription;
-import org.eclipse.milo.opcua.stack.core.types.structured.StructureField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-
-import org.apache.plc4x.java.api.model.PlcField;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;
import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ulong;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;
+
public class Plc4xNamespace extends ManagedNamespaceWithLifecycle {
- public static final String NAMESPACE_URI = "urn:eclipse:milo:plc4x:server";
+ public static final String APPLICATIONID = "urn:eclipse:milo:plc4x:server";
private Configuration config;
-
private final Logger logger = LoggerFactory.getLogger(getClass());
-
- private volatile Thread eventThread;
- private volatile boolean keepPostingEvents = true;
-
- private final Random random = new Random();
-
private final DataTypeDictionaryManager dictionaryManager;
-
private final SubscriptionModel subscriptionModel;
-
private Plc4xCommunication plc4xServer;
public Plc4xNamespace(OpcUaServer server, Configuration c) {
- super(server, NAMESPACE_URI);
-
+ super(server, APPLICATIONID);
this.config = c;
subscriptionModel = new SubscriptionModel(server, this);
- dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), NAMESPACE_URI);
-
+ dictionaryManager = new DataTypeDictionaryManager(getNodeContext(), APPLICATIONID);
plc4xServer = new Plc4xCommunication();
-
getLifecycleManager().addLifecycle(dictionaryManager);
getLifecycleManager().addLifecycle(subscriptionModel);
-
+ getLifecycleManager().addLifecycle(plc4xServer);
getLifecycleManager().addStartupTask(this::addNodes);
}
private void addNodes() {
for (DeviceConfiguration c: config.getDevices()) {
- createAndAddNodes(c);
- }
- }
+ NodeId folderNodeId = newNodeId(c.getName());
- private void createAndAddNodes(DeviceConfiguration c) {
+ UaFolderNode folderNode = new UaFolderNode(
+ getNodeContext(),
+ folderNodeId,
+ newQualifiedName(c.getName()),
+ LocalizedText.english(c.getName())
+ );
- NodeId folderNodeId = newNodeId(c.getName());
+ getNodeManager().addNode(folderNode);
- UaFolderNode folderNode = new UaFolderNode(
- getNodeContext(),
- folderNodeId,
- newQualifiedName(c.getName()),
- LocalizedText.english(c.getName())
- );
+ folderNode.addReference(new Reference(
+ folderNode.getNodeId(),
+ Identifiers.Organizes,
+ Identifiers.ObjectsFolder.expanded(),
+ false
+ ));
- getNodeManager().addNode(folderNode);
-
- // Make sure our new folder shows up under the server's Objects folder.
- folderNode.addReference(new Reference(
- folderNode.getNodeId(),
- Identifiers.Organizes,
- Identifiers.ObjectsFolder.expanded(),
- false
- ));
-
- addDynamicNodes(folderNode, c);
+ addConfiguredNodes(folderNode, c);
+ }
}
- private void addDynamicNodes(UaFolderNode rootNode, DeviceConfiguration c) {
+ private void addConfiguredNodes(UaFolderNode rootNode, DeviceConfiguration c) {
final List tags = c.getTags();
final String connectionString = c.getConnectionString();
for (int i = 0; i < tags.size(); i++) {
@@ -229,6 +183,11 @@ private void addDynamicNodes(UaFolderNode rootNode, DeviceConfiguration c) {
public void onDataItemsCreated(List dataItems) {
for (DataItem item : dataItems) {
plc4xServer.addField(item);
+
+ if (plc4xServer.getDriverManager() == null) {
+ plc4xServer.removeField(item);
+ plc4xServer.setDriverManager(new PooledPlcDriverManager());
+ }
}
subscriptionModel.onDataItemsCreated(dataItems);
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/configuration/Configuration.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/configuration/Configuration.java
index 854e8c02ef1..88efb2d64a2 100644
--- a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/configuration/Configuration.java
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/configuration/Configuration.java
@@ -47,9 +47,6 @@ public class Configuration {
@JsonProperty
private Integer tcpPort = 12686;
- @JsonProperty
- private Integer httpPort = 8443;
-
public Configuration() {
}
@@ -73,10 +70,6 @@ public Integer getTcpPort() {
return tcpPort;
}
- public Integer getHttpPort() {
- return httpPort;
- }
-
public List getDevices() {
return devices;
}
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateGenerator.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateGenerator.java
new file mode 100644
index 00000000000..56151fb8c6b
--- /dev/null
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateGenerator.java
@@ -0,0 +1,122 @@
+/*
+ * 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.plc4x.java.opcuaserver.context;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+public class CertificateGenerator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertificateGenerator.class);
+ private static final String APPURI = "urn:eclipse:milo:plc4x:server";
+
+ public static CertificateKeyPair generateCertificate() {
+ KeyPairGenerator kpg = null;
+ try {
+ kpg = KeyPairGenerator.getInstance("RSA");
+ } catch (NoSuchAlgorithmException e) {
+ LOGGER.error("Security Algorithim is unsupported for certificate");
+ }
+ kpg.initialize(2048);
+ KeyPair caKeys = kpg.generateKeyPair();
+ KeyPair userKeys = kpg.generateKeyPair();
+
+ X500NameBuilder nameBuilder = new X500NameBuilder();
+
+ nameBuilder.addRDN(BCStyle.CN, "Apache PLC4X Driver Client");
+ nameBuilder.addRDN(BCStyle.O, "Apache Software Foundation");
+ nameBuilder.addRDN(BCStyle.OU, "dev");
+ nameBuilder.addRDN(BCStyle.L, "");
+ nameBuilder.addRDN(BCStyle.ST, "DE");
+ nameBuilder.addRDN(BCStyle.C, "US");
+
+ BigInteger serial = new BigInteger(RandomUtils.nextBytes(40));
+
+ final Calendar calender = Calendar.getInstance();
+ calender.add(Calendar.DATE, -1);
+ Date startDate = calender.getTime();
+ calender.add(Calendar.DATE, 365*25);
+ Date expiryDate = calender.getTime();
+
+ KeyPairGenerator generator = null;
+ try {
+ generator = KeyPairGenerator.getInstance("RSA");
+ generator.initialize(2048, new SecureRandom());
+ KeyPair keyPair = generator.generateKeyPair();
+
+ SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(
+ keyPair.getPublic().getEncoded()
+ );
+
+ X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(
+ nameBuilder.build(),
+ serial,
+ startDate,
+ expiryDate,
+ Locale.ENGLISH,
+ nameBuilder.build(),
+ subjectPublicKeyInfo
+ );
+
+ GeneralName[] gnArray = new GeneralName[] {new GeneralName(GeneralName.dNSName, InetAddress.getLocalHost().getHostName()), new GeneralName(GeneralName.uniformResourceIdentifier, APPURI)};
+
+
+ GeneralNames subjectAltNames = GeneralNames.getInstance(new DERSequence(gnArray));
+ certificateBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
+
+ ContentSigner sigGen = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(keyPair.getPrivate());
+
+ X509CertificateHolder certificateHolder = certificateBuilder.build(sigGen);
+
+ JcaX509CertificateConverter certificateConvertor = new JcaX509CertificateConverter();
+ certificateConvertor.setProvider(new BouncyCastleProvider());
+
+ CertificateKeyPair ckp = new CertificateKeyPair(keyPair, certificateConvertor.getCertificate(certificateHolder));
+
+ return ckp;
+
+ } catch (Exception e) {
+ LOGGER.error("Security Algorithim is unsupported for certificate");
+ return null;
+ }
+ }
+}
diff --git a/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateKeyPair.java b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateKeyPair.java
new file mode 100644
index 00000000000..0aa3a7065e9
--- /dev/null
+++ b/plc4j/integrations/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/context/CertificateKeyPair.java
@@ -0,0 +1,38 @@
+/*
+ * 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.plc4x.java.opcuaserver.context;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+public class CertificateKeyPair {
+
+ private final KeyPair keyPair;
+ private final X509Certificate certificate;
+
+ public CertificateKeyPair(KeyPair keyPair, X509Certificate certificate) {
+ this.keyPair = keyPair;
+ this.certificate = certificate;
+ }
+
+ public KeyPair getKeyPair() { return keyPair; }
+
+ public X509Certificate getCertificate() { return certificate; }
+}
diff --git a/plc4j/integrations/opcua-server/src/test/java/org/apache/plc4x/java/opcuaserver/OpcuaPlcDriverTest.java b/plc4j/integrations/opcua-server/src/test/java/org/apache/plc4x/java/opcuaserver/OpcuaPlcDriverTest.java
index 256f35f7ad9..71f0f05eed9 100644
--- a/plc4j/integrations/opcua-server/src/test/java/org/apache/plc4x/java/opcuaserver/OpcuaPlcDriverTest.java
+++ b/plc4j/integrations/opcua-server/src/test/java/org/apache/plc4x/java/opcuaserver/OpcuaPlcDriverTest.java
@@ -27,51 +27,46 @@ Licensed to the Apache Software Foundation (ASF) under one
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
import org.apache.plc4x.java.api.messages.PlcWriteResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.opcua.connection.OpcuaTcpPlcConnection;
import org.junit.jupiter.api.*;
import java.math.BigInteger;
-import java.util.HashSet;
-import java.util.Set;
+
import org.apache.commons.io.FileUtils;
import java.io.File;
-import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.INET_ADDRESS_PATTERN;
-import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.OPCUA_URI_PATTERN;
-import static org.apache.plc4x.java.opcuaserver.UtilsTest.assertMatching;
import static org.assertj.core.api.Assertions.fail;
/**
*/
public class OpcuaPlcDriverTest {
// Read only variables of milo example server of version 3.6
- private static final String BOOL_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_BOOL";
- private static final String BYTE_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_BYTE";
- private static final String DOUBLE_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_LREAL";
- private static final String FLOAT_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_REAL";
- private static final String INT16_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_INT";
- private static final String INT32_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_DINT";
- private static final String INT64_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_LINT";
- private static final String INTEGER_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_DINT";
- private static final String SBYTE_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_USINT";
- private static final String STRING_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_STRING";
- private static final String UINT16_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_UINT";
- private static final String UINT32_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_UDINT";
- private static final String UINT64_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_ULINT";
- private static final String UINTEGER_IDENTIFIER_READ_WRITE = "ns=2;s=Simulated_OPC_UDINT";
- private static final String DOES_NOT_EXIST_IDENTIFIER_READ_WRITE = "ns=2;i=12512623";
+ private static final String BOOL_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_BOOL";
+ private static final String BYTE_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_BYTE";
+ private static final String DOUBLE_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_LREAL";
+ private static final String FLOAT_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_REAL";
+ private static final String INT16_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_INT";
+ private static final String INT32_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_DINT";
+ private static final String INT64_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_LINT";
+ private static final String INTEGER_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_DINT";
+ private static final String SBYTE_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_USINT";
+ private static final String STRING_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_STRING";
+ private static final String UINT16_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_UINT";
+ private static final String UINT32_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_UDINT";
+ private static final String UINT64_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_ULINT";
+ private static final String UINTEGER_IDENTIFIER_READ_WRITE = "ns=1;s=Simulated_UDINT";
+ private static final String DOES_NOT_EXIST_IDENTIFIER_READ_WRITE = "ns=1;i=12512623";
// At the moment not used in PLC4X or in the OPC UA driver
- private static final String BYTE_STRING_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/ByteString";
- private static final String DATE_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/DateTime";
- private static final String DURATION_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Duration";
- private static final String GUID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Guid";
- private static final String LOCALISED_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/LocalizedText";
- private static final String NODE_ID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/NodeId";
- private static final String QUALIFIED_NAM_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/QualifiedName";
- private static final String UTC_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UtcTime";
- private static final String VARIANT_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Variant";
- private static final String XML_ELEMENT_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/XmlElement";
+ private static final String BYTE_STRING_IDENTIFIER_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/ByteString";
+ private static final String DATE_TIME_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/DateTime";
+ private static final String DURATION_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/Duration";
+ private static final String GUID_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/Guid";
+ private static final String LOCALISED_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/LocalizedText";
+ private static final String NODE_ID_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/NodeId";
+ private static final String QUALIFIED_NAM_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/QualifiedName";
+ private static final String UTC_TIME_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/UtcTime";
+ private static final String VARIANT_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/Variant";
+ private static final String XML_ELEMENT_READ_WRITE = "ns=1;s=HelloWorld/ScalarTypes/XmlElement";
// Address of local milo server
private String miloLocalAddress = "127.0.0.1:12673/plc4x";
//Tcp pattern of OPC UA
@@ -113,7 +108,6 @@ public static void tearDown() {
@Test
public void connectionNoParams(){
-
connectionStringValidSet.forEach(connectionAddress -> {
String connectionString = connectionAddress;
try {
@@ -153,8 +147,8 @@ public void connectionWithDiscoveryParam(){
}
@Test
- public void readVariables() {
- try {
+ public void readVariables() throws Exception{
+
PlcConnection opcuaConnection = new PlcDriverManager().getConnection(tcpConnectionAddress);
assert opcuaConnection.isConnected();
@@ -198,14 +192,12 @@ public void readVariables() {
opcuaConnection.close();
assert !opcuaConnection.isConnected();
- } catch (Exception e) {
- fail("Exception during readVariables Test EXCEPTION: " + e.getMessage());
- }
+
}
@Test
- public void writeVariables() {
- try {
+ public void writeVariables() throws Exception {
+
PlcConnection opcuaConnection = new PlcDriverManager().getConnection(tcpConnectionAddress);
assert opcuaConnection.isConnected();
@@ -218,7 +210,7 @@ public void writeVariables() {
builder.addItem("Int32", INT32_IDENTIFIER_READ_WRITE, 42);
builder.addItem("Int64", INT64_IDENTIFIER_READ_WRITE, 42L);
builder.addItem("Integer", INTEGER_IDENTIFIER_READ_WRITE, 42);
- builder.addItem("SByte", SBYTE_IDENTIFIER_READ_WRITE + ":SINT", -100);
+ builder.addItem("SByte", SBYTE_IDENTIFIER_READ_WRITE + ":USINT", 100);
builder.addItem("String", STRING_IDENTIFIER_READ_WRITE, "Helllo Toddy!");
builder.addItem("UInt16", UINT16_IDENTIFIER_READ_WRITE + ":UINT", 65535);
builder.addItem("UInt32", UINT32_IDENTIFIER_READ_WRITE + ":UDINT", 100);
@@ -248,9 +240,6 @@ public void writeVariables() {
opcuaConnection.close();
assert !opcuaConnection.isConnected();
- } catch (Exception e) {
- fail("Exception during writeVariables Test EXCEPTION: " + e.getMessage());
- }
}
}
diff --git a/plc4j/integrations/opcua-server/src/test/resources/config.yml b/plc4j/integrations/opcua-server/src/test/resources/config.yml
index d0b0cd983de..c60002e7e96 100644
--- a/plc4j/integrations/opcua-server/src/test/resources/config.yml
+++ b/plc4j/integrations/opcua-server/src/test/resources/config.yml
@@ -21,7 +21,6 @@ dir: "target/test-tmp/"
name: Plc4x.OPC.UA.Server
disableInsecureEndpoint: true
tcpPort: 12673
-httpPort: 8445
devices:
- name: "Simulated Device"
connectionString: "simulated://127.0.0.1"