Return-Path: Delivered-To: apmail-openjpa-commits-archive@www.apache.org Received: (qmail 14109 invoked from network); 25 Mar 2008 03:39:09 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 25 Mar 2008 03:39:09 -0000 Received: (qmail 23785 invoked by uid 500); 25 Mar 2008 03:39:08 -0000 Delivered-To: apmail-openjpa-commits-archive@openjpa.apache.org Received: (qmail 23765 invoked by uid 500); 25 Mar 2008 03:39:08 -0000 Mailing-List: contact commits-help@openjpa.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@openjpa.apache.org Delivered-To: mailing list commits@openjpa.apache.org Received: (qmail 23689 invoked by uid 99); 25 Mar 2008 03:39:08 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 24 Mar 2008 20:39:08 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Mar 2008 03:38:33 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id C96001A985D; Mon, 24 Mar 2008 20:38:17 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r640685 [10/14] - in /openjpa/trunk: ./ openjpa-all/ openjpa-jdbc-5/ openjpa-jdbc/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/ope... Date: Tue, 25 Mar 2008 03:38:02 -0000 To: commits@openjpa.apache.org From: pcl@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080325033817.C96001A985D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/CacheMap.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/CacheMap.java?rev=640685&r1=640684&r2=640685&view=diff ============================================================================== --- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/CacheMap.java (original) +++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/CacheMap.java Mon Mar 24 20:37:56 2008 @@ -1,639 +1,645 @@ -/* - * 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.openjpa.util; - -import java.util.AbstractCollection; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.collections.Predicate; -import org.apache.commons.collections.iterators.FilterIterator; -import org.apache.commons.collections.iterators.IteratorChain; -import org.apache.openjpa.lib.util.LRUMap; -import org.apache.openjpa.lib.util.ReferenceHashMap; -import org.apache.openjpa.lib.util.ReferenceMap; -import org.apache.openjpa.lib.util.SizedMap; -import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap; -import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; -import org.apache.openjpa.lib.util.concurrent.ReentrantLock; - -/** - * Fixed-size map that has ability to pin/unpin entries and move overflow to - * a backing soft map. - * - * @author Patrick Linskey - * @author Abe White - */ -public class CacheMap - implements Map { - - /** - * The map for non-expired and non-pinned references. - */ - protected final SizedMap cacheMap; - - /** - * The map for expired references. - */ - protected final SizedMap softMap; - - /** - * The set of objects pinned into the cache. - */ - protected final Map pinnedMap; - - // number of pinned values (not including keys not mapped to values) - private int _pinnedSize = 0; - - private final ReentrantLock _writeLock = new ReentrantLock(); - private final ReentrantLock _readLock; - - /** - * Create a non-LRU (and therefore highly concurrent) cache map with a - * size of 1000. - */ - public CacheMap() { - this(false, 1000); - } - - /** - * Create a cache map with the given properties. - */ - public CacheMap(boolean lru, int max) { - this(lru, max, max / 2, .75F); - } - - /** - * Create a cache map with the given properties. - */ - public CacheMap(boolean lru, int max, int size, float load) { - if (size < 0) - size = 500; - if (!lru) { - cacheMap = new ConcurrentHashMap(size, load) { - public void overflowRemoved(Object key, Object value) { - cacheMapOverflowRemoved(key, value); - } - }; - softMap = new ConcurrentReferenceHashMap(ReferenceMap.HARD, - ReferenceMap.SOFT, size, load) { - public void overflowRemoved(Object key, Object value) { - softMapOverflowRemoved(key, value); - } - - public void valueExpired(Object key) { - softMapValueExpired(key); - } - }; - pinnedMap = new ConcurrentHashMap(); - _readLock = null; - } else { - cacheMap = new LRUMap(size, load) { - public void overflowRemoved(Object key, Object value) { - cacheMapOverflowRemoved(key, value); - } - }; - softMap = new ReferenceHashMap(ReferenceMap.HARD, - ReferenceMap.SOFT, size, load) { - public void overflowRemoved(Object key, Object value) { - softMapOverflowRemoved(key, value); - } - - public void valueExpired(Object key) { - softMapValueExpired(key); - } - }; - pinnedMap = new HashMap(); - _readLock = _writeLock; - } - cacheMap.setMaxSize((max < 0) ? Integer.MAX_VALUE : max); - } - - /** - * Called from {@link SizedMap#overflowRemoved} in the cache map. - */ - protected void cacheMapOverflowRemoved(Object key, Object value) { - if (softMap.size() < softMap.getMaxSize()) - put(softMap, key, value); - else - entryRemoved(key, value, true); - } - - /** - * Called from {@link SizedMap#overflowRemoved} in the soft map. - */ - protected void softMapOverflowRemoved(Object key, Object value) { - entryRemoved(key, value, true); - } - - /** - * Called when a value expires from the soft map. - */ - protected void softMapValueExpired(Object key) { - entryRemoved(key, null, true); - } - - /** - * Put the given entry into the given map. Allows subclasses to - * take additional actions. - */ - protected Object put(Map map, Object key, Object value) { - return map.put(key, value); - } - - /** - * Remove the given key from the given map. Allows subclasses to - * take additional actions. - */ - protected Object remove(Map map, Object key) { - return map.remove(key); - } - - /** - * Acquire read lock. - */ - public void readLock() { - if (_readLock != null) - _readLock.lock(); - } - - /** - * Release read lock. - */ - public void readUnlock() { - if (_readLock != null) - _readLock.unlock(); - } - - /** - * Acquire write lock. - */ - public void writeLock() { - _writeLock.lock(); - } - - /** - * Release write lock. - */ - public void writeUnlock() { - _writeLock.unlock(); - } - - /** - * Whether this cache map uses LRU eviction. - */ - public boolean isLRU() { - return _readLock != null; - } - - /** - * The maximum number of hard references to maintain, or -1 for no limit. - */ - public void setCacheSize(int size) { - writeLock(); - try { - cacheMap.setMaxSize((size < 0) ? Integer.MAX_VALUE : size); - } finally { - writeUnlock(); - } - } - - /** - * The maximum number of hard references to maintain, or -1 for no limit. - */ - public int getCacheSize() { - int max = cacheMap.getMaxSize(); - return (max == Integer.MAX_VALUE) ? -1 : max; - } - - /** - * The maximum number of soft references to maintain, or -1 for no limit. - */ - public void setSoftReferenceSize(int size) { - writeLock(); - try { - softMap.setMaxSize((size < 0) ? Integer.MAX_VALUE : size); - } finally { - writeUnlock(); - } - } - - /** - * The maximum number of soft references to maintain, or -1 for no limit. - */ - public int getSoftReferenceSize() { - int max = softMap.getMaxSize(); - return (max == Integer.MAX_VALUE) ? -1 : max; - } - - /** - * The keys pinned into the map. - */ - public Set getPinnedKeys() { - readLock(); - try { - return Collections.unmodifiableSet(pinnedMap.keySet()); - } finally { - readUnlock(); - } - } - - /** - * Locks the given key and its value into the map. Objects pinned into - * the map are not counted towards the maximum cache size, and are never - * evicted implicitly. You may pin keys for which no value is in the map. - * - * @return true if the givne key's value was pinned; false if no value - * for the given key is cached - */ - public boolean pin(Object key) { - writeLock(); - try { - // if we don't have a pinned map we need to create one; else if the - // pinned map already contains the key, nothing to do - if (pinnedMap.containsKey(key)) - return pinnedMap.get(key) != null; - - // check other maps for key - Object val = remove(cacheMap, key); - if (val == null) - val = remove(softMap, key); - - // pin key - put(pinnedMap, key, val); - if (val != null) { - _pinnedSize++; - return true; - } - return false; - } finally { - writeUnlock(); - } - } - - /** - * Undo a pinning. - */ - public boolean unpin(Object key) { - writeLock(); - try { - Object val = remove(pinnedMap, key); - if (val != null) { - // put back into unpinned cache - put(key, val); - _pinnedSize--; - return true; - } - return false; - } finally { - writeUnlock(); - } - } - - /** - * Invoked when a key-value pair is evicted from this data - * structure. This is invoked with expired set to - * true when an object is dropped because of space - * requirements or through garbage collection of soft references. - * It is invoked with expired set to false - * when an object is explicitly removed via the {@link #remove} or - * {@link #clear} methods. This may be invoked more than once for a - * given entry. - * - * @param value may be null if the value was a soft reference that has - * been GCd - * @since 0.2.5.0 - */ - protected void entryRemoved(Object key, Object value, boolean expired) { - } - - /** - * Invoked when an entry is added to the cache. This may be invoked - * more than once for an entry. - */ - protected void entryAdded(Object key, Object value) { - } - - public Object get(Object key) { - readLock(); - try { - Object val = pinnedMap.get(key); - if (val != null) - return val; - - val = cacheMap.get(key); - if (val == null) { - // if we find the key in the soft map, move it back into - // the primary map - val = softMap.get(key); - if (val != null) - put(key, val); - } - return val; - } finally { - readUnlock(); - } - } - - public Object put(Object key, Object value) { - writeLock(); - try { - // if the key is pinned, just interact directly with the pinned map - Object val; - if (pinnedMap.containsKey(key)) { - val = put(pinnedMap, key, value); - if (val == null) { - _pinnedSize++; - entryAdded(key, value); - } else { - entryRemoved(key, val, false); - entryAdded(key, value); - } - return val; - } - - // if no hard refs, don't put anything - if (cacheMap.getMaxSize() == 0) - return null; - - // otherwise, put the value into the map and clear it from the - // soft map - val = put(cacheMap, key, value); - if (val == null) { - val = remove(softMap, key); - if (val == null) - entryAdded(key, value); - else { - entryRemoved(key, val, false); - entryAdded(key, value); - } - } else { - entryRemoved(key, val, false); - entryAdded(key, value); - } - return val; - } finally { - writeUnlock(); - } - } - - public void putAll(Map map) { - Map.Entry entry; - for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) { - entry = (Map.Entry) itr.next(); - put(entry.getKey(), entry.getValue()); - } - } - - /** - * If key is pinned into the cache, the pin is - * cleared and the object is removed. - */ - public Object remove(Object key) { - writeLock(); - try { - // if the key is pinned, just interact directly with the - // pinned map - Object val; - if (pinnedMap.containsKey(key)) { - // re-put with null value; we still want key pinned - val = put(pinnedMap, key, null); - if (val != null) { - _pinnedSize--; - entryRemoved(key, val, false); - } - return val; - } - - val = remove(cacheMap, key); - if (val == null) - val = softMap.remove(key); - if (val != null) - entryRemoved(key, val, false); - - return val; - } finally { - writeUnlock(); - } - } - - /** - * Removes pinned objects as well as unpinned ones. - */ - public void clear() { - writeLock(); - try { - notifyEntryRemovals(pinnedMap.entrySet()); - pinnedMap.clear(); - _pinnedSize = 0; - - notifyEntryRemovals(cacheMap.entrySet()); - cacheMap.clear(); - - notifyEntryRemovals(softMap.entrySet()); - softMap.clear(); - } finally { - writeUnlock(); - } - } - - private void notifyEntryRemovals(Set set) { - Map.Entry entry; - for (Iterator itr = set.iterator(); itr.hasNext();) { - entry = (Map.Entry) itr.next(); - if (entry.getValue() != null) - entryRemoved(entry.getKey(), entry.getValue(), false); - } - } - - public int size() { - readLock(); - try { - return _pinnedSize + cacheMap.size() + softMap.size(); - } finally { - readUnlock(); - } - } - - public boolean isEmpty() { - return size() == 0; - } - - public boolean containsKey(Object key) { - readLock(); - try { - return pinnedMap.get(key) != null - || cacheMap.containsKey(key) - || softMap.containsKey(key); - } finally { - readUnlock(); - } - } - - public boolean containsValue(Object val) { - readLock(); - try { - return pinnedMap.containsValue(val) - || cacheMap.containsValue(val) - || softMap.containsValue(val); - } finally { - readUnlock(); - } - } - - public Set keySet() { - return new KeySet(); - } - - public Collection values() { - return new ValueCollection(); - } - - public Set entrySet() { - return new EntrySet(); - } - - public String toString() { - readLock(); - try { - return "CacheMap:" + cacheMap.toString() + "::" - + softMap.toString(); - } finally { - readUnlock(); - } - } - - /** - * View of the entry set. - */ - private class EntrySet - extends AbstractSet { - - public int size() { - return CacheMap.this.size(); - } - - public boolean add(Object o) { - Map.Entry entry = (Map.Entry) o; - put(entry.getKey(), entry.getValue()); - return true; - } - - public Iterator iterator() { - return new EntryIterator(EntryIterator.ENTRY); - } - } - - /** - * View of the key set. - */ - private class KeySet - extends AbstractSet { - - public int size() { - return CacheMap.this.size(); - } - - public Iterator iterator() { - return new EntryIterator(EntryIterator.KEY); - } - } - - /** - * View of the value collection. - */ - private class ValueCollection - extends AbstractCollection { - - public int size() { - return CacheMap.this.size(); - } - - public Iterator iterator() { - return new EntryIterator(EntryIterator.VALUE); - } - } - - /** - * Iterator over all entries. - */ - private class EntryIterator - implements Iterator, Predicate { - - public static final int ENTRY = 0; - public static final int KEY = 1; - public static final int VALUE = 2; - - private final IteratorChain _itr = new IteratorChain(); - private final int _type; - - public EntryIterator(int type) { - _type = type; - _itr.addIterator(new FilterIterator(getView(pinnedMap), this)); - _itr.addIterator(getView(cacheMap)); - _itr.addIterator(getView(softMap)); - } - - /** - * Return an iterator over the appropriate view of the given map. - */ - private Iterator getView(Map m) { - if (m == null) - return null; - - switch (_type) { - case KEY: - return m.keySet().iterator(); - case VALUE: - return m.values().iterator(); - default: - return m.entrySet().iterator(); - } - } - - public boolean hasNext() { - return _itr.hasNext(); - } - - public Object next() { - return _itr.next(); - } - - public void remove() { - _itr.remove(); - } - - public boolean evaluate(Object obj) { - switch (_type) { - case ENTRY: - return ((Map.Entry) obj).getValue() != null; - case VALUE: - return obj != null; - default: - return true; - } - } - } -} - +/* + * 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.openjpa.util; + +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.iterators.FilterIterator; +import org.apache.commons.collections.iterators.IteratorChain; +import org.apache.openjpa.lib.util.LRUMap; +import org.apache.openjpa.lib.util.ReferenceHashMap; +import org.apache.openjpa.lib.util.ReferenceMap; +import org.apache.openjpa.lib.util.SizedMap; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; +import org.apache.openjpa.lib.util.concurrent.SizedConcurrentHashMap; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * Fixed-size map that has ability to pin/unpin entries and move overflow to + * a backing soft map. + * + * @author Patrick Linskey + * @author Abe White + */ +public class CacheMap + implements Map { + + /** + * The map for non-expired and non-pinned references. + */ + protected final SizedMap cacheMap; + + /** + * The map for expired references. + */ + protected final SizedMap softMap; + + /** + * The set of objects pinned into the cache. + */ + protected final Map pinnedMap; + + // number of pinned values (not including keys not mapped to values) + private int _pinnedSize = 0; + + private final ReentrantLock _writeLock = new ReentrantLock(); + private final ReentrantLock _readLock; + + /** + * Create a non-LRU (and therefore highly concurrent) cache map with a + * size of 1000. + */ + public CacheMap() { + this(false, 1000); + } + + /** + * Create a cache map with the given properties. + */ + public CacheMap(boolean lru, int max) { + this(lru, max, max / 2, .75F); + } + + /** + * @deprecated use {@link CacheMap#CacheMap(boolean, int, int, float, int)} + * instead. + */ + public CacheMap(boolean lru, int max, int size, float load) { + this(lru, max, size, load, 16); + } + + /** + * Create a cache map with the given properties. + * + * @since 1.1.0 + */ + public CacheMap(boolean lru, int max, int size, float load, + int concurrencyLevel) { + if (size < 0) + size = 500; + + softMap = new ConcurrentReferenceHashMap(ReferenceMap.HARD, + ReferenceMap.SOFT, size, load) { + public void overflowRemoved(Object key, Object value) { + softMapOverflowRemoved(key, value); + } + + public void valueExpired(Object key) { + softMapValueExpired(key); + } + }; + pinnedMap = new ConcurrentHashMap(); + + if (!lru) { + cacheMap = new SizedConcurrentHashMap(size, load, concurrencyLevel){ + public void overflowRemoved(Object key, Object value) { + cacheMapOverflowRemoved(key, value); + } + }; + _readLock = null; + } else { + cacheMap = new LRUMap(size, load) { + public void overflowRemoved(Object key, Object value) { + cacheMapOverflowRemoved(key, value); + } + }; + _readLock = _writeLock; + } + if (max < 0) + max = Integer.MAX_VALUE; + cacheMap.setMaxSize(max); + } + + /** + * Called from {@link SizedMap#overflowRemoved} in the cache map. + */ + protected void cacheMapOverflowRemoved(Object key, Object value) { + if (softMap.size() < softMap.getMaxSize()) + put(softMap, key, value); + else + entryRemoved(key, value, true); + } + + /** + * Called from {@link SizedMap#overflowRemoved} in the soft map. + */ + protected void softMapOverflowRemoved(Object key, Object value) { + entryRemoved(key, value, true); + } + + /** + * Called when a value expires from the soft map. + */ + protected void softMapValueExpired(Object key) { + entryRemoved(key, null, true); + } + + /** + * Put the given entry into the given map. Allows subclasses to + * take additional actions. + */ + protected Object put(Map map, Object key, Object value) { + return map.put(key, value); + } + + /** + * Remove the given key from the given map. Allows subclasses to + * take additional actions. + */ + protected Object remove(Map map, Object key) { + return map.remove(key); + } + + /** + * Acquire read lock. + */ + public void readLock() { + if (_readLock != null) + _readLock.lock(); + } + + /** + * Release read lock. + */ + public void readUnlock() { + if (_readLock != null) + _readLock.unlock(); + } + + /** + * Acquire write lock. + */ + public void writeLock() { + _writeLock.lock(); + } + + /** + * Release write lock. + */ + public void writeUnlock() { + _writeLock.unlock(); + } + + /** + * Whether this cache map uses LRU eviction. + */ + public boolean isLRU() { + return _readLock != null; + } + + /** + * The maximum number of hard references to maintain, or -1 for no limit. + */ + public void setCacheSize(int size) { + writeLock(); + try { + cacheMap.setMaxSize((size < 0) ? Integer.MAX_VALUE : size); + } finally { + writeUnlock(); + } + } + + /** + * The maximum number of hard references to maintain, or -1 for no limit. + */ + public int getCacheSize() { + int max = cacheMap.getMaxSize(); + return (max == Integer.MAX_VALUE) ? -1 : max; + } + + /** + * The maximum number of soft references to maintain, or -1 for no limit. + */ + public void setSoftReferenceSize(int size) { + writeLock(); + try { + softMap.setMaxSize((size < 0) ? Integer.MAX_VALUE : size); + } finally { + writeUnlock(); + } + } + + /** + * The maximum number of soft references to maintain, or -1 for no limit. + */ + public int getSoftReferenceSize() { + int max = softMap.getMaxSize(); + return (max == Integer.MAX_VALUE) ? -1 : max; + } + + /** + * The keys pinned into the map. + */ + public Set getPinnedKeys() { + readLock(); + try { + return Collections.unmodifiableSet(pinnedMap.keySet()); + } finally { + readUnlock(); + } + } + + /** + * Locks the given key and its value into the map. Objects pinned into + * the map are not counted towards the maximum cache size, and are never + * evicted implicitly. You may pin keys for which no value is in the map. + * + * @return true if the givne key's value was pinned; false if no value + * for the given key is cached + */ + public boolean pin(Object key) { + writeLock(); + try { + // if we don't have a pinned map we need to create one; else if the + // pinned map already contains the key, nothing to do + if (pinnedMap.containsKey(key)) + return pinnedMap.get(key) != null; + + // check other maps for key + Object val = remove(cacheMap, key); + if (val == null) + val = remove(softMap, key); + + // pin key + put(pinnedMap, key, val); + if (val != null) { + _pinnedSize++; + return true; + } + return false; + } finally { + writeUnlock(); + } + } + + /** + * Undo a pinning. + */ + public boolean unpin(Object key) { + writeLock(); + try { + Object val = remove(pinnedMap, key); + if (val != null) { + // put back into unpinned cache + put(key, val); + _pinnedSize--; + return true; + } + return false; + } finally { + writeUnlock(); + } + } + + /** + * Invoked when a key-value pair is evicted from this data + * structure. This is invoked with expired set to + * true when an object is dropped because of space + * requirements or through garbage collection of soft references. + * It is invoked with expired set to false + * when an object is explicitly removed via the {@link #remove} or + * {@link #clear} methods. This may be invoked more than once for a + * given entry. + * + * @param value may be null if the value was a soft reference that has + * been GCd + * @since 0.2.5.0 + */ + protected void entryRemoved(Object key, Object value, boolean expired) { + } + + /** + * Invoked when an entry is added to the cache. This may be invoked + * more than once for an entry. + */ + protected void entryAdded(Object key, Object value) { + } + + public Object get(Object key) { + readLock(); + try { + Object val = pinnedMap.get(key); + if (val != null) + return val; + + val = cacheMap.get(key); + if (val == null) { + // if we find the key in the soft map, move it back into + // the primary map + val = softMap.get(key); + if (val != null) + put(key, val); + } + return val; + } finally { + readUnlock(); + } + } + + public Object put(Object key, Object value) { + writeLock(); + try { + // if the key is pinned, just interact directly with the pinned map + Object val; + if (pinnedMap.containsKey(key)) { + val = put(pinnedMap, key, value); + if (val == null) { + _pinnedSize++; + entryAdded(key, value); + } else { + entryRemoved(key, val, false); + entryAdded(key, value); + } + return val; + } + + // if no hard refs, don't put anything + if (cacheMap.getMaxSize() == 0) + return null; + + // otherwise, put the value into the map and clear it from the + // soft map + val = put(cacheMap, key, value); + if (val == null) { + val = remove(softMap, key); + if (val == null) + entryAdded(key, value); + else { + entryRemoved(key, val, false); + entryAdded(key, value); + } + } else { + entryRemoved(key, val, false); + entryAdded(key, value); + } + return val; + } finally { + writeUnlock(); + } + } + + public void putAll(Map map) { + Map.Entry entry; + for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) { + entry = (Map.Entry) itr.next(); + put(entry.getKey(), entry.getValue()); + } + } + + /** + * If key is pinned into the cache, the pin is + * cleared and the object is removed. + */ + public Object remove(Object key) { + writeLock(); + try { + // if the key is pinned, just interact directly with the + // pinned map + Object val; + if (pinnedMap.containsKey(key)) { + // re-put with null value; we still want key pinned + val = put(pinnedMap, key, null); + if (val != null) { + _pinnedSize--; + entryRemoved(key, val, false); + } + return val; + } + + val = remove(cacheMap, key); + if (val == null) + val = softMap.remove(key); + if (val != null) + entryRemoved(key, val, false); + + return val; + } finally { + writeUnlock(); + } + } + + /** + * Removes pinned objects as well as unpinned ones. + */ + public void clear() { + writeLock(); + try { + notifyEntryRemovals(pinnedMap.entrySet()); + pinnedMap.clear(); + _pinnedSize = 0; + + notifyEntryRemovals(cacheMap.entrySet()); + cacheMap.clear(); + + notifyEntryRemovals(softMap.entrySet()); + softMap.clear(); + } finally { + writeUnlock(); + } + } + + private void notifyEntryRemovals(Set set) { + Map.Entry entry; + for (Iterator itr = set.iterator(); itr.hasNext();) { + entry = (Map.Entry) itr.next(); + if (entry.getValue() != null) + entryRemoved(entry.getKey(), entry.getValue(), false); + } + } + + public int size() { + readLock(); + try { + return _pinnedSize + cacheMap.size() + softMap.size(); + } finally { + readUnlock(); + } + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean containsKey(Object key) { + readLock(); + try { + return pinnedMap.get(key) != null + || cacheMap.containsKey(key) + || softMap.containsKey(key); + } finally { + readUnlock(); + } + } + + public boolean containsValue(Object val) { + readLock(); + try { + return pinnedMap.containsValue(val) + || cacheMap.containsValue(val) + || softMap.containsValue(val); + } finally { + readUnlock(); + } + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new ValueCollection(); + } + + public Set entrySet() { + return new EntrySet(); + } + + public String toString() { + readLock(); + try { + return "CacheMap:" + cacheMap.toString() + "::" + + softMap.toString(); + } finally { + readUnlock(); + } + } + + /** + * View of the entry set. + */ + private class EntrySet + extends AbstractSet { + + public int size() { + return CacheMap.this.size(); + } + + public boolean add(Object o) { + Map.Entry entry = (Map.Entry) o; + put(entry.getKey(), entry.getValue()); + return true; + } + + public Iterator iterator() { + return new EntryIterator(EntryIterator.ENTRY); + } + } + + /** + * View of the key set. + */ + private class KeySet + extends AbstractSet { + + public int size() { + return CacheMap.this.size(); + } + + public Iterator iterator() { + return new EntryIterator(EntryIterator.KEY); + } + } + + /** + * View of the value collection. + */ + private class ValueCollection + extends AbstractCollection { + + public int size() { + return CacheMap.this.size(); + } + + public Iterator iterator() { + return new EntryIterator(EntryIterator.VALUE); + } + } + + /** + * Iterator over all entries. + */ + private class EntryIterator + implements Iterator, Predicate { + + public static final int ENTRY = 0; + public static final int KEY = 1; + public static final int VALUE = 2; + + private final IteratorChain _itr = new IteratorChain(); + private final int _type; + + public EntryIterator(int type) { + _type = type; + _itr.addIterator(new FilterIterator(getView(pinnedMap), this)); + _itr.addIterator(getView(cacheMap)); + _itr.addIterator(getView(softMap)); + } + + /** + * Return an iterator over the appropriate view of the given map. + */ + private Iterator getView(Map m) { + if (m == null) + return null; + + switch (_type) { + case KEY: + return m.keySet().iterator(); + case VALUE: + return m.values().iterator(); + default: + return m.entrySet().iterator(); + } + } + + public boolean hasNext() { + return _itr.hasNext(); + } + + public Object next() { + return _itr.next(); + } + + public void remove() { + _itr.remove(); + } + + public boolean evaluate(Object obj) { + switch (_type) { + case ENTRY: + return ((Map.Entry) obj).getValue() != null; + case VALUE: + return obj != null; + default: + return true; + } + } + } +} + Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java?rev=640685&r1=640684&r2=640685&view=diff ============================================================================== --- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java (original) +++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java Mon Mar 24 20:37:56 2008 @@ -1,322 +1,322 @@ -/* - * 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.openjpa.util; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; - -import org.apache.openjpa.enhance.PersistenceCapable; -import org.apache.openjpa.enhance.PCRegistry; -import org.apache.openjpa.enhance.StateManager; -import org.apache.openjpa.enhance.ManagedInstanceProvider; -import org.apache.openjpa.enhance.ReflectingPersistenceCapable; -import org.apache.openjpa.enhance.RuntimeUnenhancedClasssesModes; -import org.apache.openjpa.kernel.FetchConfiguration; -import org.apache.openjpa.kernel.LockManager; -import org.apache.openjpa.kernel.OpenJPAStateManager; -import org.apache.openjpa.kernel.PCState; -import org.apache.openjpa.kernel.StoreContext; -import org.apache.openjpa.kernel.StoreManager; -import org.apache.openjpa.lib.util.Closeable; -import org.apache.openjpa.lib.util.ReferenceMap; -import org.apache.openjpa.lib.util.UUIDGenerator; -import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; -import org.apache.openjpa.meta.ClassMetaData; -import org.apache.openjpa.meta.FieldMetaData; -import org.apache.openjpa.meta.JavaTypes; -import org.apache.openjpa.meta.SequenceMetaData; -import org.apache.openjpa.meta.ValueStrategies; -import org.apache.openjpa.conf.OpenJPAConfiguration; - -/** - * Helper for OpenJPA back-ends. - * - * @since 0.3.0 - * @author Abe White - * @nojavadoc - */ -public class ImplHelper { - - // Cache for from/to type assignments - private static final Map _assignableTypes = - new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); - - // map of all new unenhanced instances active in this classloader - public static final Map _unenhancedInstanceMap = - new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD) { - - protected boolean eq(Object x, Object y) { - // the Entries in ConcurrentReferenceHashMap delegate back to - // eq() in their equals() impls - if (x instanceof Map.Entry) - return super.eq(x, y); - else - return x == y; - } - - protected int hc(Object o) { - // the Entries in ConcurrentReferenceHashMap delegate back to - // hc() in their hashCode() impls - if (o instanceof Map.Entry) - return super.hc(o); - else - return System.identityHashCode(o); - } - }; - - /** - * Helper for store manager implementations. This method simply delegates - * to the proper singular method for each state manager. - * - * @see StoreManager#loadAll - * @since 0.4.0 - */ - public static Collection loadAll(Collection sms, StoreManager store, - PCState state, int load, FetchConfiguration fetch, Object context) { - Collection failed = null; - OpenJPAStateManager sm; - LockManager lm; - for (Iterator itr = sms.iterator(); itr.hasNext();) { - sm = (OpenJPAStateManager) itr.next(); - if (sm.getManagedInstance() == null) { - if (!store.initialize(sm, state, fetch, context)) - failed = addFailedId(sm, failed); - } else if (load != StoreManager.FORCE_LOAD_NONE - || sm.getPCState() == PCState.HOLLOW) { - lm = sm.getContext().getLockManager(); - if (!store.load(sm, sm.getUnloaded(fetch), fetch, - lm.getLockLevel(sm), context)) - failed = addFailedId(sm, failed); - } else if (!store.exists(sm, context)) - failed = addFailedId(sm, failed); - } - return (failed == null) ? Collections.EMPTY_LIST : failed; - } - - /** - * Add identity of given instance to collection. - */ - private static Collection addFailedId(OpenJPAStateManager sm, - Collection failed) { - if (failed == null) - failed = new ArrayList(); - failed.add(sm.getId()); - return failed; - } - - /** - * Generate a value for the given metadata, or return null. Generates - * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, - * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} - */ - public static Object generateIdentityValue(StoreContext ctx, - ClassMetaData meta, int typeCode) { - return generateValue(ctx, meta, null, typeCode); - } - - /** - * Generate a value for the given metadata, or return null. Generates - * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, - * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} - */ - public static Object generateFieldValue(StoreContext ctx, - FieldMetaData fmd) { - return generateValue(ctx, fmd.getDefiningMetaData(), fmd, - fmd.getDeclaredTypeCode()); - } - - /** - * Generate a value for the given metadaa. - */ - private static Object generateValue(StoreContext ctx, - ClassMetaData meta, FieldMetaData fmd, int typeCode) { - int strategy = (fmd == null) ? meta.getIdentityStrategy() - : fmd.getValueStrategy(); - switch (strategy) { - case ValueStrategies.SEQUENCE: - SequenceMetaData smd = (fmd == null) - ? meta.getIdentitySequenceMetaData() - : fmd.getValueSequenceMetaData(); - return JavaTypes.convert(smd.getInstance(ctx.getClassLoader()). - next(ctx, meta), typeCode); - case ValueStrategies.UUID_STRING: - return UUIDGenerator.nextString(); - case ValueStrategies.UUID_HEX: - return UUIDGenerator.nextHex(); - default: - return null; - } - } - - /** - * Returns the fields of the state that require an update. - * - * @param sm the state to check - * @return the BitSet of fields that need update, or null if none - */ - public static BitSet getUpdateFields(OpenJPAStateManager sm) { - if ((sm.getPCState() == PCState.PDIRTY - && (!sm.isFlushed() || sm.isFlushedDirty())) - || (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) { - BitSet dirty = sm.getDirty(); - if (sm.isFlushed()) { - dirty = (BitSet) dirty.clone(); - dirty.andNot(sm.getFlushed()); - } - if (dirty.length() > 0) - return dirty; - } - return null; - } - - /** - * Close the given resource. The resource can be an extent iterator, - * query result, large result set relation, or any closeable OpenJPA - * component. - */ - public static void close(Object o) { - try { - if (o instanceof Closeable) - ((Closeable) o).close(); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new GeneralException(e); - } - } - - /** - * Returns true if the specified class is a type that can be managed by - * OpenJPA. - * - * @param type the class to test - * @return true if the class is manageable. - * - * @since 1.0.0 - */ - public static boolean isManagedType(OpenJPAConfiguration conf, Class type) { - return (PersistenceCapable.class.isAssignableFrom(type) - || (type != null - && (conf == null || conf.getRuntimeUnenhancedClassesConstant() - == RuntimeUnenhancedClasssesModes.SUPPORTED) - && PCRegistry.isRegistered(type))); - } - - /** - * Returns true if the specified instance is manageable. - * - * @param instance the object to check - * @return true if the instance is a persistent type, false otherwise - */ - public static boolean isManageable(Object instance) { - return instance instanceof PersistenceCapable - || instance != null && PCRegistry.isRegistered(instance.getClass()); - } - - /** - * Returns true if the referenced "to" class is assignable to the "from" - * class. This helper method utilizes a cache to help avoid the overhead - * of the Class.isAssignableFrom() method. - * - * @param from target class instance to be checked for assignability - * @param to second class instance to be checked for assignability - * @return true if the "to" class is assignable to the "from" class - */ - public static boolean isAssignable(Class from, Class to) { - if (from == null || to == null) - return false; - - Boolean isAssignable = null; - Map assignableTo = (Map) _assignableTypes.get(from); - if (assignableTo == null) { // "to" cache doesn't exist, so create it... - assignableTo = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, - ReferenceMap.HARD); - _assignableTypes.put(from, assignableTo); - } else { // "to" cache exists... - isAssignable = (Boolean) assignableTo.get(to); - } - - if (isAssignable == null) {// we don't have a record of this pair... - isAssignable = Boolean.valueOf(from.isAssignableFrom(to)); - assignableTo.put(to, isAssignable); - } - - return isAssignable.booleanValue(); - } - - /** - * @return the persistence-capable instance responsible for managing - * o, or null if o is not manageable. - * @since 1.0.0 - */ - public static PersistenceCapable toPersistenceCapable(Object o, Object ctx){ - if (o instanceof PersistenceCapable) - return (PersistenceCapable) o; - - OpenJPAConfiguration conf = null; - if (ctx instanceof OpenJPAConfiguration) - conf = (OpenJPAConfiguration) ctx; - else if (ctx instanceof StateManager - && ((StateManager) ctx).getGenericContext() instanceof StoreContext) - conf = ((StoreContext) ((StateManager) ctx).getGenericContext()) - .getConfiguration(); - - if (!isManageable(o)) - return null; - - // if we had a putIfAbsent() method, we wouldn't need to sync here - synchronized (o) { - PersistenceCapable pc = (PersistenceCapable) - _unenhancedInstanceMap.get(o); - - if (pc != null) - return pc; - - // if we don't have a conf passed in, then we can't create a new - // ReflectingPC; this will only be the case when invoked from a - // context outside of OpenJPA. - if (conf == null) - return null; - - pc = new ReflectingPersistenceCapable(o, conf); - _unenhancedInstanceMap.put(o, pc); - return pc; - } - } - - public static void registerPersistenceCapable( - ReflectingPersistenceCapable pc) { - _unenhancedInstanceMap.put(pc.getManagedInstance(), pc); - } - - /** - * @return the user-visible representation of o. - * @since 1.0.0 - */ - public static Object getManagedInstance(Object o) { - if (o instanceof ManagedInstanceProvider) - return ((ManagedInstanceProvider) o).getManagedInstance(); - else - return o; - } -} +/* + * 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.openjpa.util; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import org.apache.openjpa.enhance.PersistenceCapable; +import org.apache.openjpa.enhance.PCRegistry; +import org.apache.openjpa.enhance.StateManager; +import org.apache.openjpa.enhance.ManagedInstanceProvider; +import org.apache.openjpa.enhance.ReflectingPersistenceCapable; +import org.apache.openjpa.enhance.RuntimeUnenhancedClasssesModes; +import org.apache.openjpa.kernel.FetchConfiguration; +import org.apache.openjpa.kernel.LockManager; +import org.apache.openjpa.kernel.OpenJPAStateManager; +import org.apache.openjpa.kernel.PCState; +import org.apache.openjpa.kernel.StoreContext; +import org.apache.openjpa.kernel.StoreManager; +import org.apache.openjpa.lib.util.Closeable; +import org.apache.openjpa.lib.util.ReferenceMap; +import org.apache.openjpa.lib.util.UUIDGenerator; +import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; +import org.apache.openjpa.meta.ClassMetaData; +import org.apache.openjpa.meta.FieldMetaData; +import org.apache.openjpa.meta.JavaTypes; +import org.apache.openjpa.meta.SequenceMetaData; +import org.apache.openjpa.meta.ValueStrategies; +import org.apache.openjpa.conf.OpenJPAConfiguration; + +/** + * Helper for OpenJPA back-ends. + * + * @since 0.3.0 + * @author Abe White + * @nojavadoc + */ +public class ImplHelper { + + // Cache for from/to type assignments + private static final Map _assignableTypes = + new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); + + // map of all new unenhanced instances active in this classloader + public static final Map _unenhancedInstanceMap = + new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD) { + + protected boolean eq(Object x, Object y) { + // the Entries in ConcurrentReferenceHashMap delegate back to + // eq() in their equals() impls + if (x instanceof Map.Entry) + return super.eq(x, y); + else + return x == y; + } + + protected int hc(Object o) { + // the Entries in ConcurrentReferenceHashMap delegate back to + // hc() in their hashCode() impls + if (o instanceof Map.Entry) + return super.hc(o); + else + return System.identityHashCode(o); + } + }; + + /** + * Helper for store manager implementations. This method simply delegates + * to the proper singular method for each state manager. + * + * @see StoreManager#loadAll + * @since 0.4.0 + */ + public static Collection loadAll(Collection sms, StoreManager store, + PCState state, int load, FetchConfiguration fetch, Object context) { + Collection failed = null; + OpenJPAStateManager sm; + LockManager lm; + for (Iterator itr = sms.iterator(); itr.hasNext();) { + sm = (OpenJPAStateManager) itr.next(); + if (sm.getManagedInstance() == null) { + if (!store.initialize(sm, state, fetch, context)) + failed = addFailedId(sm, failed); + } else if (load != StoreManager.FORCE_LOAD_NONE + || sm.getPCState() == PCState.HOLLOW) { + lm = sm.getContext().getLockManager(); + if (!store.load(sm, sm.getUnloaded(fetch), fetch, + lm.getLockLevel(sm), context)) + failed = addFailedId(sm, failed); + } else if (!store.exists(sm, context)) + failed = addFailedId(sm, failed); + } + return (failed == null) ? Collections.EMPTY_LIST : failed; + } + + /** + * Add identity of given instance to collection. + */ + private static Collection addFailedId(OpenJPAStateManager sm, + Collection failed) { + if (failed == null) + failed = new ArrayList(); + failed.add(sm.getId()); + return failed; + } + + /** + * Generate a value for the given metadata, or return null. Generates + * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, + * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} + */ + public static Object generateIdentityValue(StoreContext ctx, + ClassMetaData meta, int typeCode) { + return generateValue(ctx, meta, null, typeCode); + } + + /** + * Generate a value for the given metadata, or return null. Generates + * values for hte following strategies: {@link ValueStrategies#SEQUENCE}, + * {@link ValueStrategies#UUID_STRING}, {@link ValueStrategies#UUID_HEX} + */ + public static Object generateFieldValue(StoreContext ctx, + FieldMetaData fmd) { + return generateValue(ctx, fmd.getDefiningMetaData(), fmd, + fmd.getDeclaredTypeCode()); + } + + /** + * Generate a value for the given metadaa. + */ + private static Object generateValue(StoreContext ctx, + ClassMetaData meta, FieldMetaData fmd, int typeCode) { + int strategy = (fmd == null) ? meta.getIdentityStrategy() + : fmd.getValueStrategy(); + switch (strategy) { + case ValueStrategies.SEQUENCE: + SequenceMetaData smd = (fmd == null) + ? meta.getIdentitySequenceMetaData() + : fmd.getValueSequenceMetaData(); + return JavaTypes.convert(smd.getInstance(ctx.getClassLoader()). + next(ctx, meta), typeCode); + case ValueStrategies.UUID_STRING: + return UUIDGenerator.nextString(); + case ValueStrategies.UUID_HEX: + return UUIDGenerator.nextHex(); + default: + return null; + } + } + + /** + * Returns the fields of the state that require an update. + * + * @param sm the state to check + * @return the BitSet of fields that need update, or null if none + */ + public static BitSet getUpdateFields(OpenJPAStateManager sm) { + if ((sm.getPCState() == PCState.PDIRTY + && (!sm.isFlushed() || sm.isFlushedDirty())) + || (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) { + BitSet dirty = sm.getDirty(); + if (sm.isFlushed()) { + dirty = (BitSet) dirty.clone(); + dirty.andNot(sm.getFlushed()); + } + if (dirty.length() > 0) + return dirty; + } + return null; + } + + /** + * Close the given resource. The resource can be an extent iterator, + * query result, large result set relation, or any closeable OpenJPA + * component. + */ + public static void close(Object o) { + try { + if (o instanceof Closeable) + ((Closeable) o).close(); + } catch (RuntimeException re) { + throw re; + } catch (Exception e) { + throw new GeneralException(e); + } + } + + /** + * Returns true if the specified class is a type that can be managed by + * OpenJPA. + * + * @param type the class to test + * @return true if the class is manageable. + * + * @since 1.0.0 + */ + public static boolean isManagedType(OpenJPAConfiguration conf, Class type) { + return (PersistenceCapable.class.isAssignableFrom(type) + || (type != null + && (conf == null || conf.getRuntimeUnenhancedClassesConstant() + == RuntimeUnenhancedClasssesModes.SUPPORTED) + && PCRegistry.isRegistered(type))); + } + + /** + * Returns true if the specified instance is manageable. + * + * @param instance the object to check + * @return true if the instance is a persistent type, false otherwise + */ + public static boolean isManageable(Object instance) { + return instance instanceof PersistenceCapable + || instance != null && PCRegistry.isRegistered(instance.getClass()); + } + + /** + * Returns true if the referenced "to" class is assignable to the "from" + * class. This helper method utilizes a cache to help avoid the overhead + * of the Class.isAssignableFrom() method. + * + * @param from target class instance to be checked for assignability + * @param to second class instance to be checked for assignability + * @return true if the "to" class is assignable to the "from" class + */ + public static boolean isAssignable(Class from, Class to) { + if (from == null || to == null) + return false; + + Boolean isAssignable = null; + Map assignableTo = (Map) _assignableTypes.get(from); + if (assignableTo == null) { // "to" cache doesn't exist, so create it... + assignableTo = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, + ReferenceMap.HARD); + _assignableTypes.put(from, assignableTo); + } else { // "to" cache exists... + isAssignable = (Boolean) assignableTo.get(to); + } + + if (isAssignable == null) {// we don't have a record of this pair... + isAssignable = Boolean.valueOf(from.isAssignableFrom(to)); + assignableTo.put(to, isAssignable); + } + + return isAssignable.booleanValue(); + } + + /** + * @return the persistence-capable instance responsible for managing + * o, or null if o is not manageable. + * @since 1.0.0 + */ + public static PersistenceCapable toPersistenceCapable(Object o, Object ctx){ + if (o instanceof PersistenceCapable) + return (PersistenceCapable) o; + + OpenJPAConfiguration conf = null; + if (ctx instanceof OpenJPAConfiguration) + conf = (OpenJPAConfiguration) ctx; + else if (ctx instanceof StateManager + && ((StateManager) ctx).getGenericContext() instanceof StoreContext) + conf = ((StoreContext) ((StateManager) ctx).getGenericContext()) + .getConfiguration(); + + if (!isManageable(o)) + return null; + + // if we had a putIfAbsent() method, we wouldn't need to sync here + synchronized (o) { + PersistenceCapable pc = (PersistenceCapable) + _unenhancedInstanceMap.get(o); + + if (pc != null) + return pc; + + // if we don't have a conf passed in, then we can't create a new + // ReflectingPC; this will only be the case when invoked from a + // context outside of OpenJPA. + if (conf == null) + return null; + + pc = new ReflectingPersistenceCapable(o, conf); + _unenhancedInstanceMap.put(o, pc); + return pc; + } + } + + public static void registerPersistenceCapable( + ReflectingPersistenceCapable pc) { + _unenhancedInstanceMap.put(pc.getManagedInstance(), pc); + } + + /** + * @return the user-visible representation of o. + * @since 1.0.0 + */ + public static Object getManagedInstance(Object o) { + if (o instanceof ManagedInstanceProvider) + return ((ManagedInstanceProvider) o).getManagedInstance(); + else + return o; + } +} Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java?rev=640685&r1=640684&r2=640685&view=diff ============================================================================== --- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java (original) +++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/OpenJPAId.java Mon Mar 24 20:37:56 2008 @@ -1,149 +1,149 @@ -/* - * 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.openjpa.util; - -import java.io.Serializable; - -import org.apache.openjpa.lib.util.ReferenceMap; -import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; - -/** - * Identity class extended by builtin OpenJPA identity objects. - * - * @author Steve Kim - */ -public abstract class OpenJPAId - implements Comparable, Serializable { - - // cache the types' generated hashcodes - private static ConcurrentReferenceHashMap _typeCache = - new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); - - protected Class type; - protected boolean subs = true; - - // type has his based on the least-derived non-object class so that - // user-given ids with non-exact types match ids with exact types - private transient int _typeHash = 0; - - protected OpenJPAId() { - } - - protected OpenJPAId(Class type) { - this.type = type; - } - - protected OpenJPAId(Class type, boolean subs) { - this.type = type; - this.subs = subs; - } - - /** - * Return the persitent class which this id instance represents. - */ - public Class getType() { - return type; - } - - /** - * Whether this oid might be for a subclass of the given type. - * Defaults to true. - */ - public boolean hasSubclasses() { - return subs; - } - - /** - * Set the exact type of the described instance once it is known. - */ - public void setManagedInstanceType(Class type) { - this.type = type; - this.subs = false; - } - - /** - * Set the exact type of the described instance once it is known. - */ - public void setManagedInstanceType(Class type, boolean subs) { - this.type = type; - this.subs = subs; - } - - /** - * Return the identity value as an object. - */ - public abstract Object getIdObject(); - - /** - * Return the id's hash code. - */ - protected abstract int idHash(); - - /** - * Compare the id to the id of the given instance. - */ - protected abstract boolean idEquals(OpenJPAId other); - - /** - * Generate the hashcode for this Id. Cache the type's generated hashcode - * so that it doesn't have to be generated each time. - */ - public int hashCode() { - if (_typeHash == 0) { - Integer typeHashInt = (Integer) _typeCache.get(type); - if (typeHashInt == null) { - Class base = type; - Class superclass = base.getSuperclass(); - while (superclass != null && superclass != Object.class) { - base = base.getSuperclass(); - superclass = base.getSuperclass(); - } - _typeHash = base.hashCode(); - _typeCache.put(type, new Integer(_typeHash)); - } else { - _typeHash = typeHashInt.intValue(); - } - } - return _typeHash ^ idHash(); - } - - public boolean equals(Object o) { - if (o == this) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - OpenJPAId id = (OpenJPAId) o; - return idEquals(id) && (id.type.isAssignableFrom(type) - || (subs && type.isAssignableFrom(id.type))); - } - - public String toString() { - return type.getName() + "-" + getIdObject(); - } - - public int compareTo(Object other) { - if (other == this) - return 0; - if (other == null) - return 1; - return ((Comparable) getIdObject()).compareTo(((OpenJPAId) other). - getIdObject ()); - } -} +/* + * 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.openjpa.util; + +import java.io.Serializable; + +import org.apache.openjpa.lib.util.ReferenceMap; +import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashMap; + +/** + * Identity class extended by builtin OpenJPA identity objects. + * + * @author Steve Kim + */ +public abstract class OpenJPAId + implements Comparable, Serializable { + + // cache the types' generated hashcodes + private static ConcurrentReferenceHashMap _typeCache = + new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD); + + protected Class type; + protected boolean subs = true; + + // type has his based on the least-derived non-object class so that + // user-given ids with non-exact types match ids with exact types + private transient int _typeHash = 0; + + protected OpenJPAId() { + } + + protected OpenJPAId(Class type) { + this.type = type; + } + + protected OpenJPAId(Class type, boolean subs) { + this.type = type; + this.subs = subs; + } + + /** + * Return the persitent class which this id instance represents. + */ + public Class getType() { + return type; + } + + /** + * Whether this oid might be for a subclass of the given type. + * Defaults to true. + */ + public boolean hasSubclasses() { + return subs; + } + + /** + * Set the exact type of the described instance once it is known. + */ + public void setManagedInstanceType(Class type) { + this.type = type; + this.subs = false; + } + + /** + * Set the exact type of the described instance once it is known. + */ + public void setManagedInstanceType(Class type, boolean subs) { + this.type = type; + this.subs = subs; + } + + /** + * Return the identity value as an object. + */ + public abstract Object getIdObject(); + + /** + * Return the id's hash code. + */ + protected abstract int idHash(); + + /** + * Compare the id to the id of the given instance. + */ + protected abstract boolean idEquals(OpenJPAId other); + + /** + * Generate the hashcode for this Id. Cache the type's generated hashcode + * so that it doesn't have to be generated each time. + */ + public int hashCode() { + if (_typeHash == 0) { + Integer typeHashInt = (Integer) _typeCache.get(type); + if (typeHashInt == null) { + Class base = type; + Class superclass = base.getSuperclass(); + while (superclass != null && superclass != Object.class) { + base = base.getSuperclass(); + superclass = base.getSuperclass(); + } + _typeHash = base.hashCode(); + _typeCache.put(type, new Integer(_typeHash)); + } else { + _typeHash = typeHashInt.intValue(); + } + } + return _typeHash ^ idHash(); + } + + public boolean equals(Object o) { + if (o == this) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + OpenJPAId id = (OpenJPAId) o; + return idEquals(id) && (id.type.isAssignableFrom(type) + || (subs && type.isAssignableFrom(id.type))); + } + + public String toString() { + return type.getName() + "-" + getIdObject(); + } + + public int compareTo(Object other) { + if (other == this) + return 0; + if (other == null) + return 1; + return ((Comparable) getIdObject()).compareTo(((OpenJPAId) other). + getIdObject ()); + } +}