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