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