org.opendaylight.jsonrpc.impl.JsonRPCTx Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2017 Brocade Communications Systems, Inc. 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.jsonrpc.impl;
import static org.opendaylight.jsonrpc.provider.common.Util.store2int;
import static org.opendaylight.jsonrpc.provider.common.Util.store2str;
import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.jsonrpc.bus.messagelib.TransportFactory;
import org.opendaylight.jsonrpc.dom.codec.Codec;
import org.opendaylight.jsonrpc.dom.codec.CodecUtils;
import org.opendaylight.jsonrpc.dom.codec.JsonRpcCodecFactory;
import org.opendaylight.jsonrpc.hmap.DataType;
import org.opendaylight.jsonrpc.hmap.HierarchicalEnumMap;
import org.opendaylight.jsonrpc.model.JsonRpcTransactionFacade;
import org.opendaylight.jsonrpc.model.RemoteOmShard;
import org.opendaylight.jsonrpc.model.TransactionListener;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.jsonrpc.rev161201.Peer;
import org.opendaylight.yangtools.util.concurrent.FluentFutures;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JsonRPCTx extends RemoteShardAware implements JsonRpcTransactionFacade {
private static final Logger LOG = LoggerFactory.getLogger(JsonRPCTx.class);
private static final Function ERROR_MAPPER = msg -> RpcResultBuilder
.newError(ErrorType.APPLICATION, new ErrorTag("commit"), msg);
private static final FluentFuture> NO_DATA = FluentFutures
.immediateFluentFuture(Optional.empty());
private final @NonNull SettableFuture settableFuture = SettableFuture.create();
private final @NonNull FluentFuture completionFuture = FluentFuture.from(settableFuture);
/* Keep track of TX id to given endpoint (key is endpoint, value is TX ID) */
private final Map txIdMap = new HashMap<>();
private final List listeners = new CopyOnWriteArrayList<>();
private final Codec pathCodec;
/**
* Instantiates a new JSONRPC Transaction.
*
* @param transportFactory used to create underlying transport connections
* @param peer remote peer
* @param pathMap shared instance of {@link HierarchicalEnumMap}
* @param codecFactory codec factory
* @param schemaContext the schema context
*/
public JsonRPCTx(@NonNull TransportFactory transportFactory, @NonNull Peer peer,
@NonNull HierarchicalEnumMap pathMap,
@NonNull JsonRpcCodecFactory codecFactory, @NonNull EffectiveModelContext schemaContext) {
super(schemaContext, transportFactory, pathMap, codecFactory, peer);
Preconditions.checkArgument(!Strings.isNullOrEmpty(peer.getName()), "Peer name is missing");
this.pathCodec = codecFactory.pathCodec();
}
private T withRemoteShard(LogicalDatastoreType store, JsonElement path, Function job) {
try (RemoteOmShard shard = getShard(store, path)) {
return job.apply(shard);
}
}
/*
* Get cached TX id or allocate new one from corresponding remote shard mapped to given store and path
*/
private String getTxId(LogicalDatastoreType store, JsonElement path) {
return txIdMap.computeIfAbsent(lookupEndPoint(store, path),
k -> withRemoteShard(store, path, RemoteOmShard::txid));
}
@Override
public FluentFuture> completionFuture() {
return completionFuture;
}
@Override
public FluentFuture> read(final LogicalDatastoreType store,
final YangInstanceIdentifier path) {
LOG.debug("[{}][read] store={}, path={}", peer.getName(), store, path);
if (path.getPathArguments().isEmpty()) {
return NO_DATA;
}
final JsonObject jsonPath = pathCodec.serialize(path);
return withRemoteShard(store, jsonPath,
shard -> immediateFluentFuture(Optional.ofNullable(CodecUtils.decodeUnchecked(codecFactory, path,
shard.read(store2str(store2int(store)), peer.getName(), jsonPath)))));
}
@Override
public FluentFuture exists(LogicalDatastoreType store, YangInstanceIdentifier path) {
LOG.debug("[{}][exists] store={}, path={}", peer.getName(), store, path);
final JsonObject jsonPath = pathCodec.serialize(path);
return withRemoteShard(store, jsonPath,
shard -> immediateFluentFuture(shard.exists(store2str(store2int(store)), peer.getName(), jsonPath)));
}
@Override
public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) {
LOG.debug("[{}][put] store={}, path={}, data={}", peer.getName(), store, path, data);
final JsonObject jsonPath = pathCodec.serialize(path);
final JsonElement jsonData = CodecUtils.encodeUnchecked(codecFactory, path, data);
withRemoteShard(store, jsonPath, shard -> {
shard.put(getTxId(store, jsonPath), store2str(store2int(store)), peer.getName(), jsonPath, jsonData);
return null;
});
}
@Override
public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) {
LOG.debug("[{}][merge] store={}, path={}, data={}", peer.getName(), store, path, data);
final JsonObject jsonPath = pathCodec.serialize(path);
final JsonElement jsonData = CodecUtils.encodeUnchecked(codecFactory, path, data);
withRemoteShard(store, jsonPath, shard -> {
shard.merge(getTxId(store, jsonPath), store2str(store2int(store)), peer.getName(), jsonPath, jsonData);
return null;
});
}
@Override
public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
LOG.debug("[{}][delete] store={}, path={}", peer.getName(), store, path);
final JsonObject jsonPath = pathCodec.serialize(path);
withRemoteShard(store, jsonPath, shard -> {
shard.delete(getTxId(store, jsonPath), store2str(store2int(store)), peer.getName(), jsonPath);
return null;
});
}
@Override
public Object getIdentifier() {
return super.hashCode();
}
@SuppressWarnings("checkstyle:IllegalCatch")
@Override
public boolean cancel() {
LOG.debug("[{}][cancel]", peer.getName());
try {
boolean result = true;
for (Map.Entry entry : txIdMap.entrySet()) {
try (RemoteOmShard shard = getShard(entry.getKey())) {
result &= shard.cancel(entry.getValue());
}
}
txIdMap.clear();
listeners.forEach(listener -> listener.onCancel(this));
settableFuture.cancel(false);
return result;
} catch (Exception e) {
LOG.error("Unable to cancel transaction", e);
return false;
}
}
@Override
public FluentFuture extends CommitInfo> commit() {
LOG.debug("[{}][commit]", peer.getName());
listeners.forEach(txl -> txl.onSubmit(this));
boolean result = true;
final List errors = new ArrayList<>();
for (Map.Entry entry : txIdMap.entrySet()) {
try (RemoteOmShard shard = getShard(entry.getKey())) {
if (!shard.commit(entry.getValue())) {
result = false;
LOG.debug("Commit of {} failed, requesting more info", entry.getValue());
errors.addAll(shard.error(entry.getValue()));
}
}
}
txIdMap.clear();
if (result) {
listeners.forEach(txListener -> txListener.onSuccess(this));
settableFuture.set(CommitInfo.empty());
} else {
final Throwable failure = new TransactionCommitFailedException(
"Commit of transaction " + getIdentifier() + " failed",
errors.stream().map(ERROR_MAPPER).toArray(size -> new RpcError[size]));
listeners.forEach(txListener -> txListener.onFailure(this, failure));
settableFuture.setException(failure);
}
return completionFuture;
}
@Override
public AutoCloseable addCallback(TransactionListener listener) {
listeners.add(listener);
return () -> listeners.remove(listener);
}
@Override
public int hashCode() {
return Objects.hashCode(getIdentifier());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof JsonRpcTransactionFacade)) {
return false;
}
JsonRpcTransactionFacade other = (JsonRpcTransactionFacade) obj;
return getIdentifier().equals(other.getIdentifier());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy