propagate datastore exceptions all the way to northbound
[neutron.git] / transcriber / src / main / java / org / opendaylight / neutron / transcriber / AbstractTranscriberInterface.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.neutron.transcriber;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.Method;
16 import java.lang.reflect.ParameterizedType;
17 import java.lang.reflect.Type;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Set;
22 import javax.annotation.PreDestroy;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
28 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
29 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
34 import org.opendaylight.infrautils.utils.function.CheckedFunction;
35 import org.opendaylight.neutron.spi.INeutronAdminAttributes;
36 import org.opendaylight.neutron.spi.INeutronBaseAttributes;
37 import org.opendaylight.neutron.spi.INeutronCRUD;
38 import org.opendaylight.neutron.spi.INeutronObject;
39 import org.opendaylight.neutron.spi.NeutronObject;
40 import org.opendaylight.neutron.spi.ReadFailedRuntimeException;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.AdminAttributes;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.BaseAttributes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.IdAttributes;
45 import org.opendaylight.yangtools.concepts.Builder;
46 import org.opendaylight.yangtools.yang.binding.Augmentable;
47 import org.opendaylight.yangtools.yang.binding.ChildOf;
48 import org.opendaylight.yangtools.yang.binding.DataObject;
49 import org.opendaylight.yangtools.yang.binding.Identifiable;
50 import org.opendaylight.yangtools.yang.binding.Identifier;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.OperationFailedException;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 /**
57  * Base class of Transcriber converts data from/to neutron spi to/from md-sal yang model.
58  *
59  * <pre>
60  * {@code
61  * V -> U -> T
62  *        K: key
63  *
64  *
65  * T(MD-SAL model) <-> S (neutron spi): Neutron northbound
66  *                  -> fromMd()
67  *                 <-  toMd()
68  * }
69  * </pre>
70  *
71  * <p>
72  * Example
73  * T: Port
74  * U: Ports
75  * K: PortKey
76  * S: NeutronPort
77  * V: Neutron
78  *
79  * <p>
80  * @param <T> Target yang model
81  * @param <U> parent of T
82  * @param <K> key type to indentify T
83  * @param <S> Neutron-spi class
84  * @param <V> parent of U
85  */
86 public abstract class AbstractTranscriberInterface<
87         T extends DataObject & Identifiable<K> & ChildOf<? super U>,
88         U extends ChildOf<? super V> & Augmentable<U>,
89         K extends Identifier<T>, S extends INeutronObject<S>,
90         V extends DataObject>
91         implements AutoCloseable, INeutronCRUD<S> {
92
93     private static final Logger LOG = LoggerFactory.getLogger(AbstractTranscriberInterface.class);
94
95     // T extends DataObject & Identifiable<K> & ChildOf<? super U> as 0th type argument
96     private static final int MD_LIST_CLASS_TYPE_INDEX = 0;
97     // U extends ChildOf<? super Neutron> & Augmentable<U> as 1st type argument
98     private static final int MD_CONTAINER_CLASS_TYPE_INDEX = 1;
99     // V extends DataObject as 4th type argument
100     private static final int MD_PARENT_CLASS_TYPE_INDEX = 4;
101     // S extends INeutronObject<S> as 3rd type argument
102     private static final int NEUTRON_OBJECT_TYPE_INDEX = 3;
103
104     private static final int DEDASHED_UUID_LENGTH = 32;
105     private static final int DEDASHED_UUID_START = 0;
106     private static final int DEDASHED_UUID_DIV1 = 8;
107     private static final int DEDASHED_UUID_DIV2 = 12;
108     private static final int DEDASHED_UUID_DIV3 = 16;
109     private static final int DEDASHED_UUID_DIV4 = 20;
110
111     private static final int RETRY_MAX = 2;
112
113     private final DataBroker db;
114
115     private final Class<V> mdParentClass;
116     private final Class<U> mdContainerClass;
117     private final Class<T> mdListClass;
118
119     // Unfortunately odl yangtools doesn't model yang model "uses" as
120     // class/interface hierarchy. So we need to resort to use reflection
121     // to call setter method.
122     private final Class<? extends Builder<T>> builderClass;
123     private final Method setUuid;
124     private final Method setTenantId;
125     private final Method setProjectId;
126     private final Method setName;
127     private final Method setAdminStateUp;
128     private final Method setStatus;
129     private final Method setRevisionNumber;
130
131     protected Class<V> getMdParentClass(final Type[] types) {
132         @SuppressWarnings("unchecked")
133         Class<V> localMdParentClass = (Class<V>) types[MD_PARENT_CLASS_TYPE_INDEX];
134         return localMdParentClass;
135     }
136
137     protected AbstractTranscriberInterface(Class<? extends Builder<T>> builderClass, DataBroker db) {
138         this.db = Preconditions.checkNotNull(db);
139         this.builderClass = builderClass;
140
141         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
142         Type[] types = parameterizedType.getActualTypeArguments();
143         @SuppressWarnings("unchecked")
144         Class<T> localMdListClass = (Class<T>) types[MD_LIST_CLASS_TYPE_INDEX];
145         mdListClass = localMdListClass;
146         @SuppressWarnings("unchecked")
147         Class<U> localMdContainerClass = (Class<U>) types[MD_CONTAINER_CLASS_TYPE_INDEX];
148         mdContainerClass = localMdContainerClass;
149         mdParentClass = getMdParentClass(types);
150         @SuppressWarnings("unchecked")
151         Class<S> neutronObjectClass = (Class<S>) types[NEUTRON_OBJECT_TYPE_INDEX];
152         try {
153             setUuid = builderClass.getDeclaredMethod("setUuid", Uuid.class);
154             setTenantId = builderClass.getDeclaredMethod("setTenantId", Uuid.class);
155             setProjectId = builderClass.getDeclaredMethod("setProjectId", String.class);
156             setRevisionNumber = builderClass.getDeclaredMethod("setRevisionNumber", Long.class);
157             if (INeutronBaseAttributes.class.isAssignableFrom(neutronObjectClass)) {
158                 setName = builderClass.getDeclaredMethod("setName", String.class);
159             } else {
160                 setName = null;
161             }
162
163             if (INeutronAdminAttributes.class.isAssignableFrom(neutronObjectClass)) {
164                 setAdminStateUp = builderClass.getDeclaredMethod("setAdminStateUp", Boolean.class);
165                 setStatus = builderClass.getDeclaredMethod("setStatus", String.class);
166             } else {
167                 setAdminStateUp = null;
168                 setStatus = null;
169             }
170         } catch (NoSuchMethodException e) {
171             throw new IllegalArgumentException(e);
172         }
173     }
174
175     public DataBroker getDataBroker() {
176         Preconditions.checkNotNull(db);
177         return db;
178     }
179
180     private InstanceIdentifier<T> createInstanceIdentifier(T item) {
181         return InstanceIdentifier.create(mdParentClass).child(mdContainerClass).child(mdListClass, item.key());
182     }
183
184     private InstanceIdentifier<U> createInstanceIdentifier() {
185         return InstanceIdentifier.create(mdParentClass).child(mdContainerClass);
186     }
187
188     protected static <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
189         B toMdIds(INeutronObject<S1> neutronObject, Class<B> builderClass) {
190         B builder;
191         try {
192             builder = builderClass.newInstance();
193
194             if (neutronObject.getID() != null) {
195                 final Method setUuid = builderClass.getMethod("setUuid", Uuid.class);
196                 setUuid.invoke(builder, toUuid(neutronObject.getID()));
197             }
198             if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
199                 final Method setTenantId = builderClass.getMethod("setTenantId", Uuid.class);
200                 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
201             }
202             if (neutronObject.getProjectID() != null) {
203                 final Method setProjectId = builderClass.getMethod("setProjectId", String.class);
204                 setProjectId.invoke(builder, neutronObject.getTenantID());
205             }
206             if (neutronObject.getRevisionNumber() != null) {
207                 final Method setRevisionNumber = builderClass.getMethod("setRevisionNumber", Long.class);
208                 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
209             }
210         } catch (IllegalAccessException | InstantiationException | InvocationTargetException
211                 | NoSuchMethodException e) {
212             throw new IllegalArgumentException(e);
213         }
214         return builder;
215     }
216
217     protected <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
218         void toMdIds(INeutronObject<S1> neutronObject, B builder) {
219         try {
220             if (neutronObject.getID() != null) {
221                 setUuid.invoke(builder, toUuid(neutronObject.getID()));
222             } else {
223                 LOG.warn("Attempting to write neutron object {} without UUID", builderClass.getSimpleName());
224             }
225             if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
226                 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
227             }
228             if (neutronObject.getProjectID() != null) {
229                 setProjectId.invoke(builder, neutronObject.getTenantID());
230             }
231             if (neutronObject.getRevisionNumber() != null) {
232                 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
233             }
234         } catch (IllegalAccessException | InvocationTargetException e) {
235             throw new IllegalArgumentException(e);
236         }
237     }
238
239     protected <S1 extends INeutronObject<S1>>
240         void fromMdIds(IdAttributes idAttributes, INeutronObject<S1> answer) {
241         if (idAttributes.getUuid() != null) {
242             answer.setID(idAttributes.getUuid().getValue());
243         }
244         if (idAttributes.getTenantId() != null) {
245             answer.setTenantID(idAttributes.getTenantId());
246         }
247         if (idAttributes.getProjectId() != null) {
248             answer.setProjectID(idAttributes.getProjectId());
249         }
250         if (idAttributes.getRevisionNumber() != null) {
251             answer.setRevisionNumber(idAttributes.getRevisionNumber());
252         }
253     }
254
255     protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
256         void toMdBaseAttributes(S1 neutronObject, B builder) {
257         toMdIds(neutronObject, builder);
258         try {
259             if (neutronObject.getName() != null) {
260                 setName.invoke(builder, neutronObject.getName());
261             }
262         } catch (IllegalAccessException | InvocationTargetException e) {
263             throw new IllegalArgumentException(e);
264         }
265     }
266
267     protected <S1 extends INeutronBaseAttributes<S1>>
268         void fromMdBaseAttributes(BaseAttributes baseAttributes, S1 answer) {
269         fromMdIds(baseAttributes, answer);
270         if (baseAttributes.getName() != null) {
271             answer.setName(baseAttributes.getName());
272         }
273     }
274
275     protected <S1 extends INeutronAdminAttributes<S1>, M extends BaseAttributes & AdminAttributes, B extends Builder<M>>
276         void toMdAdminAttributes(S1 neutronObject, B builder) {
277         toMdBaseAttributes(neutronObject, builder);
278         try {
279             if (neutronObject.getAdminStateUp() != null) {
280                 setAdminStateUp.invoke(builder, neutronObject.getAdminStateUp());
281             }
282             if (neutronObject.getStatus() != null) {
283                 setStatus.invoke(builder, neutronObject.getStatus());
284             }
285         } catch (IllegalAccessException | InvocationTargetException e) {
286             throw new IllegalArgumentException(e);
287         }
288     }
289
290     protected <M extends BaseAttributes & AdminAttributes, S1 extends INeutronAdminAttributes<S1>>
291         void fromMdAdminAttributes(M attr, S1 answer) {
292         fromMdBaseAttributes(attr, answer);
293         if (attr.isAdminStateUp() != null) {
294             answer.setAdminStateUp(attr.isAdminStateUp());
295         }
296         if (attr.getStatus() != null) {
297             answer.setStatus(attr.getStatus());
298         }
299     }
300
301     protected abstract T toMd(S neutronObject);
302
303     protected T toMd(String uuid) {
304         Builder<T> builder;
305         try {
306             builder = builderClass.newInstance();
307             setUuid.invoke(builder, toUuid(uuid));
308         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
309             // should not happen.
310             throw new IllegalArgumentException(e);
311         }
312         return builder.build();
313     }
314
315     protected abstract S fromMd(T dataObject);
316
317     private <W extends DataObject> W readMd(InstanceIdentifier<W> path, ReadTransaction tx) throws ReadFailedException {
318         Preconditions.checkNotNull(tx);
319         W result = null;
320         final CheckedFuture<Optional<W>,
321                 ReadFailedException> future = tx.read(LogicalDatastoreType.CONFIGURATION, path);
322         if (future != null) {
323             Optional<W> optional = future.checkedGet();
324             if (optional.isPresent()) {
325                 result = optional.get();
326             }
327         }
328         return result;
329     }
330
331     protected <W extends DataObject> W readMd(InstanceIdentifier<W> path) throws ReadFailedException {
332         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
333             return readMd(path, tx);
334         }
335     }
336
337     private void addMd(S neutronObject, WriteTransaction tx) throws TransactionCommitFailedException {
338         // TODO think about adding existence logic
339         updateMd(neutronObject, tx);
340     }
341
342     private void updateMd(S neutronObject, WriteTransaction tx) throws TransactionCommitFailedException {
343         Preconditions.checkNotNull(tx);
344
345         final T item = toMd(neutronObject);
346         final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
347         tx.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
348         final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
349         // Check if it's successfully committed, otherwise exception will be thrown.
350         future.checkedGet();
351     }
352
353     private void removeMd(T item, WriteTransaction tx) throws TransactionCommitFailedException {
354         Preconditions.checkNotNull(tx);
355         final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
356         tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
357         final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
358         // Check if it's successfully committed, otherwise exception will be thrown.
359         future.checkedGet();
360     }
361
362     protected static Uuid toUuid(String uuid) {
363         Preconditions.checkNotNull(uuid, "uuid");
364         Uuid result;
365         try {
366             result = new Uuid(uuid);
367         } catch (final IllegalArgumentException e) {
368             // OK... someone didn't follow RFC 4122... lets try this the hard way
369             final String dedashed = uuid.replace("-", "");
370             if (dedashed.length() == DEDASHED_UUID_LENGTH) {
371                 final String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1) + "-"
372                         + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2) + "-"
373                         + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3) + "-"
374                         + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4) + "-"
375                         + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
376                 result = new Uuid(redashed);
377             } else {
378                 throw e;
379             }
380         }
381         return result;
382     }
383
384     @Override
385     @PreDestroy
386     public void close() throws Exception {
387     }
388
389     @Override
390     public boolean exists(String uuid, ReadTransaction tx) throws ReadFailedException {
391         Preconditions.checkNotNull(tx);
392         final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
393         return dataObject != null;
394     }
395
396     private S get(String uuid, ReadTransaction tx) throws ReadFailedException {
397         Preconditions.checkNotNull(tx);
398         final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
399         if (dataObject == null) {
400             return null;
401         }
402         return fromMd(dataObject);
403     }
404
405     @Override
406     public S get(String uuid) throws ReadFailedException {
407         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
408             return get(uuid, tx);
409         }
410     }
411
412     protected abstract List<T> getDataObjectList(U dataObjects);
413
414     private List<S> getAll(ReadTransaction tx) throws ReadFailedException {
415         Preconditions.checkNotNull(tx);
416         final Set<S> allNeutronObjects = new HashSet<>();
417         final U dataObjects = readMd(createInstanceIdentifier(), tx);
418         if (dataObjects != null) {
419             for (final T dataObject : getDataObjectList(dataObjects)) {
420                 allNeutronObjects.add(fromMd(dataObject));
421             }
422         }
423         LOG.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
424         final List<S> ans = new ArrayList<>();
425         ans.addAll(allNeutronObjects);
426         return ans;
427     }
428
429     @Override
430     public List<S> getAll() throws ReadFailedRuntimeException {
431         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
432             try {
433                 return getAll(tx);
434             } catch (ReadFailedException e) {
435                 throw new ReadFailedRuntimeException(e);
436             }
437         }
438     }
439
440     private Result add(S input, ReadWriteTransaction tx) throws OperationFailedException {
441         Preconditions.checkNotNull(tx);
442         if (exists(input.getID(), tx)) {
443             tx.cancel();
444             return Result.AlreadyExists;
445         }
446         addMd(input, tx);
447         return Result.Success;
448     }
449
450     @Override
451     public Result add(S input) throws OperationFailedException {
452         int retries = RETRY_MAX;
453         OptimisticLockFailedException lastOptimisticLockFailedException = null;
454         while (retries-- >= 0) {
455             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
456             try {
457                 if (areAllDependenciesAvailable(tx, input)) {
458                     return add(input, tx);
459                 } else {
460                     return Result.DependencyMissing;
461                 }
462             } catch (TransactionCommitFailedException e) {
463                 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
464                 if (e instanceof OptimisticLockFailedException) {
465                     LOG.debug("Got OptimisticLockFailedException - {} {}", input, retries);
466                     lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
467                     continue;
468                 } else {
469                     throw e;
470                 }
471             }
472         }
473         throw lastOptimisticLockFailedException;
474     }
475
476     private boolean remove(String uuid, ReadWriteTransaction tx) throws OperationFailedException {
477         Preconditions.checkNotNull(tx);
478         if (!exists(uuid, tx)) {
479             tx.cancel();
480             return false;
481         }
482         removeMd(toMd(uuid), tx);
483         return true;
484     }
485
486     @Override
487     public boolean remove(String uuid) throws OperationFailedException {
488         int retries = RETRY_MAX;
489         OptimisticLockFailedException lastOptimisticLockFailedException = null;
490         while (retries-- >= 0) {
491             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
492             try {
493                 return remove(uuid, tx);
494             } catch (TransactionCommitFailedException e) {
495                 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
496                 if (e instanceof OptimisticLockFailedException) {
497                     LOG.debug("Got OptimisticLockFailedException - {} {}", uuid, retries);
498                     lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
499                     continue;
500                 } else {
501                     throw e;
502                 }
503             }
504         }
505         throw lastOptimisticLockFailedException;
506     }
507
508     private Result update(String uuid, S delta, ReadWriteTransaction tx) throws OperationFailedException {
509         Preconditions.checkNotNull(tx);
510         if (!exists(uuid, tx)) {
511             tx.cancel();
512             return Result.DoesNotExist;
513         }
514         updateMd(delta, tx);
515         return Result.Success;
516     }
517
518     @Override
519     public Result update(String uuid, S delta) throws OperationFailedException {
520         int retries = RETRY_MAX;
521         OptimisticLockFailedException lastOptimisticLockFailedException = null;
522         while (retries-- >= 0) {
523             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
524             try {
525                 if (areAllDependenciesAvailable(tx, delta)) {
526                     return update(uuid, delta, tx);
527                 } else {
528                     return Result.DependencyMissing;
529                 }
530             } catch (TransactionCommitFailedException e) {
531                 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
532                 if (e instanceof OptimisticLockFailedException) {
533                     LOG.debug("Got OptimisticLockFailedException - {} {} {}", uuid, delta, retries);
534                     lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
535                     continue;
536                 } else {
537                     throw e;
538                 }
539             }
540         }
541         throw lastOptimisticLockFailedException;
542     }
543
544     /**
545      * Check if this particular (subclass) transcriber's dependencies are met.
546      * Default implementation just returns true.  Some but not all transcribers will customize this.
547      *
548      * <p>Implementations *MUST* use the passed in transaction.  They will typically call the
549      * {@link #exists(String, ReadTransaction)} method on ANOTHER transcriber with it.
550      *
551      * <p>Implementations should chain {@link #ifNonNull(Object, CheckedFunction)}, or perform null safe comparisons
552      * otherwise, for both optional non-mandatory {@link NeutronObject} as well as mandatory properties which may well
553      * be null. Both must mandatory and non-mandatory must be guarded, because modify (update) operation are allowed to
554      * contain partial neutron objects with missing fields.
555      *
556      * @param tx the transaction within which to perform reads to check for dependencies
557      * @param neutronObject the incoming main neutron object in which there may be references to dependencies
558      *
559      * @return true if all dependencies are available and the
560      *         {@link #add(INeutronObject)} (or {@link #update(String, INeutronObject)} operation can proceed; false if
561      *         there are unmet dependencies, which will cause the add to abort, and a respective
562      *         error code returned to the caller.
563      *
564      * @throws ReadFailedException in case of a data store problem
565      */
566     protected boolean areAllDependenciesAvailable(ReadTransaction tx, S neutronObject) throws ReadFailedException {
567         return true;
568     }
569
570     /**
571      * Utility to perform well readable code of null-safe chains of e.g.
572      * {@link #exists(String, ReadTransaction)} method calls.
573      *
574      * @throws ReadFailedException in case of a data store problem
575      */
576     protected static final <X> boolean ifNonNull(@Nullable X property,
577             CheckedFunction<@NonNull X, @NonNull Boolean, ReadFailedException> function) throws ReadFailedException {
578         if (property != null) {
579             Boolean result = function.apply(property);
580             Preconditions.checkNotNull(result, "result");
581             return result;
582         } else {
583             // We return true, in line with the default implementation
584             // in org.opendaylight.neutron.transcriber.AbstractTranscriberInterface.
585             //      areAllDependenciesAvailable(ReadTransaction, S)
586             return true;
587         }
588     }
589
590 }