/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.dom.spi; import static java.util.Objects.requireNonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.Map; import java.util.Map.Entry; import java.util.function.Function; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.common.api.TransactionDatastoreMismatchException; import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction; import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransaction; /** * Composite DOM Transaction backed by {@link DOMStoreTransaction}. * *

* Abstract base for composite transaction, which provides access only to common * functionality as retrieval of subtransaction, close method and retrieval of * identifier. * * @param Subtransaction type */ abstract class AbstractDOMForwardedTransaction implements DOMDataTreeTransaction { private static final VarHandle BACKING_TX; static { try { BACKING_TX = MethodHandles.lookup().findVarHandle(AbstractDOMForwardedTransaction.class, "backingTx", Entry.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new ExceptionInInitializerError(e); } } private final @NonNull Object identifier; private final Function backingTxFactory; @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749") private volatile Entry backingTx; /** * Creates new composite Transactions. * * @param identifier * Identifier of transaction. * @param backingTxFactory * Function which supplies transaction depending on store type */ protected AbstractDOMForwardedTransaction(final Object identifier, final Function backingTxFactory) { this.identifier = requireNonNull(identifier, "Identifier should not be null"); this.backingTxFactory = requireNonNull(backingTxFactory, "Backing transaction factory should not be null"); } /** * Returns subtransaction associated with supplied datastore type. * *

* The method allows usage of single datastore type per transaction instance; * eligible datastore type is defined by first method access. * * @param datastoreType is used to identify subtransaction object * @return the subtransaction object * @throws NullPointerException if datastoreType is {@code null} * @throws IllegalArgumentException if datastoreType is not supported * @throws TransactionDatastoreMismatchException if datastoreType mismatches the one used at first access */ protected final @NonNull T getSubtransaction(final LogicalDatastoreType datastoreType) { final var ds = requireNonNull(datastoreType, "datastoreType must not be null."); var entry = backingTx; if (entry == null) { final var tx = backingTxFactory.apply(datastoreType); final var newEntry = Map.entry(ds, tx); final var witness = (Entry) BACKING_TX.compareAndExchange(this, null, newEntry); if (witness != null) { tx.close(); entry = witness; } else { entry = newEntry; } } final var encountered = entry.getKey(); if (encountered != ds) { throw new TransactionDatastoreMismatchException(encountered, ds); } return entry.getValue(); } /** * Returns immutable Iterable of all subtransactions. */ protected @Nullable T getSubtransaction() { final Entry entry; return (entry = backingTx) == null ? null : entry.getValue(); } @Override public Object getIdentifier() { return identifier; } @SuppressWarnings("checkstyle:IllegalCatch") protected void closeSubtransactions() { /* * We share one exception for all failures, which are added * as supressedExceptions to it. */ final var subtransaction = getSubtransaction(); if (subtransaction != null) { try { subtransaction.close(); } catch (Exception e) { // If we did not allocate failure we allocate it throw new IllegalStateException("Uncaught exception occurred during closing transaction", e); } } } }