aecbf8d6aed547fe4a773075446606168c40caef
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / handlers / SchemaContextHandler.java
1 /*
2  * Copyright (c) 2016 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.restconf.nb.rfc8040.handlers;
9
10 import com.google.common.base.Preconditions;
11 import java.util.Collection;
12 import java.util.concurrent.atomic.AtomicInteger;
13 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
14 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
15 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
16 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
17 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
18 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
19 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
20 import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
21 import org.opendaylight.yangtools.concepts.ListenerRegistration;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
28 import org.opendaylight.yangtools.yang.model.api.Module;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Implementation of {@link SchemaContextHandler}.
36  *
37  */
38 @SuppressWarnings("checkstyle:FinalClass")
39 public class SchemaContextHandler implements SchemaContextListenerHandler, AutoCloseable {
40
41     private static final Logger LOG = LoggerFactory.getLogger(SchemaContextHandler.class);
42
43     private static final SchemaContextHandler INSTANCE = new SchemaContextHandler();
44
45     private final AtomicInteger moduleSetId = new AtomicInteger(0);
46
47     private TransactionChainHandler transactionChainHandler;
48     private DOMSchemaService domSchemaService;
49     private ListenerRegistration<SchemaContextListener> listenerRegistration;
50
51     private volatile SchemaContext schemaContext;
52
53     /**
54      * Constructor.
55      *
56      * @param transactionChainHandler Transaction chain handler
57      */
58     private SchemaContextHandler(final TransactionChainHandler transactionChainHandler,
59             final DOMSchemaService domSchemaService) {
60         this.transactionChainHandler = transactionChainHandler;
61         this.domSchemaService = domSchemaService;
62     }
63
64     @Deprecated
65     private SchemaContextHandler() {
66     }
67
68     @Deprecated
69     public static SchemaContextHandler instance() {
70         return INSTANCE;
71     }
72
73     public static SchemaContextHandler newInstance(TransactionChainHandler transactionChainHandler,
74             DOMSchemaService domSchemaService) {
75         INSTANCE.transactionChainHandler = transactionChainHandler;
76         INSTANCE.domSchemaService = domSchemaService;
77         return INSTANCE;
78     }
79
80     public void init() {
81         listenerRegistration = domSchemaService.registerSchemaContextListener(this);
82     }
83
84     @Override
85     public void close() {
86         if (listenerRegistration != null) {
87             listenerRegistration.close();
88         }
89     }
90
91     @Override
92     @SuppressWarnings("checkstyle:hiddenField")
93     public void onGlobalContextUpdated(final SchemaContext context) {
94         Preconditions.checkNotNull(context);
95         schemaContext = context;
96
97         final Module ietfYangLibraryModule =
98                 context.findModule(IetfYangLibrary.MODULE_QNAME).orElse(null);
99         if (ietfYangLibraryModule != null) {
100             NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode =
101                     RestconfMappingNodeUtil.mapModulesByIetfYangLibraryYang(context.getModules(), ietfYangLibraryModule,
102                             context, String.valueOf(this.moduleSetId.incrementAndGet()));
103             putData(normNode);
104         }
105
106         final Module monitoringModule =
107                 schemaContext.findModule(MonitoringModule.MODULE_QNAME).orElse(null);
108         if (monitoringModule != null) {
109             NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode =
110                     RestconfMappingNodeUtil.mapCapabilites(monitoringModule);
111             putData(normNode);
112         }
113     }
114
115     @Override
116     public SchemaContext get() {
117         return schemaContext;
118     }
119
120     private void putData(
121             final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode) {
122         final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
123         wTx.put(LogicalDatastoreType.OPERATIONAL,
124                 YangInstanceIdentifier.create(NodeIdentifier.create(normNode.getNodeType())), normNode);
125         try {
126             wTx.submit().checkedGet();
127         } catch (final TransactionCommitFailedException e) {
128             if (!(e.getCause() instanceof ConflictingModificationAppliedException)) {
129                 throw new RestconfDocumentedException("Problem occurred while putting data to DS.", e);
130             }
131
132             /*
133               Ignore error when another cluster node is already putting the same data to DS.
134               We expect that cluster is homogeneous and that node was going to write the same data
135               (that means no retry is needed). Transaction chain reset must be invoked to be able
136               to continue writing data with another transaction after failed transaction.
137               This is workaround for bug:
138               https://bugs.opendaylight.org/show_bug.cgi?id=7728
139             */
140             LOG.warn("Ignoring that another cluster node is already putting the same data to DS.", e);
141             this.transactionChainHandler.reset();
142         }
143     }
144 }