001: package org.apache.ojb.broker.metadata.fieldaccess;
002:
003: /* Copyright 2003-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.util.Map;
019:
020: import org.apache.commons.collections.map.ReferenceIdentityMap;
021: import org.apache.ojb.broker.metadata.MetadataException;
022:
023: /**
024: * This class handle an anonymous persistent fiels for 1-1 association,
025: * and ojbConcreteClass
026: * @author Houar TINE
027: * @version $Id: AnonymousPersistentField.java,v 1.13.2.2 2005/12/21 22:26:41 tomdz Exp $
028: */
029: public class AnonymousPersistentField implements PersistentField {
030: private static final long serialVersionUID = 3989863424358352941L;
031:
032: private transient Map fkCache;
033: private String fieldname;
034:
035: public AnonymousPersistentField(String fieldname) {
036: this .fieldname = fieldname;
037: }
038:
039: public synchronized void set(Object obj, Object value)
040: throws MetadataException {
041: putToFieldCache(obj, value);
042: }
043:
044: public synchronized Object get(Object anObject)
045: throws MetadataException {
046: return getFromFieldCache(anObject);
047: }
048:
049: /*
050: Use ReferenceIdentityMap (with weak key and hard value setting) instead of
051: WeakHashMap to hold anonymous field values. Here is an snip of the mail from Andy Malakov:
052: <snip>
053: I found that usage of database identity in Java produces quite interesting problem in OJB:
054: In my application all persistent Java objects use database identity instead of Java reference identity
055: (i.e. Persistable.equals() is redefined so that two persistent objects are the same if they have the same
056: primary key and top-level class).
057:
058: In OJB, for each field declared in repository there is dedicated instance of AnonymousPersistentField that stores
059: object-to-field-value mapping in WeakHashMap (in fkCache attribute). Despite usage of cache
060: (ObjectCachePerBrokerImpl in my case) it is possible that identical DB objects will end up as different
061: Java objects during retrieval of complex objects.
062:
063: Now imagine what happens when two identical instances are retrieved:
064: 1)
065: When first instance is retrieved it stores its foreign keys in AnonymousPersistentField.fkCache under instance's
066: identity. (happens in RowReaderDefaultImpl.buildWithReflection())
067: 2)
068: When second object is retrieved and stored in fkCache, first instance is probably still cached
069: [WeakHashMap entries are cleaned up only during GC]. Since keys are identical WeakHashMap only updates entry
070: value and DOES NOT update entry key.
071: 3)
072: If Full GC happens after that moment it will dispose fcCache entry if the FIRST reference becomes
073: soft-referenced only.
074: </snip>
075: */
076: protected void putToFieldCache(Object key, Object value) {
077: if (key != null) {
078: if (fkCache == null) {
079: fkCache = new ReferenceIdentityMap(
080: ReferenceIdentityMap.WEAK,
081: ReferenceIdentityMap.HARD, true);
082: }
083: if (value != null)
084: fkCache.put(key, value);
085: else
086: fkCache.remove(key);
087: }
088: }
089:
090: protected Object getFromFieldCache(Object key) {
091: return (key != null && fkCache != null) ? fkCache.get(key)
092: : null;
093: }
094:
095: /**
096: * Always returns <tt>null</tt>.
097: * @see PersistentField#getDeclaringClass()
098: */
099: public Class getDeclaringClass() {
100: return null;
101: }
102:
103: /**
104: * @see PersistentField#getName()
105: */
106: public String getName() {
107: return fieldname;
108: }
109:
110: /**
111: * Always returns <tt>null</tt>.
112: * @see PersistentField#getType()
113: */
114: public Class getType() {
115: return null;
116: }
117:
118: /**
119: * Returns <tt>false</tt>.
120: * @see PersistentField#usesAccessorsAndMutators()
121: */
122: public boolean usesAccessorsAndMutators() {
123: return false;
124: }
125: }
|