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