tom

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 21 changed files with 631 additions and 251 deletions
1 +package org.onlab.onos.net;
2 +
3 +public final class AnnotationsUtil {
4 +
5 + public static boolean isEqual(Annotations lhs, Annotations rhs) {
6 + if (lhs == rhs) {
7 + return true;
8 + }
9 + if (lhs == null || rhs == null) {
10 + return false;
11 + }
12 +
13 + if (!lhs.keys().equals(rhs.keys())) {
14 + return false;
15 + }
16 +
17 + for (String key : lhs.keys()) {
18 + if (!lhs.value(key).equals(rhs.value(key))) {
19 + return false;
20 + }
21 + }
22 + return true;
23 + }
24 +
25 + // not to be instantiated
26 + private AnnotationsUtil() {}
27 +}
1 package org.onlab.onos.net.link; 1 package org.onlab.onos.net.link;
2 2
3 import org.onlab.onos.net.ConnectPoint; 3 import org.onlab.onos.net.ConnectPoint;
4 +import org.onlab.onos.net.Description;
4 import org.onlab.onos.net.Link; 5 import org.onlab.onos.net.Link;
5 6
6 /** 7 /**
7 * Describes an infrastructure link. 8 * Describes an infrastructure link.
8 */ 9 */
9 -public interface LinkDescription { 10 +public interface LinkDescription extends Description {
10 11
11 /** 12 /**
12 * Returns the link source. 13 * Returns the link source.
......
...@@ -76,7 +76,7 @@ public class HostManager ...@@ -76,7 +76,7 @@ public class HostManager
76 eventDispatcher.addSink(HostEvent.class, listenerRegistry); 76 eventDispatcher.addSink(HostEvent.class, listenerRegistry);
77 77
78 monitor = new HostMonitor(deviceService, packetService, this); 78 monitor = new HostMonitor(deviceService, packetService, this);
79 - 79 + monitor.start();
80 } 80 }
81 81
82 @Deactivate 82 @Deactivate
......
...@@ -2,7 +2,7 @@ package org.onlab.onos.net.host.impl; ...@@ -2,7 +2,7 @@ package org.onlab.onos.net.host.impl;
2 2
3 import java.nio.ByteBuffer; 3 import java.nio.ByteBuffer;
4 import java.util.ArrayList; 4 import java.util.ArrayList;
5 -import java.util.HashSet; 5 +import java.util.Collections;
6 import java.util.List; 6 import java.util.List;
7 import java.util.Set; 7 import java.util.Set;
8 import java.util.concurrent.ConcurrentHashMap; 8 import java.util.concurrent.ConcurrentHashMap;
...@@ -33,8 +33,6 @@ import org.onlab.packet.IpAddress; ...@@ -33,8 +33,6 @@ import org.onlab.packet.IpAddress;
33 import org.onlab.packet.IpPrefix; 33 import org.onlab.packet.IpPrefix;
34 import org.onlab.packet.MacAddress; 34 import org.onlab.packet.MacAddress;
35 import org.onlab.util.Timer; 35 import org.onlab.util.Timer;
36 -import org.slf4j.Logger;
37 -import org.slf4j.LoggerFactory;
38 36
39 /** 37 /**
40 * Monitors hosts on the dataplane to detect changes in host data. 38 * Monitors hosts on the dataplane to detect changes in host data.
...@@ -44,15 +42,6 @@ import org.slf4j.LoggerFactory; ...@@ -44,15 +42,6 @@ import org.slf4j.LoggerFactory;
44 * probe for hosts that have not yet been detected (specified by IP address). 42 * probe for hosts that have not yet been detected (specified by IP address).
45 */ 43 */
46 public class HostMonitor implements TimerTask { 44 public class HostMonitor implements TimerTask {
47 - private static final Logger log = LoggerFactory.getLogger(HostMonitor.class);
48 -
49 - private static final byte[] ZERO_MAC_ADDRESS =
50 - MacAddress.valueOf("00:00:00:00:00:00").getAddress();
51 -
52 - // TODO put on Ethernet
53 - private static final byte[] BROADCAST_MAC =
54 - MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
55 -
56 private DeviceService deviceService; 45 private DeviceService deviceService;
57 private PacketService packetService; 46 private PacketService packetService;
58 private HostManager hostManager; 47 private HostManager hostManager;
...@@ -64,8 +53,15 @@ public class HostMonitor implements TimerTask { ...@@ -64,8 +53,15 @@ public class HostMonitor implements TimerTask {
64 private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds 53 private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
65 private long probeRate = DEFAULT_PROBE_RATE; 54 private long probeRate = DEFAULT_PROBE_RATE;
66 55
67 - private final Timeout timeout; 56 + private Timeout timeout;
68 57
58 + /**
59 + * Creates a new host monitor.
60 + *
61 + * @param deviceService device service used to find edge ports
62 + * @param packetService packet service used to send packets on the data plane
63 + * @param hostService host service used to look up host information
64 + */
69 public HostMonitor(DeviceService deviceService, PacketService packetService, 65 public HostMonitor(DeviceService deviceService, PacketService packetService,
70 HostManager hostService) { 66 HostManager hostService) {
71 67
...@@ -73,24 +69,59 @@ public class HostMonitor implements TimerTask { ...@@ -73,24 +69,59 @@ public class HostMonitor implements TimerTask {
73 this.packetService = packetService; 69 this.packetService = packetService;
74 this.hostManager = hostService; 70 this.hostManager = hostService;
75 71
76 - monitoredAddresses = new HashSet<>(); 72 + monitoredAddresses = Collections.newSetFromMap(
73 + new ConcurrentHashMap<IpAddress, Boolean>());
77 hostProviders = new ConcurrentHashMap<>(); 74 hostProviders = new ConcurrentHashMap<>();
78 75
79 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS); 76 timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
80 } 77 }
81 78
79 + /**
80 + * Adds an IP address to be monitored by the host monitor. The monitor will
81 + * periodically probe the host to detect changes.
82 + *
83 + * @param ip IP address of the host to monitor
84 + */
82 void addMonitoringFor(IpAddress ip) { 85 void addMonitoringFor(IpAddress ip) {
83 monitoredAddresses.add(ip); 86 monitoredAddresses.add(ip);
84 } 87 }
85 88
89 + /**
90 + * Stops monitoring the given IP address.
91 + *
92 + * @param ip IP address to stop monitoring on
93 + */
86 void stopMonitoring(IpAddress ip) { 94 void stopMonitoring(IpAddress ip) {
87 monitoredAddresses.remove(ip); 95 monitoredAddresses.remove(ip);
88 } 96 }
89 97
98 + /**
99 + * Starts the host monitor. Does nothing if the monitor is already running.
100 + */
101 + void start() {
102 + synchronized (this) {
103 + if (timeout == null) {
104 + timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
105 + }
106 + }
107 + }
108 +
109 + /**
110 + * Stops the host monitor.
111 + */
90 void shutdown() { 112 void shutdown() {
113 + synchronized (this) {
91 timeout.cancel(); 114 timeout.cancel();
115 + timeout = null;
116 + }
92 } 117 }
93 118
119 + /**
120 + * Registers a host provider with the host monitor. The monitor can use the
121 + * provider to probe hosts.
122 + *
123 + * @param provider the host provider to register
124 + */
94 void registerHostProvider(HostProvider provider) { 125 void registerHostProvider(HostProvider provider) {
95 hostProviders.put(provider.id(), provider); 126 hostProviders.put(provider.id(), provider);
96 } 127 }
...@@ -117,7 +148,7 @@ public class HostMonitor implements TimerTask { ...@@ -117,7 +148,7 @@ public class HostMonitor implements TimerTask {
117 } 148 }
118 } 149 }
119 150
120 - timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS); 151 + this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
121 } 152 }
122 153
123 /** 154 /**
...@@ -173,12 +204,12 @@ public class HostMonitor implements TimerTask { ...@@ -173,12 +204,12 @@ public class HostMonitor implements TimerTask {
173 204
174 arp.setSenderHardwareAddress(sourceMac.getAddress()) 205 arp.setSenderHardwareAddress(sourceMac.getAddress())
175 .setSenderProtocolAddress(sourceIp.toOctets()) 206 .setSenderProtocolAddress(sourceIp.toOctets())
176 - .setTargetHardwareAddress(ZERO_MAC_ADDRESS) 207 + .setTargetHardwareAddress(MacAddress.ZERO_MAC_ADDRESS)
177 .setTargetProtocolAddress(targetIp.toOctets()); 208 .setTargetProtocolAddress(targetIp.toOctets());
178 209
179 Ethernet ethernet = new Ethernet(); 210 Ethernet ethernet = new Ethernet();
180 ethernet.setEtherType(Ethernet.TYPE_ARP) 211 ethernet.setEtherType(Ethernet.TYPE_ARP)
181 - .setDestinationMACAddress(BROADCAST_MAC) 212 + .setDestinationMACAddress(MacAddress.BROADCAST_MAC)
182 .setSourceMACAddress(sourceMac.getAddress()) 213 .setSourceMACAddress(sourceMac.getAddress())
183 .setPayload(arp); 214 .setPayload(arp);
184 215
......
...@@ -11,7 +11,7 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -11,7 +11,7 @@ import org.apache.felix.scr.annotations.Deactivate;
11 import org.apache.felix.scr.annotations.Reference; 11 import org.apache.felix.scr.annotations.Reference;
12 import org.apache.felix.scr.annotations.ReferenceCardinality; 12 import org.apache.felix.scr.annotations.ReferenceCardinality;
13 import org.apache.felix.scr.annotations.Service; 13 import org.apache.felix.scr.annotations.Service;
14 -import org.onlab.onos.net.Annotations; 14 +import org.onlab.onos.net.AnnotationsUtil;
15 import org.onlab.onos.net.DefaultAnnotations; 15 import org.onlab.onos.net.DefaultAnnotations;
16 import org.onlab.onos.net.DefaultDevice; 16 import org.onlab.onos.net.DefaultDevice;
17 import org.onlab.onos.net.DefaultPort; 17 import org.onlab.onos.net.DefaultPort;
...@@ -33,6 +33,7 @@ import org.onlab.onos.store.AbstractStore; ...@@ -33,6 +33,7 @@ import org.onlab.onos.store.AbstractStore;
33 import org.onlab.onos.store.ClockService; 33 import org.onlab.onos.store.ClockService;
34 import org.onlab.onos.store.Timestamp; 34 import org.onlab.onos.store.Timestamp;
35 import org.onlab.onos.store.common.impl.Timestamped; 35 import org.onlab.onos.store.common.impl.Timestamped;
36 +import org.onlab.util.NewConcurrentHashMap;
36 import org.slf4j.Logger; 37 import org.slf4j.Logger;
37 38
38 import java.util.ArrayList; 39 import java.util.ArrayList;
...@@ -136,8 +137,7 @@ public class GossipDeviceStore ...@@ -136,8 +137,7 @@ public class GossipDeviceStore
136 137
137 // Collection of DeviceDescriptions for a Device 138 // Collection of DeviceDescriptions for a Device
138 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 139 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
139 - = createIfAbsentUnchecked(deviceDescs, deviceId, 140 + = getDeviceDescriptions(deviceId);
140 - new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
141 141
142 142
143 DeviceDescriptions descs 143 DeviceDescriptions descs
...@@ -196,7 +196,7 @@ public class GossipDeviceStore ...@@ -196,7 +196,7 @@ public class GossipDeviceStore
196 // We allow only certain attributes to trigger update 196 // We allow only certain attributes to trigger update
197 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 197 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
198 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 198 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
199 - !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) { 199 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
200 200
201 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); 201 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
202 if (!replaced) { 202 if (!replaced) {
...@@ -223,8 +223,7 @@ public class GossipDeviceStore ...@@ -223,8 +223,7 @@ public class GossipDeviceStore
223 @Override 223 @Override
224 public DeviceEvent markOffline(DeviceId deviceId) { 224 public DeviceEvent markOffline(DeviceId deviceId) {
225 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 225 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
226 - = createIfAbsentUnchecked(deviceDescs, deviceId, 226 + = getDeviceDescriptions(deviceId);
227 - new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
228 227
229 // locking device 228 // locking device
230 synchronized (providerDescs) { 229 synchronized (providerDescs) {
...@@ -327,7 +326,7 @@ public class GossipDeviceStore ...@@ -327,7 +326,7 @@ public class GossipDeviceStore
327 Port newPort, 326 Port newPort,
328 Map<PortNumber, Port> ports) { 327 Map<PortNumber, Port> ports) {
329 if (oldPort.isEnabled() != newPort.isEnabled() || 328 if (oldPort.isEnabled() != newPort.isEnabled() ||
330 - !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) { 329 + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
331 330
332 ports.put(oldPort.number(), newPort); 331 ports.put(oldPort.number(), newPort);
333 return new DeviceEvent(PORT_UPDATED, device, newPort); 332 return new DeviceEvent(PORT_UPDATED, device, newPort);
...@@ -358,7 +357,13 @@ public class GossipDeviceStore ...@@ -358,7 +357,13 @@ public class GossipDeviceStore
358 // exist, it creates and registers a new one. 357 // exist, it creates and registers a new one.
359 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { 358 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
360 return createIfAbsentUnchecked(devicePorts, deviceId, 359 return createIfAbsentUnchecked(devicePorts, deviceId,
361 - new InitConcurrentHashMap<PortNumber, Port>()); 360 + NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
361 + }
362 +
363 + private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
364 + DeviceId deviceId) {
365 + return createIfAbsentUnchecked(deviceDescs, deviceId,
366 + NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
362 } 367 }
363 368
364 @Override 369 @Override
...@@ -438,33 +443,18 @@ public class GossipDeviceStore ...@@ -438,33 +443,18 @@ public class GossipDeviceStore
438 443
439 @Override 444 @Override
440 public DeviceEvent removeDevice(DeviceId deviceId) { 445 public DeviceEvent removeDevice(DeviceId deviceId) {
441 - synchronized (this) { 446 + ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
447 + synchronized (descs) {
442 Device device = devices.remove(deviceId); 448 Device device = devices.remove(deviceId);
449 + // should DEVICE_REMOVED carry removed ports?
450 + devicePorts.get(deviceId).clear();
451 + availableDevices.remove(deviceId);
452 + descs.clear();
443 return device == null ? null : 453 return device == null ? null :
444 new DeviceEvent(DEVICE_REMOVED, device, null); 454 new DeviceEvent(DEVICE_REMOVED, device, null);
445 } 455 }
446 } 456 }
447 457
448 - private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
449 - if (lhs == rhs) {
450 - return true;
451 - }
452 - if (lhs == null || rhs == null) {
453 - return false;
454 - }
455 -
456 - if (!lhs.keys().equals(rhs.keys())) {
457 - return false;
458 - }
459 -
460 - for (String key : lhs.keys()) {
461 - if (!lhs.value(key).equals(rhs.value(key))) {
462 - return false;
463 - }
464 - }
465 - return true;
466 - }
467 -
468 /** 458 /**
469 * Returns a Device, merging description given from multiple Providers. 459 * Returns a Device, merging description given from multiple Providers.
470 * 460 *
...@@ -567,14 +557,6 @@ public class GossipDeviceStore ...@@ -567,14 +557,6 @@ public class GossipDeviceStore
567 return fallBackPrimary; 557 return fallBackPrimary;
568 } 558 }
569 559
570 - private static final class InitConcurrentHashMap<K, V> implements
571 - ConcurrentInitializer<ConcurrentMap<K, V>> {
572 - @Override
573 - public ConcurrentMap<K, V> get() throws ConcurrentException {
574 - return new ConcurrentHashMap<>();
575 - }
576 - }
577 -
578 public static final class InitDeviceDescs 560 public static final class InitDeviceDescs
579 implements ConcurrentInitializer<DeviceDescriptions> { 561 implements ConcurrentInitializer<DeviceDescriptions> {
580 562
......
...@@ -116,17 +116,19 @@ public class GossipDeviceStoreTest { ...@@ -116,17 +116,19 @@ public class GossipDeviceStoreTest {
116 deviceClockManager.deactivate(); 116 deviceClockManager.deactivate();
117 } 117 }
118 118
119 - private void putDevice(DeviceId deviceId, String swVersion) { 119 + private void putDevice(DeviceId deviceId, String swVersion,
120 + SparseAnnotations... annotations) {
120 DeviceDescription description = 121 DeviceDescription description =
121 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 122 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
122 - HW, swVersion, SN); 123 + HW, swVersion, SN, annotations);
123 deviceStore.createOrUpdateDevice(PID, deviceId, description); 124 deviceStore.createOrUpdateDevice(PID, deviceId, description);
124 } 125 }
125 126
126 - private void putDeviceAncillary(DeviceId deviceId, String swVersion) { 127 + private void putDeviceAncillary(DeviceId deviceId, String swVersion,
128 + SparseAnnotations... annotations) {
127 DeviceDescription description = 129 DeviceDescription description =
128 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 130 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
129 - HW, swVersion, SN); 131 + HW, swVersion, SN, annotations);
130 deviceStore.createOrUpdateDevice(PIDA, deviceId, description); 132 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
131 } 133 }
132 134
...@@ -448,16 +450,37 @@ public class GossipDeviceStoreTest { ...@@ -448,16 +450,37 @@ public class GossipDeviceStoreTest {
448 450
449 @Test 451 @Test
450 public final void testRemoveDevice() { 452 public final void testRemoveDevice() {
451 - putDevice(DID1, SW1); 453 + putDevice(DID1, SW1, A1);
454 + List<PortDescription> pds = Arrays.<PortDescription>asList(
455 + new DefaultPortDescription(P1, true, A2)
456 + );
457 + deviceStore.updatePorts(PID, DID1, pds);
452 putDevice(DID2, SW1); 458 putDevice(DID2, SW1);
453 459
454 assertEquals(2, deviceStore.getDeviceCount()); 460 assertEquals(2, deviceStore.getDeviceCount());
461 + assertEquals(1, deviceStore.getPorts(DID1).size());
462 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
463 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
455 464
456 DeviceEvent event = deviceStore.removeDevice(DID1); 465 DeviceEvent event = deviceStore.removeDevice(DID1);
457 assertEquals(DEVICE_REMOVED, event.type()); 466 assertEquals(DEVICE_REMOVED, event.type());
458 assertDevice(DID1, SW1, event.subject()); 467 assertDevice(DID1, SW1, event.subject());
459 468
460 assertEquals(1, deviceStore.getDeviceCount()); 469 assertEquals(1, deviceStore.getDeviceCount());
470 + assertEquals(0, deviceStore.getPorts(DID1).size());
471 +
472 + // putBack Device, Port w/o annotation
473 + putDevice(DID1, SW1);
474 + List<PortDescription> pds2 = Arrays.<PortDescription>asList(
475 + new DefaultPortDescription(P1, true)
476 + );
477 + deviceStore.updatePorts(PID, DID1, pds2);
478 +
479 + // annotations should not survive
480 + assertEquals(2, deviceStore.getDeviceCount());
481 + assertEquals(1, deviceStore.getPorts(DID1).size());
482 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
483 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
461 } 484 }
462 485
463 // If Delegates should be called only on remote events, 486 // If Delegates should be called only on remote events,
......
...@@ -9,7 +9,7 @@ import org.apache.felix.scr.annotations.Activate; ...@@ -9,7 +9,7 @@ import org.apache.felix.scr.annotations.Activate;
9 import org.apache.felix.scr.annotations.Component; 9 import org.apache.felix.scr.annotations.Component;
10 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
11 import org.apache.felix.scr.annotations.Service; 11 import org.apache.felix.scr.annotations.Service;
12 -import org.onlab.onos.net.Annotations; 12 +import org.onlab.onos.net.AnnotationsUtil;
13 import org.onlab.onos.net.DefaultAnnotations; 13 import org.onlab.onos.net.DefaultAnnotations;
14 import org.onlab.onos.net.DefaultDevice; 14 import org.onlab.onos.net.DefaultDevice;
15 import org.onlab.onos.net.DefaultPort; 15 import org.onlab.onos.net.DefaultPort;
...@@ -28,6 +28,7 @@ import org.onlab.onos.net.device.DeviceStoreDelegate; ...@@ -28,6 +28,7 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
28 import org.onlab.onos.net.device.PortDescription; 28 import org.onlab.onos.net.device.PortDescription;
29 import org.onlab.onos.net.provider.ProviderId; 29 import org.onlab.onos.net.provider.ProviderId;
30 import org.onlab.onos.store.AbstractStore; 30 import org.onlab.onos.store.AbstractStore;
31 +import org.onlab.util.NewConcurrentHashMap;
31 import org.slf4j.Logger; 32 import org.slf4j.Logger;
32 33
33 import java.util.ArrayList; 34 import java.util.ArrayList;
...@@ -52,7 +53,6 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -52,7 +53,6 @@ import static org.slf4j.LoggerFactory.getLogger;
52 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; 53 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
53 import static org.onlab.onos.net.DefaultAnnotations.merge; 54 import static org.onlab.onos.net.DefaultAnnotations.merge;
54 55
55 -// TODO: synchronization should be done in more fine-grained manner.
56 /** 56 /**
57 * Manages inventory of infrastructure devices using trivial in-memory 57 * Manages inventory of infrastructure devices using trivial in-memory
58 * structures implementation. 58 * structures implementation.
...@@ -109,8 +109,7 @@ public class SimpleDeviceStore ...@@ -109,8 +109,7 @@ public class SimpleDeviceStore
109 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId, 109 public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
110 DeviceDescription deviceDescription) { 110 DeviceDescription deviceDescription) {
111 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 111 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
112 - = createIfAbsentUnchecked(deviceDescs, deviceId, 112 + = getDeviceDescriptions(deviceId);
113 - new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
114 113
115 Device oldDevice = devices.get(deviceId); 114 Device oldDevice = devices.get(deviceId);
116 115
...@@ -151,7 +150,7 @@ public class SimpleDeviceStore ...@@ -151,7 +150,7 @@ public class SimpleDeviceStore
151 // We allow only certain attributes to trigger update 150 // We allow only certain attributes to trigger update
152 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 151 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
153 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 152 !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
154 - !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) { 153 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
155 154
156 synchronized (this) { 155 synchronized (this) {
157 devices.replace(newDevice.id(), oldDevice, newDevice); 156 devices.replace(newDevice.id(), oldDevice, newDevice);
...@@ -238,7 +237,7 @@ public class SimpleDeviceStore ...@@ -238,7 +237,7 @@ public class SimpleDeviceStore
238 Port newPort, 237 Port newPort,
239 ConcurrentMap<PortNumber, Port> ports) { 238 ConcurrentMap<PortNumber, Port> ports) {
240 if (oldPort.isEnabled() != newPort.isEnabled() || 239 if (oldPort.isEnabled() != newPort.isEnabled() ||
241 - !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) { 240 + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
242 241
243 ports.put(oldPort.number(), newPort); 242 ports.put(oldPort.number(), newPort);
244 return new DeviceEvent(PORT_UPDATED, device, newPort); 243 return new DeviceEvent(PORT_UPDATED, device, newPort);
...@@ -264,11 +263,17 @@ public class SimpleDeviceStore ...@@ -264,11 +263,17 @@ public class SimpleDeviceStore
264 return events; 263 return events;
265 } 264 }
266 265
266 + private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions(
267 + DeviceId deviceId) {
268 + return createIfAbsentUnchecked(deviceDescs, deviceId,
269 + NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded());
270 + }
271 +
267 // Gets the map of ports for the specified device; if one does not already 272 // Gets the map of ports for the specified device; if one does not already
268 // exist, it creates and registers a new one. 273 // exist, it creates and registers a new one.
269 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { 274 private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
270 return createIfAbsentUnchecked(devicePorts, deviceId, 275 return createIfAbsentUnchecked(devicePorts, deviceId,
271 - new InitConcurrentHashMap<PortNumber, Port>()); 276 + NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
272 } 277 }
273 278
274 @Override 279 @Override
...@@ -323,33 +328,21 @@ public class SimpleDeviceStore ...@@ -323,33 +328,21 @@ public class SimpleDeviceStore
323 328
324 @Override 329 @Override
325 public DeviceEvent removeDevice(DeviceId deviceId) { 330 public DeviceEvent removeDevice(DeviceId deviceId) {
326 - synchronized (this) { 331 + ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId);
332 + synchronized (descs) {
327 Device device = devices.remove(deviceId); 333 Device device = devices.remove(deviceId);
334 + // should DEVICE_REMOVED carry removed ports?
335 + ConcurrentMap<PortNumber, Port> ports = devicePorts.get(deviceId);
336 + if (ports != null) {
337 + ports.clear();
338 + }
339 + availableDevices.remove(deviceId);
340 + descs.clear();
328 return device == null ? null : 341 return device == null ? null :
329 new DeviceEvent(DEVICE_REMOVED, device, null); 342 new DeviceEvent(DEVICE_REMOVED, device, null);
330 } 343 }
331 } 344 }
332 345
333 - private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
334 - if (lhs == rhs) {
335 - return true;
336 - }
337 - if (lhs == null || rhs == null) {
338 - return false;
339 - }
340 -
341 - if (!lhs.keys().equals(rhs.keys())) {
342 - return false;
343 - }
344 -
345 - for (String key : lhs.keys()) {
346 - if (!lhs.value(key).equals(rhs.value(key))) {
347 - return false;
348 - }
349 - }
350 - return true;
351 - }
352 -
353 /** 346 /**
354 * Returns a Device, merging description given from multiple Providers. 347 * Returns a Device, merging description given from multiple Providers.
355 * 348 *
...@@ -445,15 +438,6 @@ public class SimpleDeviceStore ...@@ -445,15 +438,6 @@ public class SimpleDeviceStore
445 return fallBackPrimary; 438 return fallBackPrimary;
446 } 439 }
447 440
448 - // TODO: can be made generic
449 - private static final class InitConcurrentHashMap<K, V> implements
450 - ConcurrentInitializer<ConcurrentMap<K, V>> {
451 - @Override
452 - public ConcurrentMap<K, V> get() throws ConcurrentException {
453 - return new ConcurrentHashMap<>();
454 - }
455 - }
456 -
457 public static final class InitDeviceDescs 441 public static final class InitDeviceDescs
458 implements ConcurrentInitializer<DeviceDescriptions> { 442 implements ConcurrentInitializer<DeviceDescriptions> {
459 private final DeviceDescription deviceDesc; 443 private final DeviceDescription deviceDesc;
......
1 package org.onlab.onos.store.trivial.impl; 1 package org.onlab.onos.store.trivial.impl;
2 2
3 +import com.google.common.base.Function;
4 +import com.google.common.base.Predicate;
5 +import com.google.common.collect.FluentIterable;
3 import com.google.common.collect.HashMultimap; 6 import com.google.common.collect.HashMultimap;
4 -import com.google.common.collect.ImmutableSet; 7 +import com.google.common.collect.SetMultimap;
5 -import com.google.common.collect.Multimap;
6 8
9 +import org.apache.commons.lang3.concurrent.ConcurrentUtils;
7 import org.apache.felix.scr.annotations.Activate; 10 import org.apache.felix.scr.annotations.Activate;
8 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
9 import org.apache.felix.scr.annotations.Deactivate; 12 import org.apache.felix.scr.annotations.Deactivate;
10 import org.apache.felix.scr.annotations.Service; 13 import org.apache.felix.scr.annotations.Service;
14 +import org.onlab.onos.net.Annotations;
15 +import org.onlab.onos.net.AnnotationsUtil;
11 import org.onlab.onos.net.ConnectPoint; 16 import org.onlab.onos.net.ConnectPoint;
17 +import org.onlab.onos.net.DefaultAnnotations;
12 import org.onlab.onos.net.DefaultLink; 18 import org.onlab.onos.net.DefaultLink;
13 import org.onlab.onos.net.DeviceId; 19 import org.onlab.onos.net.DeviceId;
14 import org.onlab.onos.net.Link; 20 import org.onlab.onos.net.Link;
21 +import org.onlab.onos.net.SparseAnnotations;
22 +import org.onlab.onos.net.Link.Type;
15 import org.onlab.onos.net.LinkKey; 23 import org.onlab.onos.net.LinkKey;
24 +import org.onlab.onos.net.Provided;
25 +import org.onlab.onos.net.link.DefaultLinkDescription;
16 import org.onlab.onos.net.link.LinkDescription; 26 import org.onlab.onos.net.link.LinkDescription;
17 import org.onlab.onos.net.link.LinkEvent; 27 import org.onlab.onos.net.link.LinkEvent;
18 import org.onlab.onos.net.link.LinkStore; 28 import org.onlab.onos.net.link.LinkStore;
19 import org.onlab.onos.net.link.LinkStoreDelegate; 29 import org.onlab.onos.net.link.LinkStoreDelegate;
20 import org.onlab.onos.net.provider.ProviderId; 30 import org.onlab.onos.net.provider.ProviderId;
21 import org.onlab.onos.store.AbstractStore; 31 import org.onlab.onos.store.AbstractStore;
32 +import org.onlab.util.NewConcurrentHashMap;
22 import org.slf4j.Logger; 33 import org.slf4j.Logger;
23 34
24 import java.util.Collections; 35 import java.util.Collections;
25 import java.util.HashSet; 36 import java.util.HashSet;
26 -import java.util.Map;
27 import java.util.Set; 37 import java.util.Set;
38 +import java.util.Map.Entry;
28 import java.util.concurrent.ConcurrentHashMap; 39 import java.util.concurrent.ConcurrentHashMap;
40 +import java.util.concurrent.ConcurrentMap;
29 41
42 +import static org.onlab.onos.net.DefaultAnnotations.merge;
30 import static org.onlab.onos.net.Link.Type.DIRECT; 43 import static org.onlab.onos.net.Link.Type.DIRECT;
31 import static org.onlab.onos.net.Link.Type.INDIRECT; 44 import static org.onlab.onos.net.Link.Type.INDIRECT;
32 import static org.onlab.onos.net.link.LinkEvent.Type.*; 45 import static org.onlab.onos.net.link.LinkEvent.Type.*;
33 import static org.slf4j.LoggerFactory.getLogger; 46 import static org.slf4j.LoggerFactory.getLogger;
47 +import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
48 +import static com.google.common.base.Predicates.notNull;
34 49
35 -// TODO: Add support for multiple provider and annotations
36 /** 50 /**
37 * Manages inventory of infrastructure links using trivial in-memory structures 51 * Manages inventory of infrastructure links using trivial in-memory structures
38 * implementation. 52 * implementation.
...@@ -46,11 +60,17 @@ public class SimpleLinkStore ...@@ -46,11 +60,17 @@ public class SimpleLinkStore
46 private final Logger log = getLogger(getClass()); 60 private final Logger log = getLogger(getClass());
47 61
48 // Link inventory 62 // Link inventory
49 - private final Map<LinkKey, DefaultLink> links = new ConcurrentHashMap<>(); 63 + private final ConcurrentMap<LinkKey,
64 + ConcurrentMap<ProviderId, LinkDescription>>
65 + linkDescs = new ConcurrentHashMap<>();
66 +
67 + // Link instance cache
68 + private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
50 69
51 // Egress and ingress link sets 70 // Egress and ingress link sets
52 - private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create(); 71 + private final SetMultimap<DeviceId, LinkKey> srcLinks = createSynchronizedHashMultiMap();
53 - private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create(); 72 + private final SetMultimap<DeviceId, LinkKey> dstLinks = createSynchronizedHashMultiMap();
73 +
54 74
55 @Activate 75 @Activate
56 public void activate() { 76 public void activate() {
...@@ -59,6 +79,10 @@ public class SimpleLinkStore ...@@ -59,6 +79,10 @@ public class SimpleLinkStore
59 79
60 @Deactivate 80 @Deactivate
61 public void deactivate() { 81 public void deactivate() {
82 + linkDescs.clear();
83 + links.clear();
84 + srcLinks.clear();
85 + dstLinks.clear();
62 log.info("Stopped"); 86 log.info("Stopped");
63 } 87 }
64 88
...@@ -69,17 +93,29 @@ public class SimpleLinkStore ...@@ -69,17 +93,29 @@ public class SimpleLinkStore
69 93
70 @Override 94 @Override
71 public Iterable<Link> getLinks() { 95 public Iterable<Link> getLinks() {
72 - return Collections.unmodifiableSet(new HashSet<Link>(links.values())); 96 + return Collections.unmodifiableCollection(links.values());
73 } 97 }
74 98
75 @Override 99 @Override
76 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) { 100 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
77 - return ImmutableSet.copyOf(srcLinks.get(deviceId)); 101 + // lock for iteration
102 + synchronized (srcLinks) {
103 + return FluentIterable.from(srcLinks.get(deviceId))
104 + .transform(lookupLink())
105 + .filter(notNull())
106 + .toSet();
107 + }
78 } 108 }
79 109
80 @Override 110 @Override
81 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) { 111 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
82 - return ImmutableSet.copyOf(dstLinks.get(deviceId)); 112 + // lock for iteration
113 + synchronized (dstLinks) {
114 + return FluentIterable.from(dstLinks.get(deviceId))
115 + .transform(lookupLink())
116 + .filter(notNull())
117 + .toSet();
118 + }
83 } 119 }
84 120
85 @Override 121 @Override
...@@ -90,9 +126,9 @@ public class SimpleLinkStore ...@@ -90,9 +126,9 @@ public class SimpleLinkStore
90 @Override 126 @Override
91 public Set<Link> getEgressLinks(ConnectPoint src) { 127 public Set<Link> getEgressLinks(ConnectPoint src) {
92 Set<Link> egress = new HashSet<>(); 128 Set<Link> egress = new HashSet<>();
93 - for (Link link : srcLinks.get(src.deviceId())) { 129 + for (LinkKey linkKey : srcLinks.get(src.deviceId())) {
94 - if (link.src().equals(src)) { 130 + if (linkKey.src().equals(src)) {
95 - egress.add(link); 131 + egress.add(links.get(linkKey));
96 } 132 }
97 } 133 }
98 return egress; 134 return egress;
...@@ -101,9 +137,9 @@ public class SimpleLinkStore ...@@ -101,9 +137,9 @@ public class SimpleLinkStore
101 @Override 137 @Override
102 public Set<Link> getIngressLinks(ConnectPoint dst) { 138 public Set<Link> getIngressLinks(ConnectPoint dst) {
103 Set<Link> ingress = new HashSet<>(); 139 Set<Link> ingress = new HashSet<>();
104 - for (Link link : dstLinks.get(dst.deviceId())) { 140 + for (LinkKey linkKey : dstLinks.get(dst.deviceId())) {
105 - if (link.dst().equals(dst)) { 141 + if (linkKey.dst().equals(dst)) {
106 - ingress.add(link); 142 + ingress.add(links.get(linkKey));
107 } 143 }
108 } 144 }
109 return ingress; 145 return ingress;
...@@ -113,56 +149,172 @@ public class SimpleLinkStore ...@@ -113,56 +149,172 @@ public class SimpleLinkStore
113 public LinkEvent createOrUpdateLink(ProviderId providerId, 149 public LinkEvent createOrUpdateLink(ProviderId providerId,
114 LinkDescription linkDescription) { 150 LinkDescription linkDescription) {
115 LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst()); 151 LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst());
116 - DefaultLink link = links.get(key); 152 +
117 - if (link == null) { 153 + ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
118 - return createLink(providerId, key, linkDescription); 154 + synchronized (descs) {
155 + final Link oldLink = links.get(key);
156 + // update description
157 + createOrUpdateLinkDescription(descs, providerId, linkDescription);
158 + final Link newLink = composeLink(descs);
159 + if (oldLink == null) {
160 + return createLink(key, newLink);
161 + }
162 + return updateLink(key, oldLink, newLink);
119 } 163 }
120 - return updateLink(providerId, link, key, linkDescription);
121 } 164 }
122 165
123 - // Creates and stores the link and returns the appropriate event. 166 + // Guarded by linkDescs value (=locking each Link)
124 - private LinkEvent createLink(ProviderId providerId, LinkKey key, 167 + private LinkDescription createOrUpdateLinkDescription(
168 + ConcurrentMap<ProviderId, LinkDescription> descs,
169 + ProviderId providerId,
125 LinkDescription linkDescription) { 170 LinkDescription linkDescription) {
126 - DefaultLink link = new DefaultLink(providerId, key.src(), key.dst(), 171 +
127 - linkDescription.type()); 172 + // merge existing attributes and merge
128 - synchronized (this) { 173 + LinkDescription oldDesc = descs.get(providerId);
129 - links.put(key, link); 174 + LinkDescription newDesc = linkDescription;
130 - srcLinks.put(link.src().deviceId(), link); 175 + if (oldDesc != null) {
131 - dstLinks.put(link.dst().deviceId(), link); 176 + SparseAnnotations merged = merge(oldDesc.annotations(),
177 + linkDescription.annotations());
178 + newDesc = new DefaultLinkDescription(
179 + linkDescription.src(),
180 + linkDescription.dst(),
181 + linkDescription.type(), merged);
182 + }
183 + return descs.put(providerId, newDesc);
132 } 184 }
133 - return new LinkEvent(LINK_ADDED, link); 185 +
186 + // Creates and stores the link and returns the appropriate event.
187 + // Guarded by linkDescs value (=locking each Link)
188 + private LinkEvent createLink(LinkKey key, Link newLink) {
189 +
190 + if (newLink.providerId().isAncillary()) {
191 + // TODO: revisit ancillary only Link handling
192 +
193 + // currently treating ancillary only as down (not visible outside)
194 + return null;
195 + }
196 +
197 + links.put(key, newLink);
198 + srcLinks.put(newLink.src().deviceId(), key);
199 + dstLinks.put(newLink.dst().deviceId(), key);
200 + return new LinkEvent(LINK_ADDED, newLink);
134 } 201 }
135 202
136 // Updates, if necessary the specified link and returns the appropriate event. 203 // Updates, if necessary the specified link and returns the appropriate event.
137 - private LinkEvent updateLink(ProviderId providerId, DefaultLink link, 204 + // Guarded by linkDescs value (=locking each Link)
138 - LinkKey key, LinkDescription linkDescription) { 205 + private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
139 - if (link.type() == INDIRECT && linkDescription.type() == DIRECT) { 206 +
140 - synchronized (this) { 207 + if (newLink.providerId().isAncillary()) {
141 - srcLinks.remove(link.src().deviceId(), link); 208 + // TODO: revisit ancillary only Link handling
142 - dstLinks.remove(link.dst().deviceId(), link); 209 +
143 - 210 + // currently treating ancillary only as down (not visible outside)
144 - DefaultLink updated = 211 + return null;
145 - new DefaultLink(providerId, link.src(), link.dst(),
146 - linkDescription.type());
147 - links.put(key, updated);
148 - srcLinks.put(link.src().deviceId(), updated);
149 - dstLinks.put(link.dst().deviceId(), updated);
150 - return new LinkEvent(LINK_UPDATED, updated);
151 } 212 }
213 +
214 + if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
215 + !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
216 +
217 + links.put(key, newLink);
218 + // strictly speaking following can be ommitted
219 + srcLinks.put(oldLink.src().deviceId(), key);
220 + dstLinks.put(oldLink.dst().deviceId(), key);
221 + return new LinkEvent(LINK_UPDATED, newLink);
152 } 222 }
153 return null; 223 return null;
154 } 224 }
155 225
156 @Override 226 @Override
157 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) { 227 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
158 - synchronized (this) { 228 + final LinkKey key = new LinkKey(src, dst);
159 - Link link = links.remove(new LinkKey(src, dst)); 229 + ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key);
230 + synchronized (descs) {
231 + Link link = links.remove(key);
232 + descs.clear();
160 if (link != null) { 233 if (link != null) {
161 - srcLinks.remove(link.src().deviceId(), link); 234 + srcLinks.remove(link.src().deviceId(), key);
162 - dstLinks.remove(link.dst().deviceId(), link); 235 + dstLinks.remove(link.dst().deviceId(), key);
163 return new LinkEvent(LINK_REMOVED, link); 236 return new LinkEvent(LINK_REMOVED, link);
164 } 237 }
165 return null; 238 return null;
166 } 239 }
167 } 240 }
241 +
242 + private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
243 + return synchronizedSetMultimap(HashMultimap.<K, V>create());
244 + }
245 +
246 + /**
247 + * @return primary ProviderID, or randomly chosen one if none exists
248 + */
249 + private ProviderId pickPrimaryPID(
250 + ConcurrentMap<ProviderId, LinkDescription> providerDescs) {
251 +
252 + ProviderId fallBackPrimary = null;
253 + for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
254 + if (!e.getKey().isAncillary()) {
255 + return e.getKey();
256 + } else if (fallBackPrimary == null) {
257 + // pick randomly as a fallback in case there is no primary
258 + fallBackPrimary = e.getKey();
259 + }
260 + }
261 + return fallBackPrimary;
262 + }
263 +
264 + private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) {
265 + ProviderId primary = pickPrimaryPID(descs);
266 + LinkDescription base = descs.get(primary);
267 +
268 + ConnectPoint src = base.src();
269 + ConnectPoint dst = base.dst();
270 + Type type = base.type();
271 + Annotations annotations = DefaultAnnotations.builder().build();
272 + annotations = merge(annotations, base.annotations());
273 +
274 + for (Entry<ProviderId, LinkDescription> e : descs.entrySet()) {
275 + if (primary.equals(e.getKey())) {
276 + continue;
277 + }
278 +
279 + // TODO: should keep track of Description timestamp
280 + // and only merge conflicting keys when timestamp is newer
281 + // Currently assuming there will never be a key conflict between
282 + // providers
283 +
284 + // annotation merging. not so efficient, should revisit later
285 + annotations = merge(annotations, e.getValue().annotations());
286 + }
287 +
288 + return new DefaultLink(primary , src, dst, type, annotations);
289 + }
290 +
291 + private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) {
292 + return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key,
293 + NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded());
294 + }
295 +
296 + private final Function<LinkKey, Link> lookupLink = new LookupLink();
297 + private Function<LinkKey, Link> lookupLink() {
298 + return lookupLink;
299 + }
300 +
301 + private final class LookupLink implements Function<LinkKey, Link> {
302 + @Override
303 + public Link apply(LinkKey input) {
304 + return links.get(input);
305 + }
306 + }
307 +
308 + private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
309 + private static final Predicate<Provided> isPrimary() {
310 + return IS_PRIMARY;
311 + }
312 +
313 + private static final class IsPrimary implements Predicate<Provided> {
314 +
315 + @Override
316 + public boolean apply(Provided input) {
317 + return !input.providerId().isAncillary();
318 + }
319 + }
168 } 320 }
......
...@@ -103,17 +103,19 @@ public class SimpleDeviceStoreTest { ...@@ -103,17 +103,19 @@ public class SimpleDeviceStoreTest {
103 simpleDeviceStore.deactivate(); 103 simpleDeviceStore.deactivate();
104 } 104 }
105 105
106 - private void putDevice(DeviceId deviceId, String swVersion) { 106 + private void putDevice(DeviceId deviceId, String swVersion,
107 + SparseAnnotations... annotations) {
107 DeviceDescription description = 108 DeviceDescription description =
108 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 109 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
109 - HW, swVersion, SN); 110 + HW, swVersion, SN, annotations);
110 deviceStore.createOrUpdateDevice(PID, deviceId, description); 111 deviceStore.createOrUpdateDevice(PID, deviceId, description);
111 } 112 }
112 113
113 - private void putDeviceAncillary(DeviceId deviceId, String swVersion) { 114 + private void putDeviceAncillary(DeviceId deviceId, String swVersion,
115 + SparseAnnotations... annotations) {
114 DeviceDescription description = 116 DeviceDescription description =
115 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 117 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
116 - HW, swVersion, SN); 118 + HW, swVersion, SN, annotations);
117 deviceStore.createOrUpdateDevice(PIDA, deviceId, description); 119 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
118 } 120 }
119 121
...@@ -126,6 +128,7 @@ public class SimpleDeviceStoreTest { ...@@ -126,6 +128,7 @@ public class SimpleDeviceStoreTest {
126 assertEquals(SN, device.serialNumber()); 128 assertEquals(SN, device.serialNumber());
127 } 129 }
128 130
131 + // TODO slice this out somewhere
129 /** 132 /**
130 * Verifies that Annotations created by merging {@code annotations} is 133 * Verifies that Annotations created by merging {@code annotations} is
131 * equal to actual Annotations. 134 * equal to actual Annotations.
...@@ -133,7 +136,7 @@ public class SimpleDeviceStoreTest { ...@@ -133,7 +136,7 @@ public class SimpleDeviceStoreTest {
133 * @param actual Annotations to check 136 * @param actual Annotations to check
134 * @param annotations 137 * @param annotations
135 */ 138 */
136 - private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) { 139 + public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
137 DefaultAnnotations expected = DefaultAnnotations.builder().build(); 140 DefaultAnnotations expected = DefaultAnnotations.builder().build();
138 for (SparseAnnotations a : annotations) { 141 for (SparseAnnotations a : annotations) {
139 expected = DefaultAnnotations.merge(expected, a); 142 expected = DefaultAnnotations.merge(expected, a);
...@@ -347,6 +350,7 @@ public class SimpleDeviceStoreTest { ...@@ -347,6 +350,7 @@ public class SimpleDeviceStoreTest {
347 assertFalse("Port is disabled", event.port().isEnabled()); 350 assertFalse("Port is disabled", event.port().isEnabled());
348 351
349 } 352 }
353 +
350 @Test 354 @Test
351 public final void testUpdatePortStatusAncillary() { 355 public final void testUpdatePortStatusAncillary() {
352 putDeviceAncillary(DID1, SW1); 356 putDeviceAncillary(DID1, SW1);
...@@ -435,16 +439,37 @@ public class SimpleDeviceStoreTest { ...@@ -435,16 +439,37 @@ public class SimpleDeviceStoreTest {
435 439
436 @Test 440 @Test
437 public final void testRemoveDevice() { 441 public final void testRemoveDevice() {
438 - putDevice(DID1, SW1); 442 + putDevice(DID1, SW1, A1);
443 + List<PortDescription> pds = Arrays.<PortDescription>asList(
444 + new DefaultPortDescription(P1, true, A2)
445 + );
446 + deviceStore.updatePorts(PID, DID1, pds);
439 putDevice(DID2, SW1); 447 putDevice(DID2, SW1);
440 448
441 assertEquals(2, deviceStore.getDeviceCount()); 449 assertEquals(2, deviceStore.getDeviceCount());
450 + assertEquals(1, deviceStore.getPorts(DID1).size());
451 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
452 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
442 453
443 DeviceEvent event = deviceStore.removeDevice(DID1); 454 DeviceEvent event = deviceStore.removeDevice(DID1);
444 assertEquals(DEVICE_REMOVED, event.type()); 455 assertEquals(DEVICE_REMOVED, event.type());
445 assertDevice(DID1, SW1, event.subject()); 456 assertDevice(DID1, SW1, event.subject());
446 457
447 assertEquals(1, deviceStore.getDeviceCount()); 458 assertEquals(1, deviceStore.getDeviceCount());
459 + assertEquals(0, deviceStore.getPorts(DID1).size());
460 +
461 + // putBack Device, Port w/o annotation
462 + putDevice(DID1, SW1);
463 + List<PortDescription> pds2 = Arrays.<PortDescription>asList(
464 + new DefaultPortDescription(P1, true)
465 + );
466 + deviceStore.updatePorts(PID, DID1, pds2);
467 +
468 + // annotations should not survive
469 + assertEquals(2, deviceStore.getDeviceCount());
470 + assertEquals(1, deviceStore.getPorts(DID1).size());
471 + assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
472 + assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
448 } 473 }
449 474
450 // If Delegates should be called only on remote events, 475 // If Delegates should be called only on remote events,
......
...@@ -4,7 +4,9 @@ import static org.junit.Assert.*; ...@@ -4,7 +4,9 @@ import static org.junit.Assert.*;
4 import static org.onlab.onos.net.DeviceId.deviceId; 4 import static org.onlab.onos.net.DeviceId.deviceId;
5 import static org.onlab.onos.net.Link.Type.*; 5 import static org.onlab.onos.net.Link.Type.*;
6 import static org.onlab.onos.net.link.LinkEvent.Type.*; 6 import static org.onlab.onos.net.link.LinkEvent.Type.*;
7 +import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
7 8
9 +import java.util.Collections;
8 import java.util.HashMap; 10 import java.util.HashMap;
9 import java.util.Map; 11 import java.util.Map;
10 import java.util.Set; 12 import java.util.Set;
...@@ -18,10 +20,12 @@ import org.junit.BeforeClass; ...@@ -18,10 +20,12 @@ import org.junit.BeforeClass;
18 import org.junit.Ignore; 20 import org.junit.Ignore;
19 import org.junit.Test; 21 import org.junit.Test;
20 import org.onlab.onos.net.ConnectPoint; 22 import org.onlab.onos.net.ConnectPoint;
23 +import org.onlab.onos.net.DefaultAnnotations;
21 import org.onlab.onos.net.DeviceId; 24 import org.onlab.onos.net.DeviceId;
22 import org.onlab.onos.net.Link; 25 import org.onlab.onos.net.Link;
23 import org.onlab.onos.net.LinkKey; 26 import org.onlab.onos.net.LinkKey;
24 import org.onlab.onos.net.PortNumber; 27 import org.onlab.onos.net.PortNumber;
28 +import org.onlab.onos.net.SparseAnnotations;
25 import org.onlab.onos.net.Link.Type; 29 import org.onlab.onos.net.Link.Type;
26 import org.onlab.onos.net.link.DefaultLinkDescription; 30 import org.onlab.onos.net.link.DefaultLinkDescription;
27 import org.onlab.onos.net.link.LinkEvent; 31 import org.onlab.onos.net.link.LinkEvent;
...@@ -37,6 +41,7 @@ import com.google.common.collect.Iterables; ...@@ -37,6 +41,7 @@ import com.google.common.collect.Iterables;
37 public class SimpleLinkStoreTest { 41 public class SimpleLinkStoreTest {
38 42
39 private static final ProviderId PID = new ProviderId("of", "foo"); 43 private static final ProviderId PID = new ProviderId("of", "foo");
44 + private static final ProviderId PIDA = new ProviderId("of", "bar", true);
40 private static final DeviceId DID1 = deviceId("of:foo"); 45 private static final DeviceId DID1 = deviceId("of:foo");
41 private static final DeviceId DID2 = deviceId("of:bar"); 46 private static final DeviceId DID2 = deviceId("of:bar");
42 47
...@@ -44,6 +49,23 @@ public class SimpleLinkStoreTest { ...@@ -44,6 +49,23 @@ public class SimpleLinkStoreTest {
44 private static final PortNumber P2 = PortNumber.portNumber(2); 49 private static final PortNumber P2 = PortNumber.portNumber(2);
45 private static final PortNumber P3 = PortNumber.portNumber(3); 50 private static final PortNumber P3 = PortNumber.portNumber(3);
46 51
52 + private static final SparseAnnotations A1 = DefaultAnnotations.builder()
53 + .set("A1", "a1")
54 + .set("B1", "b1")
55 + .build();
56 + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
57 + .remove("A1")
58 + .set("B3", "b3")
59 + .build();
60 + private static final SparseAnnotations A2 = DefaultAnnotations.builder()
61 + .set("A2", "a2")
62 + .set("B2", "b2")
63 + .build();
64 + private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
65 + .remove("A2")
66 + .set("B4", "b4")
67 + .build();
68 +
47 69
48 private SimpleLinkStore simpleLinkStore; 70 private SimpleLinkStore simpleLinkStore;
49 private LinkStore linkStore; 71 private LinkStore linkStore;
...@@ -69,16 +91,17 @@ public class SimpleLinkStoreTest { ...@@ -69,16 +91,17 @@ public class SimpleLinkStoreTest {
69 } 91 }
70 92
71 private void putLink(DeviceId srcId, PortNumber srcNum, 93 private void putLink(DeviceId srcId, PortNumber srcNum,
72 - DeviceId dstId, PortNumber dstNum, Type type) { 94 + DeviceId dstId, PortNumber dstNum, Type type,
95 + SparseAnnotations... annotations) {
73 ConnectPoint src = new ConnectPoint(srcId, srcNum); 96 ConnectPoint src = new ConnectPoint(srcId, srcNum);
74 ConnectPoint dst = new ConnectPoint(dstId, dstNum); 97 ConnectPoint dst = new ConnectPoint(dstId, dstNum);
75 - linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type)); 98 + linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
76 } 99 }
77 100
78 - private void putLink(LinkKey key, Type type) { 101 + private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
79 putLink(key.src().deviceId(), key.src().port(), 102 putLink(key.src().deviceId(), key.src().port(),
80 key.dst().deviceId(), key.dst().port(), 103 key.dst().deviceId(), key.dst().port(),
81 - type); 104 + type, annotations);
82 } 105 }
83 106
84 private static void assertLink(DeviceId srcId, PortNumber srcNum, 107 private static void assertLink(DeviceId srcId, PortNumber srcNum,
...@@ -270,14 +293,67 @@ public class SimpleLinkStoreTest { ...@@ -270,14 +293,67 @@ public class SimpleLinkStoreTest {
270 } 293 }
271 294
272 @Test 295 @Test
296 + public final void testCreateOrUpdateLinkAncillary() {
297 + ConnectPoint src = new ConnectPoint(DID1, P1);
298 + ConnectPoint dst = new ConnectPoint(DID2, P2);
299 +
300 + // add Ancillary link
301 + LinkEvent event = linkStore.createOrUpdateLink(PIDA,
302 + new DefaultLinkDescription(src, dst, INDIRECT, A1));
303 +
304 + assertNull("Ancillary only link is ignored", event);
305 +
306 + // add Primary link
307 + LinkEvent event2 = linkStore.createOrUpdateLink(PID,
308 + new DefaultLinkDescription(src, dst, INDIRECT, A2));
309 +
310 + assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
311 + assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
312 + assertEquals(LINK_ADDED, event2.type());
313 +
314 + // update link type
315 + LinkEvent event3 = linkStore.createOrUpdateLink(PID,
316 + new DefaultLinkDescription(src, dst, DIRECT, A2));
317 + assertLink(DID1, P1, DID2, P2, DIRECT, event3.subject());
318 + assertAnnotationsEquals(event3.subject().annotations(), A2, A1);
319 + assertEquals(LINK_UPDATED, event3.type());
320 +
321 +
322 + // no change
323 + LinkEvent event4 = linkStore.createOrUpdateLink(PID,
324 + new DefaultLinkDescription(src, dst, DIRECT));
325 + assertNull("No change event expected", event4);
326 +
327 + // update link annotation (Primary)
328 + LinkEvent event5 = linkStore.createOrUpdateLink(PID,
329 + new DefaultLinkDescription(src, dst, DIRECT, A2_2));
330 + assertLink(DID1, P1, DID2, P2, DIRECT, event5.subject());
331 + assertAnnotationsEquals(event5.subject().annotations(), A2, A2_2, A1);
332 + assertEquals(LINK_UPDATED, event5.type());
333 +
334 + // update link annotation (Ancillary)
335 + LinkEvent event6 = linkStore.createOrUpdateLink(PIDA,
336 + new DefaultLinkDescription(src, dst, DIRECT, A1_2));
337 + assertLink(DID1, P1, DID2, P2, DIRECT, event6.subject());
338 + assertAnnotationsEquals(event6.subject().annotations(), A2, A2_2, A1, A1_2);
339 + assertEquals(LINK_UPDATED, event6.type());
340 +
341 + // update link type (Ancillary) : ignored
342 + LinkEvent event7 = linkStore.createOrUpdateLink(PIDA,
343 + new DefaultLinkDescription(src, dst, EDGE));
344 + assertNull("Ancillary change other than annotation is ignored", event7);
345 + }
346 +
347 +
348 + @Test
273 public final void testRemoveLink() { 349 public final void testRemoveLink() {
274 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1); 350 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
275 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2); 351 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
276 LinkKey linkId1 = new LinkKey(d1P1, d2P2); 352 LinkKey linkId1 = new LinkKey(d1P1, d2P2);
277 LinkKey linkId2 = new LinkKey(d2P2, d1P1); 353 LinkKey linkId2 = new LinkKey(d2P2, d1P1);
278 354
279 - putLink(linkId1, DIRECT); 355 + putLink(linkId1, DIRECT, A1);
280 - putLink(linkId2, DIRECT); 356 + putLink(linkId2, DIRECT, A2);
281 357
282 // DID1,P1 => DID2,P2 358 // DID1,P1 => DID2,P2
283 // DID2,P2 => DID1,P1 359 // DID2,P2 => DID1,P1
...@@ -285,10 +361,41 @@ public class SimpleLinkStoreTest { ...@@ -285,10 +361,41 @@ public class SimpleLinkStoreTest {
285 361
286 LinkEvent event = linkStore.removeLink(d1P1, d2P2); 362 LinkEvent event = linkStore.removeLink(d1P1, d2P2);
287 assertEquals(LINK_REMOVED, event.type()); 363 assertEquals(LINK_REMOVED, event.type());
364 + assertAnnotationsEquals(event.subject().annotations(), A1);
288 LinkEvent event2 = linkStore.removeLink(d1P1, d2P2); 365 LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
289 assertNull(event2); 366 assertNull(event2);
290 367
291 assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1)); 368 assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
369 + assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(), A2);
370 +
371 + // annotations, etc. should not survive remove
372 + putLink(linkId1, DIRECT);
373 + assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
374 + assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
375 + }
376 +
377 + @Test
378 + public final void testAncillaryOnlyNotVisible() {
379 + ConnectPoint src = new ConnectPoint(DID1, P1);
380 + ConnectPoint dst = new ConnectPoint(DID2, P2);
381 +
382 + // add Ancillary link
383 + linkStore.createOrUpdateLink(PIDA,
384 + new DefaultLinkDescription(src, dst, INDIRECT, A1));
385 +
386 + // Ancillary only link should not be visible
387 + assertEquals(0, linkStore.getLinkCount());
388 +
389 + assertTrue(Iterables.isEmpty(linkStore.getLinks()));
390 +
391 + assertNull(linkStore.getLink(src, dst));
392 +
393 + assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
394 +
395 + assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
396 +
397 + assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
398 + assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
292 } 399 }
293 400
294 // If Delegates should be called only on remote events, 401 // If Delegates should be called only on remote events,
......
...@@ -167,11 +167,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -167,11 +167,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
167 // TODO We could check for the optional bitmap, but for now 167 // TODO We could check for the optional bitmap, but for now
168 // we are just checking the version number. 168 // we are just checking the version number.
169 if (m.getVersion() == OFVersion.OF_13) { 169 if (m.getVersion() == OFVersion.OF_13) {
170 - log.info("Received {} Hello from {}", m.getVersion(), 170 + log.debug("Received {} Hello from {}", m.getVersion(),
171 h.channel.getRemoteAddress()); 171 h.channel.getRemoteAddress());
172 h.ofVersion = OFVersion.OF_13; 172 h.ofVersion = OFVersion.OF_13;
173 } else if (m.getVersion() == OFVersion.OF_10) { 173 } else if (m.getVersion() == OFVersion.OF_10) {
174 - log.info("Received {} Hello from {} - switching to OF " 174 + log.debug("Received {} Hello from {} - switching to OF "
175 + "version 1.0", m.getVersion(), 175 + "version 1.0", m.getVersion(),
176 h.channel.getRemoteAddress()); 176 h.channel.getRemoteAddress());
177 h.ofVersion = OFVersion.OF_10; 177 h.ofVersion = OFVersion.OF_10;
...@@ -222,7 +222,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -222,7 +222,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
222 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) 222 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
223 throws IOException { 223 throws IOException {
224 h.thisdpid = m.getDatapathId().getLong(); 224 h.thisdpid = m.getDatapathId().getLong();
225 - log.info("Received features reply for switch at {} with dpid {}", 225 + log.debug("Received features reply for switch at {} with dpid {}",
226 h.getSwitchInfoString(), h.thisdpid); 226 h.getSwitchInfoString(), h.thisdpid);
227 227
228 h.featuresReply = m; //temp store 228 h.featuresReply = m; //temp store
...@@ -409,7 +409,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -409,7 +409,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
409 409
410 410
411 411
412 - log.info("Switch {} bound to class {}, description {}", 412 + log.debug("Switch {} bound to class {}, description {}",
413 new Object[] {h.sw, h.sw.getClass(), drep }); 413 new Object[] {h.sw, h.sw.getClass(), drep });
414 //Put switch in EQUAL mode until we hear back from the global registry 414 //Put switch in EQUAL mode until we hear back from the global registry
415 //log.debug("Setting new switch {} to EQUAL and sending Role request", 415 //log.debug("Setting new switch {} to EQUAL and sending Role request",
...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -651,7 +651,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
651 * @param error The error message 651 * @param error The error message
652 */ 652 */
653 protected void logError(OFChannelHandler h, OFErrorMsg error) { 653 protected void logError(OFChannelHandler h, OFErrorMsg error) {
654 - log.info("{} from switch {} in state {}", 654 + log.error("{} from switch {} in state {}",
655 new Object[] { 655 new Object[] {
656 error, 656 error,
657 h.getSwitchInfoString(), 657 h.getSwitchInfoString(),
...@@ -1050,7 +1050,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -1050,7 +1050,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
1050 throws Exception { 1050 throws Exception {
1051 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10; 1051 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1052 OFMessage m = factory.buildEchoRequest().build(); 1052 OFMessage m = factory.buildEchoRequest().build();
1053 - log.info("Sending Echo Request on idle channel: {}", 1053 + log.debug("Sending Echo Request on idle channel: {}",
1054 e.getChannel().getPipeline().getLast().toString()); 1054 e.getChannel().getPipeline().getLast().toString());
1055 e.getChannel().write(Collections.singletonList(m)); 1055 e.getChannel().write(Collections.singletonList(m));
1056 // XXX S some problems here -- echo request has no transaction id, and 1056 // XXX S some problems here -- echo request has no transaction id, and
......
...@@ -22,6 +22,12 @@ import java.util.Arrays; ...@@ -22,6 +22,12 @@ import java.util.Arrays;
22 * 22 *
23 */ 23 */
24 public class MacAddress { 24 public class MacAddress {
25 + public static final byte[] ZERO_MAC_ADDRESS =
26 + MacAddress.valueOf("00:00:00:00:00:00").getAddress();
27 +
28 + public static final byte[] BROADCAST_MAC =
29 + MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
30 +
25 public static final int MAC_ADDRESS_LENGTH = 6; 31 public static final int MAC_ADDRESS_LENGTH = 6;
26 private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH]; 32 private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
27 33
......
1 +package org.onlab.util;
2 +
3 +import java.util.concurrent.ConcurrentHashMap;
4 +import java.util.concurrent.ConcurrentMap;
5 +
6 +import org.apache.commons.lang3.concurrent.ConcurrentException;
7 +import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
8 +
9 +/**
10 + * Creates an instance of new ConcurrentHashMap on each {@link #get()} call.
11 + * <p>
12 + * To be used with
13 + * {@link org.apache.commons.lang3.concurrent.ConcurrentUtils#createIfAbsent()
14 + * ConcurrentUtils#createIfAbsent}
15 + *
16 + * @param <K> ConcurrentHashMap key type
17 + * @param <V> ConcurrentHashMap value type
18 + */
19 +public final class NewConcurrentHashMap<K, V>
20 + implements ConcurrentInitializer<ConcurrentMap<K, V>> {
21 +
22 + public static final NewConcurrentHashMap<?, ?> INSTANCE = new NewConcurrentHashMap<>();
23 +
24 + @SuppressWarnings("unchecked")
25 + public static <K, V> NewConcurrentHashMap<K, V> ifNeeded() {
26 + return (NewConcurrentHashMap<K, V>) INSTANCE;
27 + }
28 +
29 + @Override
30 + public ConcurrentMap<K, V> get() throws ConcurrentException {
31 + return new ConcurrentHashMap<>();
32 + }
33 +}
...@@ -9,7 +9,7 @@ public class EchoHandler implements MessageHandler { ...@@ -9,7 +9,7 @@ public class EchoHandler implements MessageHandler {
9 9
10 @Override 10 @Override
11 public void handle(Message message) throws IOException { 11 public void handle(Message message) throws IOException {
12 - System.out.println("Received: " + message.payload() + ". Echoing it back to the sender."); 12 + System.out.println("Received message. Echoing it back to the sender.");
13 message.respond(message.payload()); 13 message.respond(message.payload());
14 } 14 }
15 } 15 }
......
...@@ -8,6 +8,14 @@ public class Endpoint { ...@@ -8,6 +8,14 @@ public class Endpoint {
8 private final int port; 8 private final int port;
9 private final String host; 9 private final String host;
10 10
11 + /**
12 + * Used for serialization.
13 + */
14 + private Endpoint() {
15 + port = 0;
16 + host = null;
17 + }
18 +
11 public Endpoint(String host, int port) { 19 public Endpoint(String host, int port) {
12 this.host = host; 20 this.host = host;
13 this.port = port; 21 this.port = port;
......
...@@ -35,6 +35,10 @@ public final class InternalMessage implements Message { ...@@ -35,6 +35,10 @@ public final class InternalMessage implements Message {
35 return payload; 35 return payload;
36 } 36 }
37 37
38 + protected void setMessagingService(NettyMessagingService messagingService) {
39 + this.messagingService = messagingService;
40 + }
41 +
38 @Override 42 @Override
39 public void respond(Object data) throws IOException { 43 public void respond(Object data) throws IOException {
40 Builder builder = new Builder(messagingService); 44 Builder builder = new Builder(messagingService);
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 import org.onlab.util.KryoPool; 3 import org.onlab.util.KryoPool;
4 -import org.slf4j.Logger;
5 -import org.slf4j.LoggerFactory;
6 4
5 +import java.nio.ByteBuffer;
7 import java.util.ArrayList; 6 import java.util.ArrayList;
8 import java.util.HashMap; 7 import java.util.HashMap;
9 8
...@@ -12,8 +11,6 @@ import java.util.HashMap; ...@@ -12,8 +11,6 @@ import java.util.HashMap;
12 */ 11 */
13 public class KryoSerializer implements Serializer { 12 public class KryoSerializer implements Serializer {
14 13
15 - private final Logger log = LoggerFactory.getLogger(getClass());
16 -
17 private KryoPool serializerPool; 14 private KryoPool serializerPool;
18 15
19 public KryoSerializer() { 16 public KryoSerializer() {
...@@ -28,7 +25,9 @@ public class KryoSerializer implements Serializer { ...@@ -28,7 +25,9 @@ public class KryoSerializer implements Serializer {
28 serializerPool = KryoPool.newBuilder() 25 serializerPool = KryoPool.newBuilder()
29 .register(ArrayList.class, 26 .register(ArrayList.class,
30 HashMap.class, 27 HashMap.class,
31 - ArrayList.class 28 + ArrayList.class,
29 + InternalMessage.class,
30 + Endpoint.class
32 ) 31 )
33 .build() 32 .build()
34 .populate(1); 33 .populate(1);
...@@ -36,7 +35,7 @@ public class KryoSerializer implements Serializer { ...@@ -36,7 +35,7 @@ public class KryoSerializer implements Serializer {
36 35
37 36
38 @Override 37 @Override
39 - public Object decode(byte[] data) { 38 + public <T> T decode(byte[] data) {
40 return serializerPool.deserialize(data); 39 return serializerPool.deserialize(data);
41 } 40 }
42 41
...@@ -44,4 +43,14 @@ public class KryoSerializer implements Serializer { ...@@ -44,4 +43,14 @@ public class KryoSerializer implements Serializer {
44 public byte[] encode(Object payload) { 43 public byte[] encode(Object payload) {
45 return serializerPool.serialize(payload); 44 return serializerPool.serialize(payload);
46 } 45 }
46 +
47 + @Override
48 + public <T> T deserialize(ByteBuffer buffer) {
49 + return serializerPool.deserialize(buffer);
50 + }
51 +
52 + @Override
53 + public void serialize(Object obj, ByteBuffer buffer) {
54 + serializerPool.serialize(obj, buffer);
55 + }
47 } 56 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 -import java.util.Arrays;
4 -import java.util.List;
5 -
6 import static com.google.common.base.Preconditions.checkState; 3 import static com.google.common.base.Preconditions.checkState;
7 -
8 import io.netty.buffer.ByteBuf; 4 import io.netty.buffer.ByteBuf;
9 import io.netty.channel.ChannelHandlerContext; 5 import io.netty.channel.ChannelHandlerContext;
10 -import io.netty.handler.codec.ByteToMessageDecoder; 6 +import io.netty.handler.codec.ReplayingDecoder;
7 +
8 +import java.util.Arrays;
9 +import java.util.List;
11 10
12 -/** 11 +// TODO: Implement performance enchancements such as those described in the javadoc for ReplayingDecoder.
13 - * Decode bytes into a InternalMessage. 12 +public class MessageDecoder extends ReplayingDecoder<InternalMessage> {
14 - */
15 -public class MessageDecoder extends ByteToMessageDecoder {
16 13
17 private final NettyMessagingService messagingService; 14 private final NettyMessagingService messagingService;
18 private final Serializer serializer; 15 private final Serializer serializer;
...@@ -23,36 +20,21 @@ public class MessageDecoder extends ByteToMessageDecoder { ...@@ -23,36 +20,21 @@ public class MessageDecoder extends ByteToMessageDecoder {
23 } 20 }
24 21
25 @Override 22 @Override
26 - protected void decode(ChannelHandlerContext context, ByteBuf in, 23 + protected void decode(
27 - List<Object> messages) throws Exception { 24 + ChannelHandlerContext context,
25 + ByteBuf buffer,
26 + List<Object> out) throws Exception {
28 27
29 - byte[] preamble = in.readBytes(MessageEncoder.PREAMBLE.length).array(); 28 + byte[] preamble = new byte[MessageEncoder.PREAMBLE.length];
29 + buffer.readBytes(preamble);
30 checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble"); 30 checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble");
31 31
32 - // read message Id. 32 + int bodySize = buffer.readInt();
33 - long id = in.readLong(); 33 + byte[] body = new byte[bodySize];
34 - 34 + buffer.readBytes(body);
35 - // read message type; first read size and then bytes.
36 - String type = new String(in.readBytes(in.readInt()).array());
37 -
38 - // read sender host name; first read size and then bytes.
39 - String host = new String(in.readBytes(in.readInt()).array());
40 -
41 - // read sender port.
42 - int port = in.readInt();
43 -
44 - Endpoint sender = new Endpoint(host, port);
45 -
46 - // read message payload; first read size and then bytes.
47 - Object payload = serializer.decode(in.readBytes(in.readInt()).array());
48 -
49 - InternalMessage message = new InternalMessage.Builder(messagingService)
50 - .withId(id)
51 - .withSender(sender)
52 - .withType(type)
53 - .withPayload(payload)
54 - .build();
55 35
56 - messages.add(message); 36 + InternalMessage message = serializer.decode(body);
37 + message.setMessagingService(messagingService);
38 + out.add(message);
57 } 39 }
58 } 40 }
......
...@@ -19,42 +19,20 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> { ...@@ -19,42 +19,20 @@ public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
19 } 19 }
20 20
21 @Override 21 @Override
22 - protected void encode(ChannelHandlerContext context, InternalMessage message, 22 + protected void encode(
23 + ChannelHandlerContext context,
24 + InternalMessage message,
23 ByteBuf out) throws Exception { 25 ByteBuf out) throws Exception {
24 26
25 // write preamble 27 // write preamble
26 out.writeBytes(PREAMBLE); 28 out.writeBytes(PREAMBLE);
27 29
28 - // write id 30 + byte[] payload = serializer.encode(message);
29 - out.writeLong(message.id());
30 31
31 - // write type length 32 + // write payload length
32 - out.writeInt(message.type().length());
33 -
34 - // write type
35 - out.writeBytes(message.type().getBytes());
36 -
37 - // write sender host name size
38 - out.writeInt(message.sender().host().length());
39 -
40 - // write sender host name.
41 - out.writeBytes(message.sender().host().getBytes());
42 -
43 - // write port
44 - out.writeInt(message.sender().port());
45 -
46 - try {
47 - serializer.encode(message.payload());
48 - } catch (Exception e) {
49 - e.printStackTrace();
50 - }
51 -
52 - byte[] payload = serializer.encode(message.payload());
53 -
54 - // write payload length.
55 out.writeInt(payload.length); 33 out.writeInt(payload.length);
56 34
57 - // write payload bytes 35 + // write payload.
58 out.writeBytes(payload); 36 out.writeBytes(payload);
59 } 37 }
60 } 38 }
......
...@@ -22,7 +22,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; ...@@ -22,7 +22,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
22 import io.netty.channel.socket.nio.NioSocketChannel; 22 import io.netty.channel.socket.nio.NioSocketChannel;
23 23
24 import org.apache.commons.lang.math.RandomUtils; 24 import org.apache.commons.lang.math.RandomUtils;
25 -import org.apache.commons.pool.KeyedObjectPool;
26 import org.apache.commons.pool.KeyedPoolableObjectFactory; 25 import org.apache.commons.pool.KeyedPoolableObjectFactory;
27 import org.apache.commons.pool.impl.GenericKeyedObjectPool; 26 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
28 import org.slf4j.Logger; 27 import org.slf4j.Logger;
...@@ -38,8 +37,8 @@ public class NettyMessagingService implements MessagingService { ...@@ -38,8 +37,8 @@ public class NettyMessagingService implements MessagingService {
38 37
39 private final Logger log = LoggerFactory.getLogger(getClass()); 38 private final Logger log = LoggerFactory.getLogger(getClass());
40 39
41 - private KeyedObjectPool<Endpoint, Channel> channels = 40 + private GenericKeyedObjectPool<Endpoint, Channel> channels;
42 - new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory()); 41 +
43 private final int port; 42 private final int port;
44 private final EventLoopGroup bossGroup = new NioEventLoopGroup(); 43 private final EventLoopGroup bossGroup = new NioEventLoopGroup();
45 private final EventLoopGroup workerGroup = new NioEventLoopGroup(); 44 private final EventLoopGroup workerGroup = new NioEventLoopGroup();
...@@ -66,6 +65,9 @@ public class NettyMessagingService implements MessagingService { ...@@ -66,6 +65,9 @@ public class NettyMessagingService implements MessagingService {
66 } 65 }
67 66
68 public void activate() throws Exception { 67 public void activate() throws Exception {
68 + channels = new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
69 + channels.setTestOnBorrow(true);
70 + channels.setTestOnReturn(true);
69 responseFutures = CacheBuilder.newBuilder() 71 responseFutures = CacheBuilder.newBuilder()
70 .maximumSize(100000) 72 .maximumSize(100000)
71 .weakValues() 73 .weakValues()
...@@ -95,17 +97,14 @@ public class NettyMessagingService implements MessagingService { ...@@ -95,17 +97,14 @@ public class NettyMessagingService implements MessagingService {
95 protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException { 97 protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException {
96 Channel channel = null; 98 Channel channel = null;
97 try { 99 try {
100 + try {
98 channel = channels.borrowObject(ep); 101 channel = channels.borrowObject(ep);
99 channel.eventLoop().execute(new WriteTask(channel, message)); 102 channel.eventLoop().execute(new WriteTask(channel, message));
100 - } catch (Exception e) {
101 - throw new IOException(e);
102 } finally { 103 } finally {
103 - try {
104 channels.returnObject(ep, channel); 104 channels.returnObject(ep, channel);
105 - } catch (Exception e) {
106 - log.warn("Error returning object back to the pool", e);
107 - // ignored.
108 } 105 }
106 + } catch (Exception e) {
107 + throw new IOException(e);
109 } 108 }
110 } 109 }
111 110
...@@ -141,6 +140,8 @@ public class NettyMessagingService implements MessagingService { ...@@ -141,6 +140,8 @@ public class NettyMessagingService implements MessagingService {
141 140
142 private void startAcceptingConnections() throws InterruptedException { 141 private void startAcceptingConnections() throws InterruptedException {
143 ServerBootstrap b = new ServerBootstrap(); 142 ServerBootstrap b = new ServerBootstrap();
143 + b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
144 + b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 32 * 1024);
144 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 145 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
145 b.group(bossGroup, workerGroup) 146 b.group(bossGroup, workerGroup)
146 .channel(NioServerSocketChannel.class) 147 .channel(NioServerSocketChannel.class)
...@@ -169,6 +170,8 @@ public class NettyMessagingService implements MessagingService { ...@@ -169,6 +170,8 @@ public class NettyMessagingService implements MessagingService {
169 public Channel makeObject(Endpoint ep) throws Exception { 170 public Channel makeObject(Endpoint ep) throws Exception {
170 Bootstrap b = new Bootstrap(); 171 Bootstrap b = new Bootstrap();
171 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 172 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
173 + b.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
174 + b.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 32 * 1024);
172 b.group(workerGroup); 175 b.group(workerGroup);
173 // TODO: Make this faster: 176 // TODO: Make this faster:
174 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0 177 // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
...@@ -197,20 +200,20 @@ public class NettyMessagingService implements MessagingService { ...@@ -197,20 +200,20 @@ public class NettyMessagingService implements MessagingService {
197 @Override 200 @Override
198 protected void initChannel(SocketChannel channel) throws Exception { 201 protected void initChannel(SocketChannel channel) throws Exception {
199 channel.pipeline() 202 channel.pipeline()
200 - .addLast(new MessageEncoder(serializer)) 203 + .addLast("encoder", new MessageEncoder(serializer))
201 - .addLast(new MessageDecoder(NettyMessagingService.this, serializer)) 204 + .addLast("decoder", new MessageDecoder(NettyMessagingService.this, serializer))
202 - .addLast(new NettyMessagingService.InboundMessageDispatcher()); 205 + .addLast("handler", new InboundMessageDispatcher());
203 } 206 }
204 } 207 }
205 208
206 private class WriteTask implements Runnable { 209 private class WriteTask implements Runnable {
207 210
208 - private final Object message; 211 + private final InternalMessage message;
209 private final Channel channel; 212 private final Channel channel;
210 213
211 - public WriteTask(Channel channel, Object message) { 214 + public WriteTask(Channel channel, InternalMessage message) {
212 - this.message = message;
213 this.channel = channel; 215 this.channel = channel;
216 + this.message = message;
214 } 217 }
215 218
216 @Override 219 @Override
...@@ -240,5 +243,11 @@ public class NettyMessagingService implements MessagingService { ...@@ -240,5 +243,11 @@ public class NettyMessagingService implements MessagingService {
240 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type); 243 MessageHandler handler = NettyMessagingService.this.getMessageHandler(type);
241 handler.handle(message); 244 handler.handle(message);
242 } 245 }
246 +
247 +
248 + @Override
249 + public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
250 + context.close();
251 + }
243 } 252 }
244 } 253 }
......
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 +import java.nio.ByteBuffer;
4 +
3 /** 5 /**
4 * Interface for encoding/decoding message payloads. 6 * Interface for encoding/decoding message payloads.
5 */ 7 */
...@@ -11,7 +13,7 @@ public interface Serializer { ...@@ -11,7 +13,7 @@ public interface Serializer {
11 * @param data byte array. 13 * @param data byte array.
12 * @return POJO 14 * @return POJO
13 */ 15 */
14 - Object decode(byte[] data); 16 + public <T> T decode(byte[] data);
15 17
16 /** 18 /**
17 * Encodes the specified POJO into a byte array. 19 * Encodes the specified POJO into a byte array.
...@@ -19,6 +21,23 @@ public interface Serializer { ...@@ -19,6 +21,23 @@ public interface Serializer {
19 * @param data POJO to be encoded 21 * @param data POJO to be encoded
20 * @return byte array. 22 * @return byte array.
21 */ 23 */
22 - byte[] encode(Object message); 24 + public byte[] encode(Object data);
25 +
26 + /**
27 + * Serializes the specified object into bytes using one of the
28 + * pre-registered serializers.
29 + *
30 + * @param obj object to be serialized
31 + * @param buffer to write serialized bytes
32 + */
33 + public void serialize(final Object obj, ByteBuffer buffer);
23 34
35 + /**
36 + * Deserializes the specified bytes into an object using one of the
37 + * pre-registered serializers.
38 + *
39 + * @param buffer bytes to be deserialized
40 + * @return deserialized object
41 + */
42 + public <T> T deserialize(final ByteBuffer buffer);
24 } 43 }
......