blob: ddbcfab20e7607a0d7a86ae5070bcafddc90bc1a [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.geode.internal.util;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.geode.DataSerializable;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.cache.Node;
/**
* Versioned ArrayList which maintains the version everytime the list gets modified. This is
* thread-safe in terms of add and remove operations and also list is an unmodifiable list to avoid
* ConcurrentModificationException.
*
* @see java.util.ConcurrentModificationException
*
*/
public class VersionedArrayList implements DataSerializable, Versionable, Iterable<Node> {
private static final long serialVersionUID = -1455442285961593385L;
/** Version of the list. */
private long version = -1;
/** ArrayList */
private List<Node> list;
// private List vhist = new ArrayList(); // DEBUG
/**
* Constructor for DataSerializable.
*/
public VersionedArrayList() {
this.list = new ArrayList<Node>();
}
/**
* Constructor.
*
*/
public VersionedArrayList(int size) {
this.list = new ArrayList<Node>(size);
}
public VersionedArrayList(List<? extends Node> list) {
this.list = Collections.unmodifiableList(list);
// incrementVersion("i->" + list);
incrementVersion();
}
/**
* Adds obj to the list. Addition is done by making a copy of the existing list and then adding
* the obj to the new list and assigning the old list to the new unmodifiable list. This is to
* ensure that the iterator of the list doesn't get ConcurrentModificationException.
*
* @see java.util.ConcurrentModificationException
*/
public synchronized void add(Node obj) {
ArrayList newList = new ArrayList<Node>(this.list);
newList.add(obj);
this.list = Collections.unmodifiableList(newList);
// incrementVersion("a->" + obj);
incrementVersion();
}
/**
* Removes obj from the list. Removal is done by making a copy of the existing list and then
* removing the obj from the new list. If the object was removed, the list is assigning to the new
* unmodifiable list. This is to ensure that the iterator of the list doesn't get
* ConcurrentModificationException.
*
* @return true if the element was removed and the version was changed, otherwise version and list
* are left unmodified.
*
* @see java.util.ConcurrentModificationException
* @param obj the object to remove from the list
*/
public synchronized boolean remove(Node obj) {
ArrayList<Node> newList = new ArrayList<Node>(this.list);
boolean ret = newList.remove(obj);
if (ret) {
this.list = Collections.unmodifiableList(newList);
// incrementVersion("r->" + obj);
incrementVersion();
}
return ret;
}
/**
* Returns the iterator.
*
* @return a list Iterator
*/
@Override
public synchronized Iterator<Node> iterator() {
return this.list.iterator();
}
/**
* Returns the size of the list.
*
*/
public synchronized int size() {
return this.list.size();
}
/**
* Returns true if obj is present in the list otherwise false.
*
* @return true if obj is present in the list
*/
public boolean contains(Node obj) {
final List<Node> l;
synchronized (this) {
l = this.list;
}
return l.contains(obj);
}
/**
* Returns Object at index i.
*
*/
public Object get(int i) {
final List<Node> l;
synchronized (this) {
l = this.list;
}
return l.get(i);
}
/**
* Returns the index of Object if present, else -1.
*
*/
public int indexOf(Object obj) {
final List<Node> l;
synchronized (this) {
l = this.list;
}
return l.indexOf(obj);
}
/**
* Returns a copy of the arraylist contained.
*
*/
public Set<Node> getListCopy() {
final List<Node> l;
synchronized (this) {
l = this.list;
}
return new HashSet<Node>(l);
}
/**
* Prints the version and elements of the list.
*
* @return String with version and elements of the list.
*/
@Override
public String toString() {
final List<Node> l;
// final List vh;
synchronized (this) {
l = this.list;
// vh = this.vhist;
}
StringBuffer sb = new StringBuffer();
sb.append("ArrayList version = " + getVersion() + " Elements = { ");
for (int i = 0; i < l.size(); i++) {
sb.append(l.get(i).toString() + ", ");
}
// sb.append("vhist:\n " + vh);
sb.append("}");
return sb.toString();
}
@Override
public void toData(DataOutput out) throws IOException {
long v = -1;
final List<Node> l;
// final List vh;
synchronized (this) {
v = this.version;
l = this.list;
// vh = this.vhist;
}
out.writeLong(v);
final int s = l.size();
out.writeInt(s);
for (int k = 0; k < s; k++) {
InternalDataSerializer.invokeToData((l.get(k)), out);
}
// final int sh = vh.size();
// out.writeInt(sh);
// for (int k = 0; k < sh; k++) {
// out.writeUTF((String) vh.get(k));
// }
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
final ArrayList<Node> l = new ArrayList<Node>();
final long v = in.readLong();
final int size = in.readInt();
for (int k = 0; k < size; k++) {
l.add(new Node(in));
}
// final ArrayList vh = new ArrayList();
// final int vhsize = in.readInt();
// for (int k = 0; k < vhsize; k++) {
// vh.add(in.readUTF());
// }
synchronized (this) {
this.version = v;
this.list = Collections.unmodifiableList(l);
// this.vhist = Collections.unmodifiableList(vh);
}
}
/*
* (non-Javadoc)
*
* @see org.apache.geode.internal.util.Versionable#getVersion()
*/
@Override
public synchronized Comparable getVersion() {
return Long.valueOf(this.version);
}
@Override
public boolean isNewerThan(Versionable other) {
if (other instanceof VersionedArrayList) {
final long v = ((Long) other.getVersion()).longValue();
synchronized (this) {
return this.version > v;
}
} else {
final Comparable o = other.getVersion();
return getVersion().compareTo(o) > 0;
}
}
@Override
public boolean isOlderThan(Versionable other) {
if (other instanceof VersionedArrayList) {
final long v;
synchronized (other) {
v = ((VersionedArrayList) other).version;
}
synchronized (this) {
return this.version < v;
}
} else {
final Comparable o = other.getVersion();
return getVersion().compareTo(o) < 0;
}
}
@Override
public boolean isSame(Versionable other) {
if (other instanceof VersionedArrayList) {
final long v;
synchronized (other) {
v = ((VersionedArrayList) other).version;
}
synchronized (this) {
return this.version == v;
}
} else {
final Comparable o = other.getVersion();
return getVersion().compareTo(o) == 0;
}
}
// private synchronized void incrementVersion(String op)
private synchronized void incrementVersion() {
// DEBUG
// {
// StringWriter s = new StringWriter();
// final String pre =
// "o=" + op +
// ":t=" + System.currentTimeMillis() +
// ":m=" + InternalDistributedSystem.getAnyInstance().getDistributedMember().toString() + ": ";
// s.write(pre);
// PrintWriter p = new PrintWriter(s);
// new Exception().fillInStackTrace().printStackTrace(p);
//
// ArrayList newList = new ArrayList(this.vhist);
// newList.add(pre); // Capture what code added this version
// this.vhist = Collections.unmodifiableList(newList);
// }
++this.version;
}
}