Return-Path: X-Original-To: apmail-river-dev-archive@www.apache.org Delivered-To: apmail-river-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0E51C77D7 for ; Mon, 26 Sep 2011 07:53:06 +0000 (UTC) Received: (qmail 27307 invoked by uid 500); 26 Sep 2011 07:53:05 -0000 Delivered-To: apmail-river-dev-archive@river.apache.org Received: (qmail 27274 invoked by uid 500); 26 Sep 2011 07:53:05 -0000 Mailing-List: contact dev-help@river.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@river.apache.org Delivered-To: mailing list dev@river.apache.org Received: (qmail 27261 invoked by uid 99); 26 Sep 2011 07:53:04 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Sep 2011 07:53:04 +0000 X-ASF-Spam-Status: No, hits=-0.0 required=5.0 tests=SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) Received: from [207.57.65.70] (HELO zeus.net.au) (207.57.65.70) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Sep 2011 07:52:57 +0000 Received: (qmail 3139 invoked by uid 16710); 26 Sep 2011 07:52:33 -0000 Received: from unknown (HELO [10.202.5.157]) (peter.firmstone@[101.169.89.87]) (envelope-sender ) by 207.57.65.70 (qmail-ldap-1.03) with AES256-SHA encrypted SMTP for ; 26 Sep 2011 07:52:33 -0000 Message-ID: <4E802F3E.50204@zeus.net.au> Date: Mon, 26 Sep 2011 17:52:30 +1000 From: Peter Firmstone User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.22) Gecko/20110902 Thunderbird/3.1.14 MIME-Version: 1.0 To: dev@river.apache.org Subject: New Collection Utilities. Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit The new reference collection utilities I've just committed, probably appear a little daunting, so many classes... Anyway, all the client needs are these two, the rest is private implementation. It has an extremely compact API. org.apache.river.impl.util.RC - Stands for ReferenceCollection, contains static methods. org.apache.river.impl.util.Ref - An enum parameter, allows the developer to select the desired reference type. RC provides static methods to encapsulate Collections containing references behind facades handling reference creation, calls to get, removal and garbage collecting of References. All Java Collection Framework interfaces are implemented fully, right down to the correct equals behaviour for Set, List and Map, and Map.Entry and Set's of Entry's. Even the reference classes themselves are overridden (although package private) to implement the correct equals and hashcode behaviour. It's all been tested with unit tests. Two Sets, one utilising Weak References and any other Set containing the same objects will be equal as per the Set interface requirement, no matter what reference types are utilised, the same goes for List's, Maps, NavigableMap, SortedMap, SortedSet, ConcurrentMap, ConcurrentNavigableMap and all the other JCF interfaces you can think of. All views remain referenced, so their referents don't inadvertantly become strongly referenced. Only the developers calling classes will make the referents strongly referenced upon retrieval. Synchronization must be performed by the internal collection passed in by the developer, it can't be performed externally - garbage collection causes mutation. To assist concurrency there's a third class with static methods org.apache.river.impl.utils.ConcurrentCollections to provide synchronization wrappers (multi read, single write) similar to wrapper classes provided in java.util.Collections The RC class makes it extremely simple to create Reference based Collections: Set strings = RC.set(new TreeSet>(), Ref.SOFT); Static methods were chosen over constructors because they're easier to read (not filled with generics) and make the API appear much smaller, you only need two new classes and don't need to know the implementation details. Yes it supports Comparator's too: ConcurrentNavigableMap instance; Comparator comparator; // A simple comparator - demonstration purposes only. comparator = new Comparator(){ @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }; Comparator> ci = RC.comparator(comparator); ConcurrentNavigableMap, Reference> internal = new ConcurrentSkipListMap, Reference>(ci); instance = RC.concurrentNavigableMap(internal, Ref.WEAK, Ref.STRONG); The developer gets to choose the collection implementation and the reference types, all the hard work's done. I hope you find these reference utilities as useful as I have. N.B. I'll create a jar archive for them and merge this back into the main trunk, after peer review. Check it out from svn: https://svn.apache.org/repos/asf/river/jtsk/skunk/peterConcurrentPolicy/ Or browse at: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/ /* * 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.river.impl.util; /** * Ref enum represents the types of references provided by the jvm, it also * defines equals contract behaviour for Reference implementations suitable * for use in Collections. * * It is recommended to use STRONG or IDENTITY based references as keys * in Maps, due to the unpredictability that occurs, when an equal * Reference Key disappears due to garbage collection. * * Map implementations must delete their key -> value mapping when either the * key or value References become unreachable. * * Object.toString() is overridden in the reference implementations to return * toString() of the referent, if it is still reachable, otherwise the reference * calls their superclass toString() method, where superclass is a java * Reference subclass. * * Phantom references are not used, they are designed to replace * {@link Object#finalize() } and remain unreachable, but not garbage collected until * the {@link PhantomReference} also becomes unreachable, get() always returns * null. * * @see Reference * @see WeakReference * @see SoftReference * @see PhantomReference * @see Map * @see ConcurrentMap * @see List * @see Set * @see Collection * @see Comparable * @author Peter Firmstone. */ public enum Ref { /** * SOFT References implement equals based on equality of the referent * objects, while the referent is still reachable. The hashCode * implementation is based on the referent implementation of hashCode, * while the referent is reachable. * * After garbage collection, Reference equality is based * on the original identity of the referents using System.identityHashCode(). * * Because {@link System#identityHashCode(java.lang.Object)} is not unique, * the referents Class.hashCode() is also used to calculate the hashCode, * generated during Reference construction. * * SOFT References implement Comparable allowing the referent Objects * to be compared if they implement Comparable. If the referent Object * doesn't implement Comparable, the hashCode's of the Reference is * compared instead. If the referent Objects don't implement Comparable, * then they shouldn't really be used in sorted collections. * * Garbage collection must be the same as SoftReference. * @see SoftReference * @see WeakReference * @see Comparable */ SOFT, /** * SOFT_IDENTY References implement equals based on identity == of the * referent objects. * * Garbage collection must be the same as SoftReference. * @see SoftReference */ SOFT_IDENTITY, /** * WEAK References implement equals based on equality of the referent * objects, while the referent is still reachable. The hashCode * implementation is based on the referent implementation of hashCode, * while the referent is reachable. * * After garbage collection, Reference equality is based * on the original identity of the referents using System.identityHashCode(). * * Because System.identityHashCode() is not unique, the referents * Class.hashCode() is also used to calculate the hashCode, generated during * Reference construction. * * WEAK References implement comparable allowing the referent Objects * to be compared if they implement Comparable. If the referent Object * doesn't implement Comparable, the hashCode's of the Reference is * compared instead. If the referent Object's don't implement Comparable, * then they shouldn't really be used in sorted collections. * * Garbage collection must be the same as WeakReference. * @see WeakReference * @see Comparable */ WEAK, /** * WEAK_IDENTY References implement equals based on identity == of the * referent objects. * * Garbage collection must be the same as WeakReference. * * @see WeakReference */ WEAK_IDENTITY, /** * STRONG References implement equals and hashCode() based on the * equality of the underlying Object. * * STRONG References implement Comparable allowing the referent Objects * to be compared if they implement Comparable. If the referent Object * doesn't implement Comparable, the hashCode's of the Reference is * compared instead. If the referent Object's don't implement Comparable, * then they shouldn't really be used in sorted collections. * * Garbage collection doesn't occur until the Reference is cleared. * @see Comparable */ STRONG } /* * 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.river.impl.util; import java.lang.ref.Reference; import java.lang.Comparable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ListIterator; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Queue; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; /** *

* This class contains a number of static methods for using and abstracting * References in Collections. Interfaces from the Java Collections Framework * are supported. *

* Referents in these collections may implement {@link Comparable} or * a {@link Comparator} may be used for sorting. When Comparator's are utilised, * they must first be encapsulated {@link RC#comparator(java.util.Comparator) }, * before passing to a constructor for your preferred underlying Collection * implementation. *

* {@link Comparable} is not supported for IDENTITY == referenced Collections, * in this case a Comparator must be used. *

* All other references support {@link Comparable}, if the referent Object * doesn't implement {@link Comparable}, then {@link Reference#hashCode()} is used * for sorting. If two referent Objects have identical hashCodes, * but are unequal and do not implement {@link Comparable}, their references * will also have identical hashCodes, so only one of the referents can * be added to a {@link SortedSet} or {@link SortedMap}. This can be fixed by using a * {@link Comparator}. *

* For all intents and purposes these utilities behave the same as your preferred * underlying {@link Collection} implementation, with the exception of * {@link Reference} reachability. An Object or Key,Value entry is removed * from a {@link Collection} or {@link Map}, upon becoming eligible for * garbage collection. *

* Synchronisation must be implemented by your preferred {@link Collection} * and cannot be performed externally to the returned {@link Collection}. * Your chosen underlying {@link Collection} must also be mutable. * Objects will be removed automatically from underlying Collections when * they are eligible for garbage collection, this breaks external synchronisation. * {@link CollectionsConcurrent#multiReadCollection(java.util.Collection)} may * be useful for synchronising your chosen underlying {@link Collection}, * especially if Objects are not being garbage collected often and writes * are minimal. *

* An Unmodifiable wrapper {@link Collections#unmodifiableCollection(java.util.Collection)} * may be used externally to prevent additions to the underlying Collections, * referents will still be removed as they become unreachable however. *

* Note that any Sub List, Sub Set or Sub Map obtained by any of the Java * Collections Framework interfaces, must be views of the underlying * Collection, if the Collection uses defensive copies instead of views, * References could potentially remain in one copy after garbage collection, * causing null returns. If using standard Java Collections Framework * implementations, these problems don't occur as all Sub Lists, * Sub Sets or Sub Maps are views only. *

* {@link Map#entrySet() } view instances returned preserve your chosen reference * behaviour, they even support {@link Set#add(java.lang.Object)} or * {@link Set#addAll(java.util.Collection)} methods, although you'll be hard * pressed to find a standard java implementation that does. If you have a * Map with a Set of Entry's implementing add, the implementation will need a * Comparator, that compares Entry's only by their keys, to avoid duplicating * keys, primarily because an Entry hashCode includes the both key and value in its * calculation. {@link Entry#hashCode() } *

* All other {@link Map#entrySet() } methods are fully implemented and supported. *

* {@link Entry} view instances returned by these methods preserve reference * behaviour, all methods are fully implemented and supported. *

* {@link Set} and it's sub interfaces {@link SortedSet} and * {@link NavigableSet}, return views that preserve reference behaviour, * all methods are fully implemented and supported. *

* {@link Map} and it's sub interfaces {@link SortedMap}, {@link NavigableMap}, * {@link ConcurrentMap} and {@link ConcurrentNavigableMap} return * views that preserve reference behaviour, all methods are fully implemented * and supported. *

* {@link List} returns views that preserve reference behaviour, all methods are * fully implemented and supported. *

* {@link Queue} and it's sub interfaces {@link Deque}, {@link BlockingQueue} and * {@link BlockingDeque} return views that preserve reference behaviour, * all methods are fully implemented and supported. *

* {@link Iterator} and {@link ListIterator} views preserve reference behaviour, all methods * are fully implemented and supported. *

* RC stands for Reference Collection and is abbreviated due to the length of * generic parameter arguments typically required. *

* @see Ref * @see Reference * @author Peter Firmstone. */ public class RC { private RC(){} // Non instantiable /** * When using a Comparator in SortedSet's and SortedMap's, the Comparator * must be encapsulated using this method, to order the Set or Map * by referents and not References. * * @param * @param comparator * @return */ public static Comparator> comparator(Comparator comparator){ return new ReferenceComparator(comparator); } /** * Wrap a Collection for holding references so it appears as a Collection * containing referents. * * @param * @param internal * @param type * @return */ public static Collection collection(Collection> internal, Ref type){ return new ReferenceCollection(internal, type); } /** * Wrap a List for holding references so it appears as a List * containing referents. * * @param * @param internal * @param type * @return */ public static List list(List> internal, Ref type){ return new ReferenceList(internal, type); } /** * Wrap a Set for holding references so it appears as a Set * containing referents. * * @param * @param internal * @param type * @return */ public static Set set(Set> internal, Ref type){ return new ReferenceSet(internal, type); } /** * Wrap a SortedSet for holding references so it appears as a SortedSet * containing referents. * * @para m * @param internal * @param type * @return */ public static SortedSet sortedSet( SortedSet> internal, Ref type){ return new ReferenceSortedSet(internal, type); } /** * Wrap a NavigableSet for holding references so it appears as a NavigableSet * containing referents. * * @param * @param internal * @param type * @return */ public static NavigableSet navigableSet( NavigableSet> internal, Ref type){ return new ReferenceNavigableSet(internal, type); } /** * Wrap a Queue for holding references so it appears as a Queue * containing referents. * * @param * @param internal * @param type * @return */ public static Queue queue(Queue> internal, Ref type){ return new ReferencedQueue(internal, type); } /** * Wrap a Deque for holding references so it appears as a Deque * containing referents. * * @param * @param internal * @param type * @return */ public static Deque deque(Deque> internal, Ref type){ return new ReferenceDeque(internal, type); } /** * Wrap a BlockingQueue for holding references so it appears as a BlockingQueue * containing referents. * * @param * @param internal * @param type * @return */ public static BlockingQueue blockingQueue( BlockingQueue> internal, Ref type){ return new ReferenceBlockingQueue(internal, type); } /** * Wrap a BlockingDeque for holding references so it appears as a BlockingDeque * containing referents. * * @param * @param internal * @param type * @return */ public static BlockingDeque blockingDeque( BlockingDeque> internal, Ref type){ return new ReferenceBlockingDeque(internal, type); } /** * Wrap a Map for holding references so it appears as a Map * containing referents. * * @param * @param * @param internal * @param key * @param value * @return */ public static Map map( Map, Reference> internal, Ref key, Ref value){ return new ReferenceMap(internal, key, value); } /** * Wrap a SortedMap for holding references so it appears as a SortedMap * containing referents. * * @param * @param * @param internal * @param key * @param value * @return */ public static SortedMap sortedMap( SortedMap, Reference> internal, Ref key, Ref value){ return new ReferenceSortedMap(internal, key, value); } /** * Wrap a NavigableMap for holding references so it appears as a NavigableMap * containing referents. * * @param * @param * @param internal * @param key * @param value * @return */ public static NavigableMap navigableMap( NavigableMap, Reference> internal, Ref key, Ref value){ return new ReferenceNavigableMap(internal, key, value); } /** * Wrap a ConcurrentMap for holding references so it appears as a ConcurrentMap * containing referents. * * @param - key type. * @param - value type. * @param internal - for holding references. * @param key - key reference type. * @param value - value reference type. * @return */ public static ConcurrentMap concurrentMap( ConcurrentMap, Reference> internal, Ref key, Ref value){ return new ReferenceConcurrentMap(internal, key, value); } /** * Wrap a ConcurrentNavigableMap for holding references so it appears as a * ConcurrentNavigableMap containing referents. * * @param * @param * @param internal * @param key * @param value * @return */ public static ConcurrentNavigableMap concurrentNavigableMap( ConcurrentNavigableMap, Reference> internal, Ref key, Ref value){ return new ReferenceConcurrentNavigableMap(internal, key, value); } }