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