blob: 9f4755ae81488a9e06da3e1a8ff82f2b54953f03 [file] [log] [blame]
/*
* Copyright 2014 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.onlab.onos.net.intent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Fake implementation of the intent service to assist in developing tests of
* the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<Intent>> installables = new HashMap<>();
private final Set<IntentListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
private final Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> installers
= new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
@Override
public List<IntentException> getExceptions() {
return exceptions;
}
// Provides an out-of-thread simulation of intent submit life-cycle
private void executeSubmit(final Intent intent) {
registerSubclassCompilerIfNeeded(intent);
executor.execute(new Runnable() {
@Override
public void run() {
try {
executeCompilingPhase(intent);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
// Provides an out-of-thread simulation of intent withdraw life-cycle
private void executeWithdraw(final Intent intent) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
List<Intent> installable = getInstallable(intent.id());
executeWithdrawingPhase(intent, installable);
} catch (IntentException e) {
exceptions.add(e);
}
}
});
}
private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
@SuppressWarnings("unchecked")
IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
if (compiler == null) {
throw new IntentException("no compiler for class " + intent.getClass());
}
return compiler;
}
private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent
.getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
return installer;
}
private <T extends Intent> void executeCompilingPhase(T intent) {
setState(intent, IntentState.COMPILING);
try {
// For the fake, we compile using a single level pass
List<Intent> installable = new ArrayList<>();
for (Intent compiled : getCompiler(intent).compile(intent)) {
installable.add((Intent) compiled);
}
executeInstallingPhase(intent, installable);
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
private void executeInstallingPhase(Intent intent,
List<Intent> installable) {
setState(intent, IntentState.INSTALLING);
try {
for (Intent ii : installable) {
registerSubclassInstallerIfNeeded(ii);
getInstaller(ii).install(ii);
}
setState(intent, IntentState.INSTALLED);
putInstallable(intent.id(), installable);
dispatch(new IntentEvent(IntentEvent.Type.INSTALLED, intent));
} catch (IntentException e) {
setState(intent, IntentState.FAILED);
dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
private void executeWithdrawingPhase(Intent intent,
List<Intent> installable) {
setState(intent, IntentState.WITHDRAWING);
try {
for (Intent ii : installable) {
getInstaller(ii).uninstall(ii);
}
removeInstallable(intent.id());
setState(intent, IntentState.WITHDRAWN);
dispatch(new IntentEvent(IntentEvent.Type.WITHDRAWN, intent));
} catch (IntentException e) {
// FIXME: Rework this to always go from WITHDRAWING to WITHDRAWN!
setState(intent, IntentState.FAILED);
dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
}
}
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
intentStates.put(intent.id(), state);
}
private void putInstallable(IntentId id, List<Intent> installable) {
installables.put(id, installable);
}
private void removeInstallable(IntentId id) {
installables.remove(id);
}
private List<Intent> getInstallable(IntentId id) {
List<Intent> installable = installables.get(id);
if (installable != null) {
return installable;
} else {
return Collections.emptyList();
}
}
@Override
public void submit(Intent intent) {
intents.put(intent.id(), intent);
setState(intent, IntentState.SUBMITTED);
dispatch(new IntentEvent(IntentEvent.Type.SUBMITTED, intent));
executeSubmit(intent);
}
@Override
public void withdraw(Intent intent) {
intents.remove(intent.id());
executeWithdraw(intent);
}
@Override
public void replace(IntentId oldIntentId, Intent newIntent) {
// TODO: implement later
}
@Override
public Future<IntentOperations> execute(IntentOperations operations) {
// TODO: implement later
return null;
}
@Override
public Set<Intent> getIntents() {
return Collections.unmodifiableSet(new HashSet<>(intents.values()));
}
@Override
public long getIntentCount() {
return intents.size();
}
@Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
@Override
public IntentState getIntentState(IntentId id) {
return intentStates.get(id);
}
@Override
public List<Intent> getInstallableIntents(IntentId intentId) {
return installables.get(intentId);
}
@Override
public void addListener(IntentListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(IntentListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
for (IntentListener listener : listeners) {
listener.event(event);
}
}
@Override
public <T extends Intent> void registerCompiler(Class<T> cls,
IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@Override
public <T extends Intent> void unregisterCompiler(Class<T> cls) {
compilers.remove(cls);
}
@Override
public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
return Collections.unmodifiableMap(compilers);
}
@Override
public <T extends Intent> void registerInstaller(Class<T> cls,
IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@Override
public <T extends Intent> void unregisterInstaller(Class<T> cls) {
installers.remove(cls);
}
@Override
public Map<Class<? extends Intent>,
IntentInstaller<? extends Intent>> getInstallers() {
return Collections.unmodifiableMap(installers);
}
private void registerSubclassCompilerIfNeeded(Intent intent) {
if (!compilers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the Intent class descendants
if (Intent.class.isAssignableFrom(cls)) {
IntentCompiler<?> compiler = compilers.get(cls);
if (compiler != null) {
compilers.put(intent.getClass(), compiler);
return;
}
}
cls = cls.getSuperclass();
}
}
}
private void registerSubclassInstallerIfNeeded(Intent intent) {
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
// As long as we're within the Intent class
// descendants
if (Intent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
installers.put(intent.getClass(), installer);
return;
}
}
cls = cls.getSuperclass();
}
}
}
}