package org.deephacks.confit.model;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import org.deephacks.confit.serialization.Bytes;
import org.deephacks.confit.serialization.BytesUtils;
import org.deephacks.confit.serialization.UniqueIds;
import java.io.Serializable;
import java.util.Arrays;
import static org.deephacks.confit.model.Events.CFG107_MISSING_ID;
/**
* Identifies bean instances of a particular schema. Instances are unique per id and schema.
*/
public class BeanId implements Comparable<BeanId> {
private final String instanceId;
private final String schemaName;
private Bean bean;
private transient Schema schema;
private boolean isSingleton;
private BeanId(final String instanceId, final String schemaName) {
this.instanceId = Preconditions.checkNotNull(instanceId);
this.schemaName = Preconditions.checkNotNull(schemaName);
this.isSingleton = false;
}
private BeanId(final String instanceId, final String schemaName, final boolean isSingleton) {
this.instanceId = Preconditions.checkNotNull(instanceId);
this.schemaName = Preconditions.checkNotNull(schemaName);
this.isSingleton = isSingleton;
}
/**
* Create a bean identification.
*
* @param instanceId of this bean.
* @param schemaName The bean schema name.
* @return AdminBeanId
*/
public static BeanId create(final String instanceId, final String schemaName) {
if (instanceId == null || "".equals(instanceId)) {
throw CFG107_MISSING_ID();
}
return new BeanId(instanceId, schemaName);
}
/**
* Create a bean identification from its binary representation.
*
* @param data binary data
* @return A BeanId
*/
public static BeanId create(final byte[] data) {
return new BinaryBeanId(data).getBeanId();
}
/**
* This method should NOT be used by users.
*
* @param schemaName schema of bean.
* @return a singleton id.
*/
public static BeanId createSingleton(final String schemaName) {
return new BeanId(schemaName, schemaName, true);
}
/**
* @return the instance id of the bean.
*/
public String getInstanceId() {
return instanceId;
}
/**
* @return the schema name of the bean.
*/
public String getSchemaName() {
return schemaName;
}
/**
* Schema will only be present if the Bean was fetched or given from
* a managed administrative context.
*
* @return the schema that belong to the bean instance.
*/
public Schema getSchema() {
return schema;
}
/**
* Set the schema that define content structure and constraints of this
* the bean instance.
*
* @param schema schema
*/
public void set(final Schema schema) {
this.schema = schema;
}
/**
* Check for singleton.
*
* @return true if singleton.
*/
public boolean isSingleton() {
return isSingleton;
}
/**
* Return the bean that is identified by this BeanId. The actual
* bean will only be available if the BeanId was initalized by the
* from admin context.
*
* @return bean
*/
public Bean getBean() {
return bean;
}
/**
* Do not use. Only used by the admin context.
*
* @param bean bean
*/
public void setBean(Bean bean) {
this.bean = bean;
}
/**
* @return binary representation of a BeanId
*/
public BinaryBeanId toBinary() {
return new BinaryBeanId(this);
}
/**
* @return bean id as a byte array
*/
public byte[] write() {
return new BinaryBeanId(this).getKey();
}
/**
* Read BeanId from binary representation.
*
* @param data byte array
* @return BeanId
*/
public static BeanId read(byte[] data) {
return new BinaryBeanId(data).getBeanId();
}
@Override
public String toString() {
return getSchemaName() + "@" + getInstanceId();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode());
result = prime * result + ((schemaName == null) ? 0 : schemaName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BeanId other = (BeanId) obj;
if (instanceId == null) {
if (other.instanceId != null)
return false;
} else if (!instanceId.equals(other.instanceId))
return false;
if (schemaName == null) {
if (other.schemaName != null)
return false;
} else if (!schemaName.equals(other.schemaName))
return false;
return true;
}
@Override
public int compareTo(BeanId that) {
return ComparisonChain.start()
.compare(this.schemaName, that.schemaName)
.compare(this.instanceId, that.instanceId)
.result();
}
/**
* Binary representation of a BeanId that consist of 4 bytes schema id followed
* by 8 bytes instance id. By prefixing each id with its schema makes it is possible
* to store all bean ids in the same sorted collection, close to each other, making
* querying much more efficient.
*
* This also make pagination trivial to implement since the last key always points
* to the next set of entries.
*/
public static class BinaryBeanId implements Comparable<BinaryBeanId>, Serializable {
/** lazy */
private static UniqueIds ids;
private byte[] key;
public BinaryBeanId(BeanId beanId) {
if (ids == null) {
ids = UniqueIds.lookup();
}
int schemaId = ids.getSchemaId(beanId.getSchemaName());
long instanceId = ids.getInstanceId(beanId.getInstanceId());
this.key = toKey(schemaId, instanceId);
}
public BinaryBeanId(byte[] key) {
this.key = key;
}
public byte[] getKey() {
return key;
}
private static byte[] toKey(int schemaId, long instanceId) {
byte[] s = Bytes.fromInt(schemaId);
byte[] i = Bytes.fromLong(instanceId);
return new byte[] {s[0], s[1], s[2], s[3], i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7]};
}
public BeanId getBeanId() {
String schemaName = ids.getSchemaName(getSchemaId());
String instanceId = ids.getInstanceName(getInstanceId());
return BeanId.create(instanceId, schemaName);
}
public static BinaryBeanId getMinId(String schemaName) {
byte[] key = toKey(ids.getSchemaId(schemaName), 0);
return new BinaryBeanId(key);
}
public static BinaryBeanId getMaxId(String schemaName) {
byte[] key = toKey(ids.getSchemaId(schemaName), -1);
return new BinaryBeanId(key);
}
private byte[] getSchemaBytes() {
return new byte[] { key[0], key[1], key[2], key[3] };
}
private int getSchemaId() {
return Bytes.getInt(getSchemaBytes());
}
private byte[] getInstanceBytes() {
return new byte[] { key[4], key[5], key[6], key[7], key[8], key[9], key[10], key[11] };
}
private long getInstanceId() {
return Bytes.getLong(getInstanceBytes());
}
@Override
public int compareTo(BinaryBeanId o) {
return BytesUtils.compareTo(key, 0, key.length, o.getKey(), 0, o.getKey().length);
}
@Override
public String toString() {
return Arrays.toString(key);
}
}
}