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