/* * Copyright 2016-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.net.optical.device; import static org.slf4j.LoggerFactory.getLogger; import java.util.List; import java.util.Map; import java.util.Optional; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; import org.onosproject.net.device.PortStatistics; import org.onosproject.net.optical.OpticalDevice; import org.onosproject.net.utils.ForwardingDeviceService; import org.slf4j.Logger; import com.google.common.annotations.Beta; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.lang3.tuple.Pair; import org.onosproject.net.DeviceId; import org.onosproject.net.Element; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; // TODO replace places using DeviceService expecting Optical specific ports. // with this /** * Decorator, which provides a DeviceService view, which returns * Ports in optical specific ports. */ @Beta public class OpticalDeviceServiceView extends ForwardingDeviceService implements DeviceService { private static final Logger log = getLogger(OpticalDeviceServiceView.class); /** * DeviceListener to wrapped DeviceListener map. * <p> * {@literal original listener -> wrapped listener} */ private final Map<DeviceListener, DeviceListener> wrapped = Maps.newIdentityHashMap(); // May need a way to monitor Drivers loaded on ONOS and // invalidate this Cache if a driver was added/updated /** * Device to {@link OpticalDevice} map cache. */ private final LoadingCache<Element, Optional<OpticalDevice>> optdev = CacheBuilder.newBuilder() .weakKeys() // == for Key comparison .maximumSize(100) .build(CacheLoader.from(elm -> { if (elm.is(OpticalDevice.class)) { return Optional.of(elm.as(OpticalDevice.class)); } else { return Optional.empty(); } })); // Not intended to be instantiated directly protected OpticalDeviceServiceView(DeviceService base) { super(base); } /** * Wraps the given DeviceService to provide a view, * which returns port as optical specific Port class. * * @param base {@link DeviceService} view to use as baseline. * @return Decorated view of {@code base} */ public static OpticalDeviceServiceView opticalView(DeviceService base) { // TODO might make sense to track and assign an instance for each `base` return new OpticalDeviceServiceView(base); } /** * Transform Port instance on the event to Optical specific port, if it is well-formed. * * @param event original event to transform * @return transformed {@link DeviceEvent} */ public DeviceEvent augment(DeviceEvent event) { final Port port = augment(event.port()); if (port == event.port()) { // If the Port not changed, pass through return event; } return new DeviceEvent(event.type(), event.subject(), port, event.time()); } /** * Transform Port instance to Optical specific port, if it is well-formed. * * @param port Port instance to translate * @return Optical specific port instance or original {@code port}. */ public Port augment(Port port) { if (port == null) { return null; } return optdev.getUnchecked(port.element()) .map(odev -> odev.port(port)) .orElse(port); } @Override public void addListener(DeviceListener listener) { super.addListener(wrapped.computeIfAbsent(listener, OpticalDeviceListener::new)); } @Override public void removeListener(DeviceListener listener) { DeviceListener wrappedListener = wrapped.remove(listener); if (wrappedListener != null) { super.removeListener(wrappedListener); } } @Override public List<Port> getPorts(DeviceId deviceId) { return Lists.transform(super.getPorts(deviceId), this::augment); } @Override public PortStatistics getStatisticsForPort(DeviceId deviceId, PortNumber portNumber) { return null; } @Override public PortStatistics getDeltaStatisticsForPort(DeviceId deviceId, PortNumber portNumber) { return null; } @Override public Port getPort(DeviceId deviceId, PortNumber portNumber) { return augment(super.getPort(deviceId, portNumber)); } /** * DeviceListener, which translates generic Port to optical specific Port * before passing. */ class OpticalDeviceListener implements DeviceListener { private final DeviceListener listener; // shallow cache to reuse transformed event in isRelevant and event call private Pair<DeviceEvent, DeviceEvent> cache; public OpticalDeviceListener(DeviceListener listener) { this.listener = listener; } private DeviceEvent opticalEvent(DeviceEvent event) { Pair<DeviceEvent, DeviceEvent> entry = cache; if (entry != null && entry.getLeft() == event) { return entry.getRight(); } DeviceEvent opticalEvent = augment(event); cache = Pair.of(event, opticalEvent); return opticalEvent; } @Override public boolean isRelevant(DeviceEvent event) { return listener.isRelevant(opticalEvent(event)); } @Override public void event(DeviceEvent event) { listener.event(opticalEvent(event)); } } }