|
28 | 28 | import java.util.Optional; |
29 | 29 | import java.util.Random; |
30 | 30 | import java.util.Set; |
31 | | -import java.util.concurrent.CompletableFuture; |
32 | | -import java.util.concurrent.ConcurrentHashMap; |
33 | | -import java.util.concurrent.ExecutorService; |
34 | | -import java.util.concurrent.ScheduledExecutorService; |
| 31 | +import java.util.concurrent.*; |
35 | 32 | import java.util.concurrent.atomic.AtomicLong; |
36 | 33 | import java.util.stream.Collectors; |
37 | 34 | import java.util.stream.Stream; |
@@ -135,6 +132,8 @@ public class OpcUaServer extends AbstractServiceHandler { |
135 | 132 |
|
136 | 133 | private final ServerDiagnosticsSummary diagnosticsSummary = new ServerDiagnosticsSummary(this); |
137 | 134 |
|
| 135 | + private final List<EndpointConfig> boundEndpoints = new CopyOnWriteArrayList<>(); |
| 136 | + |
138 | 137 | /** |
139 | 138 | * SecureChannel id sequence, starting at a random value in [1..{@link Integer#MAX_VALUE}], and |
140 | 139 | * wrapping back to 1 after {@link UInteger#MAX_VALUE}. |
@@ -257,15 +256,29 @@ public CompletableFuture<OpcUaServer> startup() { |
257 | 256 | transport.bind(applicationContext, bindAddress); |
258 | 257 |
|
259 | 258 | transports.put(transportProfile, transport); |
| 259 | + |
| 260 | + boundEndpoints.add(endpoint); |
260 | 261 | } catch (Exception e) { |
261 | | - throw new RuntimeException(e); |
| 262 | + logger.warn( |
| 263 | + "Failed to bind endpoint {} to {}:{} [{}/{}]", |
| 264 | + endpoint.getEndpointUrl(), |
| 265 | + endpoint.getBindAddress(), |
| 266 | + endpoint.getBindPort(), |
| 267 | + endpoint.getSecurityPolicy(), |
| 268 | + endpoint.getSecurityMode(), |
| 269 | + e); |
262 | 270 | } |
263 | 271 | } else { |
264 | 272 | logger.warn("No OpcServerTransport for TransportProfile: {}", transportProfile); |
265 | 273 | } |
266 | 274 | }); |
267 | 275 |
|
268 | | - return CompletableFuture.completedFuture(this); |
| 276 | + if (boundEndpoints.isEmpty()) { |
| 277 | + return CompletableFuture.failedFuture( |
| 278 | + new UaException(StatusCodes.Bad_ConfigurationError, "No endpoints bound")); |
| 279 | + } else { |
| 280 | + return CompletableFuture.completedFuture(this); |
| 281 | + } |
269 | 282 | } |
270 | 283 |
|
271 | 284 | public CompletableFuture<OpcUaServer> shutdown() { |
@@ -458,6 +471,15 @@ public Optional<RoleMapper> getRoleMapper() { |
458 | 471 | return config.getRoleMapper(); |
459 | 472 | } |
460 | 473 |
|
| 474 | + /** |
| 475 | + * Get the {@link EndpointConfig}s that were successfully bound during {@link #startup()}. |
| 476 | + * |
| 477 | + * @return the {@link EndpointConfig}s that were successfully bound during {@link #startup()}. |
| 478 | + */ |
| 479 | + public List<EndpointConfig> getBoundEndpoints() { |
| 480 | + return List.copyOf(boundEndpoints); |
| 481 | + } |
| 482 | + |
461 | 483 | private class ServerApplicationContextImpl implements ServerApplicationContext { |
462 | 484 |
|
463 | 485 | @Override |
|
0 commit comments