Thomas Vachuska

Making number of components configurable using the central component configuration subsystem.

Change-Id: Ia32c51480913689339a766d9849e792d62f7d133
Showing 24 changed files with 138 additions and 51 deletions
......@@ -38,6 +38,7 @@ import org.onlab.packet.ICMP6;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.Ip6Prefix;
import org.onlab.packet.VlanId;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Host;
......@@ -87,6 +88,9 @@ public class ReactiveForwarding {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private ReactivePacketProcessor processor = new ReactivePacketProcessor();
private ApplicationId appId;
......@@ -150,6 +154,7 @@ public class ReactiveForwarding {
@Activate
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
appId = coreService.registerApplication("org.onosproject.fwd");
packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
......@@ -174,6 +179,7 @@ public class ReactiveForwarding {
@Deactivate
public void deactivate() {
cfgService.unregisterProperties(getClass(), false);
flowRuleService.removeFlowRulesById(appId);
packetService.removeProcessor(processor);
processor = null;
......
# Temporary: to be auto-generated in near future
packetOutOnly|BOOLEAN|false|Enable packet-out only forwarding; default is false
packetOutOfppTable|BOOLEAN|false|Enable first packet forwarding using OFPP_TABLE port instead of PacketOut with actual port; default is false
flowTimeout|INTEGER|10|Configure Flow Timeout for installed flow rules; default is 10 sec
flowPriority|INTEGER|10|Configure Flow Priority for installed flow rules; default is 10
ipv6Forwarding|BOOLEAN|false|Enable IPv6 forwarding; default is false
matchDstMacOnly|BOOLEAN|false|Enable matching Dst Mac Only; default is false
matchVlanId|BOOLEAN|false|Enable matching Vlan ID; default is false
matchIpv4Address|BOOLEAN|false|Enable matching IPv4 Addresses; default is false
matchIpv4Dscp|BOOLEAN|false|Enable matching IPv4 DSCP and ECN; default is false
matchIpv6Address|BOOLEAN|false|Enable matching IPv6 Addresses; default is false
matchIpv6FlowLabel|BOOLEAN|false|Enable matching IPv6 FlowLabel; default is false
matchTcpUdpPorts|BOOLEAN|false|Enable matching TCP/UDP ports; default is false
matchIcmpFields|BOOLEAN|false|Enable matching ICMPv4 and ICMPv6 fields; default is false
......@@ -15,8 +15,6 @@
*/
package org.onosproject.proxyarp;
import java.util.Dictionary;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -27,6 +25,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv6;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.flow.DefaultTrafficSelector;
......@@ -39,6 +38,8 @@ import org.onosproject.net.proxyarp.ProxyArpService;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Dictionary;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -61,6 +62,9 @@ public class ProxyArp {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private ApplicationId appId;
@Property(name = "ipv6NeighborDiscovery", boolValue = false,
......@@ -69,6 +73,7 @@ public class ProxyArp {
@Activate
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
appId = coreService.registerApplication("org.onosproject.proxyarp");
readComponentConfiguration(context);
......@@ -103,6 +108,7 @@ public class ProxyArp {
@Deactivate
public void deactivate() {
cfgService.unregisterProperties(getClass(), false);
packetService.removeProcessor(processor);
processor = null;
log.info("Stopped");
......
# Temporary: to be auto-generated in near future
ipv6NeighborDiscovery|BOOLEAN|false|Enable IPv6 Neighbor Discovery; default is false
......@@ -76,6 +76,7 @@ public abstract class Intent {
* @param appId application identifier
* @param key optional key
* @param resources required network resources (optional)
* @param priority flow rule priority
*/
protected Intent(ApplicationId appId,
Key key,
......@@ -156,6 +157,7 @@ public abstract class Intent {
* Binds an id generator for unique intent id generation.
*
* Note: A generator cannot be bound if there is already a generator bound.
*
* @param newIdGenerator id generator
*/
public static void bindIdGenerator(IdGenerator newIdGenerator) {
......@@ -167,6 +169,7 @@ public abstract class Intent {
* Unbinds an id generator.
*
* Note: The caller must provide the old id generator to succeed.
*
* @param oldIdGenerator the current id generator
*/
public static void unbindIdGenerator(IdGenerator oldIdGenerator) {
......
......@@ -203,9 +203,8 @@ public class ComponentConfigManager implements ComponentConfigService {
// Loads existing property values that may have been set.
private void loadExistingValues(String componentName) {
// FIXME: implement this by talking to the config admin.
try {
Configuration cfg = cfgAdmin.getConfiguration(componentName);
Configuration cfg = cfgAdmin.getConfiguration(componentName, null);
Map<String, ConfigProperty> map = properties.get(componentName);
Dictionary<String, Object> props = cfg.getProperties();
if (props != null) {
......@@ -229,7 +228,7 @@ public class ComponentConfigManager implements ComponentConfigService {
// after each other.
private void triggerUpdate(String componentName) {
try {
Configuration cfg = cfgAdmin.getConfiguration(componentName);
Configuration cfg = cfgAdmin.getConfiguration(componentName, null);
Map<String, ConfigProperty> map = properties.get(componentName);
Dictionary<String, Object> props = new Hashtable<>();
map.values().forEach(p -> props.put(p.name(), p.value()));
......
......@@ -17,6 +17,7 @@ package org.onosproject.net.topology.impl;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
......@@ -162,16 +163,16 @@ public class DefaultTopologyProvider extends AbstractProvider
return;
}
Dictionary properties = context.getProperties();
Dictionary<?, ?> properties = context.getProperties();
int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
try {
String s = (String) properties.get("maxEvents");
String s = get(properties, "maxEvents");
newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
s = (String) properties.get("maxBatchMs");
s = get(properties, "maxBatchMs");
newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
s = (String) properties.get("maxIdleMs");
s = get(properties, "maxIdleMs");
newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
} catch (NumberFormatException | ClassCastException e) {
......
#
# Copyright 2015 Open Networking Laboratory
#
# Licensed 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.
#
# This is for temporary provision for testing purposes and will be auto-generated.
# Temporary: to be auto-generated in near future
maxEvents|INTEGER|1000|Maximum number of events to accumulate
maxIdleMs|INTEGER|10|Maximum number of millis between events
maxBatchMs|INTEGER|50|Maximum number of millis for whole batch
......
......@@ -95,6 +95,7 @@ import java.util.stream.Collectors;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.onosproject.store.flow.impl.FlowStoreMessageSubjects.*;
......@@ -267,10 +268,10 @@ public class DistributedFlowRuleStore
int newPoolSize;
boolean newBackupEnabled;
try {
String s = (String) properties.get("msgHandlerPoolSize");
String s = get(properties, "msgHandlerPoolSize");
newPoolSize = isNullOrEmpty(s) ? msgHandlerPoolSize : Integer.parseInt(s.trim());
s = (String) properties.get("backupEnabled");
s = get(properties, "backupEnabled");
newBackupEnabled = isNullOrEmpty(s) ? backupEnabled : Boolean.parseBoolean(s.trim());
} catch (NumberFormatException | ClassCastException e) {
......
# Temporary: to be auto-generated in near future
msgHandlerPoolSize|INTEGER|8|Number of threads in the message handler pool
backupEnabled|BOOLEAN|false|Indicates whether backups are enabled or not
# Temporary: to be auto-generated in near future
msgHandlerPoolSize|INTEGER|8|Number of threads in the message handler pool
backupEnabled|BOOLEAN|DEFAULT_BACKUP_ENABLED|Indicates whether backups are enabled or not
......@@ -37,6 +37,7 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.VlanId;
import org.onlab.packet.ndp.NeighborAdvertisement;
import org.onlab.packet.ndp.NeighborSolicitation;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
......@@ -93,6 +94,9 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private HostProviderService providerService;
private final InternalHostProvider processor = new InternalHostProvider();
......@@ -118,8 +122,8 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid
@Activate
public void activate(ComponentContext context) {
appId =
coreService.registerApplication("org.onosproject.provider.host");
cfgService.registerProperties(getClass());
appId = coreService.registerApplication("org.onosproject.provider.host");
readComponentConfiguration(context);
providerService = providerRegistry.register(this);
......@@ -155,6 +159,7 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid
@Deactivate
public void deactivate() {
cfgService.unregisterProperties(getClass(), false);
providerRegistry.unregister(this);
packetService.removeProcessor(processor);
deviceService.removeListener(deviceListener);
......
# Temporary: to be auto-generated in near future
hostRemovalEnabled|BOOLEAN|true|Enable host removal on port/device down events
ipv6NeighborDiscovery|BOOLEAN|false|Enable using IPv6 Neighbor Discovery by the Host Location Provider; default is false
......@@ -46,6 +46,7 @@ import org.onlab.packet.Ethernet;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.DefaultApplicationId;
......@@ -145,6 +146,7 @@ public class HostLocationProviderTest {
.andReturn(appId).anyTimes();
replay(coreService);
provider.cfgService = new ComponentConfigAdapter();
provider.coreService = coreService;
provider.providerRegistry = hostRegistry;
......
......@@ -26,6 +26,7 @@ import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipEvent;
......@@ -61,10 +62,10 @@ import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an OpenFlow controller to detect network
* infrastructure links.
......@@ -95,22 +96,27 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService masterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private LinkProviderService providerService;
private ScheduledExecutorService executor;
@Property(name = PROP_USE_BDDP, label = "use BDDP for link discovery")
@Property(name = PROP_USE_BDDP, boolValue = true,
label = "Use BDDP for link discovery")
private boolean useBDDP = true;
@Property(name = PROP_DISABLE_LD, label = "permanently disable link discovery")
private boolean disableLD = false;
@Property(name = PROP_DISABLE_LD, boolValue = false,
label = "Permanently disable link discovery")
private boolean disableLinkDiscovery = false;
private static final long INIT_DELAY = 5;
private static final long DELAY = 5;
@Property(name = PROP_LLDP_SUPPRESSION,
@Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
label = "Path to LLDP suppression configuration file")
private String filePath = DEFAULT_LLDP_SUPPRESSION_CONFIG;
private String lldpSuppression = DEFAULT_LLDP_SUPPRESSION_CONFIG;
private final InternalLinkProvider listener = new InternalLinkProvider();
......@@ -131,12 +137,12 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
@Activate
public void activate(ComponentContext context) {
appId =
coreService.registerApplication("org.onosproject.provider.lldp");
cfgService.registerProperties(getClass());
appId = coreService.registerApplication("org.onosproject.provider.lldp");
// to load configuration at startup
modified(context);
if (disableLD) {
if (disableLinkDiscovery) {
log.info("Link Discovery has been permanently disabled by configuration");
return;
}
......@@ -177,7 +183,8 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
@Deactivate
public void deactivate() {
if (disableLD) {
cfgService.unregisterProperties(getClass(), false);
if (disableLinkDiscovery) {
return;
}
executor.shutdownNow();
......@@ -202,29 +209,29 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
@SuppressWarnings("rawtypes")
Dictionary properties = context.getProperties();
String s = (String) properties.get(PROP_DISABLE_LD);
String s = get(properties, PROP_DISABLE_LD);
if (!Strings.isNullOrEmpty(s)) {
disableLD = Boolean.valueOf(s);
disableLinkDiscovery = Boolean.valueOf(s);
}
s = (String) properties.get(PROP_USE_BDDP);
s = get(properties, PROP_USE_BDDP);
if (!Strings.isNullOrEmpty(s)) {
useBDDP = Boolean.valueOf(s);
}
s = (String) properties.get(PROP_LLDP_SUPPRESSION);
s = get(properties, PROP_LLDP_SUPPRESSION);
if (!Strings.isNullOrEmpty(s)) {
filePath = s;
lldpSuppression = s;
}
loadSuppressionRules();
}
private void loadSuppressionRules() {
SuppressionRulesStore store = new SuppressionRulesStore(filePath);
SuppressionRulesStore store = new SuppressionRulesStore(lldpSuppression);
try {
log.info("Reading suppression rules from {}", filePath);
log.info("Reading suppression rules from {}", lldpSuppression);
rules = store.read();
} catch (IOException e) {
log.info("Failed to load {}, using built-in rules", filePath);
log.info("Failed to load {}, using built-in rules", lldpSuppression);
// default rule to suppress ROADM to maintain compatibility
rules = new SuppressionRules(ImmutableSet.of(),
EnumSet.of(Device.Type.ROADM),
......@@ -307,7 +314,8 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
log.debug("Device added ({}) {}", event.type(),
deviceId);
discoverers.put(deviceId, new LinkDiscovery(device,
packetService, masterService, providerService, useBDDP));
packetService, masterService,
providerService, useBDDP));
} else {
if (ld.isStopped()) {
log.debug("Device restarted ({}) {}", event.type(),
......
# Temporary: to be auto-generated in near future
useBDDP|BOOLEAN|true|Use BDDP for link discovery
disableLinkDiscovery|BOOLEAN|false|Permanently disable link discovery
lldpSuppression|STRING|../config/lldp_suppression.json|Path to LLDP suppression configuration file
# Temporary: to be auto-generated in near future
useBDDP|BOOLEAN|true|Use BDDP for link discovery
disableLinkDiscovery|BOOLEAN|false|Permanently disable link discovery
lldpSuppression|STRING|../config/lldp_suppression.json|Path to LLDP suppression configuration file
......@@ -37,6 +37,7 @@ import org.junit.Test;
import org.onlab.packet.ChassisId;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONOSLLDP;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.cluster.NodeId;
import org.onosproject.cluster.RoleInfo;
import org.onosproject.core.ApplicationId;
......@@ -103,12 +104,12 @@ public class LLDPLinkProviderTest {
@Before
public void setUp() {
coreService = createMock(CoreService.class);
expect(coreService.registerApplication(appId.name()))
.andReturn(appId).anyTimes();
replay(coreService);
provider.cfgService = new ComponentConfigAdapter();
provider.coreService = coreService;
provider.deviceService = deviceService;
......
......@@ -22,6 +22,7 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
......@@ -65,6 +66,9 @@ public class NullHostProvider extends AbstractProvider implements HostProvider {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private HostProviderService providerService;
//make sure the device has enough ports to accomodate all of them.
......@@ -90,6 +94,7 @@ public class NullHostProvider extends AbstractProvider implements HostProvider {
@Activate
public void activate() {
cfgService.registerProperties(getClass());
providerService = providerRegistry.register(this);
for (Device dev : deviceService.getDevices()) {
addHosts(dev);
......@@ -101,6 +106,7 @@ public class NullHostProvider extends AbstractProvider implements HostProvider {
@Deactivate
public void deactivate() {
cfgService.unregisterProperties(getClass(), false);
providerRegistry.unregister(this);
deviceService.removeListener(hostProvider);
providerService = null;
......
......@@ -27,6 +27,7 @@ import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipService;
......@@ -101,6 +102,9 @@ public class NullLinkProvider extends AbstractProvider implements LinkProvider {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private LinkProviderService providerService;
private final InternalLinkProvider linkProvider = new InternalLinkProvider();
......@@ -119,7 +123,7 @@ public class NullLinkProvider extends AbstractProvider implements LinkProvider {
Executors.newScheduledThreadPool(THREADS, groupedThreads("onos/null", "link-driver-%d"));
// For flicker = true, duration between events in msec.
@Property(name = "eventRate", value = "0", label = "Duration between Link Event")
@Property(name = "eventRate", intValue = 0, label = "Duration between Link Event")
private int eventRate = DEFAULT_RATE;
// topology configuration file
......@@ -137,6 +141,7 @@ public class NullLinkProvider extends AbstractProvider implements LinkProvider {
@Activate
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
providerService = providerRegistry.register(this);
modified(context);
......@@ -166,6 +171,7 @@ public class NullLinkProvider extends AbstractProvider implements LinkProvider {
@Deactivate
public void deactivate(ComponentContext context) {
cfgService.unregisterProperties(getClass(), false);
linkDriver.shutdown();
try {
linkDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
......
# Temporary: to be auto-generated in near future
"eventRate"|INTEGER|0|Duration between Link Event
"cfgFile"|STRING|"/opt/onos/apache-karaf-3.0.2/etc/linkGraph.cfg"|Topology file location
......@@ -24,6 +24,7 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
......@@ -77,6 +78,9 @@ public class NullPacketProvider extends AbstractProvider implements
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
// Rate to generate PacketEvents, per second
@Property(name = "pktRate", intValue = DEFAULT_RATE,
label = "Rate of PacketEvent generation")
......@@ -91,6 +95,7 @@ public class NullPacketProvider extends AbstractProvider implements
@Activate
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
providerService = providerRegistry.register(this);
if (!modified(context)) {
packetDriver.submit(new PacketDriver());
......@@ -100,6 +105,7 @@ public class NullPacketProvider extends AbstractProvider implements
@Deactivate
public void deactivate(ComponentContext context) {
cfgService.unregisterProperties(getClass(), false);
try {
packetDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
......
# Temporary: to be auto-generated in near future
"pktRate"|INTEGER|5|Rate of PacketEvent generation
......@@ -35,6 +35,7 @@ import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.List;
import java.util.concurrent.ThreadFactory;
......@@ -142,6 +143,20 @@ public abstract class Tools {
}
/**
* Get property as a string value.
*
* @param properties properties to be looked up
* @param propertyName the name of the property to look up
* @return value when the propertyName is defined or return null
*/
public static String get(Dictionary<?, ?> properties, String propertyName) {
Object v = properties.get(propertyName);
String s = (v instanceof String) ? (String) v :
v != null ? v.toString() : null;
return Strings.isNullOrEmpty(s) ? null : s.trim();
}
/**
* Suspends the current thread for a specified number of millis.
*
* @param ms number of millis
......