doesExists() utility method
[genius.git] / mdsalutil / mdsalutil-api / src / main / java / org / opendaylight / genius / datastoreutils / SingleTransactionDataBroker.java
1 /*
2  * Copyright (c) 2016 Red Hat, 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.genius.datastoreutils;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.genius.datastoreutils.TransactionCommitFailedExceptionMapper.SUBMIT_MAPPER;
12
13 import com.google.common.base.Optional;
14 import java.util.concurrent.ExecutionException;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
18 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
21 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
23 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
24 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Utility methods for single transaction DataBroker usage.
32  *
33  * <p>Please consider using a {@link ManagedNewTransactionRunner} instead.
34  *
35  * @author Michael Vorburger
36  */
37 // do *NOT* make this a BP @Singleton; see https://wiki.opendaylight.org/view/BestPractices/DI_Guidelines#Nota_Bene for why
38 public class SingleTransactionDataBroker {
39
40     private static final Logger LOG = LoggerFactory.getLogger(SingleTransactionDataBroker.class);
41
42     private static final int DEFAULT_RETRIES = 3; // duplicated in RetryingManagedNewTransactionRunnerImpl
43
44     private final DataBroker broker;
45
46     // do *NOT* use BP @Inject here, see comment above
47     public SingleTransactionDataBroker(@NonNull DataBroker broker) {
48         this.broker = requireNonNull(broker, "dataBroker");
49     }
50
51     /**
52      * Synchronously read; preferred &amp; strongly recommended method variant
53      * over other ones offered by this class (because this is the most explicit
54      * variant).
55      *
56      * <p>See {@link ReadTransaction#read(LogicalDatastoreType, InstanceIdentifier)}.
57      *
58      * @param datastoreType
59      *            Logical data store from which read should occur.
60      * @param path
61      *            Path which uniquely identifies subtree which client want to read
62      * @param <T>
63      *            DataObject subclass
64      *
65      * @return If the data at the supplied path exists, returns an Optional
66      *         object containing the data; if the data at the supplied path does
67      *         not exist, returns Optional#absent().
68      * @throws ReadFailedException in case of a technical (!) error while reading
69      */
70     public <T extends DataObject> Optional<T> syncReadOptional(
71             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
72             throws ReadFailedException {
73         return syncReadOptional(broker, datastoreType, path);
74     }
75
76     public static <T extends DataObject> Optional<T> syncReadOptional(
77             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
78             throws ReadFailedException {
79
80         try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
81             return tx.read(datastoreType, path).checkedGet();
82         }
83     }
84
85     /**
86      * Synchronously read; method variant to use by code which expecting that data MUST exist at given path.
87      *
88      * <p>This variant is only recommended if the calling code would treat the Optional
89      * returned by the other method variant as a terminal failure anyway, and would itself throw
90      * an Exception for that.
91      *
92      * <p>If calling code can more sensibly handle non-present data, then use
93      * {@link #syncReadOptional(LogicalDatastoreType, InstanceIdentifier)} instead of this.
94      *
95      * <p>See {@link ReadTransaction#read(LogicalDatastoreType, InstanceIdentifier)}.
96      *
97      * @param datastoreType
98      *            Logical data store from which read should occur.
99      * @param path
100      *            Path which uniquely identifies subtree which client want to read
101      * @param <T>
102      *            DataObject subclass
103      *
104      * @return If the data at the supplied path exists, returns the data.
105      * @throws ReadFailedException in case of a technical (!) error while reading
106      * @throws ExpectedDataObjectNotFoundException a ReadFailedException sub-type, if no data exists at path
107      */
108     public <T extends DataObject> T syncRead(
109             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
110             throws ReadFailedException {
111         return syncRead(broker, datastoreType, path);
112     }
113
114     public static <T extends DataObject> T syncRead(
115             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
116             throws ReadFailedException {
117
118         try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
119             Optional<T> optionalDataObject = tx.read(datastoreType, path).checkedGet();
120             if (optionalDataObject.isPresent()) {
121                 return optionalDataObject.get();
122             } else {
123                 throw new ExpectedDataObjectNotFoundException(datastoreType, path);
124             }
125         }
126     }
127
128     /**
129      * Synchronously read; swallowing (!) ReadFailedException.
130      *
131      * <p>See {@link ReadTransaction#read(LogicalDatastoreType, InstanceIdentifier)}.
132      *
133      * @deprecated This variant is not recommended, and only exists for legacy
134      *             purposes for code which does not yet correctly propagate
135      *             technical exceptions. Prefer using
136      *             {@link #syncReadOptional(LogicalDatastoreType, InstanceIdentifier)}.
137      *
138      * @param datastoreType
139      *            Logical data store from which read should occur.
140      * @param path
141      *            Path which uniquely identifies subtree which client want to read
142      * @param <T>
143      *            DataObject subclass
144      *
145      * @return If the data at the supplied path exists, returns an Optional
146      *         object containing the data; if the data at the supplied path does
147      *         not exist, or a technical error occurred (logged), returns
148      *         Optional#absent().
149      */
150     @Deprecated
151     public <T extends DataObject> Optional<T> syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
152             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
153         return syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker, datastoreType, path);
154     }
155
156     /**
157      * Synchronously read; swallowing (!) ReadFailedException.
158      *
159      * @deprecated This variant is not recommended, and only exists for legacy
160      *             purposes for code which does not yet correctly propagate
161      *             technical exceptions. Prefer using
162      *             {@link #syncReadOptional(DataBroker, LogicalDatastoreType, InstanceIdentifier)}.
163      */
164     @Deprecated
165     public static <T extends DataObject> Optional<T> syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
166             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
167
168         try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
169             return tx.read(datastoreType, path).checkedGet();
170         } catch (ReadFailedException e) {
171             LOG.error("ReadFailedException while reading data from {} store path {}; returning Optional.absent()",
172                     datastoreType, path, e);
173             return Optional.absent();
174         }
175     }
176
177     public <T extends DataObject> void syncWrite(
178             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data)
179             throws TransactionCommitFailedException {
180         syncWrite(broker, datastoreType, path, data);
181     }
182
183     public <T extends DataObject> void syncWrite(
184             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data, int maxRetries)
185             throws TransactionCommitFailedException {
186         syncWrite(broker, datastoreType, path, data, maxRetries);
187     }
188
189     public static <T extends DataObject> void syncWrite(
190             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data)
191             throws TransactionCommitFailedException {
192         syncWrite(broker, datastoreType, path, data, DEFAULT_RETRIES);
193     }
194
195     public static <T extends DataObject> void syncWrite(
196             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data, int maxRetries)
197             throws TransactionCommitFailedException {
198
199         RetryingManagedNewTransactionRunner runner = new RetryingManagedNewTransactionRunner(broker, maxRetries);
200         ListenableFutures.checkedGet(
201                 runner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(datastoreType, path, data, true)),
202                 SUBMIT_MAPPER);
203     }
204
205     public <T extends DataObject> void syncUpdate(
206             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data)
207             throws TransactionCommitFailedException {
208         syncUpdate(broker, datastoreType, path, data);
209     }
210
211     public <T extends DataObject> void syncUpdate(
212             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data, int maxRetries)
213             throws TransactionCommitFailedException {
214         syncUpdate(broker, datastoreType, path, data, maxRetries);
215     }
216
217     public static <T extends DataObject> void syncUpdate(
218             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data)
219             throws TransactionCommitFailedException {
220         syncUpdate(broker, datastoreType, path, data, DEFAULT_RETRIES);
221     }
222
223     public static <T extends DataObject> void syncUpdate(
224             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, T data, int maxRetries)
225             throws TransactionCommitFailedException {
226         RetryingManagedNewTransactionRunner runner = new RetryingManagedNewTransactionRunner(broker, maxRetries);
227         ListenableFutures.checkedGet(
228                 runner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.merge(datastoreType, path, data, true)),
229                 SUBMIT_MAPPER);
230     }
231
232     public <T extends DataObject> void syncDelete(
233             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
234             throws TransactionCommitFailedException {
235         syncDelete(broker, datastoreType, path);
236     }
237
238     public <T extends DataObject> void syncDelete(
239             LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, int maxRetries)
240             throws TransactionCommitFailedException {
241         syncDelete(broker, datastoreType, path, maxRetries);
242     }
243
244     public static <T extends DataObject> void syncDelete(
245             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
246             throws TransactionCommitFailedException {
247         syncDelete(broker, datastoreType, path, DEFAULT_RETRIES);
248     }
249
250     public static <T extends DataObject> void syncDelete(
251             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path, int maxRetries)
252             throws TransactionCommitFailedException {
253
254         RetryingManagedNewTransactionRunner runner = new RetryingManagedNewTransactionRunner(broker, maxRetries);
255         ListenableFutures.checkedGet(
256                 runner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(datastoreType, path)), SUBMIT_MAPPER);
257     }
258
259     public static <T extends DataObject> Boolean doesExists(
260             DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
261             throws ReadFailedException {
262         try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
263             return tx.exists(datastoreType, path).get();
264         } catch (InterruptedException | ExecutionException e) {
265             throw new ReadFailedException(e.getMessage(), e);
266         }
267     }
268 }