/* * 2012-3 Red Hat Inc. and/or its affiliates and other contributors. * * Licensed 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.overlord.rtgov.active.collection; import java.text.MessageFormat; import java.util.Collection; import java.util.Iterator; import java.util.logging.Logger; import org.overlord.rtgov.active.collection.QuerySpec.Style; import org.overlord.rtgov.active.collection.QuerySpec.Truncate; import org.overlord.rtgov.active.collection.predicate.Predicate; /** * This interface represents an active list. * */ public class ActiveList extends ActiveCollection implements java.lang.Iterable<Object> { private static final int INITIAL_CAPACITY = 1000; private static final Logger LOG=Logger.getLogger(ActiveList.class.getName()); private java.util.List<Object> _list=new java.util.ArrayList<Object>(INITIAL_CAPACITY); private java.util.List<Long> _listTimestamps=new java.util.ArrayList<Long>(INITIAL_CAPACITY); private java.util.List<Object> _readCopy=null; private boolean _copyOnRead=true; /** * This constructor initializes the active list. * * @param name The name */ public ActiveList(String name) { super(name); } /** * This constructor initializes the active collection. * * @param acs The Active Collection source */ public ActiveList(ActiveCollectionSource acs) { super(acs); } /** * This constructor initializes the active list. * * @param acs The Active Collection source * @param list The list */ public ActiveList(ActiveCollectionSource acs, java.util.List<Object> list) { super(acs); _list = list; } /** * This constructor initializes the active list. * * @param name The name * @param capacity The initial capacity of the list */ protected ActiveList(String name, int capacity) { super(name); _list = new java.util.ArrayList<Object>(capacity); _listTimestamps = new java.util.ArrayList<Long>(capacity); } /** * This constructor initializes the active list as a derived * version of the supplied collection, that applies the supplied predicate. * * @param name The name * @param parent The parent collection * @param context The context * @param predicate The predicate * @param properties The optional properties */ protected ActiveList(String name, ActiveCollection parent, ActiveCollectionContext context, Predicate predicate, java.util.Map<String,Object> properties) { super(name, parent, context, predicate, properties); if (isActive()) { // Filter the parent list items, to determine which pass the predicate for (Object value : (ActiveList)parent) { if (predicate.evaluate(context, value)) { doInsert(null, value); } } } } /** * This method derives the content. * * @return The derived content */ protected java.util.List<Object> deriveContent() { java.util.List<Object> ret=new java.util.ArrayList<Object>(); ActiveCollection parent=getParent(); Predicate pred=getPredicate(); ActiveCollectionContext context=getContext(); if (parent != null && pred != null) { for (Object value : (ActiveList)parent) { if (pred.evaluate(context, value)) { ret.add(value); } } } return (ret); } /** * This method sets the copy on read flag. * * @param b Whether to 'copy on read' */ protected void setCopyOnRead(boolean b) { _copyOnRead = b; } /** * {@inheritDoc} */ public int getSize() { if (!isDerived() || isActive()) { synchronized (_list) { return (_list.size()); } } else { java.util.List<Object> derived=deriveContent(); return (derived.size()); } } /** * {@inheritDoc} */ @Override protected void doInsert(Object key, Object value) { // If top level active collection, or a derive collection that is being actively // maintained, then apply the insertion if (!isDerived() || isActive()) { synchronized (_list) { if (key == null) { _list.add(value); if (getItemExpiration() > 0) { _listTimestamps.add(System.currentTimeMillis()); } } else if (key instanceof Integer) { _list.add((Integer)key, value); if (getItemExpiration() > 0) { _listTimestamps.add((Integer)key, System.currentTimeMillis()); } } else { LOG.severe(MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "active-collection.Messages").getString("ACTIVE-COLLECTION-7"), key)); } _readCopy = null; } inserted(key, value); } } /** * {@inheritDoc} */ @Override protected void doUpdate(Object key, Object value) { // If top level active collection, or a derive collection that is being actively // maintained, then apply the update if (!isDerived() || isActive()) { synchronized (_list) { if (key != null) { if (key instanceof Integer) { _list.set((Integer)key, value); if (getItemExpiration() > 0) { _listTimestamps.set((Integer)key, System.currentTimeMillis()); } updated(key, value); } else { LOG.severe(MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "active-collection.Messages").getString("ACTIVE-COLLECTION-8"), key)); } } else { // Can only assume that value maintains its own identity int index=_list.indexOf(value); if (index == -1) { // Can't find updated entry, so log error LOG.severe(MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "active-collection.Messages").getString("ACTIVE-COLLECTION-9"), getName(), value)); } else { _list.set(index, value); if (getItemExpiration() > 0) { _listTimestamps.set(index, System.currentTimeMillis()); } updated(index, value); } } _readCopy = null; } } } /** * {@inheritDoc} */ @Override protected void doRemove(Object key, Object value) { // If top level active collection, or a derive collection that is being actively // maintained, then apply the deletion if (!isDerived() || isActive()) { int pos=-1; synchronized (_list) { if (key instanceof Integer) { pos = (Integer)key; } else { pos = _list.indexOf(value); } if (pos != -1) { _list.remove(pos); if (getItemExpiration() > 0) { _listTimestamps.remove(pos); } } else { LOG.severe(MessageFormat.format( java.util.PropertyResourceBundle.getBundle( "active-collection.Messages").getString("ACTIVE-COLLECTION-10"), getName(), value)); } _readCopy = null; } if (pos != -1) { removed(pos, value); } } } /** * {@inheritDoc} */ public Iterator<Object> iterator() { if (!isDerived() || isActive()) { synchronized (_list) { if (_readCopy != null) { return (_readCopy.iterator()); } if (_copyOnRead) { _readCopy = new java.util.ArrayList<Object>(_list); return (_readCopy.iterator()); } else { return (_list.iterator()); } } } else { java.util.List<Object> derived=deriveContent(); return (derived.iterator()); } } /** * {@inheritDoc} */ protected ActiveCollection derive(String name, ActiveCollectionContext context, Predicate predicate, java.util.Map<String,Object> properties) { return (new ActiveList(name, this, context, predicate, properties)); } /** * {@inheritDoc} */ public java.util.List<Object> query(QuerySpec qs) { java.util.List<Object> ret=null; java.util.List<Object> list=_list; if (isDerived() && !isActive()) { list = deriveContent(); } synchronized (list) { // If no max items set, or list size is smaller than the max // size, and the style is normal, then just copy if ((qs.getMaxItems() == 0 || qs.getMaxItems() >= list.size()) && qs.getStyle() == Style.Normal) { ret = new java.util.ArrayList<Object>(list); } else { int start=0; int end=list.size(); if (qs.getMaxItems() > 0 && qs.getMaxItems() <= list.size()) { if (qs.getTruncate() == Truncate.End) { end = qs.getMaxItems(); } else { start = list.size()-qs.getMaxItems(); } } ret = new java.util.ArrayList<Object>(); if (qs.getStyle() == Style.Reversed) { for (int i=end-1; i >= start; i--) { ret.add(list.get(i)); } } else { for (int i=start; i < end; i++) { ret.add(list.get(i)); } } } } return (ret); } /** * {@inheritDoc} */ protected void cleanup() { if (!isDerived() || isActive()) { // TODO: Provide some separate cleanup policy class to enable // different policies to be used. For now just have a simple // directly implemented mechanism. if (getMaxItems() > 0) { synchronized (_list) { int num=getSize()-getMaxItems(); while (num > 0) { doRemove(0, _list.get(0)); num--; } } } if (getItemExpiration() > 0) { synchronized (_list) { // Calculate expiration time long expiration = System.currentTimeMillis()-getItemExpiration(); // Work through list backwards to determine if the entry // has expired for (int i=getSize()-1; i >= 0; i--) { if (_listTimestamps.get(i) < expiration) { // TODO: Could do bulk remove and then // send notifications all at once??? doRemove(i, _list.get(i)); } } } } } } /** * {@inheritDoc} */ public boolean isEmpty() { if (!isDerived() || isActive()) { synchronized (_list) { return (_list.isEmpty()); } } else { java.util.List<Object> derived=deriveContent(); return (derived.isEmpty()); } } /** * {@inheritDoc} */ public boolean contains(Object o) { if (!isDerived() || isActive()) { synchronized (_list) { return (_list.contains(o)); } } else { java.util.List<Object> derived=deriveContent(); return (derived.contains(o)); } } /** * {@inheritDoc} */ public int size() { return (getSize()); } /** * {@inheritDoc} */ public Object[] toArray() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public <T> T[] toArray(T[] a) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean add(Object e) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean remove(Object o) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean containsAll(Collection<?> c) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean addAll(Collection<? extends Object> c) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void clear() { throw new UnsupportedOperationException(); } }