blob: 88ef950058db87a69fa83c163c963f095c7ff1c0 [file] [log] [blame]
/*
* 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));
}
}
}