blob: c898f70ecaef69cfd68a6302b1d7b85f5c141e58 [file] [log] [blame]
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -08001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.net.optical.device;
17
18import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.List;
21import java.util.Map;
22import java.util.Optional;
23
24import org.onosproject.net.device.DeviceEvent;
25import org.onosproject.net.device.DeviceListener;
26import org.onosproject.net.device.DeviceService;
27import org.onosproject.net.optical.OpticalDevice;
28import org.onosproject.net.optical.utils.ForwardingDeviceService;
29import org.slf4j.Logger;
30
31import com.google.common.annotations.Beta;
32import com.google.common.cache.CacheBuilder;
33import com.google.common.cache.CacheLoader;
34import com.google.common.cache.LoadingCache;
35import com.google.common.collect.Lists;
36import com.google.common.collect.Maps;
37
38import org.apache.commons.lang3.tuple.Pair;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.Element;
41import org.onosproject.net.Port;
42import org.onosproject.net.PortNumber;
43
44
45// TODO replace places using DeviceService expecting Optical specific ports.
46// with this
47
48/**
49 * Decorator, which provides a DeviceService view, which returns
50 * Ports in optical specific ports.
51 */
52@Beta
53public class OpticalDeviceServiceView
54 extends ForwardingDeviceService
55 implements DeviceService {
56
57 private static final Logger log = getLogger(OpticalDeviceServiceView.class);
58
59 /**
60 * DeviceListener to wrapped DeviceListener map.
61 * <p>
62 * {@literal original listener -> wrapped listener}
63 */
64 private final Map<DeviceListener, DeviceListener> wrapped = Maps.newIdentityHashMap();
65
66 // May need a way to monitor Drivers loaded on ONOS and
67 // invalidate this Cache if a driver was added/updated
68 /**
69 * Device to {@link OpticalDevice} map cache.
70 */
71 private final LoadingCache<Element, Optional<OpticalDevice>> optdev
72 = CacheBuilder.newBuilder()
73 .weakKeys() // == for Key comparison
74 .maximumSize(100)
75 .build(CacheLoader.from(elm -> {
76 if (elm.is(OpticalDevice.class)) {
77 return Optional.of(elm.as(OpticalDevice.class));
78 } else {
79 return Optional.empty();
80 }
81 }));
82
83 // Not intended to be instantiated directly
84 protected OpticalDeviceServiceView(DeviceService base) {
85 super(base);
86 }
87
88 /**
89 * Wraps the given DeviceService to provide a view,
90 * which returns port as optical specific Port class.
91 *
92 * @param base {@link DeviceService} view to use as baseline.
93 * @return Decorated view of {@code base}
94 */
95 public static OpticalDeviceServiceView opticalView(DeviceService base) {
96 // TODO might make sense to track and assign an instance for each `base`
97 return new OpticalDeviceServiceView(base);
98 }
99
100 /**
101 * Transform Port instance on the event to Optical specific port, if it is well-formed.
102 *
103 * @param event original event to transform
104 * @return transformed {@link DeviceEvent}
105 */
106 public DeviceEvent augment(DeviceEvent event) {
107 final Port port = augment(event.port());
108 if (port == event.port()) {
109 // If the Port not changed, pass through
110 return event;
111 }
112 return new DeviceEvent(event.type(), event.subject(), port, event.time());
113 }
114
115 /**
116 * Transform Port instance to Optical specific port, if it is well-formed.
117 *
118 * @param port Port instance to translate
119 * @return Optical specific port instance or original {@code port}.
120 */
121 public Port augment(Port port) {
122 if (port == null) {
123 return null;
124 }
125 return optdev.getUnchecked(port.element())
126 .map(odev -> odev.port(port))
127 .orElse(port);
128 }
129
130 @Override
131 public void addListener(DeviceListener listener) {
132 super.addListener(wrapped.computeIfAbsent(listener, OpticalDeviceListener::new));
133 }
134
135 @Override
136 public void removeListener(DeviceListener listener) {
137 DeviceListener wrappedListener = wrapped.remove(listener);
138 if (wrappedListener != null) {
139 super.removeListener(wrappedListener);
140 }
141 }
142
143
144 @Override
145 public List<Port> getPorts(DeviceId deviceId) {
146 return Lists.transform(super.getPorts(deviceId),
147 this::augment);
148 }
149
150 @Override
151 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
152 return augment(super.getPort(deviceId, portNumber));
153 }
154
155
156 /**
157 * DeviceListener, which translates generic Port to optical specific Port
158 * before passing.
159 */
160 class OpticalDeviceListener implements DeviceListener {
161
162 private final DeviceListener listener;
163
164 // shallow cache to reuse transformed event in isRelevant and event call
165 private Pair<DeviceEvent, DeviceEvent> cache;
166
167 public OpticalDeviceListener(DeviceListener listener) {
168 this.listener = listener;
169 }
170
171 private DeviceEvent opticalEvent(DeviceEvent event) {
172
173 Pair<DeviceEvent, DeviceEvent> entry = cache;
174 if (entry != null && entry.getLeft() == event) {
175 return entry.getRight();
176 }
177
178 DeviceEvent opticalEvent = augment(event);
179 cache = Pair.of(event, opticalEvent);
180 return opticalEvent;
181 }
182
183 @Override
184 public boolean isRelevant(DeviceEvent event) {
185 return listener.isRelevant(opticalEvent(event));
186 }
187
188 @Override
189 public void event(DeviceEvent event) {
190 listener.event(opticalEvent(event));
191 }
192 }
193
194}