Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, without warranties or
* conditions of any kind, EITHER EXPRESS OR IMPLIED. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.vmware.xenon.common;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.vmware.xenon.common.OperationJoin.JoinedCompletionHandler;
/**
* Operations to be executed in sequence.
*
* Example usage:
*
*
* {@code
* OperationSequence.create(op1, op2, op3) // initial joined operations to be executed in parallel
* .setCompletion((ops, exs) -> { //shared completion handler for the first level (optional)
* if (exs != null) {
* // Map exceptions = exc;
* for(Throwable e:exc.values()){
* //log exceptions or something else.
* }
*
* // NOTE: if there is at least one exception on the current level
* // the next level will not be executed.
* // In case, the next level should proceed the exception map
* // should be cleared: exc.clear()
* // This might lead to inconsistent data in the next level completions.
*
* return;
* }
*
* // Map operations = ops;
* Operation opr1 = ops.get(op1.getId());
* SomeState body = opr1.getBody(SomeState.class);
*
* // Can set properties on the operations in the next levels
* NextState nextStateBody = new NextState();
* nextState.property = body.otherProperty;
*
* op4.setUri(body.selfLink);
* op4.setBody(nextStateBody);
* })
* // next level of parallel operation to be executed after the first level operations
* // are completed first.
* .next(op4, op5, op6)
* .setCompletion((ops, exs) -> { // shared completion handler for the second level (optional)
* if (exs != null) {
* return;
* }
*
* Operation opr4 = ops.get(op4.getId());
*
* // have access to the first level completed operations
* Operation opr1 = ops.get(op1.getId());
* })
* .next(op7, op8, op9)
* .setCompletion((ops, exs) -> {
* // shared completion handler for the third level (optional)
* // all previously completed operations are accessible.
* Operation opr1 = ops.get(op1.getId());
* Operation opr4 = ops.get(op4.getId());
* Operation opr1 = ops.get(op1.getId());
* // In many cases, the last shared completion could be the only one needed.
* })
* .sendWith(host);
* }
*
*/
public class OperationSequence {
private final OperationJoin join;
private OperationSequence child;
private OperationSequence parent;
private ServiceRequestSender sender;
private boolean cumulative = true;
private boolean abortOnFirstFailure = false;
private OperationSequence(OperationJoin join) {
this.join = join;
}
/**
* Create {@link OperationSequence} with an instance of {@link OperationJoin} to be linked in a
* sequence with other {@link OperationJoin}s.
*/
public static OperationSequence create(OperationJoin... joins) {
if (joins.length == 0) {
throw new IllegalArgumentException("At least one 'operationJoin' is required.");
}
return chainJoins(null, joins);
}
private static OperationSequence chainJoins(OperationSequence root, OperationJoin... joins) {
for (OperationJoin join : joins) {
OperationSequence current = new OperationSequence(join);
if (root != null) {
root.child = current;
current.parent = root;
}
root = current;
}
return root;
}
/**
* Create {@link OperationSequence} with a list of {@link Operation}s to be joined together in
* parallel execution.
*/
public static OperationSequence create(Operation... ops) {
return create(OperationJoin.create(ops));
}
/**
* Create {@link OperationSequence} with an instance of {@link OperationJoin} to be linked in a
* sequence with the current {@link OperationSequence}s.
*/
public OperationSequence next(OperationJoin... joins) {
return chainJoins(this, joins);
}
public OperationSequence next(Operation... ops) {
return next(OperationJoin.create(ops));
}
public OperationSequence setCompletion(JoinedCompletionHandler joinedCompletion) {
return setCompletion(true, joinedCompletion);
}
public OperationSequence setCompletion(boolean cumulative, JoinedCompletionHandler joinedCompletion) {
this.cumulative = cumulative;
this.join.setCompletion(joinedCompletion);
return this;
}
/**
* Abort entire sequence on first operation failure.
*
* The joinedCompletion handler set by {@link #setCompletion(JoinedCompletionHandler)}
* will NOT be called when the sequence encounters an operation failure.
*
*
* {@code
* Operation op1 = Operation.createGet(...)
* .setCompletion((o, e) -> {
* // This will be called always
* });
*
* OperationSequence.create(op1)
* .setCompletion((ops, exs) -> {
* // This will NOT be called if op1 failed
* })
* .abortOnFirstFailure();
* }
*
*/
public OperationSequence abortOnFirstFailure() {
this.abortOnFirstFailure = true;
return this;
}
/**
* Send using the {@link ServiceRequestSender}.
* @see OperationJoin#sendWith(ServiceRequestSender)
*/
public void sendWith(ServiceRequestSender sender) {
if (this.parent != null) {
this.parent.sendWith(sender);
} else {
send(sender);
}
}
private void send(ServiceRequestSender sender) {
validateSendRequest(sender);
this.sender = sender;
setProxyCompletion();
this.join.sendWith(sender);
}
private void setProxyCompletion() {
this.join
.setCompletion(new CompletionHandlerSequenceProxy(this, this.join.joinedCompletion));
}
private static class CompletionHandlerSequenceProxy implements JoinedCompletionHandler {
private final JoinedCompletionHandler joinedCompletionHandler;
private final OperationSequence sequence;
private final AtomicBoolean completed;
private CompletionHandlerSequenceProxy(OperationSequence sequence,
JoinedCompletionHandler joinedCompletionHandler) {
this.sequence = sequence;
this.joinedCompletionHandler = joinedCompletionHandler;
this.completed = new AtomicBoolean();
}
@Override
public void handle(final Map ops, final Map failures) {
if (!this.completed.compareAndSet(false, true)) {
return;
}
boolean hasFailure = failures != null && !failures.isEmpty();
boolean abortImmediately = this.sequence.hasAbortOnFirstFailureInChildSequences();
if (hasFailure && abortImmediately) {
return;
}
final AtomicBoolean errors = new AtomicBoolean();
if (this.joinedCompletionHandler != null) {
if (this.sequence.cumulative) {
final Map allOps = this.sequence.getAllCompletedOperations();
final Map allFailures = this.sequence.getAllFailures();
this.joinedCompletionHandler.handle(allOps, allFailures);
errors.set(allFailures != null && !allFailures.isEmpty());
} else {
this.joinedCompletionHandler.handle(ops, failures);
errors.set(failures != null && !failures.isEmpty());
}
}
if (this.sequence.child != null && !errors.get()) {
try {
this.sequence.child.send(this.sequence.sender);
} catch (Exception e) {
// complete with failure
this.sequence.child.join.fail(e);
}
}
}
}
private Map getAllCompletedOperations() {
final Map operations = new ConcurrentHashMap<>();
OperationSequence current = this;
while (current != null) {
for (Operation op : current.join.getOperations()) {
operations.put(op.getId(), op);
}
current = current.parent;
}
return operations;
}
private Map getAllFailures() {
Map failures = null;
OperationSequence current = this;
while (current != null) {
Map currentFailures = current.join.getFailures();
if (currentFailures != null) {
if (failures == null) {
failures = currentFailures;
} else {
failures.putAll(currentFailures);
}
}
current = current.parent;
}
return failures;
}
private void validateSendRequest(Object sender) {
if (sender == null) {
throw new IllegalArgumentException("'sender' must not be null.");
}
if (this.join == null) {
throw new IllegalStateException("No joined operation to be sent.");
}
}
private boolean hasAbortOnFirstFailureInChildSequences() {
OperationSequence childSequence = this.child;
while (childSequence != null) {
if (childSequence.abortOnFirstFailure) {
return true;
}
childSequence = childSequence.child;
}
return false;
}
}