blob: 4ddacaceae8b31e9d1817012ae415cbb4d5d970d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.zookeeper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.jute.InputArchive;
import org.apache.jute.OutputArchive;
import org.apache.jute.Record;
import org.apache.zookeeper.proto.CheckVersionRequest;
import org.apache.zookeeper.proto.CreateRequest;
import org.apache.zookeeper.proto.CreateTTLRequest;
import org.apache.zookeeper.proto.DeleteRequest;
import org.apache.zookeeper.proto.GetChildrenRequest;
import org.apache.zookeeper.proto.GetDataRequest;
import org.apache.zookeeper.proto.MultiHeader;
import org.apache.zookeeper.proto.SetDataRequest;
/**
* Encodes a composite operation. In the wire format, each operation
* consists of a single MultiHeader followed by the appropriate request.
* Each of these MultiHeaders has a type which indicates
* the type of the following operation or a negative number if no more operations
* are included.
* All of the operations must be from the same OpKind.
*/
public class MultiOperationRecord implements Record, Iterable<Op> {
private List<Op> ops = new ArrayList<>();
private Op.OpKind opKind = null;
public MultiOperationRecord() {
}
public MultiOperationRecord(Iterable<Op> ops) throws IllegalArgumentException {
for (Op op : ops) {
setOrCheckOpKind(op.getKind());
add(op);
}
}
@Override
public Iterator<Op> iterator() {
return ops.iterator();
}
public void add(Op op) throws IllegalArgumentException {
setOrCheckOpKind(op.getKind());
ops.add(op);
}
public int size() {
return ops.size();
}
/**
* Returns the kind of the operations contained by the record.
* @return The OpKind value of all the elements in the record.
*/
public Op.OpKind getOpKind() {
return opKind;
}
private void setOrCheckOpKind(Op.OpKind ok) throws IllegalArgumentException {
if (opKind == null) {
opKind = ok;
} else if (ok != opKind) {
throw new IllegalArgumentException("Mixing read and write operations (transactions)"
+ " is not allowed in a multi request.");
}
}
@Override
public void serialize(OutputArchive archive, String tag) throws IOException {
archive.startRecord(this, tag);
for (Op op : ops) {
MultiHeader h = new MultiHeader(op.getType(), false, -1);
h.serialize(archive, tag);
switch (op.getType()) {
case ZooDefs.OpCode.create:
case ZooDefs.OpCode.create2:
case ZooDefs.OpCode.createTTL:
case ZooDefs.OpCode.createContainer:
case ZooDefs.OpCode.delete:
case ZooDefs.OpCode.setData:
case ZooDefs.OpCode.check:
case ZooDefs.OpCode.getChildren:
case ZooDefs.OpCode.getData:
op.toRequestRecord().serialize(archive, tag);
break;
default:
throw new IOException("Invalid type of op");
}
}
new MultiHeader(-1, true, -1).serialize(archive, tag);
archive.endRecord(this, tag);
}
@Override
public void deserialize(InputArchive archive, String tag) throws IOException {
archive.startRecord(tag);
MultiHeader h = new MultiHeader();
h.deserialize(archive, tag);
try {
while (!h.getDone()) {
switch (h.getType()) {
case ZooDefs.OpCode.create:
case ZooDefs.OpCode.create2:
case ZooDefs.OpCode.createContainer:
CreateRequest cr = new CreateRequest();
cr.deserialize(archive, tag);
add(Op.create(cr.getPath(), cr.getData(), cr.getAcl(), cr.getFlags()));
break;
case ZooDefs.OpCode.createTTL:
CreateTTLRequest crTtl = new CreateTTLRequest();
crTtl.deserialize(archive, tag);
add(Op.create(crTtl.getPath(), crTtl.getData(), crTtl.getAcl(), crTtl.getFlags(), crTtl.getTtl()));
break;
case ZooDefs.OpCode.delete:
DeleteRequest dr = new DeleteRequest();
dr.deserialize(archive, tag);
add(Op.delete(dr.getPath(), dr.getVersion()));
break;
case ZooDefs.OpCode.setData:
SetDataRequest sdr = new SetDataRequest();
sdr.deserialize(archive, tag);
add(Op.setData(sdr.getPath(), sdr.getData(), sdr.getVersion()));
break;
case ZooDefs.OpCode.check:
CheckVersionRequest cvr = new CheckVersionRequest();
cvr.deserialize(archive, tag);
add(Op.check(cvr.getPath(), cvr.getVersion()));
break;
case ZooDefs.OpCode.getChildren:
GetChildrenRequest gcr = new GetChildrenRequest();
gcr.deserialize(archive, tag);
add(Op.getChildren(gcr.getPath()));
break;
case ZooDefs.OpCode.getData:
GetDataRequest gdr = new GetDataRequest();
gdr.deserialize(archive, tag);
add(Op.getData(gdr.getPath()));
break;
default:
throw new IOException("Invalid type of op");
}
h.deserialize(archive, tag);
}
} catch (IllegalArgumentException e) {
throw new IOException("Mixing different kind of ops");
}
archive.endRecord(tag);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MultiOperationRecord)) {
return false;
}
MultiOperationRecord that = (MultiOperationRecord) o;
if (ops != null) {
Iterator<Op> other = that.ops.iterator();
for (Op op : ops) {
boolean hasMoreData = other.hasNext();
if (!hasMoreData) {
return false;
}
Op otherOp = other.next();
if (!op.equals(otherOp)) {
return false;
}
}
return !other.hasNext();
} else {
return that.ops == null;
}
}
@Override
public int hashCode() {
int h = 1023;
for (Op op : ops) {
h = h * 25 + op.hashCode();
}
return h;
}
}