| 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); |
| } |
| } |
| } |
| } |