Bug 1010: Implement restconf error responses
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / BrokerFacade.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.restconf.impl;
9
10 import java.util.concurrent.Future;
11
12 import javax.ws.rs.core.Response.Status;
13
14 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
15 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
16 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
17 import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
18 import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
19 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
20 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
21 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
22 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
23 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
24 import org.opendaylight.yangtools.concepts.ListenerRegistration;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.RpcResult;
27 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
33     private final static Logger LOG = LoggerFactory.getLogger( BrokerFacade.class );
34
35     private final static BrokerFacade INSTANCE = new BrokerFacade();
36
37     private volatile DataBrokerService dataService;
38     private volatile ConsumerSession context;
39
40     private BrokerFacade() {
41     }
42
43     public void setContext( final ConsumerSession context ) {
44         this.context = context;
45     }
46
47     public void setDataService( final DataBrokerService dataService ) {
48         this.dataService = dataService;
49     }
50
51     public static BrokerFacade getInstance() {
52         return BrokerFacade.INSTANCE;
53     }
54
55     private void checkPreconditions() {
56         if( context == null || dataService == null ) {
57             throw new RestconfDocumentedException( Status.SERVICE_UNAVAILABLE );
58         }
59     }
60
61     @Override
62     public CompositeNode readConfigurationData( final InstanceIdentifier path ) {
63         this.checkPreconditions();
64
65         LOG.trace( "Read Configuration via Restconf: {}", path );
66
67         return dataService.readConfigurationData( path );
68     }
69
70     public CompositeNode readConfigurationDataBehindMountPoint( final MountInstance mountPoint,
71                                                                 final InstanceIdentifier path ) {
72         this.checkPreconditions();
73
74         LOG.trace( "Read Configuration via Restconf: {}", path );
75
76         return mountPoint.readConfigurationData( path );
77     }
78
79     @Override
80     public CompositeNode readOperationalData( final InstanceIdentifier path ) {
81         this.checkPreconditions();
82
83         BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
84
85         return dataService.readOperationalData( path );
86     }
87
88     public CompositeNode readOperationalDataBehindMountPoint( final MountInstance mountPoint,
89                                                               final InstanceIdentifier path ) {
90         this.checkPreconditions();
91
92         BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
93
94         return mountPoint.readOperationalData( path );
95     }
96
97     public Future<RpcResult<CompositeNode>> invokeRpc( final QName type, final CompositeNode payload ) {
98         this.checkPreconditions();
99
100         return context.rpc( type, payload );
101     }
102
103     public Future<RpcResult<TransactionStatus>> commitConfigurationDataPut( final InstanceIdentifier path,
104                                                                             final CompositeNode payload ) {
105         this.checkPreconditions();
106
107         final DataModificationTransaction transaction = dataService.beginTransaction();
108         BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
109         transaction.putConfigurationData( path, payload );
110         return transaction.commit();
111     }
112
113     public Future<RpcResult<TransactionStatus>> commitConfigurationDataPutBehindMountPoint(
114             final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
115         this.checkPreconditions();
116
117         final DataModificationTransaction transaction = mountPoint.beginTransaction();
118         BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
119         transaction.putConfigurationData( path, payload );
120         return transaction.commit();
121     }
122
123     public Future<RpcResult<TransactionStatus>> commitConfigurationDataPost( final InstanceIdentifier path,
124                                                                              final CompositeNode payload) {
125         this.checkPreconditions();
126
127         final DataModificationTransaction transaction = dataService.beginTransaction();
128         /* check for available Node in Configuration DataStore by path */
129         CompositeNode availableNode = transaction.readConfigurationData( path );
130         if (availableNode != null) {
131             String errMsg = "Post Configuration via Restconf was not executed because data already exists";
132             BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
133
134             throw new RestconfDocumentedException(
135                     "Data already exists for path: " + path, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS );
136         }
137         BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
138         transaction.putConfigurationData( path, payload );
139         return transaction.commit();
140     }
141
142     public Future<RpcResult<TransactionStatus>> commitConfigurationDataPostBehindMountPoint(
143             final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
144         this.checkPreconditions();
145
146         final DataModificationTransaction transaction = mountPoint.beginTransaction();
147         /* check for available Node in Configuration DataStore by path */
148         CompositeNode availableNode = transaction.readConfigurationData( path );
149         if (availableNode != null) {
150             String errMsg = "Post Configuration via Restconf was not executed because data already exists";
151             BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
152
153             throw new RestconfDocumentedException(
154                     "Data already exists for path: " + path, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS );
155         }
156         BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
157         transaction.putConfigurationData( path, payload );
158         return transaction.commit();
159     }
160
161     public Future<RpcResult<TransactionStatus>> commitConfigurationDataDelete( final InstanceIdentifier path ) {
162         this.checkPreconditions();
163
164         final DataModificationTransaction transaction = dataService.beginTransaction();
165         LOG.info( "Delete Configuration via Restconf: {}", path );
166         transaction.removeConfigurationData( path );
167         return transaction.commit();
168     }
169
170     public Future<RpcResult<TransactionStatus>> commitConfigurationDataDeleteBehindMountPoint(
171                                           final MountInstance mountPoint, final InstanceIdentifier path ) {
172         this.checkPreconditions();
173
174         final DataModificationTransaction transaction = mountPoint.beginTransaction();
175         LOG.info( "Delete Configuration via Restconf: {}", path );
176         transaction.removeConfigurationData( path );
177         return transaction.commit();
178     }
179
180     public void registerToListenDataChanges( final ListenerAdapter listener ) {
181         this.checkPreconditions();
182
183         if( listener.isListening() ) {
184             return;
185         }
186
187         InstanceIdentifier path = listener.getPath();
188         final ListenerRegistration<DataChangeListener> registration =
189                                              dataService.registerDataChangeListener( path, listener );
190
191         listener.setRegistration( registration );
192     }
193 }