/* * 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.kernel; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.BitSet; import java.util.Collection; import java.util.Date; import java.util.Map; import org.apache.openjpa.enhance.PersistenceCapable; import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.util.ProxyManager; /** * FieldManager type used to store information for rollback. * * @author Abe White */ public class SaveFieldManager extends ClearFieldManager implements Serializable { private final StateManagerImpl _sm; private final BitSet _unloaded; private BitSet _saved = null; private int[] _copyField = null; private transient PersistenceCapable _state = null; // used to track field value during store/fetch cycle private Object _field = null; /** * Constructor. Provide {@link StateManagerImpl} of instance to save. */ SaveFieldManager(StateManagerImpl sm, PersistenceCapable pc, BitSet dirty) { _sm = sm; _state = pc; // if instance is new or transient all fields will be marked dirty even // though they have their original values, so we can restore them; // otherwise, we need to record already-dirty persistent fields as // ones we won't be able to restore FieldMetaData[] fields = _sm.getMetaData().getFields(); if (_sm.isNew() || !_sm.isPersistent() || dirty == null) _unloaded = new BitSet(fields.length); else { _unloaded = (BitSet) dirty.clone(); for (int i = 0; i < fields.length; i++) if (fields[i].getManagement() != fields[i].MANAGE_PERSISTENT) _unloaded.clear(i); } } /** * Return the persistence capable copy holding the rollback field values. */ public PersistenceCapable getState() { return _state; } /** * Return the currently-loaded fields that will be unloaded after rollback. */ public BitSet getUnloaded() { return _unloaded; } /** * Save the given field. If this method returns true, then you need * to use this field manager to replace the given field in the instance * returned by {@link #getState}. */ public boolean saveField(int field) { // if not loaded we can't save orig value; mark as unloaded on rollback if (_sm.getLoaded() != null && !_sm.getLoaded().get(field)) { _unloaded.set(field); return false; } // already saved? if (_saved != null && _saved.get(field)) return false; FieldMetaData fmd = _sm.getMetaData().getField(field); boolean mutable = false; switch (fmd.getDeclaredTypeCode()) { case JavaTypes.DATE: case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: case JavaTypes.OBJECT: mutable = true; } // if this is not an inverse field and the proper restore flag is // not set, skip it if (_sm.getBroker().getInverseManager() == null || fmd.getInverseMetaDatas().length == 0) { // use sm's restore directive, not broker's int restore = _sm.getBroker().getRestoreState(); if (restore == RestoreState.RESTORE_NONE || (mutable && restore == RestoreState.RESTORE_IMMUTABLE)) { _unloaded.set(field); return false; } } // prepare to save the field if (_state == null) _state = _sm.getPersistenceCapable().pcNewInstance(_sm, true); if (_saved == null) _saved = new BitSet(_sm.getMetaData().getFields().length); _saved.set(field); // if mutable, return true to indicate that the field needs to be // copied by providing and replacing it using this field manager if (mutable) return true; // immutable fields can just be copied over if (_copyField == null) _copyField = new int[1]; _copyField[0] = field; getState().pcCopyFields(_sm.getPersistenceCapable(), _copyField); return false; } /** * Restore the given field. If this method returns true, then you need * to use this field manager to replace the given field in the state * manager's instance. */ public boolean restoreField(int field) { // if the given field needs to be unloaded, return true so that it gets // replaced with a default value if (_unloaded.get(field)) return true; // if the field was not saved, it must not have gotten dirty; just // return false so that the current value is kept if (_saved == null || !_saved.get(field)) return false; // copy the saved field over if (_copyField == null) _copyField = new int[1]; _copyField[0] = field; _sm.getPersistenceCapable().pcCopyFields(getState(), _copyField); return false; } /** * Compare the given field. * @return <code>true</code> if the field is the same in the current * state and in the saved state; otherwise, <code>false</code>. */ public boolean isFieldEqual(int field, Object current) { // if the field is not available, assume that it has changed. if (_saved == null || !_saved.get(field)) return false; if (!(getState().pcGetStateManager() instanceof StateManagerImpl)) return false; StateManagerImpl sm = (StateManagerImpl) getState().pcGetStateManager(); SingleFieldManager single = new SingleFieldManager(sm, sm.getBroker()); sm.provideField(getState(), single, field); Object old = single.fetchObjectField(field); return current == old || current != null && current.equals(old); } public Object fetchObjectField(int field) { // return the copied field during save, or a null value during restore return _field; } public void storeObjectField(int field, Object curVal) { // copy mutable fields ProxyManager proxy = _sm.getBroker().getConfiguration(). getProxyManagerInstance(); FieldMetaData fmd = _sm.getMetaData().getField(field); switch (fmd.getDeclaredTypeCode()) { case JavaTypes.ARRAY: _field = proxy.copyArray(curVal); break; case JavaTypes.COLLECTION: _field = proxy.copyCollection((Collection) curVal); break; case JavaTypes.MAP: _field = proxy.copyMap((Map) curVal); break; case JavaTypes.DATE: _field = proxy.copyDate((Date) curVal); break; case JavaTypes.OBJECT: _field = proxy.copyCustom(curVal); if (_field == null) _field = curVal; break; default: _field = curVal; } // if we couldn't get a copy of the sco, act like it wasn't saved if (curVal != null && _field == null) { _unloaded.set(field); _saved.clear(field); } } private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); _sm.writePC(oos, _state); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); _state = _sm.readPC(ois); } }