package net.onrc.onos.core.newintent; import com.google.common.collect.ImmutableMap; import com.hazelcast.core.EntryEvent; import com.hazelcast.core.EntryListener; import net.onrc.onos.api.newintent.InstallableIntent; import net.onrc.onos.api.newintent.Intent; import net.onrc.onos.api.newintent.IntentCompiler; import net.onrc.onos.api.newintent.IntentEvent; import net.onrc.onos.api.newintent.IntentEventListener; import net.onrc.onos.api.newintent.IntentException; import net.onrc.onos.api.newintent.IntentId; import net.onrc.onos.api.newintent.IntentInstaller; import net.onrc.onos.api.newintent.IntentManager; import net.onrc.onos.api.newintent.IntentOperations; import net.onrc.onos.api.newintent.IntentState; import net.onrc.onos.core.datagrid.ISharedCollectionsService; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import static com.google.common.base.Preconditions.checkNotNull; import static net.onrc.onos.api.newintent.IntentState.COMPILED; import static net.onrc.onos.api.newintent.IntentState.FAILED; import static net.onrc.onos.api.newintent.IntentState.INSTALLED; import static net.onrc.onos.api.newintent.IntentState.SUBMITTED; import static net.onrc.onos.api.newintent.IntentState.WITHDRAWING; import static net.onrc.onos.api.newintent.IntentState.WITHDRAWN; /** * An implementation of Intent Manager. */ public class IntentManagerRuntime implements IntentManager { // Collections for intent, installable intent, and intent state are globally shared private final IntentMap<IntentEvent> intentEvents; private final IntentMap<IntentCompilationResult> installableIntents; // Collections for compiler, installer, and listener are ONOS instance local private final ConcurrentMap<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>(); private final ConcurrentMap<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>(); private final CopyOnWriteArrayList<IntentEventListener> listeners = new CopyOnWriteArrayList<>(); /** * Constructs a Intent Manager runtime with the specified shared collections service. * * @param collectionsService shared collections service */ public IntentManagerRuntime(ISharedCollectionsService collectionsService) { checkNotNull(collectionsService); this.intentEvents = new IntentMap<>("intentState", IntentEvent.class, collectionsService); this.installableIntents = new IntentMap<>("installableIntents", IntentCompilationResult.class, collectionsService); this.intentEvents.addListener(new InternalEntryListener(new InternalIntentEventListener())); } @Override public void submit(Intent intent) { registerSubclassCompilerIfNeeded(intent); setState(intent, SUBMITTED); } @Override public void withdraw(Intent intent) { setState(intent, WITHDRAWING); } // FIXME: implement this method @Override public void execute(IntentOperations operations) { throw new UnsupportedOperationException("execute() is not implemented yet"); } @Override public Set<Intent> getIntents() { Collection<IntentEvent> events = intentEvents.values(); Set<Intent> intents = new HashSet<>(events.size()); for (IntentEvent event : events) { intents.add(event.getIntent()); } return intents; } @Override public Intent getIntent(IntentId id) { IntentEvent event = intentEvents.get(id); if (event == null) { return null; } return event.getIntent(); } @Override public IntentState getIntentState(IntentId id) { IntentEvent event = intentEvents.get(id); if (event == null) { return null; } return event.getState(); } @Override public void addListener(IntentEventListener listener) { listeners.add(listener); } @Override public void removeListener(IntentEventListener listener) { listeners.remove(listener); } @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 ImmutableMap.copyOf(compilers); } @Override public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) { installers.put(cls, installer); } @Override public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) { installers.remove(cls); } @Override public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() { return ImmutableMap.copyOf(installers); } /** * Sets the state of the specified intent to the new state. * * @param intent intent whose state is to be changed * @param newState new state */ private void setState(Intent intent, IntentState newState) { IntentState oldState = getIntentState(intent.getId()); IntentEvent event = new IntentEvent(intent, newState, oldState, System.currentTimeMillis()); intentEvents.put(intent.getId(), event); } /** * Invokes all of registered intent event listener. * * @param event event supplied to a listener as an argument */ private void invokeListeners(IntentEvent event) { for (IntentEventListener listener : listeners) { listener.event(event); } } /** * Returns the corresponding intent compiler to the specified intent. * * @param intent intent * @param <T> the type of intent * @return intent compiler corresponding to the specified intent */ 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; } /** * Returns the corresponding intent installer to the specified installable intent. * @param intent intent * @param <T> the type of installable intent * @return intent installer corresponding to the specified installable intent */ private <T extends InstallableIntent> 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; } /** * Compiles an intent. * * @param intent intent */ private void compileIntent(Intent intent) { // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented // TODO: implement compilation traversing tree structure List<InstallableIntent> installable = new ArrayList<>(); for (Intent compiled : getCompiler(intent).compile(intent)) { installable.add((InstallableIntent) compiled); } installableIntents.put(intent.getId(), new IntentCompilationResult(installable)); setState(intent, COMPILED); } /** * Installs an intent. * * @param intent intent */ private void installIntent(Intent intent) { IntentCompilationResult compiled = installableIntents.get(intent.getId()); for (InstallableIntent installable : compiled.getResult()) { registerSubclassInstallerIfNeeded(installable); getInstaller(installable).install(installable); } setState(intent, INSTALLED); } /** * Uninstalls an intent. * * @param intent intent */ private void uninstallIntent(Intent intent) { IntentCompilationResult compiled = installableIntents.get(intent.getId()); for (InstallableIntent installable : compiled.getResult()) { getInstaller(installable).remove(installable); } installableIntents.remove(intent.getId()); setState(intent, WITHDRAWN); } /** * Registers an intent compiler of the specified intent if an intent compiler * for the intent is not registered. This method traverses the class hierarchy of * the intent. Once an intent compiler for a parent type is found, this method * registers the found intent compiler. * * @param intent intent */ @SuppressWarnings("unchecked") 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(); } } } /** * Registers an intent installer of the specified intent if an intent installer * for the intent is not registered. This method traverses the class hierarchy of * the intent. Once an intent installer for a parent type is found, this method * registers the found intent installer. * * @param intent intent */ @SuppressWarnings("unchecked") private void registerSubclassInstallerIfNeeded(InstallableIntent intent) { if (!installers.containsKey(intent.getClass())) { Class<?> cls = intent.getClass(); while (cls != Object.class) { // As long as we're within the InstallableIntent class descendants if (InstallableIntent.class.isAssignableFrom(cls)) { IntentInstaller<?> installer = installers.get(cls); if (installer != null) { installers.put(intent.getClass(), installer); return; } } cls = cls.getSuperclass(); } } } /** * Destroys underlying {@link IntentMap IntentMaps}. * This method is only for testing purpose. */ void destroy() { intentEvents.destroy(); installableIntents.destroy(); } /** * An entry listener used internally. * * This listener is a kind of bridge of listener mechanism * between {@link IntentMap} and {@link IntentEventListener}. */ private static class InternalEntryListener implements EntryListener<IntentId, IntentEvent> { private final IntentEventListener listener; public InternalEntryListener(IntentEventListener listener) { this.listener = listener; } @Override public void entryAdded(EntryEvent<IntentId, IntentEvent> event) { listener.event(event.getValue()); } @Override public void entryRemoved(EntryEvent<IntentId, IntentEvent> event) { listener.event(event.getValue()); } @Override public void entryUpdated(EntryEvent<IntentId, IntentEvent> event) { listener.event(event.getValue()); } @Override public void entryEvicted(EntryEvent<IntentId, IntentEvent> event) { // no-op } } /** * An intent event listener used internally. * * event() method handles state transition of submitted intents. */ private class InternalIntentEventListener implements IntentEventListener { @Override public void event(IntentEvent event) { invokeListeners(event); Intent intent = event.getIntent(); try { switch (event.getState()) { case SUBMITTED: compileIntent(intent); break; case COMPILED: installIntent(intent); break; case INSTALLED: break; case WITHDRAWING: uninstallIntent(intent); break; case WITHDRAWN: break; case FAILED: break; default: throw new IllegalStateException( "the state of IntentEvent is illegal: " + event.getState()); } } catch (IntentException e) { setState(intent, FAILED); } } } }