| /* |
| * 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; |
| } |
| } |