| package org.apache.helix; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.lang.reflect.Constructor; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.I0Itec.zkclient.serialize.ZkSerializer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import org.apache.helix.api.exceptions.HelixMetaDataAccessException; |
| |
| /** |
| * A wrapper class for ZNRecord. Used as a base class for IdealState, CurrentState, etc. |
| */ |
| public class HelixProperty { |
| private static Logger LOG = LoggerFactory.getLogger(HelixProperty.class); |
| |
| public enum HelixPropertyAttribute { |
| BUCKET_SIZE, |
| BATCH_MESSAGE_MODE |
| } |
| |
| protected final ZNRecord _record; |
| |
| /** |
| * Metadata of a HelixProperty |
| */ |
| public static class Stat { |
| // the version field of zookeeper Stat |
| private int _version; |
| private long _creationTime; |
| private long _modifiedTime; |
| private long _ephemeralOwner; |
| |
| public Stat(int version, long creationTime, long modifiedTime, long ephemeralOwner) { |
| _version = version; |
| _creationTime = creationTime; |
| _modifiedTime = modifiedTime; |
| _ephemeralOwner = ephemeralOwner; |
| } |
| |
| public Stat(Stat stat) { |
| _version = stat.getVersion(); |
| _creationTime = stat.getCreationTime(); |
| _modifiedTime = stat.getModifiedTime(); |
| _ephemeralOwner = stat.getEphemeralOwner(); |
| } |
| |
| public Stat() { |
| _version = -1; |
| _creationTime = -1; |
| _modifiedTime = -1; |
| _ephemeralOwner = -1; |
| } |
| |
| public int getVersion() { |
| return _version; |
| } |
| |
| public void setVersion(int version) { |
| _version = version; |
| } |
| |
| public long getCreationTime() { |
| return _creationTime; |
| } |
| |
| public void setCreationTime(long creationTime) { |
| _creationTime = creationTime; |
| } |
| |
| public long getModifiedTime() { |
| return _modifiedTime; |
| } |
| |
| public void setModifiedTime(long modifiedTime) { |
| _modifiedTime = modifiedTime; |
| } |
| |
| public long getEphemeralOwner() { |
| return _ephemeralOwner; |
| } |
| |
| public void setEphemeralOwner(long ephemeralOwner) { |
| _ephemeralOwner = ephemeralOwner; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof Stat)) { |
| return false; |
| } |
| |
| Stat stat = (Stat) o; |
| |
| if (_ephemeralOwner != stat._ephemeralOwner) { |
| return false; |
| } |
| if (_version != stat._version) { |
| return false; |
| } |
| if (_creationTime != stat._creationTime) { |
| return false; |
| } |
| return _modifiedTime == stat._modifiedTime; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = _version; |
| result = 31 * result + (int) (_creationTime ^ (_creationTime >>> 32)); |
| result = 31 * result + (int) (_modifiedTime ^ (_modifiedTime >>> 32)); |
| result = 31 * result + (int) (_ephemeralOwner ^ (_ephemeralOwner >>> 32)); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| return "Stat {" + "_version=" + _version + ", _creationTime=" + _creationTime |
| + ", _modifiedTime=" + _modifiedTime + ", _ephemeralOwner=" + _ephemeralOwner + '}'; |
| } |
| } |
| |
| private Stat _stat; |
| |
| /** |
| * Initialize the property with an identifier |
| * @param id |
| */ |
| public HelixProperty(String id) { |
| this(new ZNRecord(id), id); |
| } |
| |
| /** |
| * Initialize the property with an existing ZNRecord |
| * @param record |
| */ |
| public HelixProperty(ZNRecord record) { |
| this(record, record.getId()); |
| } |
| |
| /** |
| * Initialize the property with an existing ZNRecord with new record id |
| * @param record |
| * @param id |
| */ |
| public HelixProperty(ZNRecord record, String id) { |
| _record = new ZNRecord(record, id); |
| _stat = new Stat(_record.getVersion(), _record.getCreationTime(), _record.getModifiedTime(), _record.getEphemeralOwner()); |
| } |
| |
| /** |
| * Get the property identifier |
| * @return the property id |
| */ |
| public final String getId() { |
| return _record.getId(); |
| } |
| |
| /** |
| * Get the backing ZNRecord |
| * @return ZNRecord object associated with this property |
| */ |
| public final ZNRecord getRecord() { |
| return _record; |
| } |
| |
| /** |
| * Set the changes to the backing ZNRecord |
| * @param deltaList list of ZNRecord updates to be made |
| */ |
| public final void setDeltaList(List<ZNRecordDelta> deltaList) { |
| _record.setDeltaList(deltaList); |
| } |
| |
| @Override |
| public String toString() { |
| return "ZnRecord=" + _record.toString() + ", Stat=" + _stat.toString() ; |
| } |
| |
| /** |
| * Get the size of buckets defined |
| * @return the bucket size, or 0 if not defined |
| */ |
| public int getBucketSize() { |
| String bucketSizeStr = _record.getSimpleField(HelixPropertyAttribute.BUCKET_SIZE.toString()); |
| int bucketSize = 0; |
| if (bucketSizeStr != null) { |
| try { |
| bucketSize = Integer.parseInt(bucketSizeStr); |
| } catch (NumberFormatException e) { |
| // OK |
| } |
| } |
| return bucketSize; |
| } |
| |
| /** |
| * Set the size of buckets defined |
| * @param bucketSize the bucket size (will default to 0 if negative) |
| */ |
| public void setBucketSize(int bucketSize) { |
| if (bucketSize <= 0) |
| bucketSize = 0; |
| |
| _record.setSimpleField(HelixPropertyAttribute.BUCKET_SIZE.toString(), "" + bucketSize); |
| } |
| |
| /** |
| * static method that converts ZNRecord to an instance that subclasses HelixProperty |
| * @param clazz subclass of HelixProperty |
| * @param record the ZNRecord describing the property |
| * @return typed instance corresponding to the record, or null if conversion fails |
| */ |
| public static <T extends HelixProperty> T convertToTypedInstance(Class<T> clazz, ZNRecord record) { |
| if (record == null) { |
| return null; |
| } |
| |
| try { |
| Constructor<T> constructor = clazz.getConstructor(ZNRecord.class); |
| return constructor.newInstance(record); |
| } catch (Exception e) { |
| LOG.error("Exception convert znrecord: " + record + " to class: " + clazz, e); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Convert a collection of records to typed properties |
| * @param clazz Subclass of HelixProperty |
| * @param records the ZNRecords describing the property |
| * @return list of typed instances for which the conversion succeeded, or null if records is null |
| */ |
| public static <T extends HelixProperty> List<T> convertToTypedList(Class<T> clazz, |
| Collection<ZNRecord> records) { |
| if (records == null) { |
| return null; |
| } |
| |
| List<T> decorators = new ArrayList<T>(); |
| for (ZNRecord record : records) { |
| T decorator = HelixProperty.convertToTypedInstance(clazz, record); |
| if (decorator != null) { |
| decorators.add(decorator); |
| } |
| } |
| return decorators; |
| } |
| |
| /** |
| * Converts a list of records to a map of the record identifier to typed properties |
| * @param records the ZNRecords to convert |
| * @return id --> HelixProperty subclass map |
| */ |
| public static <T extends HelixProperty> Map<String, T> convertListToMap(List<T> records) { |
| if (records == null) { |
| return Collections.emptyMap(); |
| } |
| |
| Map<String, T> decorators = new HashMap<String, T>(); |
| for (T record : records) { |
| decorators.put(record.getId(), record); |
| } |
| return decorators; |
| } |
| |
| /** |
| * Convert typed properties to a list of records |
| * @param typedInstances objects subclassing HelixProperty |
| * @return a list of ZNRecord objects |
| */ |
| public static <T extends HelixProperty> List<ZNRecord> convertToList(List<T> typedInstances) { |
| if (typedInstances == null) { |
| return Collections.emptyList(); |
| } |
| |
| List<ZNRecord> records = new ArrayList<ZNRecord>(); |
| for (T typedInstance : typedInstances) { |
| records.add(typedInstance.getRecord()); |
| } |
| |
| return records; |
| } |
| |
| /** |
| * Change the state of batch messaging |
| * @param enable true to enable, false to disable |
| */ |
| public void setBatchMessageMode(boolean enable) { |
| _record.setSimpleField(HelixPropertyAttribute.BATCH_MESSAGE_MODE.toString(), "" + enable); |
| } |
| |
| /** |
| * Get the state of batch messaging |
| * @return true if enabled, false if disabled |
| */ |
| public boolean getBatchMessageMode() { |
| String enableStr = _record.getSimpleField(HelixPropertyAttribute.BATCH_MESSAGE_MODE.toString()); |
| if (enableStr == null) { |
| return false; |
| } |
| |
| try { |
| return Boolean.parseBoolean(enableStr.toLowerCase()); |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Get the metadata (stat) of this record |
| * @return HelixProperty.Stat |
| */ |
| public Stat getStat() { |
| return _stat; |
| } |
| |
| /** |
| * Set the metadata (stat) for this record |
| * @param stat |
| */ |
| public void setStat(Stat stat) { |
| _stat = new Stat(stat); |
| } |
| |
| /** |
| * Get property validity |
| * @return true if valid, false if invalid |
| */ |
| public boolean isValid() { |
| return true; |
| } |
| |
| public byte[] serialize(ZkSerializer serializer) { |
| return serializer.serialize(_record); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == null) { |
| return false; |
| } |
| if (obj instanceof HelixProperty) { |
| HelixProperty that = (HelixProperty) obj; |
| if (that.getRecord() != null) { |
| return that.getRecord().equals(this.getRecord()); |
| } |
| } |
| return false; |
| } |
| } |