| /* |
| * 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.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| import org.apache.jute.Record; |
| import org.apache.zookeeper.common.PathUtils; |
| import org.apache.zookeeper.data.ACL; |
| import org.apache.zookeeper.data.Stat; |
| 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.SetDataRequest; |
| import org.apache.zookeeper.server.EphemeralType; |
| |
| /** |
| * Represents a single operation in a multi-operation transaction. Each operation can be a create, update, |
| * delete, a version check or just read operations like getChildren or getData. |
| * |
| * Sub-classes of Op each represent each detailed type but should not normally be referenced except via |
| * the provided factory methods. |
| * |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode, org.apache.zookeeper.AsyncCallback.StringCallback, Object) |
| * @see ZooKeeper#delete(String, int) |
| * @see ZooKeeper#setData(String, byte[], int) |
| * @see ZooKeeper#getData(String, boolean, Stat) |
| * @see ZooKeeper#getChildren(String, boolean) |
| */ |
| public abstract class Op { |
| |
| public enum OpKind { |
| TRANSACTION, |
| READ |
| } |
| |
| private int type; |
| private String path; |
| private OpKind opKind; |
| |
| // prevent untyped construction |
| private Op(int type, String path, OpKind opKind) { |
| this.type = type; |
| this.path = path; |
| this.opKind = opKind; |
| } |
| |
| /** |
| * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name. |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) |
| * @see CreateMode#fromFlag(int) |
| * |
| * @param path |
| * the path for the node |
| * @param data |
| * the initial data for the node |
| * @param acl |
| * the acl for the node |
| * @param flags |
| * specifying whether the node to be created is ephemeral |
| * and/or sequential but using the integer encoding. |
| */ |
| public static Op create(String path, byte[] data, List<ACL> acl, int flags) { |
| return new Create(path, data, acl, flags); |
| } |
| |
| /** |
| * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name |
| * but adding an optional ttl |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) |
| * @see CreateMode#fromFlag(int) |
| * |
| * @param path |
| * the path for the node |
| * @param data |
| * the initial data for the node |
| * @param acl |
| * the acl for the node |
| * @param flags |
| * specifying whether the node to be created is ephemeral |
| * and/or sequential but using the integer encoding. |
| * @param ttl |
| * optional ttl or 0 (flags must imply a TTL creation mode) |
| */ |
| public static Op create(String path, byte[] data, List<ACL> acl, int flags, long ttl) { |
| CreateMode createMode = CreateMode.fromFlag(flags, CreateMode.PERSISTENT); |
| if (createMode.isTTL()) { |
| return new CreateTTL(path, data, acl, createMode, ttl); |
| } |
| return new Create(path, data, acl, flags); |
| } |
| |
| /** |
| * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name. |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) |
| * |
| * @param path |
| * the path for the node |
| * @param data |
| * the initial data for the node |
| * @param acl |
| * the acl for the node |
| * @param createMode |
| * specifying whether the node to be created is ephemeral |
| * and/or sequential |
| */ |
| public static Op create(String path, byte[] data, List<ACL> acl, CreateMode createMode) { |
| return new Create(path, data, acl, createMode); |
| } |
| |
| /** |
| * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name |
| * but adding an optional ttl |
| * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) |
| * |
| * @param path |
| * the path for the node |
| * @param data |
| * the initial data for the node |
| * @param acl |
| * the acl for the node |
| * @param createMode |
| * specifying whether the node to be created is ephemeral |
| * and/or sequential |
| * @param ttl |
| * optional ttl or 0 (createMode must imply a TTL) |
| */ |
| public static Op create(String path, byte[] data, List<ACL> acl, CreateMode createMode, long ttl) { |
| if (createMode.isTTL()) { |
| return new CreateTTL(path, data, acl, createMode, ttl); |
| } |
| return new Create(path, data, acl, createMode); |
| } |
| |
| /** |
| * Constructs a delete operation. Arguments are as for the ZooKeeper method of the same name. |
| * @see ZooKeeper#delete(String, int) |
| * |
| * @param path |
| * the path of the node to be deleted. |
| * @param version |
| * the expected node version. |
| */ |
| public static Op delete(String path, int version) { |
| return new Delete(path, version); |
| } |
| |
| /** |
| * Constructs an update operation. Arguments are as for the ZooKeeper method of the same name. |
| * @see ZooKeeper#setData(String, byte[], int) |
| * |
| * @param path |
| * the path of the node |
| * @param data |
| * the data to set |
| * @param version |
| * the expected matching version |
| */ |
| public static Op setData(String path, byte[] data, int version) { |
| return new SetData(path, data, version); |
| } |
| |
| /** |
| * Constructs an version check operation. Arguments are as for the ZooKeeper.setData method except that |
| * no data is provided since no update is intended. The purpose for this is to allow read-modify-write |
| * operations that apply to multiple znodes, but where some of the znodes are involved only in the read, |
| * not the write. A similar effect could be achieved by writing the same data back, but that leads to |
| * way more version updates than are necessary and more writing in general. |
| * |
| * @param path |
| * the path of the node |
| * @param version |
| * the expected matching version |
| */ |
| public static Op check(String path, int version) { |
| return new Check(path, version); |
| } |
| |
| public static Op getChildren(String path) { |
| return new GetChildren(path); |
| } |
| |
| public static Op getData(String path) { |
| return new GetData(path); |
| } |
| |
| /** |
| * Gets the integer type code for an Op. This code should be as from ZooDefs.OpCode |
| * @see ZooDefs.OpCode |
| * @return The type code. |
| */ |
| public int getType() { |
| return type; |
| } |
| |
| /** |
| * Gets the path for an Op. |
| * @return The path. |
| */ |
| public String getPath() { |
| return path; |
| } |
| |
| /** |
| * Gets the kind of an Op. |
| * @return The OpKind value. |
| */ |
| public OpKind getKind() { |
| return opKind; |
| } |
| |
| /** |
| * Encodes an op for wire transmission. |
| * @return An appropriate Record structure. |
| */ |
| public abstract Record toRequestRecord(); |
| |
| /** |
| * Reconstructs the transaction with the chroot prefix. |
| * @return transaction with chroot. |
| */ |
| abstract Op withChroot(String addRootPrefix); |
| |
| /** |
| * Performs client path validations. |
| * |
| * @throws IllegalArgumentException |
| * if an invalid path is specified |
| * @throws KeeperException.BadArgumentsException |
| * if an invalid create mode flag is specified |
| */ |
| void validate() throws KeeperException { |
| PathUtils.validatePath(path); |
| } |
| |
| ////////////////// |
| // these internal classes are public, but should not generally be referenced. |
| // |
| public static class Create extends Op { |
| |
| protected byte[] data; |
| protected List<ACL> acl; |
| protected int flags; |
| |
| private Create(String path, byte[] data, List<ACL> acl, int flags) { |
| super(getOpcode(CreateMode.fromFlag(flags, CreateMode.PERSISTENT)), path, OpKind.TRANSACTION); |
| this.data = data; |
| this.acl = acl; |
| this.flags = flags; |
| } |
| |
| private static int getOpcode(CreateMode createMode) { |
| if (createMode.isTTL()) { |
| return ZooDefs.OpCode.createTTL; |
| } |
| return createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create; |
| } |
| |
| private Create(String path, byte[] data, List<ACL> acl, CreateMode createMode) { |
| super(getOpcode(createMode), path, OpKind.TRANSACTION); |
| this.data = data; |
| this.acl = acl; |
| this.flags = createMode.toFlag(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof Create)) { |
| return false; |
| } |
| |
| Create op = (Create) o; |
| |
| boolean aclEquals = true; |
| Iterator<ACL> i = op.acl.iterator(); |
| for (ACL acl : op.acl) { |
| boolean hasMoreData = i.hasNext(); |
| if (!hasMoreData) { |
| aclEquals = false; |
| break; |
| } |
| ACL otherAcl = i.next(); |
| if (!acl.equals(otherAcl)) { |
| aclEquals = false; |
| break; |
| } |
| } |
| return !i.hasNext() |
| && getType() == op.getType() |
| && Arrays.equals(data, op.data) |
| && flags == op.flags |
| && aclEquals; |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode() + Arrays.hashCode(data); |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new CreateRequest(getPath(), data, acl, flags); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new Create(path, data, acl, flags); |
| } |
| |
| @Override |
| void validate() throws KeeperException { |
| CreateMode createMode = CreateMode.fromFlag(flags); |
| PathUtils.validatePath(getPath(), createMode.isSequential()); |
| EphemeralType.validateTTL(createMode, -1); |
| } |
| |
| } |
| |
| public static class CreateTTL extends Create { |
| |
| private final long ttl; |
| |
| private CreateTTL(String path, byte[] data, List<ACL> acl, int flags, long ttl) { |
| super(path, data, acl, flags); |
| this.ttl = ttl; |
| } |
| |
| private CreateTTL(String path, byte[] data, List<ACL> acl, CreateMode createMode, long ttl) { |
| super(path, data, acl, createMode); |
| this.ttl = ttl; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return super.equals(o) && (o instanceof CreateTTL) && (ttl == ((CreateTTL) o).ttl); |
| } |
| |
| @Override |
| public int hashCode() { |
| return super.hashCode() + (int) (ttl ^ (ttl >>> 32)); |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new CreateTTLRequest(getPath(), data, acl, flags, ttl); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new CreateTTL(path, data, acl, flags, ttl); |
| } |
| |
| @Override |
| void validate() throws KeeperException { |
| CreateMode createMode = CreateMode.fromFlag(flags); |
| PathUtils.validatePath(getPath(), createMode.isSequential()); |
| EphemeralType.validateTTL(createMode, ttl); |
| } |
| |
| } |
| |
| public static class Delete extends Op { |
| |
| private int version; |
| |
| private Delete(String path, int version) { |
| super(ZooDefs.OpCode.delete, path, OpKind.TRANSACTION); |
| this.version = version; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof Delete)) { |
| return false; |
| } |
| |
| Delete op = (Delete) o; |
| |
| return getType() == op.getType() && version == op.version && getPath().equals(op.getPath()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode() + version; |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new DeleteRequest(getPath(), version); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new Delete(path, version); |
| } |
| |
| } |
| |
| public static class SetData extends Op { |
| |
| private byte[] data; |
| private int version; |
| |
| private SetData(String path, byte[] data, int version) { |
| super(ZooDefs.OpCode.setData, path, OpKind.TRANSACTION); |
| this.data = data; |
| this.version = version; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof SetData)) { |
| return false; |
| } |
| |
| SetData op = (SetData) o; |
| |
| return getType() == op.getType() |
| && version == op.version |
| && getPath().equals(op.getPath()) |
| && Arrays.equals(data, op.data); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode() + Arrays.hashCode(data) + version; |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new SetDataRequest(getPath(), data, version); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new SetData(path, data, version); |
| } |
| |
| } |
| |
| public static class Check extends Op { |
| |
| private int version; |
| |
| private Check(String path, int version) { |
| super(ZooDefs.OpCode.check, path, OpKind.TRANSACTION); |
| this.version = version; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof Check)) { |
| return false; |
| } |
| |
| Check op = (Check) o; |
| |
| return getType() == op.getType() && getPath().equals(op.getPath()) && version == op.version; |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode() + version; |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new CheckVersionRequest(getPath(), version); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new Check(path, version); |
| } |
| |
| } |
| |
| public static class GetChildren extends Op { |
| |
| GetChildren(String path) { |
| super(ZooDefs.OpCode.getChildren, path, OpKind.READ); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof GetChildren)) { |
| return false; |
| } |
| |
| GetChildren op = (GetChildren) o; |
| |
| return getType() == op.getType() && getPath().equals(op.getPath()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode(); |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new GetChildrenRequest(getPath(), false); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new GetChildren(path); |
| } |
| |
| } |
| |
| public static class GetData extends Op { |
| |
| GetData(String path) { |
| super(ZooDefs.OpCode.getData, path, OpKind.READ); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof GetData)) { |
| return false; |
| } |
| |
| GetData op = (GetData) o; |
| |
| return getType() == op.getType() && getPath().equals(op.getPath()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return getType() + getPath().hashCode(); |
| } |
| |
| @Override |
| public Record toRequestRecord() { |
| return new GetDataRequest(getPath(), false); |
| } |
| |
| @Override |
| Op withChroot(String path) { |
| return new GetData(path); |
| } |
| |
| } |
| |
| } |