/* * 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.util; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.security.AccessController; import java.security.PrivilegedActionException; import java.util.Date; import org.apache.openjpa.enhance.FieldManager; import org.apache.openjpa.enhance.PCRegistry; import org.apache.openjpa.enhance.PersistenceCapable; import org.apache.openjpa.enhance.Reflection; import org.apache.openjpa.kernel.ObjectIdStateManager; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.StateManagerImpl; import org.apache.openjpa.kernel.StoreManager; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.AccessCode; import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.meta.ValueStrategies; /** * Utility class for manipulating application object ids. * * @author Abe White */ public class ApplicationIds { private static final Localizer _loc = Localizer.forPackage (ApplicationIds.class); private static final Localizer _loc2 = Localizer.forPackage (StateManagerImpl.class); /** * Return the primary key values for the given object id. The values * will be returned in the same order as the metadata primary key fields. * Values for PC primary key fields will be the primary key value or * oid value of the related instance (depending on * {@link FieldMetaData#isObjectIdFieldIdOfPC}). */ public static Object[] toPKValues(Object oid, ClassMetaData meta) { if (meta == null) return null; Object[] pks; if (meta.isOpenJPAIdentity()) { pks = new Object[1]; if (oid != null) pks[0] = ((OpenJPAId) oid).getIdObject(); return pks; } // reset owning 'meta' to the owner of the primary key fields, because // the one passed in might be a proxy, like for embedded mappings; // since getPrimaryKeyFields is guaranteed to return the primary // keys in the order of inheritance, we are guaranteed that // the last element will be the most-derived class. FieldMetaData[] fmds = meta.getPrimaryKeyFields(); meta = fmds[fmds.length - 1].getDeclaringMetaData(); pks = new Object[fmds.length]; if (oid == null) return pks; if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) { // copy fields from the oid PrimaryKeyFieldManager consumer = new PrimaryKeyFieldManager(); consumer.setStore(pks); oid = wrap(meta, oid); PCRegistry.copyKeyFieldsFromObjectId(meta.getDescribedType(), consumer, oid); return consumer.getStore(); } // default to reflection if (meta.isObjectIdTypeShared()) oid = ((ObjectId) oid).getId(); Class<?> oidType = oid.getClass(); for (int i = 0; i < fmds.length; i++) { if (AccessCode.isField(meta.getAccessType())) pks[i] = Reflection.get(oid, Reflection.findField(oidType, fmds[i].getName(), true)); else pks[i] = Reflection.get(oid, Reflection.findGetter(oidType, fmds[i].getName(), true)); } return pks; } /** * Wraps the given object for the given type into a OpenJPA specific * application identity object wrapper instance (i.e. ObjectId) if all of * the following is true: * the given type is not using built-in OpenJPA identity types * the given type is using a shared OpenJPA identity type * the given object is not already a wrapper identity type */ public static Object wrap(ClassMetaData meta, Object oid) { if (!meta.isOpenJPAIdentity() && meta.isObjectIdTypeShared() && !(oid instanceof ObjectId)) { return new ObjectId(meta.getDescribedType(), oid); } return oid; } /** * Return a new object id constructed from the given primary key values. * Values for PC primary key fields should be the primary key value or * oid value of the related instance (depending on * {@link FieldMetaData#isObjectIdFieldIdOfPC}). */ public static Object fromPKValues(Object[] pks, ClassMetaData meta) { if (meta == null || pks == null) return null; boolean convert = !meta.getRepository().getConfiguration(). getCompatibilityInstance().getStrictIdentityValues(); if (meta.isOpenJPAIdentity()) { int type = meta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode(); Object val = (convert) ? JavaTypes.convert(pks[0], type) : pks[0]; switch (type) { case JavaTypes.BYTE: case JavaTypes.BYTE_OBJ: if (!convert && !(val instanceof Byte)) throw new ClassCastException("!(x instanceof Byte)"); return new ByteId(meta.getDescribedType(), val == null ? 0 : ((Number) val).byteValue()); case JavaTypes.CHAR: case JavaTypes.CHAR_OBJ: return new CharId(meta.getDescribedType(), val == null ? 0 : ((Character) val).charValue()); case JavaTypes.DOUBLE: case JavaTypes.DOUBLE_OBJ: if (!convert && !(val instanceof Double)) throw new ClassCastException("!(x instanceof Double)"); return new DoubleId(meta.getDescribedType(), val == null ? 0 : ((Number) val).doubleValue()); case JavaTypes.FLOAT: case JavaTypes.FLOAT_OBJ: if (!convert && !(val instanceof Float)) throw new ClassCastException("!(x instanceof Float)"); return new FloatId(meta.getDescribedType(), val == null ? 0 : ((Number) val).floatValue()); case JavaTypes.INT: case JavaTypes.INT_OBJ: if (!convert && !(val instanceof Integer)) throw new ClassCastException("!(x instanceof Integer)"); return new IntId(meta.getDescribedType(), val == null ? 0 : ((Number) val).intValue()); case JavaTypes.LONG: case JavaTypes.LONG_OBJ: if (!convert && !(val instanceof Long)) throw new ClassCastException("!(x instanceof Long)"); return new LongId(meta.getDescribedType(), val == null ? 0 : ((Number) val).longValue()); case JavaTypes.SHORT: case JavaTypes.SHORT_OBJ: if (!convert && !(val instanceof Short)) throw new ClassCastException("!(x instanceof Short)"); return new ShortId(meta.getDescribedType(), val == null ? 0 : ((Number) val).shortValue()); case JavaTypes.STRING: return new StringId(meta.getDescribedType(), (String) val); case JavaTypes.DATE: return new DateId(meta.getDescribedType(), (Date) val); case JavaTypes.OID: case JavaTypes.OBJECT: return new ObjectId(meta.getDescribedType(), val); case JavaTypes.BIGDECIMAL: if (!convert && !(val instanceof BigDecimal)) throw new ClassCastException( "!(x instanceof BigDecimal)"); return new BigDecimalId(meta.getDescribedType(), (BigDecimal)val); case JavaTypes.BIGINTEGER: if (!convert && !(val instanceof BigInteger)) throw new ClassCastException( "!(x instanceof BigInteger)"); return new BigIntegerId(meta.getDescribedType(), (BigInteger)val); case JavaTypes.BOOLEAN: case JavaTypes.BOOLEAN_OBJ: if (!convert && !(val instanceof Boolean)) throw new ClassCastException("!(x instanceof Boolean)"); return new BooleanId(meta.getDescribedType(), val == null ? false : (Boolean)val); default: throw new InternalException(); } } // copy pks to oid if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) { Object oid = PCRegistry.newObjectId(meta.getDescribedType()); PrimaryKeyFieldManager producer = new PrimaryKeyFieldManager(); producer.setStore(pks); if (convert) producer.setMetaData(meta); PCRegistry.copyKeyFieldsToObjectId(meta.getDescribedType(), producer, oid); return ApplicationIds.wrap(meta, oid); } // default to reflection Class<?> oidType = meta.getObjectIdType(); if (Modifier.isAbstract(oidType.getModifiers())) throw new UserException(_loc.get("objectid-abstract", meta)); Object copy = null; try { copy = AccessController.doPrivileged( J2DoPrivHelper.newInstanceAction(oidType)); } catch (Throwable t) { if (t instanceof PrivilegedActionException) t = ((PrivilegedActionException) t).getException(); throw new GeneralException(t); } FieldMetaData[] fmds = meta.getPrimaryKeyFields(); Object val; for (int i = 0; i < fmds.length; i++) { val = (convert) ? JavaTypes.convert(pks[i], fmds[i].getObjectIdFieldTypeCode()) : pks[i]; if (AccessCode.isField(meta.getAccessType())) Reflection.set(copy, Reflection.findField(oidType, fmds[i].getName(), true), val); else Reflection.set(copy, Reflection.findSetter(oidType, fmds[i].getName(), fmds[i].getDeclaredType(), true), val); } if (meta.isObjectIdTypeShared()) copy = new ObjectId(meta.getDescribedType(), copy); return copy; } /** * Copy the given oid value. */ public static Object copy(Object oid, ClassMetaData meta) { if (meta == null || oid == null) return null; if (meta.isOpenJPAIdentity()) { // use meta type instead of oid type in case it's a subclass Class<?> cls = meta.getDescribedType(); OpenJPAId koid = (OpenJPAId) oid; FieldMetaData pk = meta.getPrimaryKeyFields()[0]; switch (pk.getObjectIdFieldTypeCode()) { case JavaTypes.BYTE: case JavaTypes.BYTE_OBJ: return new ByteId(cls, ((ByteId) oid).getId(), koid.hasSubclasses()); case JavaTypes.CHAR: case JavaTypes.CHAR_OBJ: return new CharId(cls, ((CharId) oid).getId(), koid.hasSubclasses()); case JavaTypes.DOUBLE: case JavaTypes.DOUBLE_OBJ: return new DoubleId(cls, ((DoubleId) oid).getId(), koid.hasSubclasses()); case JavaTypes.FLOAT: case JavaTypes.FLOAT_OBJ: return new FloatId(cls, ((FloatId) oid).getId(), koid.hasSubclasses()); case JavaTypes.INT: case JavaTypes.INT_OBJ: return new IntId(cls, ((IntId) oid).getId(), koid.hasSubclasses()); case JavaTypes.LONG: case JavaTypes.LONG_OBJ: return new LongId(cls, ((LongId) oid).getId(), koid.hasSubclasses()); case JavaTypes.SHORT: case JavaTypes.SHORT_OBJ: return new ShortId(cls, ((ShortId) oid).getId(), koid.hasSubclasses()); case JavaTypes.STRING: return new StringId(cls, oid.toString(), koid.hasSubclasses()); case JavaTypes.OID: ClassMetaData embed = pk.getEmbeddedMetaData(); Object inner = koid.getIdObject(); if (embed != null) inner = copy(inner, embed, embed.getFields()); return new ObjectId(cls, inner, koid.hasSubclasses()); case JavaTypes.OBJECT: return new ObjectId(cls, koid.getIdObject(), koid.hasSubclasses()); case JavaTypes.DATE: return new DateId(cls, ((DateId) oid).getId(), koid.hasSubclasses()); case JavaTypes.BIGDECIMAL: return new BigDecimalId(cls, ((BigDecimalId) oid).getId(), koid.hasSubclasses()); case JavaTypes.BIGINTEGER: return new BigIntegerId(cls, ((BigIntegerId) oid).getId(), koid.hasSubclasses()); default: throw new InternalException(); } } // create a new pc instance of the right type, set its key fields // to the original oid values, then copy its key fields to a new // oid instance if (!Modifier.isAbstract(meta.getDescribedType().getModifiers()) && !hasPCPrimaryKeyFields(meta)) { Class<?> type = meta.getDescribedType(); PersistenceCapable pc = PCRegistry.newInstance(type, null, oid, false); Object copy = pc.pcNewObjectIdInstance(); pc.pcCopyKeyFieldsToObjectId(copy); return copy; } Object copy = (!meta.isObjectIdTypeShared()) ? oid : ((ObjectId) oid).getId(); copy = copy(copy, meta, meta.getPrimaryKeyFields()); if (meta.isObjectIdTypeShared()) copy = new ObjectId(meta.getDescribedType(), copy, ((OpenJPAId) oid).hasSubclasses()); return copy; } /** * Return true if any of the given type's primary key fields are * persistent objects. */ private static boolean hasPCPrimaryKeyFields(ClassMetaData meta) { FieldMetaData[] fmds = meta.getPrimaryKeyFields(); for (int i = 0; i < fmds.length; i++) if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC) return true; return false; } /** * Copy the given identity object using reflection. */ private static Object copy(Object oid, ClassMetaData meta, FieldMetaData[] fmds) { if (oid == null) return null; Class<?> oidType = oid.getClass(); Object copy = null; try { copy = AccessController.doPrivileged( J2DoPrivHelper.newInstanceAction(oidType)); } catch (Throwable t) { if (t instanceof PrivilegedActionException) t = ((PrivilegedActionException) t).getException(); throw new GeneralException(t); } Field field; Object val; for (int i = 0; i < fmds.length; i++) { if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT) continue; if (AccessCode.isField(meta.getAccessType())) { field = Reflection.findField(oidType, fmds[i].getName(), true); Reflection.set(copy, field, Reflection.get(oid, field)); } else { // property val = Reflection.get(oid, Reflection.findGetter(oidType, fmds[i].getName(), true)); Reflection.set(copy, Reflection.findSetter(oidType, fmds[i]. getName(), fmds[i].getObjectIdFieldType(), true), val); } } return copy; } /** * Return the given primary key field value from the given oid. */ public static Object get(Object oid, FieldMetaData fmd) { if (oid == null) return null; if (oid instanceof OpenJPAId) return ((OpenJPAId) oid).getIdObject(); ClassMetaData meta = fmd.getDefiningMetaData(); Class<?> oidType = oid.getClass(); if (AccessCode.isField(meta.getAccessType())) return Reflection.get(oid, Reflection.findField(oidType, fmd.getName(), true)); return Reflection.get(oid, Reflection.findGetter(oidType, fmd.getName(), true)); } /** * Generate an application id based on the current primary key field state * of the given instance. */ public static Object create(PersistenceCapable pc, ClassMetaData meta) { if (pc == null) return null; Object oid = pc.pcNewObjectIdInstance(); if (oid == null) return null; if (!meta.isOpenJPAIdentity()) { pc.pcCopyKeyFieldsToObjectId(oid); return oid; } FieldMetaData pk = meta.getPrimaryKeyFields()[0]; if (pk.getDeclaredTypeCode() != JavaTypes.OID) return oid; // always copy oid object in case field value mutates or becomes // managed ObjectId objid = (ObjectId) oid; ClassMetaData embed = pk.getEmbeddedMetaData(); objid.setId(copy(objid.getId(), embed, embed.getFields())); return objid; } /** * Assign an application identity object to the given state, or return * false if determining the application identity requires a flush. */ public static boolean assign(OpenJPAStateManager sm, StoreManager store, boolean preFlush) { ClassMetaData meta = sm.getMetaData(); if (meta.getIdentityType() != ClassMetaData.ID_APPLICATION) throw new InternalException(); boolean ret; FieldMetaData[] pks = meta.getPrimaryKeyFields(); if (meta.isOpenJPAIdentity() && pks[0].getDeclaredTypeCode() == JavaTypes.OID) { OpenJPAStateManager oidsm = new ObjectIdStateManager (sm.fetchObjectField(pks[0].getIndex()), sm, pks[0]); ret = assign(oidsm, store, pks[0].getEmbeddedMetaData(). getFields(), preFlush); sm.storeObjectField(pks[0].getIndex(), oidsm.getManagedInstance()); } else ret = assign(sm, store, meta.getPrimaryKeyFields(), preFlush); if (!ret) return false; // base oid on field values sm.setObjectId(create(sm.getPersistenceCapable(), meta)); return true; } /** * Assign generated values to given primary key fields. */ private static boolean assign(OpenJPAStateManager sm, StoreManager store, FieldMetaData[] pks, boolean preFlush) { for (int i = 0; i < pks.length; i++) // If we are generating values... if (pks[i].getValueStrategy() != ValueStrategies.NONE) { // If a value already exists on this field, throw exception. // This is considered an application coding error. if (!sm.isDefaultValue(pks[i].getIndex())) throw new InvalidStateException(_loc2.get("existing-value-override-excep", pks[i].getFullName(false), Exceptions.toString(sm.getPersistenceCapable()), sm.getPCState().getClass().getSimpleName())); // Assign the generated value if (store.assignField(sm, pks[i].getIndex(), preFlush)) pks[i].setValueGenerated(true); else return false; } return true; } /** * Check if object id is set or not. */ public static boolean isIdSet(Object id, ClassMetaData meta, String mappedByIdFieldName) { Object key = null; if (meta.isOpenJPAIdentity()) key = ApplicationIds.getKey(id, meta); else key = ((ObjectId)id).getId(); Object val = null; if (mappedByIdFieldName.length() != 0) { if (((ObjectId)id).getId() == null) return false; Class<?> idClass = ((ObjectId)id).getId().getClass(); val = Reflection.get(key, Reflection.findField(idClass, mappedByIdFieldName, true)); } else val = key; boolean notSet = (val == null || (val instanceof String && ((String)val).length() == 0) || (val instanceof Number && ((Number)val).longValue() == 0)); return !notSet; } /** * Return the key from the given id. */ public static Object getKey(Object id, ClassMetaData meta) { if (meta == null || id == null) return null; if (meta.isOpenJPAIdentity()) { int type = meta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode(); switch (type) { case JavaTypes.BYTE: case JavaTypes.BYTE_OBJ: return ((ByteId)id).getId(); case JavaTypes.CHAR: case JavaTypes.CHAR_OBJ: return ((CharId)id).getId(); case JavaTypes.DOUBLE: case JavaTypes.DOUBLE_OBJ: return ((DoubleId)id).getId(); case JavaTypes.FLOAT: case JavaTypes.FLOAT_OBJ: return ((FloatId)id).getId(); case JavaTypes.INT: case JavaTypes.INT_OBJ: return ((IntId)id).getId(); case JavaTypes.LONG: case JavaTypes.LONG_OBJ: return ((LongId)id).getId(); case JavaTypes.SHORT: case JavaTypes.SHORT_OBJ: return ((ShortId)id).getId(); case JavaTypes.STRING: return ((StringId)id).getId(); case JavaTypes.DATE: return ((DateId)id).getId(); case JavaTypes.OID: case JavaTypes.OBJECT: return ((ObjectId)id).getId(); case JavaTypes.BIGDECIMAL: return ((BigDecimalId)id).getId(); case JavaTypes.BIGINTEGER: return ((BigIntegerId)id).getId(); default: throw new InternalException(); } } else { // IdClass return ((ObjectId)id).getId(); } } /** * Sets the underlying id of an ObjectId. Should only * be used with simple (idclass) types. */ public static void setAppId(ObjectId id, Object newId) { id.setId(newId); } /** * Helper class used to transfer pk values to/from application oids. */ private static class PrimaryKeyFieldManager implements FieldManager { private Object[] _store = null; private int _index = 0; private ClassMetaData _meta = null; public void setMetaData(ClassMetaData meta) { _meta = meta; } public Object[] getStore() { return _store; } public void setStore(Object[] store) { _store = store; } public void storeBooleanField(int field, boolean val) { store((val) ? Boolean.TRUE : Boolean.FALSE); } public void storeByteField(int field, byte val) { store(Byte.valueOf(val)); } public void storeCharField(int field, char val) { store(Character.valueOf(val)); } public void storeShortField(int field, short val) { store(Short.valueOf(val)); } public void storeIntField(int field, int val) { store(val); } public void storeLongField(int field, long val) { store(val); } public void storeFloatField(int field, float val) { store(Float.valueOf(val)); } public void storeDoubleField(int field, double val) { store(Double.valueOf(val)); } public void storeStringField(int field, String val) { store(val); } public void storeObjectField(int field, Object val) { store(val); } public boolean fetchBooleanField(int field) { return (retrieve(field) == Boolean.TRUE) ? true : false; } public char fetchCharField(int field) { return ((Character) retrieve(field)).charValue(); } public byte fetchByteField(int field) { return ((Number) retrieve(field)).byteValue(); } public short fetchShortField(int field) { return ((Number) retrieve(field)).shortValue(); } public int fetchIntField(int field) { return ((Number) retrieve(field)).intValue(); } public long fetchLongField(int field) { return ((Number) retrieve(field)).longValue(); } public float fetchFloatField(int field) { return ((Number) retrieve(field)).floatValue(); } public double fetchDoubleField(int field) { return ((Number) retrieve(field)).doubleValue(); } public String fetchStringField(int field) { return (String) retrieve(field); } public Object fetchObjectField(int field) { return retrieve(field); } private void store(Object val) { _store[_index++] = val; } private Object retrieve(int field) { Object val = _store[_index++]; if (_meta != null) { FieldMetaData fmd = _meta.getField(field); if (fmd.getDeclaredTypeCode() != JavaTypes.PC) val = JavaTypes.convert(val, fmd.getDeclaredTypeCode()); else val = JavaTypes.convert(val, JavaTypes.getTypeCode(fmd. getObjectIdFieldType())); } return val; } } }