Thomas Vachuska | 4f1a60c | 2014-10-28 13:39:07 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2014 Open Networking Laboratory |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 16 | package org.onlab.onos.net.intent.impl; |
| 17 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 18 | import com.google.common.collect.ImmutableList; |
| 19 | import com.google.common.collect.ImmutableMap; |
| 20 | import com.google.common.collect.Lists; |
| 21 | import com.google.common.collect.Maps; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 22 | import org.apache.felix.scr.annotations.Activate; |
| 23 | import org.apache.felix.scr.annotations.Component; |
| 24 | import org.apache.felix.scr.annotations.Deactivate; |
| 25 | import org.apache.felix.scr.annotations.Reference; |
| 26 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 27 | import org.apache.felix.scr.annotations.Service; |
| 28 | import org.onlab.onos.event.AbstractListenerRegistry; |
| 29 | import org.onlab.onos.event.EventDeliveryService; |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 30 | import org.onlab.onos.net.flow.CompletedBatchOperation; |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 31 | import org.onlab.onos.net.flow.FlowRuleBatchOperation; |
| 32 | import org.onlab.onos.net.flow.FlowRuleService; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 33 | import org.onlab.onos.net.intent.Intent; |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 34 | import org.onlab.onos.net.intent.IntentBatchDelegate; |
| 35 | import org.onlab.onos.net.intent.IntentBatchService; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 36 | import org.onlab.onos.net.intent.IntentCompiler; |
| 37 | import org.onlab.onos.net.intent.IntentEvent; |
| 38 | import org.onlab.onos.net.intent.IntentException; |
| 39 | import org.onlab.onos.net.intent.IntentExtensionService; |
| 40 | import org.onlab.onos.net.intent.IntentId; |
| 41 | import org.onlab.onos.net.intent.IntentInstaller; |
| 42 | import org.onlab.onos.net.intent.IntentListener; |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 43 | import org.onlab.onos.net.intent.IntentOperation; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 44 | import org.onlab.onos.net.intent.IntentOperations; |
| 45 | import org.onlab.onos.net.intent.IntentService; |
| 46 | import org.onlab.onos.net.intent.IntentState; |
| 47 | import org.onlab.onos.net.intent.IntentStore; |
| 48 | import org.onlab.onos.net.intent.IntentStoreDelegate; |
| 49 | import org.slf4j.Logger; |
| 50 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 51 | import java.util.ArrayList; |
| 52 | import java.util.List; |
| 53 | import java.util.Map; |
| 54 | import java.util.Objects; |
| 55 | import java.util.concurrent.ConcurrentHashMap; |
| 56 | import java.util.concurrent.ConcurrentMap; |
| 57 | import java.util.concurrent.ExecutionException; |
| 58 | import java.util.concurrent.ExecutorService; |
| 59 | import java.util.concurrent.Future; |
| 60 | import java.util.concurrent.TimeUnit; |
| 61 | import java.util.concurrent.TimeoutException; |
| 62 | |
| 63 | import static com.google.common.base.Preconditions.checkArgument; |
| 64 | import static com.google.common.base.Preconditions.checkNotNull; |
| 65 | import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| 66 | import static org.onlab.onos.net.intent.IntentState.*; |
| 67 | import static org.onlab.util.Tools.namedThreads; |
| 68 | import static org.slf4j.LoggerFactory.getLogger; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 69 | |
| 70 | /** |
| 71 | * An implementation of Intent Manager. |
| 72 | */ |
| 73 | @Component(immediate = true) |
| 74 | @Service |
| 75 | public class IntentManager |
| 76 | implements IntentService, IntentExtensionService { |
Sho SHIMIZU | 8b5051d | 2014-11-05 11:24:13 -0800 | [diff] [blame] | 77 | private static final Logger log = getLogger(IntentManager.class); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 78 | |
| 79 | public static final String INTENT_NULL = "Intent cannot be null"; |
| 80 | public static final String INTENT_ID_NULL = "Intent ID cannot be null"; |
| 81 | |
| 82 | // Collections for compiler, installer, and listener are ONOS instance local |
| 83 | private final ConcurrentMap<Class<? extends Intent>, |
| 84 | IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>(); |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 85 | private final ConcurrentMap<Class<? extends Intent>, |
| 86 | IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>(); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 87 | |
| 88 | private final AbstractListenerRegistry<IntentEvent, IntentListener> |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 89 | listenerRegistry = new AbstractListenerRegistry<>(); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 90 | |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 91 | private ExecutorService executor; |
| 92 | private ExecutorService monitorExecutor; |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 93 | |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 94 | private final IntentStoreDelegate delegate = new InternalStoreDelegate(); |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 95 | private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate(); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 96 | private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate(); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 97 | |
| 98 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 99 | protected IntentStore store; |
| 100 | |
| 101 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 102 | protected IntentBatchService batchService; |
| 103 | |
| 104 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 105 | protected ObjectiveTrackerService trackerService; |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 106 | |
| 107 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 108 | protected EventDeliveryService eventDispatcher; |
| 109 | |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 110 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 111 | protected FlowRuleService flowRuleService; |
| 112 | |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 113 | @Activate |
| 114 | public void activate() { |
| 115 | store.setDelegate(delegate); |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 116 | trackerService.setDelegate(topoDelegate); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 117 | batchService.setDelegate(batchDelegate); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 118 | eventDispatcher.addSink(IntentEvent.class, listenerRegistry); |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 119 | executor = newSingleThreadExecutor(namedThreads("onos-intents")); |
| 120 | monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor")); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 121 | log.info("Started"); |
| 122 | } |
| 123 | |
| 124 | @Deactivate |
| 125 | public void deactivate() { |
| 126 | store.unsetDelegate(delegate); |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 127 | trackerService.unsetDelegate(topoDelegate); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 128 | batchService.unsetDelegate(batchDelegate); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 129 | eventDispatcher.removeSink(IntentEvent.class); |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 130 | executor.shutdown(); |
| 131 | monitorExecutor.shutdown(); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 132 | log.info("Stopped"); |
| 133 | } |
| 134 | |
| 135 | @Override |
| 136 | public void submit(Intent intent) { |
| 137 | checkNotNull(intent, INTENT_NULL); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 138 | execute(IntentOperations.builder().addSubmitOperation(intent).build()); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | @Override |
| 142 | public void withdraw(Intent intent) { |
| 143 | checkNotNull(intent, INTENT_NULL); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 144 | execute(IntentOperations.builder().addWithdrawOperation(intent.id()).build()); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 145 | } |
| 146 | |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 147 | @Override |
Thomas Vachuska | 83e090e | 2014-10-22 14:25:35 -0700 | [diff] [blame] | 148 | public void replace(IntentId oldIntentId, Intent newIntent) { |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 149 | checkNotNull(oldIntentId, INTENT_ID_NULL); |
| 150 | checkNotNull(newIntent, INTENT_NULL); |
| 151 | execute(IntentOperations.builder() |
| 152 | .addReplaceOperation(oldIntentId, newIntent) |
| 153 | .build()); |
Thomas Vachuska | 83e090e | 2014-10-22 14:25:35 -0700 | [diff] [blame] | 154 | } |
| 155 | |
Thomas Vachuska | 83e090e | 2014-10-22 14:25:35 -0700 | [diff] [blame] | 156 | @Override |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 157 | public void execute(IntentOperations operations) { |
| 158 | batchService.addIntentOperations(operations); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | @Override |
| 162 | public Iterable<Intent> getIntents() { |
| 163 | return store.getIntents(); |
| 164 | } |
| 165 | |
| 166 | @Override |
| 167 | public long getIntentCount() { |
| 168 | return store.getIntentCount(); |
| 169 | } |
| 170 | |
| 171 | @Override |
| 172 | public Intent getIntent(IntentId id) { |
| 173 | checkNotNull(id, INTENT_ID_NULL); |
| 174 | return store.getIntent(id); |
| 175 | } |
| 176 | |
| 177 | @Override |
| 178 | public IntentState getIntentState(IntentId id) { |
| 179 | checkNotNull(id, INTENT_ID_NULL); |
| 180 | return store.getIntentState(id); |
| 181 | } |
| 182 | |
| 183 | @Override |
Thomas Vachuska | 10d4abc | 2014-10-21 12:47:26 -0700 | [diff] [blame] | 184 | public List<Intent> getInstallableIntents(IntentId intentId) { |
| 185 | checkNotNull(intentId, INTENT_ID_NULL); |
| 186 | return store.getInstallableIntents(intentId); |
| 187 | } |
| 188 | |
| 189 | @Override |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 190 | public void addListener(IntentListener listener) { |
| 191 | listenerRegistry.addListener(listener); |
| 192 | } |
| 193 | |
| 194 | @Override |
| 195 | public void removeListener(IntentListener listener) { |
| 196 | listenerRegistry.removeListener(listener); |
| 197 | } |
| 198 | |
| 199 | @Override |
| 200 | public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) { |
| 201 | compilers.put(cls, compiler); |
| 202 | } |
| 203 | |
| 204 | @Override |
| 205 | public <T extends Intent> void unregisterCompiler(Class<T> cls) { |
| 206 | compilers.remove(cls); |
| 207 | } |
| 208 | |
| 209 | @Override |
| 210 | public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() { |
| 211 | return ImmutableMap.copyOf(compilers); |
| 212 | } |
| 213 | |
| 214 | @Override |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 215 | public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 216 | installers.put(cls, installer); |
| 217 | } |
| 218 | |
| 219 | @Override |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 220 | public <T extends Intent> void unregisterInstaller(Class<T> cls) { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 221 | installers.remove(cls); |
| 222 | } |
| 223 | |
| 224 | @Override |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 225 | public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 226 | return ImmutableMap.copyOf(installers); |
| 227 | } |
| 228 | |
| 229 | /** |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 230 | * Returns the corresponding intent compiler to the specified intent. |
| 231 | * |
| 232 | * @param intent intent |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 233 | * @param <T> the type of intent |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 234 | * @return intent compiler corresponding to the specified intent |
| 235 | */ |
| 236 | private <T extends Intent> IntentCompiler<T> getCompiler(T intent) { |
| 237 | @SuppressWarnings("unchecked") |
| 238 | IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass()); |
| 239 | if (compiler == null) { |
| 240 | throw new IntentException("no compiler for class " + intent.getClass()); |
| 241 | } |
| 242 | return compiler; |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Returns the corresponding intent installer to the specified installable intent. |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 247 | * |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 248 | * @param intent intent |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 249 | * @param <T> the type of installable intent |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 250 | * @return intent installer corresponding to the specified installable intent |
| 251 | */ |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 252 | private <T extends Intent> IntentInstaller<T> getInstaller(T intent) { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 253 | @SuppressWarnings("unchecked") |
| 254 | IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass()); |
| 255 | if (installer == null) { |
| 256 | throw new IntentException("no installer for class " + intent.getClass()); |
| 257 | } |
| 258 | return installer; |
| 259 | } |
| 260 | |
| 261 | /** |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 262 | * Compiles the specified intent. |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 263 | * |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 264 | * @param update intent update |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 265 | */ |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 266 | private void executeCompilingPhase(IntentUpdate update) { |
| 267 | Intent intent = update.newIntent(); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 268 | // Indicate that the intent is entering the compiling phase. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 269 | update.setState(intent, COMPILING); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 270 | |
| 271 | try { |
| 272 | // Compile the intent into installable derivatives. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 273 | List<Intent> installables = compileIntent(intent, update); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 274 | |
| 275 | // If all went well, associate the resulting list of installable |
| 276 | // intents with the top-level intent and proceed to install. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 277 | update.setInstallables(installables); |
| 278 | } catch (IntentException e) { |
Jonathan Hart | 1109640 | 2014-10-20 17:31:49 -0700 | [diff] [blame] | 279 | log.warn("Unable to compile intent {} due to:", intent.id(), e); |
tom | 53945d5 | 2014-10-07 11:01:36 -0700 | [diff] [blame] | 280 | |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 281 | // If compilation failed, mark the intent as failed. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 282 | update.setState(intent, FAILED); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 283 | } |
| 284 | } |
| 285 | |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 286 | /** |
| 287 | * Compiles an intent recursively. |
| 288 | * |
| 289 | * @param intent intent |
| 290 | * @return result of compilation |
| 291 | */ |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 292 | private List<Intent> compileIntent(Intent intent, IntentUpdate update) { |
Thomas Vachuska | 4926c1b | 2014-10-21 00:44:10 -0700 | [diff] [blame] | 293 | if (intent.isInstallable()) { |
| 294 | return ImmutableList.of(intent); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 295 | } |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 296 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 297 | registerSubclassCompilerIfNeeded(intent); |
| 298 | List<Intent> previous = update.oldInstallables(); |
| 299 | // FIXME: get previous resources |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 300 | List<Intent> installable = new ArrayList<>(); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 301 | for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) { |
| 302 | installable.addAll(compileIntent(compiled, update)); |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 303 | } |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 304 | return installable; |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 305 | } |
| 306 | |
| 307 | /** |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 308 | * Installs all installable intents associated with the specified top-level |
| 309 | * intent. |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 310 | * |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 311 | * @param update intent update |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 312 | */ |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 313 | private void executeInstallingPhase(IntentUpdate update) { |
| 314 | if (update.newInstallables() == null) { |
| 315 | //no failed intents allowed past this point... |
| 316 | return; |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 317 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 318 | // Indicate that the intent is entering the installing phase. |
| 319 | update.setState(update.newIntent(), INSTALLING); |
| 320 | |
| 321 | List<FlowRuleBatchOperation> batches = Lists.newArrayList(); |
| 322 | for (Intent installable : update.newInstallables()) { |
| 323 | registerSubclassInstallerIfNeeded(installable); |
| 324 | trackerService.addTrackedResources(update.newIntent().id(), |
| 325 | installable.resources()); |
| 326 | try { |
| 327 | batches.addAll(getInstaller(installable).install(installable)); |
| 328 | } catch (IntentException e) { |
| 329 | log.warn("Unable to install intent {} due to:", update.newIntent().id(), e); |
| 330 | //FIXME we failed... intent should be recompiled |
| 331 | // TODO: remove resources |
| 332 | // recompile!!! |
| 333 | } |
| 334 | } |
| 335 | update.setBatches(batches); |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Uninstalls the specified intent by uninstalling all of its associated |
| 340 | * installable derivatives. |
| 341 | * |
| 342 | * @param update intent update |
| 343 | */ |
| 344 | private void executeWithdrawingPhase(IntentUpdate update) { |
| 345 | if (!update.oldIntent().equals(update.newIntent())) { |
| 346 | update.setState(update.oldIntent(), WITHDRAWING); |
| 347 | } // else newIntent is FAILED |
| 348 | uninstallIntent(update); |
| 349 | |
| 350 | // If all went well, disassociate the top-level intent with its |
| 351 | // installable derivatives and mark it as withdrawn. |
| 352 | // FIXME need to clean up |
| 353 | //store.removeInstalledIntents(intent.id()); |
| 354 | } |
| 355 | |
| 356 | /** |
| 357 | * Uninstalls all installable intents associated with the given intent. |
| 358 | * |
| 359 | * @param update intent update |
| 360 | */ |
| 361 | //FIXME: need to handle next state properly |
| 362 | private void uninstallIntent(IntentUpdate update) { |
| 363 | if (update.oldInstallables == null) { |
| 364 | return; |
| 365 | } |
| 366 | List<FlowRuleBatchOperation> batches = Lists.newArrayList(); |
| 367 | for (Intent installable : update.oldInstallables()) { |
| 368 | trackerService.removeTrackedResources(update.oldIntent().id(), |
| 369 | installable.resources()); |
| 370 | try { |
| 371 | batches.addAll(getInstaller(installable).uninstall(installable)); |
| 372 | } catch (IntentException e) { |
| 373 | log.warn("Unable to uninstall intent {} due to:", update.oldIntent().id(), e); |
| 374 | // TODO: this should never happen. but what if it does? |
| 375 | } |
| 376 | } |
| 377 | update.setBatches(batches); |
| 378 | // FIXME: next state for old is WITHDRAWN or FAILED |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | /** |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 382 | * Recompiles the specified intent. |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 383 | * |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 384 | * @param update intent update |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 385 | */ |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 386 | // FIXME: update this to work |
| 387 | private void executeRecompilingPhase(IntentUpdate update) { |
| 388 | Intent intent = update.newIntent(); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 389 | // Indicate that the intent is entering the recompiling phase. |
| 390 | store.setState(intent, RECOMPILING); |
| 391 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 392 | List<FlowRuleBatchOperation> batches = Lists.newArrayList(); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 393 | try { |
| 394 | // Compile the intent into installable derivatives. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 395 | List<Intent> installable = compileIntent(intent, update); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 396 | |
| 397 | // If all went well, compare the existing list of installable |
| 398 | // intents with the newly compiled list. If they are the same, |
| 399 | // bail, out since the previous approach was determined not to |
| 400 | // be viable. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 401 | // FIXME do we need this? |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 402 | List<Intent> originalInstallable = store.getInstallableIntents(intent.id()); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 403 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 404 | //FIXME let's be smarter about how we perform the update |
| 405 | //batches.addAll(uninstallIntent(intent, null)); |
| 406 | |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 407 | if (Objects.equals(originalInstallable, installable)) { |
| 408 | eventDispatcher.post(store.setState(intent, FAILED)); |
| 409 | } else { |
| 410 | // Otherwise, re-associate the newly compiled installable intents |
| 411 | // with the top-level intent and kick off installing phase. |
Yuta HIGUCHI | 10a31c3 | 2014-10-28 14:42:06 -0700 | [diff] [blame] | 412 | store.setInstallableIntents(intent.id(), installable); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 413 | // FIXME commented out for now |
| 414 | //batches.addAll(executeInstallingPhase(update)); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 415 | } |
| 416 | } catch (Exception e) { |
Jonathan Hart | 1109640 | 2014-10-20 17:31:49 -0700 | [diff] [blame] | 417 | log.warn("Unable to recompile intent {} due to:", intent.id(), e); |
tom | 53945d5 | 2014-10-07 11:01:36 -0700 | [diff] [blame] | 418 | |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 419 | // If compilation failed, mark the intent as failed. |
| 420 | eventDispatcher.post(store.setState(intent, FAILED)); |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | /** |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 425 | * Withdraws the old intent and installs the new intent as one operation. |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 426 | * |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 427 | * @param update intent update |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 428 | */ |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 429 | private void executeReplacementPhase(IntentUpdate update) { |
| 430 | checkArgument(update.oldInstallables().size() == update.newInstallables().size(), |
| 431 | "Old and New Intent must have equivalent installable intents."); |
| 432 | if (!update.oldIntent().equals(update.newIntent())) { |
| 433 | // only set the old intent's state if it is different |
| 434 | update.setState(update.oldIntent(), WITHDRAWING); |
tom | 53945d5 | 2014-10-07 11:01:36 -0700 | [diff] [blame] | 435 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 436 | update.setState(update.newIntent(), INSTALLING); |
| 437 | |
| 438 | List<FlowRuleBatchOperation> batches = Lists.newArrayList(); |
| 439 | for (int i = 0; i < update.oldInstallables().size(); i++) { |
| 440 | Intent oldInstallable = update.oldInstallables().get(i); |
| 441 | Intent newInstallable = update.newInstallables().get(i); |
| 442 | if (oldInstallable.equals(newInstallable)) { |
| 443 | continue; |
| 444 | } |
| 445 | checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()), |
| 446 | "Installable Intent type mismatch."); |
| 447 | trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources()); |
| 448 | trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources()); |
| 449 | try { |
| 450 | batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable)); |
| 451 | } catch (IntentException e) { |
| 452 | log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e); |
| 453 | //FIXME... we failed. need to uninstall (if same) or revert (if different) |
| 454 | } |
| 455 | } |
| 456 | update.setBatches(batches); |
tom | 53945d5 | 2014-10-07 11:01:36 -0700 | [diff] [blame] | 457 | } |
| 458 | |
| 459 | /** |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 460 | * Registers an intent compiler of the specified intent if an intent compiler |
| 461 | * for the intent is not registered. This method traverses the class hierarchy of |
| 462 | * the intent. Once an intent compiler for a parent type is found, this method |
| 463 | * registers the found intent compiler. |
| 464 | * |
| 465 | * @param intent intent |
| 466 | */ |
| 467 | private void registerSubclassCompilerIfNeeded(Intent intent) { |
| 468 | if (!compilers.containsKey(intent.getClass())) { |
| 469 | Class<?> cls = intent.getClass(); |
| 470 | while (cls != Object.class) { |
| 471 | // As long as we're within the Intent class descendants |
| 472 | if (Intent.class.isAssignableFrom(cls)) { |
| 473 | IntentCompiler<?> compiler = compilers.get(cls); |
| 474 | if (compiler != null) { |
| 475 | compilers.put(intent.getClass(), compiler); |
| 476 | return; |
| 477 | } |
| 478 | } |
| 479 | cls = cls.getSuperclass(); |
| 480 | } |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | /** |
| 485 | * Registers an intent installer of the specified intent if an intent installer |
| 486 | * for the intent is not registered. This method traverses the class hierarchy of |
| 487 | * the intent. Once an intent installer for a parent type is found, this method |
| 488 | * registers the found intent installer. |
| 489 | * |
| 490 | * @param intent intent |
| 491 | */ |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 492 | private void registerSubclassInstallerIfNeeded(Intent intent) { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 493 | if (!installers.containsKey(intent.getClass())) { |
| 494 | Class<?> cls = intent.getClass(); |
| 495 | while (cls != Object.class) { |
Thomas Vachuska | b97cf28 | 2014-10-20 23:31:12 -0700 | [diff] [blame] | 496 | // As long as we're within the Intent class descendants |
| 497 | if (Intent.class.isAssignableFrom(cls)) { |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 498 | IntentInstaller<?> installer = installers.get(cls); |
| 499 | if (installer != null) { |
| 500 | installers.put(intent.getClass(), installer); |
| 501 | return; |
| 502 | } |
| 503 | } |
| 504 | cls = cls.getSuperclass(); |
| 505 | } |
| 506 | } |
| 507 | } |
| 508 | |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 509 | // Store delegate to re-post events emitted from the store. |
| 510 | private class InternalStoreDelegate implements IntentStoreDelegate { |
| 511 | @Override |
| 512 | public void notify(IntentEvent event) { |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 513 | eventDispatcher.post(event); |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 514 | } |
| 515 | } |
| 516 | |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 517 | // Topology change delegate |
| 518 | private class InternalTopoChangeDelegate implements TopologyChangeDelegate { |
| 519 | @Override |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 520 | public void triggerCompile(Iterable<IntentId> intentIds, |
| 521 | boolean compileAllFailed) { |
| 522 | // Attempt recompilation of the specified intents first. |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 523 | IntentOperations.Builder builder = IntentOperations.builder(); |
| 524 | for (IntentId id : intentIds) { |
| 525 | builder.addUpdateOperation(id); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 526 | } |
| 527 | |
| 528 | if (compileAllFailed) { |
| 529 | // If required, compile all currently failed intents. |
| 530 | for (Intent intent : getIntents()) { |
| 531 | if (getIntentState(intent.id()) == FAILED) { |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 532 | builder.addUpdateOperation(intent.id()); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 533 | } |
| 534 | } |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 535 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 536 | execute(builder.build()); |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 537 | } |
tom | 95329eb | 2014-10-06 08:40:06 -0700 | [diff] [blame] | 538 | } |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 539 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 540 | /** |
| 541 | * TODO. |
| 542 | * @param op intent operation |
| 543 | * @return intent update |
| 544 | */ |
| 545 | private IntentUpdate processIntentOperation(IntentOperation op) { |
| 546 | IntentUpdate update = new IntentUpdate(op); |
| 547 | |
| 548 | if (update.newIntent() != null) { |
| 549 | executeCompilingPhase(update); |
| 550 | } |
| 551 | |
| 552 | if (update.oldInstallables() != null && update.newInstallables() != null) { |
| 553 | executeReplacementPhase(update); |
| 554 | } else if (update.newInstallables() != null) { |
| 555 | executeInstallingPhase(update); |
| 556 | } else if (update.oldInstallables() != null) { |
| 557 | executeWithdrawingPhase(update); |
| 558 | } else { |
| 559 | if (update.oldIntent() != null) { |
| 560 | // TODO this shouldn't happen |
| 561 | return update; //FIXME |
| 562 | } |
| 563 | if (update.newIntent() != null) { |
| 564 | // TODO assert that next state is failed |
| 565 | return update; //FIXME |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | return update; |
| 570 | } |
| 571 | |
| 572 | // TODO comments... |
| 573 | private class IntentUpdate { |
| 574 | private final IntentOperation op; |
| 575 | private final Intent oldIntent; |
| 576 | private final Intent newIntent; |
| 577 | private final Map<Intent, IntentState> stateMap = Maps.newHashMap(); |
| 578 | |
| 579 | private final List<Intent> oldInstallables; |
| 580 | private List<Intent> newInstallables; |
| 581 | private List<FlowRuleBatchOperation> batches; |
| 582 | |
| 583 | IntentUpdate(IntentOperation op) { |
| 584 | this.op = op; |
| 585 | switch (op.type()) { |
| 586 | case SUBMIT: |
| 587 | newIntent = op.intent(); |
| 588 | oldIntent = null; |
| 589 | break; |
| 590 | case WITHDRAW: |
| 591 | newIntent = null; |
| 592 | oldIntent = store.getIntent(op.intentId()); |
| 593 | break; |
| 594 | case REPLACE: |
| 595 | newIntent = op.intent(); |
| 596 | oldIntent = store.getIntent(op.intentId()); |
| 597 | break; |
| 598 | case UPDATE: |
| 599 | oldIntent = store.getIntent(op.intentId()); |
| 600 | newIntent = oldIntent; //InnerAssignment: Inner assignments should be avoided. |
| 601 | break; |
| 602 | default: |
| 603 | oldIntent = null; |
| 604 | newIntent = null; |
| 605 | break; |
| 606 | } |
| 607 | // add new intent to store (if required) |
| 608 | if (newIntent != null) { |
| 609 | IntentEvent event = store.createIntent(newIntent); |
| 610 | if (event != null) { |
| 611 | eventDispatcher.post(event); |
| 612 | } |
| 613 | } |
| 614 | // fetch the old intent's installables from the store |
| 615 | if (oldIntent != null) { |
| 616 | oldInstallables = store.getInstallableIntents(oldIntent.id()); |
| 617 | // TODO: remove intent from store after uninstall |
| 618 | } else { |
| 619 | oldInstallables = null; |
| 620 | } |
| 621 | } |
| 622 | |
| 623 | Intent oldIntent() { |
| 624 | return oldIntent; |
| 625 | } |
| 626 | |
| 627 | Intent newIntent() { |
| 628 | return newIntent; |
| 629 | } |
| 630 | |
| 631 | List<Intent> oldInstallables() { |
| 632 | return oldInstallables; |
| 633 | } |
| 634 | |
| 635 | List<Intent> newInstallables() { |
| 636 | return newInstallables; |
| 637 | } |
| 638 | |
| 639 | void setInstallables(List<Intent> installables) { |
| 640 | newInstallables = installables; |
| 641 | store.setInstallableIntents(newIntent.id(), installables); |
| 642 | } |
| 643 | |
| 644 | List<FlowRuleBatchOperation> batches() { |
| 645 | return batches; |
| 646 | } |
| 647 | |
| 648 | void setBatches(List<FlowRuleBatchOperation> batches) { |
| 649 | this.batches = batches; |
| 650 | } |
| 651 | |
| 652 | IntentState getState(Intent intent) { |
| 653 | return stateMap.get(intent); |
| 654 | } |
| 655 | |
| 656 | void setState(Intent intent, IntentState newState) { |
| 657 | // TODO: clean this up, or set to debug |
| 658 | IntentState oldState = stateMap.get(intent); |
| 659 | log.info("intent id: {}, old state: {}, new state: {}", |
| 660 | intent.id(), oldState, newState); |
| 661 | |
| 662 | stateMap.put(intent, newState); |
| 663 | IntentEvent event = store.setState(intent, newState); |
| 664 | if (event != null) { |
| 665 | eventDispatcher.post(event); |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | Map<Intent, IntentState> stateMap() { |
| 670 | return stateMap; |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | private static List<FlowRuleBatchOperation> mergeBatches(Map<IntentOperation, |
| 675 | IntentUpdate> intentUpdates) { |
| 676 | //TODO test this. |
| 677 | List<FlowRuleBatchOperation> batches = Lists.newArrayList(); |
| 678 | for (IntentUpdate update : intentUpdates.values()) { |
| 679 | if (update.batches() == null) { |
| 680 | continue; |
| 681 | } |
| 682 | int i = 0; |
| 683 | for (FlowRuleBatchOperation batch : update.batches()) { |
| 684 | if (i == batches.size()) { |
| 685 | batches.add(batch); |
| 686 | } else { |
| 687 | FlowRuleBatchOperation existing = batches.get(i); |
| 688 | existing.addAll(batch); |
| 689 | } |
| 690 | i++; |
| 691 | } |
| 692 | } |
| 693 | return batches; |
| 694 | } |
| 695 | |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 696 | // Auxiliary runnable to perform asynchronous tasks. |
| 697 | private class IntentTask implements Runnable { |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 698 | private final IntentOperations operations; |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 699 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 700 | public IntentTask(IntentOperations operations) { |
| 701 | this.operations = operations; |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 702 | } |
| 703 | |
| 704 | @Override |
| 705 | public void run() { |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 706 | Map<IntentOperation, IntentUpdate> intentUpdates = Maps.newHashMap(); |
| 707 | for (IntentOperation op : operations.operations()) { |
| 708 | intentUpdates.put(op, processIntentOperation(op)); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 709 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 710 | List<FlowRuleBatchOperation> batches = mergeBatches(intentUpdates); |
| 711 | monitorExecutor.execute(new IntentInstallMonitor(operations, intentUpdates, batches)); |
tom | 85258ee | 2014-10-07 00:10:02 -0700 | [diff] [blame] | 712 | } |
| 713 | } |
| 714 | |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 715 | private class IntentInstallMonitor implements Runnable { |
| 716 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 717 | private static final long TIMEOUT = 5000; // ms |
| 718 | private final IntentOperations ops; |
| 719 | private final Map<IntentOperation, IntentUpdate> intentUpdateMap; |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 720 | private final List<FlowRuleBatchOperation> work; |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 721 | private Future<CompletedBatchOperation> future; |
| 722 | private final long startTime = System.currentTimeMillis(); |
| 723 | private final long endTime = startTime + TIMEOUT; |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 724 | |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 725 | public IntentInstallMonitor(IntentOperations ops, |
| 726 | Map<IntentOperation, IntentUpdate> intentUpdateMap, |
| 727 | List<FlowRuleBatchOperation> work) { |
| 728 | this.ops = ops; |
| 729 | this.intentUpdateMap = intentUpdateMap; |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 730 | this.work = work; |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 731 | future = applyNextBatch(); |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 732 | } |
| 733 | |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 734 | /** |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 735 | * Applies the next batch, and returns the future. |
| 736 | * |
| 737 | * @return Future for next batch |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 738 | */ |
| 739 | private Future<CompletedBatchOperation> applyNextBatch() { |
| 740 | if (work.isEmpty()) { |
| 741 | return null; |
| 742 | } |
| 743 | FlowRuleBatchOperation batch = work.remove(0); |
| 744 | return flowRuleService.applyBatch(batch); |
| 745 | } |
| 746 | |
| 747 | /** |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 748 | * Update the intent store with the next status for this intent. |
| 749 | */ |
| 750 | private void updateIntents() { |
| 751 | // FIXME we assume everything passes for now. |
| 752 | for (IntentUpdate update : intentUpdateMap.values()) { |
| 753 | for (Intent intent : update.stateMap().keySet()) { |
| 754 | switch (update.getState(intent)) { |
| 755 | case INSTALLING: |
| 756 | update.setState(intent, INSTALLED); |
| 757 | break; |
| 758 | case WITHDRAWING: |
| 759 | update.setState(intent, WITHDRAWN); |
| 760 | // Fall-through |
| 761 | case FAILED: |
| 762 | store.removeInstalledIntents(intent.id()); |
| 763 | break; |
| 764 | |
| 765 | case SUBMITTED: |
| 766 | case COMPILING: |
| 767 | case RECOMPILING: |
| 768 | case WITHDRAWN: |
| 769 | case INSTALLED: |
| 770 | default: |
| 771 | //FIXME clean this up (we shouldn't ever get here) |
| 772 | log.warn("Bad state: {} for {}", update.getState(intent), intent); |
| 773 | break; |
| 774 | } |
| 775 | } |
| 776 | } |
| 777 | /* |
| 778 | for (IntentOperation op : ops.operations()) { |
| 779 | switch (op.type()) { |
| 780 | case SUBMIT: |
| 781 | store.setState(op.intent(), INSTALLED); |
| 782 | break; |
| 783 | case WITHDRAW: |
| 784 | Intent intent = store.getIntent(op.intentId()); |
| 785 | store.setState(intent, WITHDRAWN); |
| 786 | break; |
| 787 | case REPLACE: |
| 788 | store.setState(op.intent(), INSTALLED); |
| 789 | intent = store.getIntent(op.intentId()); |
| 790 | store.setState(intent, WITHDRAWN); |
| 791 | break; |
| 792 | case UPDATE: |
| 793 | intent = store.getIntent(op.intentId()); |
| 794 | store.setState(intent, INSTALLED); |
| 795 | break; |
| 796 | default: |
| 797 | break; |
| 798 | } |
| 799 | } |
| 800 | */ |
| 801 | /* |
| 802 | if (nextState == RECOMPILING) { |
| 803 | eventDispatcher.post(store.setState(intent, FAILED)); |
| 804 | // FIXME try to recompile |
| 805 | // executor.execute(new IntentTask(nextState, intent)); |
| 806 | } else if (nextState == INSTALLED || nextState == WITHDRAWN) { |
| 807 | eventDispatcher.post(store.setState(intent, nextState)); |
| 808 | } else { |
| 809 | log.warn("Invalid next intent state {} for intent {}", nextState, intent); |
| 810 | }*/ |
| 811 | } |
| 812 | |
| 813 | /** |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 814 | * Iterate through the pending futures, and remove them when they have completed. |
| 815 | */ |
| 816 | private void processFutures() { |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 817 | if (future == null) { |
| 818 | return; //FIXME look at this |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 819 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 820 | try { |
| 821 | CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS); |
| 822 | if (completed.isSuccess()) { |
| 823 | future = applyNextBatch(); |
| 824 | } else { |
| 825 | // TODO check if future succeeded and if not report fail items |
| 826 | log.warn("Failed items: {}", completed.failedItems()); |
| 827 | // FIXME revert.... by submitting a new batch |
| 828 | //uninstallIntent(intent, RECOMPILING); |
| 829 | } |
| 830 | } catch (TimeoutException | InterruptedException | ExecutionException te) { |
| 831 | //TODO look into error message |
| 832 | log.debug("Intallations of intent {} is still pending", ops); |
| 833 | } |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 834 | } |
| 835 | |
| 836 | @Override |
| 837 | public void run() { |
| 838 | processFutures(); |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 839 | if (future == null) { |
Brian O'Connor | f2dbde5 | 2014-10-10 16:20:24 -0700 | [diff] [blame] | 840 | // woohoo! we are done! |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 841 | updateIntents(); |
| 842 | batchService.removeIntentOperations(ops); |
| 843 | } else if (endTime < System.currentTimeMillis()) { |
| 844 | log.warn("Install request timed out"); |
| 845 | // future.cancel(true); |
| 846 | // TODO retry and/or report the failure |
Brian O'Connor | cb900f4 | 2014-10-07 21:55:33 -0700 | [diff] [blame] | 847 | } else { |
| 848 | // resubmit ourselves if we are not done yet |
| 849 | monitorExecutor.submit(this); |
| 850 | } |
| 851 | } |
| 852 | } |
Brian O'Connor | fa81eae | 2014-10-30 13:20:05 -0700 | [diff] [blame^] | 853 | |
| 854 | private class InternalBatchDelegate implements IntentBatchDelegate { |
| 855 | @Override |
| 856 | public void execute(IntentOperations operations) { |
| 857 | log.info("Execute operations: {}", operations); |
| 858 | //FIXME: perhaps we want to track this task so that we can cancel it. |
| 859 | executor.execute(new IntentTask(operations)); |
| 860 | } |
| 861 | |
| 862 | @Override |
| 863 | public void cancel(IntentOperations operations) { |
| 864 | //FIXME: implement this |
| 865 | log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations); |
| 866 | } |
| 867 | } |
Brian O'Connor | 66630c8 | 2014-10-02 21:08:19 -0700 | [diff] [blame] | 868 | } |