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 | } | ... | ... |
-
Please register or login to post a comment