blob: 68437edf98db1e2d246fe1d5c91584d6c41af25d [file] [log] [blame]
/*
* 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.lib.util.Localizer;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.ProxyManager;
/**
* FieldManager type used to store information for savepoint rollback.
*
* @author Steve Kim
* @since 0.3.4
*/
class SavepointFieldManager
extends ClearFieldManager
implements Serializable {
private static final long serialVersionUID = 1L;
private static final Localizer _loc = Localizer.forPackage
(SavepointFieldManager.class);
private final StateManagerImpl _sm;
private final BitSet _loaded;
private final BitSet _dirty;
private final BitSet _flush;
private final PCState _state;
private transient PersistenceCapable _copy;
private final Object _version;
private final Object _loadVersion;
// used to track field value during store/fetch cycle
private Object _field = null;
private int[] _copyField = null;
private BitSet _mutable;
/**
* Constructor. Provide instance to save and indicate whether
* to copy persistent fields. Transactional fields will be
* copied regardless of copy setting.
*/
public SavepointFieldManager(StateManagerImpl sm, boolean copy) {
_sm = sm;
_state = _sm.getPCState();
_dirty = (BitSet) _sm.getDirty().clone();
_flush = (BitSet) _sm.getFlushed().clone();
_loaded = (BitSet) _sm.getLoaded().clone();
FieldMetaData[] fields = _sm.getMetaData().getFields();
for (int i = 0; i < _loaded.length(); i++) {
if (!_loaded.get(i))
continue;
if (copy || fields[i].getManagement() ==
FieldMetaData.MANAGE_TRANSACTIONAL) {
if (_copy == null)
_copy = _sm.getPersistenceCapable().pcNewInstance
(_sm, true);
storeField(fields[i]);
} else
_loaded.clear(i);
}
// we need to proxy the fields so that we can track future changes
// from this savepoint forward for PNew instances' mutable fields
_sm.proxyFields(false, false);
_version = _sm.getVersion ();
_loadVersion = _sm.getLoadVersion ();
}
/**
* Return the state manager that this manager is associated with.
*/
public StateManagerImpl getStateManager() {
return _sm;
}
public Object getVersion() {
return _version;
}
public Object getLoadVersion() {
return _loadVersion;
}
/**
* Return the persistence capable copy holding the savepoint field values.
*/
public PersistenceCapable getCopy() {
return _copy;
}
/**
* Return the saved {@link PCState}
*/
public PCState getPCState() {
return _state;
}
/**
* Return the fields stored in this manager.
*/
public BitSet getLoaded() {
return _loaded;
}
/**
* Return the dirty fields during the saved state.
*/
public BitSet getDirty() {
return _dirty;
}
/**
* Return the flushed fields during the saved state.
*/
public BitSet getFlushed() {
return _flush;
}
/**
* Store the data for the given field.
*/
public void storeField(FieldMetaData field) {
switch (field.getDeclaredTypeCode()) {
case JavaTypes.DATE:
case JavaTypes.ARRAY:
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
case JavaTypes.OBJECT:
if (_mutable == null)
_mutable = new BitSet(_sm.getMetaData().getFields().length);
_mutable.set(field.getIndex());
}
if (_mutable == null || !_mutable.get(field.getIndex())) {
// immutable fields can just be copied over
if (_copyField == null)
_copyField = new int[1];
_copyField[0] = field.getIndex();
_copy.pcCopyFields(_sm.getPersistenceCapable(), _copyField);
} else {
_sm.provideField(_sm.getPersistenceCapable(), this,
field.getIndex());
_sm.replaceField(_copy, this, field.getIndex());
}
}
/**
* 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 (!_loaded.get(field))
return false;
if (_mutable != null && _mutable.get(field))
return true;
// copy the saved field over
if (_copyField == null)
_copyField = new int[1];
_copyField[0] = field;
_sm.getPersistenceCapable().pcCopyFields(_copy, _copyField);
return false;
}
@Override
public Object fetchObjectField(int field) {
return _field;
}
@Override
public void storeObjectField(int field, Object curVal) {
// copy mutable fields
ProxyManager proxy = _sm.getContext().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
// should throw an exception
if (curVal != null && _field == null)
throw new InternalException(_loc.get("no-savepoint-copy", fmd));
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
_sm.writePC(oos, _copy);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
_copy = _sm.readPC(ois);
}
}