org.eclipse.californium.cloud.BaseServer Maven / Gradle / Ivy
Show all versions of cf-cloud-demo-server Show documentation
/********************************************************************************
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
********************************************************************************/
package org.eclipse.californium.cloud;
import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.californium.cloud.http.HttpService;
import org.eclipse.californium.cloud.http.HttpService.CoapProxyHandler;
import org.eclipse.californium.cloud.http.HttpService.ForwardHandler;
import org.eclipse.californium.cloud.http.HttpService.WebAnonymous;
import org.eclipse.californium.cloud.resources.Devices;
import org.eclipse.californium.cloud.resources.Diagnose;
import org.eclipse.californium.cloud.resources.MyContext;
import org.eclipse.californium.cloud.resources.Provisioning;
import org.eclipse.californium.cloud.util.CredentialsStore;
import org.eclipse.californium.cloud.util.DeviceGredentialsProvider;
import org.eclipse.californium.cloud.util.DeviceManager;
import org.eclipse.californium.cloud.util.DeviceParser;
import org.eclipse.californium.cloud.util.DeviceProvisioningConsumer;
import org.eclipse.californium.cloud.util.ResourceStore;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.config.CoapConfig;
import org.eclipse.californium.core.config.CoapConfig.MatcherMode;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.californium.core.network.interceptors.HealthStatisticLogger;
import org.eclipse.californium.core.observe.ObserveStatisticLogger;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.elements.EndpointContextMatcher;
import org.eclipse.californium.elements.PrincipalEndpointContextMatcher;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.californium.elements.config.Configuration.DefinitionsProvider;
import org.eclipse.californium.elements.config.IntegerDefinition;
import org.eclipse.californium.elements.config.SystemConfig;
import org.eclipse.californium.elements.config.TimeDefinition;
import org.eclipse.californium.elements.util.ClockUtil;
import org.eclipse.californium.elements.util.CounterStatisticManager;
import org.eclipse.californium.elements.util.DatagramWriter;
import org.eclipse.californium.elements.util.EncryptedPersistentComponentUtil;
import org.eclipse.californium.elements.util.ExecutorsUtil;
import org.eclipse.californium.elements.util.NamedThreadFactory;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil.InetAddressFilter;
import org.eclipse.californium.elements.util.NetworkInterfacesUtil.SimpleInetAddressFilter;
import org.eclipse.californium.elements.util.SslContextUtil.Credentials;
import org.eclipse.californium.elements.util.StringUtil;
import org.eclipse.californium.elements.util.SystemResourceMonitors;
import org.eclipse.californium.elements.util.SystemResourceMonitors.SystemResourceMonitor;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.DtlsHealthLogger;
import org.eclipse.californium.scandium.MdcConnectionListener;
import org.eclipse.californium.scandium.auth.ApplicationLevelInfoSupplier;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
import org.eclipse.californium.scandium.dtls.pskstore.AdvancedPskStore;
import org.eclipse.californium.scandium.dtls.x509.CertificateProvider;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.unixhealth.NetSocketHealthLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.ParseResult;
/**
* The basic cloud server.
*
* Creates {@link Endpoint}s using DTLS. Adds resources {@link Diagnose} and
* {@link MyContext}.
*
* @since 3.12
*/
public class BaseServer extends CoapServer {
static {
// only coap + dtls
CoapConfig.register();
DtlsConfig.register();
}
private static final Logger LOGGER = LoggerFactory.getLogger(CoapServer.class);
private static final Logger STATISTIC_LOGGER = LoggerFactory.getLogger("org.eclipse.californium.statistics");
private static final int DEFAULT_MAX_CONNECTIONS = 200000;
private static final int DEFAULT_MAX_RESOURCE_SIZE = 8192;
private static final int DEFAULT_MAX_MESSAGE_SIZE = 1280;
private static final int DEFAULT_BLOCK_SIZE = 1024;
/**
* Name of private key file for DTLS 1.2 (device communication).
*/
public static final String DTLS_PRIVATE_KEY = "privkey.pem";
/**
* Name of public key file for DTLS 1.2 (device communication).
*/
public static final String DTLS_PUBLIC_KEY = "pubkey.pem";
/**
* Name of certificate file for DTLS 1.2 (device communication).
*/
public static final String DTLS_FULLCHAIN = "fullchain.pem";
// exit codes for runtime errors
public static final int ERR_INIT_FAILED = 1;
public static final List PRESELECTED_CIPHER_SUITES = Arrays.asList(
CipherSuite.TLS_PSK_WITH_AES_128_CCM_8, CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256);
public enum InterfaceType {
LOCAL, EXTERNAL, IPV4, IPV6,
}
/**
* Interval to read number of dropped UDP messages.
*/
public static final TimeDefinition UDP_DROPS_READ_INTERVAL = new TimeDefinition("UDP_DROPS_READ_INTERVAL",
"Interval to read UDP drops from OS (currently only Linux).", 2000, TimeUnit.MILLISECONDS);
/**
* Maximum device in cache.
*/
public static final IntegerDefinition CACHE_MAX_DEVICES = new IntegerDefinition("CACHE_MAX_DEVICES",
"Cache maximum devices.", 5000, 100);
/**
* Threshold for stale devices.
*/
public static final TimeDefinition CACHE_STALE_DEVICE_THRESHOLD = new TimeDefinition("CACHE_STALE_DEVICE_THRESHOLD",
"Threshold for stale devices. Devices will only get removed for new ones, "
+ "if at least for that threshold no messages are exchanged with that device.",
24, TimeUnit.HOURS);
/**
* Interval to reload HTTPS credentials.
*/
public static final TimeDefinition HTTPS_CREDENTIALS_RELOAD_INTERVAL = new TimeDefinition(
"HTTPS_CREDENTIALS_RELOAD_INTERVAL",
"Reload HTTPS credentials interval. 0 to load credentials only on startup.", 30, TimeUnit.MINUTES);
/**
* Interval to reload device credentials.
*/
public static final TimeDefinition DEVICE_CREDENTIALS_RELOAD_INTERVAL = new TimeDefinition(
"DEVICE_CREDENTIALS_RELOAD_INTERVAL",
"Reload device credentials interval. 0 to load credentials only on startup.", 60, TimeUnit.SECONDS);
/**
* Request timeout for adding device credentials in auto-provisioning.
*
* @since 4.0
*/
public static final TimeDefinition DEVICE_CREDENTIALS_ADD_TIMEOUT = new TimeDefinition(
"DEVICE_CREDENTIALS_ADD_TIMEOUT",
"Request timeout for adding device credentials in auto-provisioning. Credentials must be added in series and concurrent requests may cause overload resulting in timeouts.",
5000, TimeUnit.MILLISECONDS);
/**
* Default configuration setup.
*
* @see Configuration#createWithFile(File, String, DefinitionsProvider)
*/
public static DefinitionsProvider DEFAULTS = new DefinitionsProvider() {
@Override
public void applyDefinitions(Configuration config) {
int processors = Runtime.getRuntime().availableProcessors();
config.set(SystemConfig.HEALTH_STATUS_INTERVAL, 300, TimeUnit.SECONDS);
config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_MAX_MESSAGE_SIZE);
config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_COUNT, 4);
config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_TIME, 30, TimeUnit.SECONDS);
config.set(CoapConfig.MAX_ACTIVE_PEERS, DEFAULT_MAX_CONNECTIONS);
config.set(CoapConfig.PEERS_MARK_AND_SWEEP_MESSAGES, 16);
config.set(CoapConfig.DEDUPLICATOR, CoapConfig.DEDUPLICATOR_PEERS_MARK_AND_SWEEP);
config.set(CoapConfig.RESPONSE_MATCHING, MatcherMode.PRINCIPAL_IDENTITY);
config.set(CoapConfig.ACK_TIMEOUT, 2500, TimeUnit.MILLISECONDS);
config.set(DtlsConfig.DTLS_ROLE, DtlsRole.SERVER_ONLY);
config.set(DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT, 2500, TimeUnit.MILLISECONDS);
config.set(DtlsConfig.DTLS_ADDITIONAL_ECC_TIMEOUT, 8, TimeUnit.SECONDS);
config.set(DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT, null, TimeUnit.SECONDS);
config.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, 6);
config.set(DtlsConfig.DTLS_PRESELECTED_CIPHER_SUITES, PRESELECTED_CIPHER_SUITES);
config.set(DtlsConfig.DTLS_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS);
config.set(DtlsConfig.DTLS_REMOVE_STALE_DOUBLE_PRINCIPALS, true);
config.set(DtlsConfig.DTLS_SERVER_USE_SESSION_ID, false);
config.set(DtlsConfig.DTLS_RECEIVE_BUFFER_SIZE, 1000000);
config.set(DtlsConfig.DTLS_RECEIVER_THREAD_COUNT, processors > 3 ? 2 : 1);
config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_QUIET_TIME, 4, TimeUnit.SECONDS);
config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_THRESHOLD, 8);
config.set(UDP_DROPS_READ_INTERVAL, 2000, TimeUnit.MILLISECONDS);
config.set(CACHE_MAX_DEVICES, 5000);
config.set(CACHE_STALE_DEVICE_THRESHOLD, 24, TimeUnit.HOURS);
config.set(HTTPS_CREDENTIALS_RELOAD_INTERVAL, 30, TimeUnit.MINUTES);
config.set(DEVICE_CREDENTIALS_RELOAD_INTERVAL, 30, TimeUnit.SECONDS);
}
};
public static class ServerConfig {
@Option(names = { "-h", "--help" }, usageHelp = true, description = "display a help message")
public boolean helpRequested;
@ArgGroup(exclusive = true)
public NetworkConfig network;
public static class NetworkConfig {
@Option(names = "--wildcard-interface", description = "Use local wildcard-address for coap endpoints. Default mode.")
public boolean wildcard;
@ArgGroup(exclusive = false)
public NetworkSelectConfig selectInterfaces;
}
public static class NetworkSelectConfig {
@Option(names = "--no-loopback", negatable = true, description = "enable coap endpoints on loopback network.")
public boolean loopback = true;
@Option(names = "--no-external", negatable = true, description = "enable coap endpoints on external network.")
public boolean external = true;
@Option(names = "--no-ipv4", negatable = true, description = "enable coap endpoints for ipv4.")
public boolean ipv4 = true;
@Option(names = "--no-ipv6", negatable = true, description = "enable coap endpoints for ipv6.")
public boolean ipv6 = true;
@Option(names = "--interfaces-pattern", split = ",", description = "interface regex patterns for coap endpoints.")
public List interfacePatterns;
public InetAddressFilter getFilter(String tag) {
if (interfacePatterns == null || interfacePatterns.isEmpty()) {
return new SimpleInetAddressFilter(tag, external, loopback, ipv4, ipv6);
} else {
String[] patterns = new String[interfacePatterns.size()];
patterns = interfacePatterns.toArray(patterns);
return new SimpleInetAddressFilter(tag, external, loopback, ipv4, ipv6, patterns);
}
}
}
@ArgGroup(exclusive = false)
public CoapsConfig coaps;
public static class CoapsConfig {
@Option(names = "--coaps-credentials", required = true, description = "Folder containing coaps credentials in 'privkey.pem' and 'pubkey.pem'")
public String credentials;
@Option(names = "--coaps-password64", required = false, description = "Password for coaps credentials. Base 64 encoded.")
public String password64;
}
@ArgGroup(exclusive = false)
public DeviceStore deviceStore;
public static class DeviceStore {
@Option(names = "--device-file", required = true, description = "Filename of device store for coap.")
public String file;
@Option(names = "--device-file-password64", required = false, description = "Password for device store. Base 64 encoded.")
public String password64;
}
@ArgGroup(exclusive = false)
public Store store;
public static class Store {
@Option(names = "--store-file", required = true, description = "file-store for dtls state.")
public String file;
@Option(names = "--store-max-age", required = true, description = "maximum age of connections in hours to store dtls state.")
public Integer maxAge;
@Option(names = "--store-password64", required = false, description = "password to store dtls state. Base 64 encoded.")
public String password64;
}
@Option(names = "--diagnose", description = "enable 'diagnose'-resource.")
public boolean diagnose;
@ArgGroup(exclusive = false)
public Provisioning provisioning;
public static class Provisioning {
@Option(names = "--provisioning", required = true, description = "enable 'prov'-resource for auto-provisioning.")
public boolean provisioning;
@Option(names = "--replace", required = false, description = "replaces previous device credentials entries with new entries. For use during development. Don't use it for production!")
public boolean replace;
}
public boolean noCoap;
public HttpsConfig https;
public static class HttpsConfig {
@Option(names = "--https-port", defaultValue = "8080", description = "Port of https service. Default: ${DEFAULT-VALUE}")
public int port;
@Option(names = "--https-credentials", required = true, description = "Folder containing https credentials in 'privkey.pem' and 'fullchain.pem'.")
public String credentials;
@Option(names = "--https-password64", description = "Password for https credentials. Base 64 encoded.")
public String password64;
}
/**
* Setup dependent defaults.
*/
public void defaults() {
if (network == null) {
network = new NetworkConfig();
network.wildcard = true;
}
}
}
public static final String CALIFORNIUM_BUILD_VERSION;
static {
String version = StringUtil.CALIFORNIUM_VERSION;
if (version != null) {
String build = StringUtil.readFile(new File("build"), null);
if (build != null && !build.isEmpty()) {
version = version + "_" + build;
}
} else {
version = "";
}
CALIFORNIUM_BUILD_VERSION = version;
}
public static void start(String[] args, String name, ServerConfig cliArguments, BaseServer server) {
CommandLine cmd = new CommandLine(cliArguments);
try {
ParseResult result = cmd.parseArgs(args);
if (result.isVersionHelpRequested()) {
System.out.println("\nCalifornium (Cf) " + cmd.getCommandName() + " " + CALIFORNIUM_BUILD_VERSION);
cmd.printVersionHelp(System.out);
System.out.println();
}
if (result.isUsageHelpRequested()) {
cmd.usage(System.out);
return;
}
} catch (ParameterException ex) {
System.err.println(ex.getMessage());
System.err.println();
cmd.usage(System.err);
System.exit(-1);
}
cliArguments.defaults();
// print startup message
long max = Runtime.getRuntime().maxMemory();
StringBuilder builder = new StringBuilder(name);
if (!CALIFORNIUM_BUILD_VERSION.isEmpty()) {
builder.append(", version ").append(CALIFORNIUM_BUILD_VERSION);
}
builder.append(", ").append(max / (1024 * 1024)).append("MB heap, started ...");
LOGGER.info("{}", builder);
// management statistic
STATISTIC_LOGGER.error("start!");
ManagementStatistic management = new ManagementStatistic(STATISTIC_LOGGER);
boolean http = false;
if (cliArguments.https != null) {
if (cliArguments.https.port > 0) {
LOGGER.info("Create HTTPS service at port {}, credentials {}", cliArguments.https.port,
cliArguments.https.credentials);
http = HttpService.createHttpService(cliArguments.https.port, cliArguments.https.credentials,
cliArguments.https.password64, false);
} else {
LOGGER.info("HTTPS service at port {} is not supported! Must be [1-65535]", cliArguments.https.port);
}
}
// create server
try {
server.initialize(cliArguments);
if (!cliArguments.noCoap && server.getEndpoints().isEmpty()) {
System.err.println("no endpoint available!");
System.exit(ERR_INIT_FAILED);
}
} catch (Exception e) {
System.err.printf("Failed to create " + BaseServer.class.getSimpleName() + ": %s\n", e.getMessage());
e.printStackTrace(System.err);
System.err.println("Exiting");
System.exit(ERR_INIT_FAILED);
}
if (cliArguments.store != null) {
server.setupPersistence(cliArguments.store);
}
server.start();
LOGGER.info("{} started ...", name);
if (http) {
if (HttpService.startHttpService()) {
LOGGER.info("HTTPS service at port {} started", cliArguments.https.port);
long interval = server.getConfig().get(HTTPS_CREDENTIALS_RELOAD_INTERVAL, TimeUnit.MINUTES);
SystemResourceMonitor httpsCredentialsMonitor = HttpService.getHttpService().getFileMonitor();
server.monitors.addOptionalMonitor("https credentials", interval, TimeUnit.MINUTES,
httpsCredentialsMonitor);
}
}
long interval = server.getConfig().get(SystemConfig.HEALTH_STATUS_INTERVAL, TimeUnit.MILLISECONDS);
long inputTimeout = interval < 15000 ? interval : 15000;
long lastGcCount = 0;
long lastDumpNanos = ClockUtil.nanoRealtime();
for (;;) {
try {
Thread.sleep(inputTimeout);
} catch (InterruptedException e) {
break;
}
long gcCount = management.getCollectionCount();
if (lastGcCount < gcCount) {
management.printManagementStatistic();
lastGcCount = gcCount;
long clones = DatagramWriter.COPIES.get();
long takes = DatagramWriter.TAKES.get();
if (clones + takes > 0) {
STATISTIC_LOGGER.info("DatagramWriter {} clones, {} takes, {}%", clones, takes,
(takes * 100L) / (takes + clones));
}
}
long now = ClockUtil.nanoRealtime();
if ((now - lastDumpNanos - TimeUnit.MILLISECONDS.toNanos(interval)) > 0) {
lastDumpNanos = now;
server.dump();
}
}
LOGGER.info("Executor shutdown ...");
if (http) {
HttpService.stopHttpService();
LOGGER.info("HTTPS service at port {} stopped", cliArguments.https.port);
}
server.stop();
server.destroy();
exit();
LOGGER.info("Exit ...");
}
public static void exit() {
int count = Thread.activeCount();
while (count > 0) {
int size = Thread.activeCount();
Thread[] all = new Thread[size];
int available = Thread.enumerate(all);
if (available < size) {
size = available;
}
count = 0;
for (int index = 0; index < size; ++index) {
Thread thread = all[index];
if (!thread.isDaemon() && thread.isAlive()) {
++count;
LOGGER.info("Thread [{}] {}", thread.getId(), thread.getName());
}
}
if (count == 1) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
}
}
protected SystemResourceMonitors monitors;
protected DeviceGredentialsProvider deviceCredentials;
protected List diagnoseStatistics = new ArrayList<>();
public BaseServer(Configuration config) {
super(config);
setVersion(CALIFORNIUM_BUILD_VERSION);
setTag("CLOUD-DEMO");
}
@Override
public void start() {
if (!getEndpoints().isEmpty()) {
super.start();
}
if (monitors != null) {
monitors.start();
}
}
@Override
public void stop() {
if (!getEndpoints().isEmpty()) {
super.stop();
}
if (monitors != null) {
monitors.stop();
}
}
/**
* Initialize demo server.
*
* @param cliArguments command line arguments
* @throws SocketException if an I/O error occurred.
*/
public void initialize(ServerConfig cliArguments) throws SocketException {
Configuration config = getConfig();
// executors
ScheduledExecutorService secondaryExecutor = ExecutorsUtil
.newDefaultSecondaryScheduler("CoapServer(secondary)#");
monitors = new SystemResourceMonitors(secondaryExecutor);
setupDeviceCredentials(cliArguments);
if (!cliArguments.noCoap) {
addEndpoints(cliArguments);
ScheduledExecutorService executor = ExecutorsUtil.newScheduledThreadPool(//
config.get(CoapConfig.PROTOCOL_STAGE_THREAD_COUNT), //
new NamedThreadFactory("CoapServer(main)#")); //$NON-NLS-1$
addResource(cliArguments, executor);
setExecutors(executor, secondaryExecutor, false);
// additional health loggers
setupUdpHealthLogger(secondaryExecutor);
setupObserveHealthLogger();
}
setupHttpService(cliArguments);
setupProcessors(secondaryExecutor);
LOGGER.info("{} initialized.", getTag());
}
/**
* Setup device credentials.
*
* Load the private and public key of the DTLS 1.2 server for the device
* communication and the device credentials.
*
* @param cliArguments command line arguments.
*/
public void setupDeviceCredentials(ServerConfig cliArguments) {
Credentials credentials = null;
if (cliArguments.coaps != null) {
String path = cliArguments.coaps.credentials;
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
File directory = new File(path);
if (!directory.exists()) {
LOGGER.error("Missing directory {} for coap credentials!", path);
} else {
CredentialsStore store = new CredentialsStore();
store.setTag("coaps ");
String privateKeyPath = path + "/" + DTLS_PRIVATE_KEY;
String publicKeyPath = path + "/" + DTLS_PUBLIC_KEY;
String fullChainPath = path + "/" + DTLS_FULLCHAIN;
credentials = store.loadAndCreateMonitor(cliArguments.coaps.password64, false, fullChainPath,
privateKeyPath, publicKeyPath);
}
}
setupDeviceCredentials(cliArguments, credentials);
}
/**
* Setup device credentials.
*
* Load the device credentials.
*
* @param cliArguments command line arguments.
* @param credentials server's credentials for DTLS 1.2 certificate based
* authentication
* @since 4.0
*/
public void setupDeviceCredentials(ServerConfig cliArguments, Credentials credentials) {
ResourceStore deviceCredentialsResource = null;
if (cliArguments.deviceStore != null) {
long interval = getConfig().get(DEVICE_CREDENTIALS_RELOAD_INTERVAL, TimeUnit.SECONDS);
boolean replace = cliArguments.provisioning != null ? cliArguments.provisioning.replace : false;
if (replace) {
LOGGER.info(
"New device credentials will replace already available ones. Use this only for development!");
}
DeviceParser factory = new DeviceParser(true, replace, null);
deviceCredentialsResource = new ResourceStore<>(factory).setTag("Devices ");
deviceCredentialsResource.loadAndCreateMonitor(cliArguments.deviceStore.file,
cliArguments.deviceStore.password64, interval > 0);
monitors.addMonitor("Devices", interval, TimeUnit.SECONDS, deviceCredentialsResource.getMonitor());
}
long addTimeout = getConfig().get(DEVICE_CREDENTIALS_ADD_TIMEOUT, TimeUnit.MILLISECONDS);
deviceCredentials = new DeviceManager(deviceCredentialsResource, credentials, addTimeout);
}
/**
* Add CoAP endpoints.
*
* @param cliArguments command line arguments.
*/
public void addEndpoints(ServerConfig cliArguments) {
Configuration config = getConfig();
int coapsPort = config.get(CoapConfig.COAP_SECURE_PORT);
boolean healthLogger = config.get(SystemConfig.HEALTH_STATUS_INTERVAL, TimeUnit.MILLISECONDS) > 0;
if (deviceCredentials.getCertificateVerifier() == null && deviceCredentials.getPskStore() == null) {
// no device credentials
LOGGER.warn("Missing device credentials!");
return;
}
// Context matcher
EndpointContextMatcher customContextMatcher = null;
if (MatcherMode.PRINCIPAL == config.get(CoapConfig.RESPONSE_MATCHING)) {
customContextMatcher = new PrincipalEndpointContextMatcher(true);
}
// explore network interfaces
Collection localAddresses;
String serializationLabel = null;
if (cliArguments.network.wildcard) {
localAddresses = Collections.singleton(new InetSocketAddress(0).getAddress());
serializationLabel = "*";
} else {
localAddresses = NetworkInterfacesUtil
.getNetworkInterfaces(cliArguments.network.selectInterfaces.getFilter(getTag()));
}
for (InetAddress addr : localAddresses) {
InetSocketAddress bindToAddress = new InetSocketAddress(addr, coapsPort);
DtlsConnectorConfig.Builder dtlsConfigBuilder = DtlsConnectorConfig.builder(config);
dtlsConfigBuilder.setAddress(bindToAddress);
String tag = "dtls:" + StringUtil.toString(bindToAddress);
dtlsConfigBuilder.setLoggingTag(tag);
if (serializationLabel != null) {
dtlsConfigBuilder.setSerializationLabel(serializationLabel);
}
AdvancedPskStore pskStore = deviceCredentials.getPskStore();
if (pskStore != null) {
dtlsConfigBuilder.setAdvancedPskStore(pskStore);
}
CertificateProvider certificateProvider = deviceCredentials.getCertificateProvider();
if (certificateProvider != null) {
dtlsConfigBuilder.setCertificateIdentityProvider(certificateProvider);
}
NewAdvancedCertificateVerifier certificateVerifier = deviceCredentials.getCertificateVerifier();
if (certificateVerifier != null) {
dtlsConfigBuilder.setAdvancedCertificateVerifier(certificateVerifier);
}
ApplicationLevelInfoSupplier infoSupplier = deviceCredentials.getInfoSupplier();
if (infoSupplier != null) {
dtlsConfigBuilder.setApplicationLevelInfoSupplier(infoSupplier);
}
dtlsConfigBuilder.setConnectionListener(new MdcConnectionListener());
// setup health logger
if (healthLogger) {
DtlsHealthLogger health = new DtlsHealthLogger(tag);
dtlsConfigBuilder.setHealthHandler(health);
add(health);
}
DTLSConnector connector = new DTLSConnector(dtlsConfigBuilder.build());
tag = "coaps:" + StringUtil.toString(bindToAddress);
CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
builder.setLoggingTag(tag);
builder.setConnector(connector);
builder.setConfiguration(config);
if (customContextMatcher != null) {
builder.setEndpointContextMatcher(customContextMatcher);
}
CoapEndpoint endpoint = builder.build();
if (healthLogger) {
HealthStatisticLogger health = new HealthStatisticLogger(tag, true);
endpoint.addPostProcessInterceptor(health);
add(health);
}
addEndpoint(endpoint);
LOGGER.info("{}listen on {} ({})", getTag(), endpoint.getUri(),
addr.isLoopbackAddress() ? "LOCAL" : "EXTERNAL");
}
}
/**
* Add resources to CoAP server.
*
* @param cliArguments command line arguments.
* @param executor primary executor
*/
public void addResource(ServerConfig cliArguments, ScheduledExecutorService executor) {
// add resources to the server
if (cliArguments.diagnose) {
add(new Diagnose(this));
}
add(new Devices(getConfig()));
if (cliArguments.provisioning != null && cliArguments.provisioning.provisioning
&& deviceCredentials instanceof DeviceProvisioningConsumer) {
add(new Provisioning((DeviceProvisioningConsumer) deviceCredentials));
}
add(new MyContext(MyContext.RESOURCE_NAME, CALIFORNIUM_BUILD_VERSION, false));
}
/**
* Setup HTTP service.
*
* @param cliArguments command line arguments.
*/
public void setupHttpService(ServerConfig cliArguments) {
HttpService httpService = HttpService.getHttpService();
if (httpService != null) {
ForwardHandler forward = new ForwardHandler("devices", "Devices:");
httpService.createContext("/", forward);
CoapProxyHandler proxy = new CoapProxyHandler(getMessageDeliverer(), WebAnonymous.create(),
httpService.getExecutor());
httpService.createContext(Devices.RESOURCE_NAME, proxy);
if (cliArguments.diagnose) {
httpService.createContext(Diagnose.RESOURCE_NAME, proxy);
}
}
}
/**
* Setup UDP health logger.
*
* Generate UDP statistic.
*
* @param secondaryExecutor secondary executor for slow interval jobs
*/
public void setupUdpHealthLogger(ScheduledExecutorService secondaryExecutor) {
Configuration config = getConfig();
final NetSocketHealthLogger socketLogger = new NetSocketHealthLogger("udp");
long interval = config.get(SystemConfig.HEALTH_STATUS_INTERVAL, TimeUnit.MILLISECONDS);
if (interval > 0 && socketLogger.isEnabled()) {
long readInterval = config.get(UDP_DROPS_READ_INTERVAL, TimeUnit.MILLISECONDS);
if (interval > readInterval) {
secondaryExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
socketLogger.read();
}
}, readInterval, readInterval, TimeUnit.MILLISECONDS);
}
addDefaultEndpointObserver(new EndpointNetSocketObserver(socketLogger));
}
}
/**
* Setup observe health logger.
*
* Generate observer-notify statistic.
*/
public void setupObserveHealthLogger() {
ObserveStatisticLogger obsStatLogger = new ObserveStatisticLogger(getTag());
if (obsStatLogger.isEnabled()) {
setObserveHealth(obsStatLogger);
add(obsStatLogger);
addServerStatistic(obsStatLogger);
}
}
/**
* Setup persistence.
*
* Support DTLS 1.2 graceful restart,
*
* @param store store to keep persisted data
*/
public void setupPersistence(ServerConfig.Store store) {
Runnable hook = new Runnable() {
@Override
public void run() {
stop();
}
};
char[] password64 = store.password64 == null ? null : store.password64.toCharArray();
EncryptedPersistentComponentUtil serialization = new EncryptedPersistentComponentUtil();
serialization.addProvider(this);
serialization.loadAndRegisterShutdown(store.file, password64, TimeUnit.HOURS.toSeconds(store.maxAge), hook);
}
/**
* Setup processors.
*
* @param secondaryExecutor secondary executor for slow interval jobs
*/
public void setupProcessors(ScheduledExecutorService secondaryExecutor) {
}
/**
* Add {@link CounterStatisticManager} to {@link Diagnose} resource.
*
* @param health {@link CounterStatisticManager} to add.
*/
protected void addServerStatistic(CounterStatisticManager health) {
diagnoseStatistics.add(health);
Resource child = getRoot().getChild(Diagnose.RESOURCE_NAME);
if (child instanceof Diagnose) {
((Diagnose) child).update(diagnoseStatistics);
LOGGER.info("{} {} added to diagnose resource.", health.getTag(), health.getClass().getSimpleName());
} else {
LOGGER.info("{} {} not added, diagnose resource missing.", health.getTag(),
health.getClass().getSimpleName());
}
}
}