Java tutorial
/* * Copyright 2017-present Open Networking Foundation * * 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.incubator.store.virtual.impl; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Service; import org.onosproject.incubator.net.virtual.NetworkId; import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentData; import org.onosproject.net.intent.IntentEvent; import org.onosproject.net.intent.IntentState; import org.onosproject.net.intent.IntentStoreDelegate; import org.onosproject.net.intent.Key; import org.onosproject.store.Timestamp; import org.slf4j.Logger; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.net.intent.IntentState.PURGE_REQ; import static org.slf4j.LoggerFactory.getLogger; /** * Simple single-instance implementation of the intent store for virtual networks. */ @Component(immediate = true) @Service public class SimpleVirtualIntentStore extends AbstractVirtualStore<IntentEvent, IntentStoreDelegate> implements VirtualNetworkIntentStore { private final Logger log = getLogger(getClass()); private final Map<NetworkId, Map<Key, IntentData>> currentByNetwork = Maps.newConcurrentMap(); private final Map<NetworkId, Map<Key, IntentData>> pendingByNetwork = Maps.newConcurrentMap(); @Activate public void activate() { log.info("Started"); } @Deactivate public void deactivate() { log.info("Stopped"); } @Override public long getIntentCount(NetworkId networkId) { return getCurrentMap(networkId).size(); } @Override public Iterable<Intent> getIntents(NetworkId networkId) { return getCurrentMap(networkId).values().stream().map(IntentData::intent).collect(Collectors.toList()); } @Override public Iterable<IntentData> getIntentData(NetworkId networkId, boolean localOnly, long olderThan) { if (localOnly || olderThan > 0) { long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns final SystemClockTimestamp time = new SystemClockTimestamp(older); return getCurrentMap(networkId).values().stream().filter( data -> data.version().isOlderThan(time) && (!localOnly || isMaster(networkId, data.key()))) .collect(Collectors.toList()); } return Lists.newArrayList(getCurrentMap(networkId).values()); } @Override public IntentState getIntentState(NetworkId networkId, Key intentKey) { IntentData data = getCurrentMap(networkId).get(intentKey); return (data != null) ? data.state() : null; } @Override public List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey) { IntentData data = getCurrentMap(networkId).get(intentKey); if (data != null) { return data.installables(); } return null; } @Override public void write(NetworkId networkId, IntentData newData) { checkNotNull(newData); synchronized (this) { // TODO this could be refactored/cleaned up IntentData currentData = getCurrentMap(networkId).get(newData.key()); IntentData pendingData = getPendingMap(networkId).get(newData.key()); if (IntentData.isUpdateAcceptable(currentData, newData)) { if (pendingData != null) { if (pendingData.state() == PURGE_REQ) { getCurrentMap(networkId).remove(newData.key(), newData); } else { getCurrentMap(networkId).put(newData.key(), IntentData.copy(newData)); } if (pendingData.version().compareTo(newData.version()) <= 0) { // pendingData version is less than or equal to newData's // Note: a new update for this key could be pending (it's version will be greater) getPendingMap(networkId).remove(newData.key()); } } IntentEvent.getEvent(newData).ifPresent(e -> notifyDelegate(networkId, e)); } } } @Override public void batchWrite(NetworkId networkId, Iterable<IntentData> updates) { for (IntentData data : updates) { write(networkId, data); } } @Override public Intent getIntent(NetworkId networkId, Key key) { IntentData data = getCurrentMap(networkId).get(key); return (data != null) ? data.intent() : null; } @Override public IntentData getIntentData(NetworkId networkId, Key key) { IntentData currentData = getCurrentMap(networkId).get(key); if (currentData == null) { return null; } return IntentData.copy(currentData); } @Override public void addPending(NetworkId networkId, IntentData data) { if (data.version() == null) { // recompiled intents will already have a version data = new IntentData(data.intent(), data.state(), new SystemClockTimestamp()); } synchronized (this) { IntentData existingData = getPendingMap(networkId).get(data.key()); if (existingData == null || // existing version is strictly less than data's version // Note: if they are equal, we already have the update // TODO maybe we should still make this <= to be safe? existingData.version().compareTo(data.version()) < 0) { getPendingMap(networkId).put(data.key(), data); checkNotNull(delegateMap.get(networkId), "Store delegate is not set") .process(IntentData.copy(data)); IntentEvent.getEvent(data).ifPresent(e -> notifyDelegate(networkId, e)); } else { log.debug("IntentData {} is older than existing: {}", data, existingData); } //TODO consider also checking the current map at this point } } @Override public boolean isMaster(NetworkId networkId, Key intentKey) { return true; } @Override public Iterable<Intent> getPending(NetworkId networkId) { return getPendingMap(networkId).values().stream().map(IntentData::intent).collect(Collectors.toList()); } @Override public Iterable<IntentData> getPendingData(NetworkId networkId) { return Lists.newArrayList(getPendingMap(networkId).values()); } @Override public IntentData getPendingData(NetworkId networkId, Key intentKey) { return getPendingMap(networkId).get(intentKey); } @Override public Iterable<IntentData> getPendingData(NetworkId networkId, boolean localOnly, long olderThan) { long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns final SystemClockTimestamp time = new SystemClockTimestamp(older); return getPendingMap(networkId).values().stream() .filter(data -> data.version().isOlderThan(time) && (!localOnly || isMaster(networkId, data.key()))) .collect(Collectors.toList()); } /** * Returns the current intent map for a specific virtual network. * * @param networkId a virtual network identifier * @return the current map for the requested virtual network */ private Map<Key, IntentData> getCurrentMap(NetworkId networkId) { currentByNetwork.computeIfAbsent(networkId, n -> Maps.newConcurrentMap()); return currentByNetwork.get(networkId); } /** * Returns the pending intent map for a specific virtual network. * * @param networkId a virtual network identifier * @return the pending intent map for the requested virtual network */ private Map<Key, IntentData> getPendingMap(NetworkId networkId) { pendingByNetwork.computeIfAbsent(networkId, n -> Maps.newConcurrentMap()); return pendingByNetwork.get(networkId); } public class SystemClockTimestamp implements Timestamp { private final long nanoTimestamp; public SystemClockTimestamp() { nanoTimestamp = System.nanoTime(); } public SystemClockTimestamp(long timestamp) { nanoTimestamp = timestamp; } @Override public int compareTo(Timestamp o) { checkArgument(o instanceof SystemClockTimestamp, "Must be SystemClockTimestamp", o); SystemClockTimestamp that = (SystemClockTimestamp) o; return ComparisonChain.start().compare(this.nanoTimestamp, that.nanoTimestamp).result(); } } }