1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.kernel;
20
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.util.BitSet;
26 import java.util.Collection;
27 import java.util.Date;
28 import java.util.Map;
29
30 import org.apache.openjpa.enhance.PersistenceCapable;
31 import org.apache.openjpa.meta.FieldMetaData;
32 import org.apache.openjpa.meta.JavaTypes;
33 import org.apache.openjpa.util.ProxyManager;
34
35 /**
36 * FieldManager type used to store information for rollback.
37 *
38 * @author Abe White
39 */
40 public class SaveFieldManager
41 extends ClearFieldManager
42 implements Serializable {
43
44 private final StateManagerImpl _sm;
45 private final BitSet _unloaded;
46 private BitSet _saved = null;
47 private int[] _copyField = null;
48 private transient PersistenceCapable _state = null;
49
50 // used to track field value during store/fetch cycle
51 private Object _field = null;
52
53 /**
54 * Constructor. Provide {@link StateManagerImpl} of instance to save.
55 */
56 SaveFieldManager(StateManagerImpl sm, PersistenceCapable pc, BitSet dirty) {
57 _sm = sm;
58 _state = pc;
59
60 // if instance is new or transient all fields will be marked dirty even
61 // though they have their original values, so we can restore them;
62 // otherwise, we need to record already-dirty persistent fields as
63 // ones we won't be able to restore
64 FieldMetaData[] fields = _sm.getMetaData().getFields();
65 if (_sm.isNew() || !_sm.isPersistent() || dirty == null)
66 _unloaded = new BitSet(fields.length);
67 else {
68 _unloaded = (BitSet) dirty.clone();
69 for (int i = 0; i < fields.length; i++)
70 if (fields[i].getManagement() != fields[i].MANAGE_PERSISTENT)
71 _unloaded.clear(i);
72 }
73 }
74
75 /**
76 * Return the persistence capable copy holding the rollback field values.
77 */
78 public PersistenceCapable getState() {
79 return _state;
80 }
81
82 /**
83 * Return the currently-loaded fields that will be unloaded after rollback.
84 */
85 public BitSet getUnloaded() {
86 return _unloaded;
87 }
88
89 /**
90 * Save the given field. If this method returns true, then you need
91 * to use this field manager to replace the given field in the instance
92 * returned by {@link #getState}.
93 */
94 public boolean saveField(int field) {
95 // if not loaded we can't save orig value; mark as unloaded on rollback
96 if (_sm.getLoaded() != null && !_sm.getLoaded().get(field)) {
97 _unloaded.set(field);
98 return false;
99 }
100
101 // already saved?
102 if (_saved != null && _saved.get(field))
103 return false;
104
105 FieldMetaData fmd = _sm.getMetaData().getField(field);
106 boolean mutable = false;
107 switch (fmd.getDeclaredTypeCode()) {
108 case JavaTypes.DATE:
109 case JavaTypes.ARRAY:
110 case JavaTypes.COLLECTION:
111 case JavaTypes.MAP:
112 case JavaTypes.OBJECT:
113 mutable = true;
114 }
115
116 // if this is not an inverse field and the proper restore flag is
117 // not set, skip it
118
119 if (_sm.getBroker().getInverseManager() == null
120 || fmd.getInverseMetaDatas().length == 0) {
121 // use sm's restore directive, not broker's
122 int restore = _sm.getBroker().getRestoreState();
123 if (restore == RestoreState.RESTORE_NONE
124 || (mutable && restore == RestoreState.RESTORE_IMMUTABLE)) {
125 _unloaded.set(field);
126 return false;
127 }
128 }
129
130 // prepare to save the field
131 if (_state == null)
132 _state = _sm.getPersistenceCapable().pcNewInstance(_sm, true);
133 if (_saved == null)
134 _saved = new BitSet(_sm.getMetaData().getFields().length);
135
136 _saved.set(field);
137
138 // if mutable, return true to indicate that the field needs to be
139 // copied by providing and replacing it using this field manager
140 if (mutable)
141 return true;
142
143 // immutable fields can just be copied over
144 if (_copyField == null)
145 _copyField = new int[1];
146 _copyField[0] = field;
147 getState().pcCopyFields(_sm.getPersistenceCapable(), _copyField);
148 return false;
149 }
150
151 /**
152 * Restore the given field. If this method returns true, then you need
153 * to use this field manager to replace the given field in the state
154 * manager's instance.
155 */
156 public boolean restoreField(int field) {
157 // if the given field needs to be unloaded, return true so that it gets
158 // replaced with a default value
159 if (_unloaded.get(field))
160 return true;
161
162 // if the field was not saved, it must not have gotten dirty; just
163 // return false so that the current value is kept
164 if (_saved == null || !_saved.get(field))
165 return false;
166
167 // copy the saved field over
168 if (_copyField == null)
169 _copyField = new int[1];
170 _copyField[0] = field;
171 _sm.getPersistenceCapable().pcCopyFields(getState(), _copyField);
172 return false;
173 }
174
175 /**
176 * Compare the given field.
177 * @return <code>true</code> if the field is the same in the current
178 * state and in the saved state; otherwise, <code>false</code>.
179 */
180 public boolean isFieldEqual(int field, Object current) {
181 // if the field is not available, assume that it has changed.
182 if (_saved == null || !_saved.get(field))
183 return false;
184 if (!(getState().pcGetStateManager() instanceof StateManagerImpl))
185 return false;
186
187 StateManagerImpl sm = (StateManagerImpl) getState().pcGetStateManager();
188 SingleFieldManager single = new SingleFieldManager(sm, sm.getBroker());
189 sm.provideField(getState(), single, field);
190 Object old = single.fetchObjectField(field);
191 return current == old || current != null && current.equals(old);
192 }
193
194 public Object fetchObjectField(int field) {
195 // return the copied field during save, or a null value during restore
196 Object val = _field;
197 _field = null;
198 return val;
199 }
200
201 public void storeObjectField(int field, Object curVal) {
202 // copy mutable fields
203 ProxyManager proxy = _sm.getBroker().getConfiguration().
204 getProxyManagerInstance();
205 FieldMetaData fmd = _sm.getMetaData().getField(field);
206 switch (fmd.getDeclaredTypeCode()) {
207 case JavaTypes.ARRAY:
208 _field = proxy.copyArray(curVal);
209 break;
210 case JavaTypes.COLLECTION:
211 _field = proxy.copyCollection((Collection) curVal);
212 break;
213 case JavaTypes.MAP:
214 _field = proxy.copyMap((Map) curVal);
215 break;
216 case JavaTypes.DATE:
217 _field = proxy.copyDate((Date) curVal);
218 break;
219 case JavaTypes.OBJECT:
220 _field = proxy.copyCustom(curVal);
221 if (_field == null)
222 _field = curVal;
223 break;
224 default:
225 _field = curVal;
226 }
227
228 // if we couldn't get a copy of the sco, act like it wasn't saved
229 if (curVal != null && _field == null) {
230 _unloaded.set(field);
231 _saved.clear(field);
232 }
233 }
234
235 private void writeObject(ObjectOutputStream oos) throws IOException {
236 oos.defaultWriteObject();
237 _sm.writePC(oos, _state);
238 }
239
240 private void readObject(ObjectInputStream ois)
241 throws IOException, ClassNotFoundException {
242 ois.defaultReadObject();
243 _state = _sm.readPC(ois);
244 }
245 }