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.1 2007-08-24 22:17:35 ewestfal Exp $
028     */
029    public class AnonymousPersistentField implements PersistentField
030    {
031        private static final long serialVersionUID = 3989863424358352941L;
032    
033        private transient Map fkCache;
034        private String fieldname;
035    
036        public AnonymousPersistentField(String fieldname)
037        {
038            this.fieldname = fieldname;
039        }
040    
041        public synchronized void set(Object obj, Object value) throws MetadataException
042        {
043            putToFieldCache(obj, value);
044        }
045    
046        public synchronized Object get(Object anObject) throws MetadataException
047        {
048            return getFromFieldCache(anObject);
049        }
050    
051    /*
052        Use ReferenceIdentityMap (with weak key and hard value setting) instead of
053        WeakHashMap to hold anonymous field values. Here is an snip of the mail from Andy Malakov:
054        <snip>
055            I found that usage of database identity in Java produces quite interesting problem in OJB:
056            In my application all persistent Java objects use database identity instead of Java reference identity
057            (i.e. Persistable.equals() is redefined so that two persistent objects are the same if they have the same
058            primary key and top-level class).
059    
060            In OJB, for each field declared in repository there is dedicated instance of AnonymousPersistentField that stores
061            object-to-field-value mapping in WeakHashMap (in fkCache attribute). Despite usage of cache
062            (ObjectCachePerBrokerImpl in my case) it is possible that identical DB objects will end up as different
063            Java objects during retrieval of complex objects.
064    
065            Now imagine what happens when two identical instances are retrieved:
066            1)
067            When first instance is retrieved it stores its foreign keys in AnonymousPersistentField.fkCache under instance's
068            identity. (happens in RowReaderDefaultImpl.buildWithReflection())
069            2)
070            When second object is retrieved and stored in fkCache, first instance is probably still cached
071            [WeakHashMap entries are cleaned up only during GC]. Since keys are identical WeakHashMap only updates entry
072            value and DOES NOT update entry key.
073            3)
074            If Full GC happens after that moment it will dispose fcCache entry if the FIRST reference becomes
075            soft-referenced only.
076        </snip>
077    */
078        protected void putToFieldCache(Object key, Object value)
079        {
080            if (key != null)
081            {
082                if (fkCache == null)
083                {
084                    fkCache = new ReferenceIdentityMap (ReferenceIdentityMap.WEAK, ReferenceIdentityMap.HARD, true);
085                }
086                if (value != null)
087                     fkCache.put(key, value);
088                 else
089                     fkCache.remove (key);
090            }
091        }
092    
093        protected Object getFromFieldCache(Object key)
094        {
095            return (key != null && fkCache != null) ? fkCache.get(key) : null;
096        }
097    
098        /**
099         * Always returns <tt>null</tt>.
100         * @see PersistentField#getDeclaringClass()
101         */
102        public Class getDeclaringClass()
103        {
104            return null;
105        }
106    
107        /**
108         * @see PersistentField#getName()
109         */
110        public String getName()
111        {
112            return fieldname;
113        }
114    
115        /**
116         * Always returns <tt>null</tt>.
117         * @see PersistentField#getType()
118         */
119        public Class getType()
120        {
121            return null;
122        }
123    
124        /**
125         * Returns <tt>false</tt>.
126         * @see PersistentField#usesAccessorsAndMutators()
127         */
128        public boolean usesAccessorsAndMutators()
129        {
130            return false;
131        }
132    }