Merge "Better handle remove interface from router for unknown port uuid"
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / BackwardsCompatibleMountPoint.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html.
7  */
8 package org.opendaylight.controller.sal.dom.broker;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Function;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.CheckedFuture;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.JdkFutureAdapters;
17 import com.google.common.util.concurrent.ListenableFuture;
18
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22
23 import javax.annotation.Nullable;
24
25 import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
26 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
27 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
28 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
29 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
34 import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
35 import org.opendaylight.controller.md.sal.common.impl.ListenerRegistry;
36 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
37 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
38 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
39 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
40 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
41 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
42 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
43 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
44 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
45 import org.opendaylight.controller.md.sal.dom.api.DOMService;
46 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
47 import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker;
48 import org.opendaylight.controller.sal.common.DataStoreIdentifier;
49 import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
50 import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
51 import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
52 import org.opendaylight.controller.sal.core.api.RpcImplementation;
53 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
54 import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
55 import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
56 import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
57 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
58 import org.opendaylight.controller.sal.core.api.data.DataProviderService;
59 import org.opendaylight.controller.sal.core.api.data.DataValidator;
60 import org.opendaylight.controller.sal.core.api.model.SchemaService;
61 import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
62 import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
63 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
64 import org.opendaylight.controller.sal.dom.broker.impl.NotificationRouterImpl;
65 import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
66 import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
67 import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
68 import org.opendaylight.controller.sal.dom.broker.util.ProxySchemaContext;
69 import org.opendaylight.yangtools.concepts.ListenerRegistration;
70 import org.opendaylight.yangtools.concepts.Registration;
71 import org.opendaylight.yangtools.yang.common.QName;
72 import org.opendaylight.yangtools.yang.common.RpcResult;
73 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
76 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
77 import org.opendaylight.yangtools.yang.model.api.Module;
78 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
79 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
80
81 public class BackwardsCompatibleMountPoint implements MountProvisionInstance, SchemaContextProvider, SchemaService {
82
83     private final DataProviderService dataReader;
84     private final DataReader<YangInstanceIdentifier,CompositeNode> readWrapper;
85
86     private final YangInstanceIdentifier mountPath;
87     private final NotificationPublishService notificationPublishService;
88     private final RpcProvisionRegistry rpcs;
89
90     private final ListenerRegistry<SchemaContextListener> schemaListenerRegistry = new ListenerRegistry<>();
91
92     private SchemaContext schemaContext;
93
94     public BackwardsCompatibleMountPoint(final YangInstanceIdentifier path, final DOMMountPointService.DOMMountPointBuilder mountPointBuilder) {
95         this.mountPath = Preconditions.checkNotNull(path);
96         Preconditions.checkNotNull(mountPointBuilder);
97
98         dataReader = new DataBrokerImpl();
99         readWrapper = new ReadWrapper();
100         notificationPublishService = new DelgatingNotificationPublishService();
101         rpcs = new SchemaAwareRpcBroker(path.toString(), this);
102
103         mountPointBuilder.addService(DOMDataBroker.class, new BackwardsCompatibleDomStore(dataReader, this));
104         mountPointBuilder.addService(NotificationPublishService.class, notificationPublishService);
105         mountPointBuilder.addService(RpcProvisionRegistry.class, rpcs);
106
107         mountPointBuilder.addInitialSchemaContext(new ProxySchemaContext(this));
108
109         mountPointBuilder.register();
110     }
111
112     public BackwardsCompatibleMountPoint(final YangInstanceIdentifier path, final DOMMountPoint mount) {
113         this.mountPath = Preconditions.checkNotNull(path);
114         Preconditions.checkNotNull(mount);
115
116         final DOMDataBroker domBroker = getServiceWithCheck(mount, DOMDataBroker.class);
117
118         this.schemaContext = mount.getSchemaContext();
119
120         dataReader = new BackwardsCompatibleDataBroker(domBroker, this);
121
122         // Set schema context to provide it for BackwardsCompatibleDataBroker
123         if(schemaContext != null) {
124             setSchemaContext(schemaContext);
125         }
126
127         readWrapper = new ReadWrapper();
128
129         notificationPublishService = getServiceWithCheck(mount, NotificationPublishService.class);
130         rpcs = getServiceWithCheck(mount, RpcProvisionRegistry.class);
131     }
132
133     private <T extends DOMService> T getServiceWithCheck(final DOMMountPoint mount, final Class<T> type) {
134         final Optional<T> serviceOptional = mount.getService(type);
135         Preconditions.checkArgument(serviceOptional.isPresent(), "Service {} has to be set in {}. " +
136                 "Cannot construct backwards compatible mount wrapper without it", type, mount);
137         return serviceOptional.get();
138     }
139
140     @Override
141     public void addModule(final Module module) {
142         throw new UnsupportedOperationException();
143     }
144
145     @Override
146     public void removeModule(final Module module) {
147         throw new UnsupportedOperationException();
148     }
149
150     @Override
151     public SchemaContext getSessionContext() {
152         return getSchemaContext();
153     }
154
155     @Override
156     public SchemaContext getGlobalContext() {
157         return getSchemaContext();
158     }
159
160     @Override
161     public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
162         return schemaListenerRegistry.register(listener);
163     }
164
165     @Override
166     public void publish(final CompositeNode notification) {
167         notificationPublishService.publish(notification);
168     }
169
170     @Override
171     public ListenerRegistration<NotificationListener> addNotificationListener(final QName notification, final NotificationListener listener) {
172         return notificationPublishService.addNotificationListener(notification, listener);
173     }
174
175     // TODO Read wrapper is never used ... same in org.opendaylight.controller.sal.dom.broker.MountPointImpl
176     public DataReader<YangInstanceIdentifier, CompositeNode> getReadWrapper() {
177         return readWrapper;
178     }
179
180     @Override
181     public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
182         return dataReader.readConfigurationData(path);
183     }
184
185     @Override
186     public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
187         return dataReader.readOperationalData(path);
188     }
189
190     @Override
191     public Registration registerOperationalReader(
192             final YangInstanceIdentifier path, final DataReader<YangInstanceIdentifier, CompositeNode> reader) {
193         return dataReader.registerOperationalReader(path, reader);
194     }
195
196     @Override
197     public Registration registerConfigurationReader(
198             final YangInstanceIdentifier path, final DataReader<YangInstanceIdentifier, CompositeNode> reader) {
199         return dataReader.registerConfigurationReader(path, reader);
200     }
201
202     @Override
203     public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
204         return rpcs.addRoutedRpcImplementation(rpcType, implementation);
205     }
206
207     @Override
208     public void setRoutedRpcDefaultDelegate(final RoutedRpcDefaultImplementation defaultImplementation) {
209         rpcs.setRoutedRpcDefaultDelegate(defaultImplementation);
210     }
211
212     @Override
213     public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation)
214             throws IllegalArgumentException {
215         return rpcs.addRpcImplementation(rpcType, implementation);
216     }
217
218     @Override
219     public Set<QName> getSupportedRpcs() {
220         return rpcs.getSupportedRpcs();
221     }
222
223     @Override
224     public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
225         return rpcs.invokeRpc(rpc, input);
226     }
227
228     @Override
229     public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(final RpcRegistrationListener listener) {
230         return rpcs.addRpcRegistrationListener(listener);
231     }
232
233     @Override
234     public ListenableFuture<RpcResult<CompositeNode>> rpc(final QName type, final CompositeNode input) {
235         return rpcs.invokeRpc(type, input);
236     }
237
238     @Override
239     public DataModificationTransaction beginTransaction() {
240         return dataReader.beginTransaction();
241     }
242
243     @Override
244     public ListenerRegistration<DataChangeListener> registerDataChangeListener(final YangInstanceIdentifier path,
245             final DataChangeListener listener) {
246         return dataReader.registerDataChangeListener(path, listener);
247     }
248
249     @Override
250     public Registration registerCommitHandler(
251             final YangInstanceIdentifier path, final DataCommitHandler<YangInstanceIdentifier, CompositeNode> commitHandler) {
252         return dataReader.registerCommitHandler(path, commitHandler);
253     }
254
255     @Override
256     public void removeRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
257         // NOOP
258     }
259
260     @Override
261     public void addRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
262         // NOOP
263     }
264
265     @Override
266     public void addValidator(final DataStoreIdentifier store, final DataValidator validator) {
267         // NOOP
268     }
269     @Override
270     public void removeValidator(final DataStoreIdentifier store, final DataValidator validator) {
271         // NOOP
272     }
273
274     @Override
275     public SchemaContext getSchemaContext() {
276         return schemaContext;
277     }
278
279     @Override
280     public void setSchemaContext(final SchemaContext schemaContext) {
281         this.schemaContext = schemaContext;
282         for (ListenerRegistration<SchemaContextListener> schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
283             schemaServiceListenerListenerRegistration.getInstance().onGlobalContextUpdated(schemaContext);
284         }
285     }
286
287     class ReadWrapper implements DataReader<YangInstanceIdentifier, CompositeNode> {
288         private YangInstanceIdentifier shortenPath(final YangInstanceIdentifier path) {
289             YangInstanceIdentifier ret = null;
290             if(mountPath.contains(path)) {
291                 final List<PathArgument> newArgs = path.getPath().subList(mountPath.getPath().size(), path.getPath().size());
292                 ret = YangInstanceIdentifier.create(newArgs);
293             }
294             return ret;
295         }
296
297         @Override
298         public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
299             final YangInstanceIdentifier newPath = shortenPath(path);
300             if(newPath == null) {
301                 return null;
302             }
303             return BackwardsCompatibleMountPoint.this.readConfigurationData(newPath);
304         }
305
306         @Override
307         public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
308             final YangInstanceIdentifier newPath = shortenPath(path);
309             if(newPath == null) {
310                 return null;
311             }
312             return BackwardsCompatibleMountPoint.this.readOperationalData(newPath);
313         }
314     }
315
316     @Override
317     public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<YangInstanceIdentifier, CompositeNode>>> registerCommitHandlerListener(
318             final RegistrationListener<DataCommitHandlerRegistration<YangInstanceIdentifier, CompositeNode>> commitHandlerListener) {
319         return dataReader.registerCommitHandlerListener(commitHandlerListener);
320     }
321
322     @Override
323     public <L extends RouteChangeListener<RpcRoutingContext, YangInstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(
324             final L listener) {
325         return rpcs.registerRouteChangeListener(listener);
326     }
327
328     @VisibleForTesting
329     static final class BackwardsCompatibleDomStore implements DOMDataBroker {
330         private final DataProviderService dataReader;
331         private final SchemaContextProvider schemaContextProvider;
332
333         public BackwardsCompatibleDomStore(final DataProviderService dataReader, final SchemaContextProvider schemaContextProvider) {
334             this.dataReader = dataReader;
335             this.schemaContextProvider = schemaContextProvider;
336         }
337
338         @Override
339         public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
340             final DataNormalizer dataNormalizer = new DataNormalizer(schemaContextProvider.getSchemaContext());
341             return new BackwardsCompatibleReadTransaction(dataReader, dataNormalizer);
342         }
343
344         @Override
345         public DOMDataWriteTransaction newWriteOnlyTransaction() {
346             final DataNormalizer dataNormalizer = new DataNormalizer(schemaContextProvider.getSchemaContext());
347             return new BackwardsCompatibleWriteTransaction(dataReader, dataNormalizer);
348         }
349
350         @Override
351         public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store, final YangInstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
352             throw new UnsupportedOperationException("Register data listener not supported for mount point");
353         }
354
355         @Override
356         public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
357             throw new UnsupportedOperationException("Transaction chain not supported for mount point");
358         }
359
360         @Override
361         public DOMDataReadWriteTransaction newReadWriteTransaction() {
362             final DataNormalizer dataNormalizer = new DataNormalizer(schemaContextProvider.getSchemaContext());
363             return new BackwardsCompatibleReadWriteTransaction(dataReader, dataNormalizer);
364         }
365
366         @VisibleForTesting
367         static final class BackwardsCompatibleReadTransaction implements DOMDataReadOnlyTransaction {
368             private final DataProviderService dataReader;
369             private final DataNormalizer normalizer;
370
371             public BackwardsCompatibleReadTransaction(final DataProviderService dataReader, final DataNormalizer normalizer) {
372                 this.dataReader = dataReader;
373                 this.normalizer = normalizer;
374             }
375
376             @Override
377             public Object getIdentifier() {
378                 return this;
379             }
380
381             @Override
382             public void close() {
383                 // NOOP
384             }
385
386             @Override
387             public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
388                     final LogicalDatastoreType store, final YangInstanceIdentifier path) {
389
390                 CompositeNode rawData = null;
391
392                 switch (store) {
393                     case CONFIGURATION: {
394                         rawData = dataReader.readConfigurationData(path);
395                         break;
396                     }
397                     case OPERATIONAL: {
398                         rawData = dataReader.readOperationalData(path);
399                         break;
400                     }
401                 }
402                 Preconditions.checkNotNull(rawData, "Unable to read %s data on path %s", store, path);
403
404                 final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = normalizer.toNormalized(path, rawData);
405                 final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = Optional.<NormalizedNode<?, ?>>fromNullable(normalized.getValue());
406                 return Futures.immediateCheckedFuture(normalizedNodeOptional);
407             }
408         }
409
410         @VisibleForTesting
411         static final class BackwardsCompatibleWriteTransaction implements DOMDataWriteTransaction {
412             private DataModificationTransaction oldTx;
413             private final DataNormalizer dataNormalizer;
414
415             public BackwardsCompatibleWriteTransaction(final DataProviderService dataReader, final DataNormalizer dataNormalizer) {
416                 this.oldTx = dataReader.beginTransaction();
417                 this.dataNormalizer = dataNormalizer;
418             }
419
420             @Override
421             public Object getIdentifier() {
422                 return this;
423             }
424
425             @Override
426             public boolean cancel() {
427                 oldTx = null;
428                 return true;
429             }
430
431             @Override
432             public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
433                 final CompositeNode legacyData = dataNormalizer.toLegacy(path, data);
434                 try {
435                     final YangInstanceIdentifier legacyPath = dataNormalizer.toLegacy(path);
436
437                     switch (store) {
438                         case CONFIGURATION: {
439                             oldTx.putConfigurationData(legacyPath, legacyData);
440                             return;
441                         }
442                     }
443
444                     throw new IllegalArgumentException("Cannot put data " + path + " to datastore " + store);
445                 } catch (final DataNormalizationException e) {
446                     throw new IllegalArgumentException(String.format("Cannot transform path %s to legacy format", path), e);
447                 }
448             }
449
450             @Override
451             public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
452                 // TODO not supported
453                 throw new UnsupportedOperationException("Merge not supported for mount point");
454             }
455
456             @Override
457             public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
458                 try {
459                     final YangInstanceIdentifier legacyPath = dataNormalizer.toLegacy(path);
460
461                     switch (store) {
462                         case CONFIGURATION: {
463                             oldTx.removeConfigurationData(legacyPath);
464                             return;
465                         }
466                     }
467                     throw new IllegalArgumentException("Cannot delete data " + path + " from datastore " + store);
468                 } catch (final DataNormalizationException e) {
469                     throw new IllegalArgumentException(String.format("Cannot transform path %s to legacy format", path), e);
470                 }
471             }
472
473             @Override
474             public CheckedFuture<Void, TransactionCommitFailedException> submit() {
475                 final ListenableFuture<Void> commitAsVoid = Futures.transform(commit(), new Function<RpcResult<TransactionStatus>, Void>() {
476                     @Override
477                     public Void apply(@Nullable final RpcResult<TransactionStatus> input) {
478                         return null;
479                     }
480                 });
481
482                 return Futures.makeChecked(commitAsVoid, new Function<Exception, TransactionCommitFailedException>() {
483                     @Override
484                     public TransactionCommitFailedException apply(@Nullable final Exception input) {
485                         return new TransactionCommitFailedException("Commit failed", input);
486                     }
487                 });
488             }
489
490             @Override
491             public ListenableFuture<RpcResult<TransactionStatus>> commit() {
492                 return JdkFutureAdapters.listenInPoolThread(oldTx.commit());
493             }
494         }
495
496
497         @VisibleForTesting
498         static class BackwardsCompatibleReadWriteTransaction implements DOMDataReadWriteTransaction {
499
500             private final DataProviderService dataReader;
501             private final DataNormalizer dataNormalizer;
502             private final BackwardsCompatibleWriteTransaction delegateWriteTx;
503
504             public BackwardsCompatibleReadWriteTransaction(final DataProviderService dataReader, final DataNormalizer dataNormalizer) {
505                 this.dataReader = dataReader;
506                 this.dataNormalizer = dataNormalizer;
507                 this.delegateWriteTx = new BackwardsCompatibleWriteTransaction(dataReader, dataNormalizer);
508             }
509
510             @Override
511             public Object getIdentifier() {
512                 return this;
513             }
514
515             @Override
516             public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
517                     final LogicalDatastoreType store, final YangInstanceIdentifier path) {
518                 return new BackwardsCompatibleReadTransaction(dataReader, dataNormalizer).read(store, path);
519             }
520
521             @Override
522             public boolean cancel() {
523                 return delegateWriteTx.cancel();
524             }
525
526             @Override
527             public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
528                 delegateWriteTx.put(store, path, data);
529             }
530
531             @Override
532             public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
533                 delegateWriteTx.merge(store, path, data);
534             }
535
536             @Override
537             public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
538                 delegateWriteTx.delete(store, path);
539             }
540
541             @Override
542             public CheckedFuture<Void, TransactionCommitFailedException> submit() {
543                 return delegateWriteTx.submit();
544             }
545
546             @Override
547             public ListenableFuture<RpcResult<TransactionStatus>> commit() {
548                 return delegateWriteTx.commit();
549             }
550         }
551     }
552
553     private class DelgatingNotificationPublishService implements NotificationPublishService {
554         private final NotificationRouter notificationRouter;
555
556         public DelgatingNotificationPublishService(final NotificationRouter notificationRouter) {
557             this.notificationRouter = notificationRouter;
558         }
559
560         private DelgatingNotificationPublishService() {
561             this(new NotificationRouterImpl());
562         }
563
564         @Override
565         public void publish(final CompositeNode notification) {
566             notificationRouter.publish(notification);
567         }
568
569         @Override
570         public ListenerRegistration<NotificationListener> addNotificationListener(final QName notification, final NotificationListener listener) {
571             return notificationRouter.addNotificationListener(notification, listener);
572         }
573     }
574 }