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 }