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.util;
20
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Modifier;
23 import java.math.BigDecimal;
24 import java.math.BigInteger;
25 import java.security.AccessController;
26 import java.security.PrivilegedActionException;
27 import java.util.Date;
28
29 import org.apache.openjpa.enhance.FieldManager;
30 import org.apache.openjpa.enhance.PCRegistry;
31 import org.apache.openjpa.enhance.PersistenceCapable;
32 import org.apache.openjpa.enhance.Reflection;
33 import org.apache.openjpa.kernel.ObjectIdStateManager;
34 import org.apache.openjpa.kernel.OpenJPAStateManager;
35 import org.apache.openjpa.kernel.StateManagerImpl;
36 import org.apache.openjpa.kernel.StoreManager;
37 import org.apache.openjpa.lib.util.J2DoPrivHelper;
38 import org.apache.openjpa.lib.util.Localizer;
39 import org.apache.openjpa.meta.ClassMetaData;
40 import org.apache.openjpa.meta.FieldMetaData;
41 import org.apache.openjpa.meta.JavaTypes;
42 import org.apache.openjpa.meta.ValueStrategies;
43 import serp.util.Numbers;
44
45 /**
46 * Utility class for manipulating application object ids.
47 *
48 * @author Abe White
49 * @nojavadoc
50 */
51 public class ApplicationIds {
52
53 private static final Localizer _loc = Localizer.forPackage
54 (ApplicationIds.class);
55 private static final Localizer _loc2 = Localizer.forPackage
56 (StateManagerImpl.class);
57
58 /**
59 * Return the primary key values for the given object id. The values
60 * will be returned in the same order as the metadata primary key fields.
61 * Values for PC primary key fields will be the primarky key value or
62 * oid value of the related instance (depending on
63 * {@link FieldMetaData#isObjectIdFieldIdOfPC}).
64 */
65 public static Object[] toPKValues(Object oid, ClassMetaData meta) {
66 if (meta == null)
67 return null;
68
69 Object[] pks;
70 if (meta.isOpenJPAIdentity()) {
71 pks = new Object[1];
72 if (oid != null)
73 pks[0] = ((OpenJPAId) oid).getIdObject();
74 return pks;
75 }
76
77 // reset owning 'meta' to the owner of the primary key fields, because
78 // the one passed in might be a proxy, like for embedded mappings;
79 // since getPrimaryKeyFields is guaranteed to return the primary
80 // keys in the order of inheritance, we are guaranteed that
81 // the last element will be the most-derived class.
82 FieldMetaData[] fmds = meta.getPrimaryKeyFields();
83 meta = fmds[fmds.length - 1].getDeclaringMetaData();
84 pks = new Object[fmds.length];
85 if (oid == null)
86 return pks;
87
88 if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) {
89 // copy fields from the oid
90 PrimaryKeyFieldManager consumer = new PrimaryKeyFieldManager();
91 consumer.setStore(pks);
92 PCRegistry.copyKeyFieldsFromObjectId(meta.getDescribedType(),
93 consumer, oid);
94 return consumer.getStore();
95 }
96
97 // default to reflection
98 if (meta.isObjectIdTypeShared())
99 oid = ((ObjectId) oid).getId();
100 Class oidType = oid.getClass();
101 for (int i = 0; i < fmds.length; i++) {
102 if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD)
103 pks[i] = Reflection.get(oid, Reflection.findField(oidType,
104 fmds[i].getName(), true));
105 else
106 pks[i] = Reflection.get(oid, Reflection.findGetter(oidType,
107 fmds[i].getName(), true));
108 }
109 return pks;
110 }
111
112 /**
113 * Return a new object id constructed from the given primary key values.
114 * Values for PC primary key fields should be the primarky key value or
115 * oid value of the related instance (depending on
116 * {@link FieldMetaData#isObjectIdFieldIdOfPC}).
117 */
118 public static Object fromPKValues(Object[] pks, ClassMetaData meta) {
119 if (meta == null || pks == null)
120 return null;
121
122 boolean convert = !meta.getRepository().getConfiguration().
123 getCompatibilityInstance().getStrictIdentityValues();
124 if (meta.isOpenJPAIdentity()) {
125 int type = meta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode();
126 Object val = (convert) ? JavaTypes.convert(pks[0], type) : pks[0];
127 switch (type) {
128 case JavaTypes.BYTE:
129 case JavaTypes.BYTE_OBJ:
130 if (!convert && !(val instanceof Byte))
131 throw new ClassCastException("!(x instanceof Byte)");
132 return new ByteId(meta.getDescribedType(),
133 val == null ? 0 : ((Number) val).byteValue());
134 case JavaTypes.CHAR:
135 case JavaTypes.CHAR_OBJ:
136 return new CharId(meta.getDescribedType(),
137 val == null ? 0 : ((Character) val).charValue());
138 case JavaTypes.DOUBLE:
139 case JavaTypes.DOUBLE_OBJ:
140 if (!convert && !(val instanceof Double))
141 throw new ClassCastException("!(x instanceof Double)");
142 return new DoubleId(meta.getDescribedType(),
143 val == null ? 0 : ((Number) val).doubleValue());
144 case JavaTypes.FLOAT:
145 case JavaTypes.FLOAT_OBJ:
146 if (!convert && !(val instanceof Float))
147 throw new ClassCastException("!(x instanceof Float)");
148 return new FloatId(meta.getDescribedType(),
149 val == null ? 0 : ((Number) val).floatValue());
150 case JavaTypes.INT:
151 case JavaTypes.INT_OBJ:
152 if (!convert && !(val instanceof Integer))
153 throw new ClassCastException("!(x instanceof Integer)");
154 return new IntId(meta.getDescribedType(),
155 val == null ? 0 : ((Number) val).intValue());
156 case JavaTypes.LONG:
157 case JavaTypes.LONG_OBJ:
158 if (!convert && !(val instanceof Long))
159 throw new ClassCastException("!(x instanceof Long)");
160 return new LongId(meta.getDescribedType(),
161 val == null ? 0 : ((Number) val).longValue());
162 case JavaTypes.SHORT:
163 case JavaTypes.SHORT_OBJ:
164 if (!convert && !(val instanceof Short))
165 throw new ClassCastException("!(x instanceof Short)");
166 return new ShortId(meta.getDescribedType(),
167 val == null ? 0 : ((Number) val).shortValue());
168 case JavaTypes.STRING:
169 return new StringId(meta.getDescribedType(), (String) val);
170 case JavaTypes.DATE:
171 return new DateId(meta.getDescribedType(), (Date) val);
172 case JavaTypes.OID:
173 case JavaTypes.OBJECT:
174 return new ObjectId(meta.getDescribedType(), val);
175 case JavaTypes.BIGDECIMAL:
176 if (!convert && !(val instanceof BigDecimal))
177 throw new ClassCastException("!(x instanceof BigDecimal)");
178 return new BigDecimalId(meta.getDescribedType(), (BigDecimal)val);
179 case JavaTypes.BIGINTEGER:
180 if (!convert && !(val instanceof BigInteger))
181 throw new ClassCastException("!(x instanceof BigInteger)");
182 return new BigIntegerId(meta.getDescribedType(), (BigInteger)val);
183 default:
184 throw new InternalException();
185 }
186 }
187
188 // copy pks to oid
189 if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) {
190 Object oid = PCRegistry.newObjectId(meta.getDescribedType());
191 PrimaryKeyFieldManager producer = new PrimaryKeyFieldManager();
192 producer.setStore(pks);
193 if (convert)
194 producer.setMetaData(meta);
195 PCRegistry.copyKeyFieldsToObjectId(meta.getDescribedType(),
196 producer, oid);
197 return oid;
198 }
199
200 // default to reflection
201 Class oidType = meta.getObjectIdType();
202 if (Modifier.isAbstract(oidType.getModifiers()))
203 throw new UserException(_loc.get("objectid-abstract", meta));
204 Object copy = null;
205 try {
206 copy = AccessController.doPrivileged(
207 J2DoPrivHelper.newInstanceAction(oidType));
208 } catch (Throwable t) {
209 if (t instanceof PrivilegedActionException)
210 t = ((PrivilegedActionException) t).getException();
211 throw new GeneralException(t);
212 }
213
214 FieldMetaData[] fmds = meta.getPrimaryKeyFields();
215 Object val;
216 for (int i = 0; i < fmds.length; i++) {
217 val = (convert) ? JavaTypes.convert(pks[i],
218 fmds[i].getObjectIdFieldTypeCode()) : pks[i];
219 if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD)
220 Reflection.set(copy, Reflection.findField(oidType,
221 fmds[i].getName(), true), val);
222 else
223 Reflection.set(copy, Reflection.findSetter(oidType,
224 fmds[i].getName(), fmds[i].getDeclaredType(), true), val);
225 }
226
227 if (meta.isObjectIdTypeShared())
228 copy = new ObjectId(meta.getDescribedType(), copy);
229 return copy;
230 }
231
232 /**
233 * Copy the given oid value.
234 */
235 public static Object copy(Object oid, ClassMetaData meta) {
236 if (meta == null || oid == null)
237 return null;
238
239 if (meta.isOpenJPAIdentity()) {
240 // use meta type instead of oid type in case it's a subclass
241 Class cls = meta.getDescribedType();
242 OpenJPAId koid = (OpenJPAId) oid;
243 FieldMetaData pk = meta.getPrimaryKeyFields()[0];
244 switch (pk.getObjectIdFieldTypeCode()) {
245 case JavaTypes.BYTE:
246 case JavaTypes.BYTE_OBJ:
247 return new ByteId(cls, ((ByteId) oid).getId(),
248 koid.hasSubclasses());
249 case JavaTypes.CHAR:
250 case JavaTypes.CHAR_OBJ:
251 return new CharId(cls, ((CharId) oid).getId(),
252 koid.hasSubclasses());
253 case JavaTypes.DOUBLE:
254 case JavaTypes.DOUBLE_OBJ:
255 return new DoubleId(cls, ((DoubleId) oid).getId(),
256 koid.hasSubclasses());
257 case JavaTypes.FLOAT:
258 case JavaTypes.FLOAT_OBJ:
259 return new FloatId(cls, ((FloatId) oid).getId(),
260 koid.hasSubclasses());
261 case JavaTypes.INT:
262 case JavaTypes.INT_OBJ:
263 return new IntId(cls, ((IntId) oid).getId(),
264 koid.hasSubclasses());
265 case JavaTypes.LONG:
266 case JavaTypes.LONG_OBJ:
267 return new LongId(cls, ((LongId) oid).getId(),
268 koid.hasSubclasses());
269 case JavaTypes.SHORT:
270 case JavaTypes.SHORT_OBJ:
271 return new ShortId(cls, ((ShortId) oid).getId(),
272 koid.hasSubclasses());
273 case JavaTypes.STRING:
274 return new StringId(cls, oid.toString(),
275 koid.hasSubclasses());
276 case JavaTypes.OID:
277 ClassMetaData embed = pk.getEmbeddedMetaData();
278 Object inner = koid.getIdObject();
279 if (embed != null)
280 inner = copy(inner, embed, embed.getFields());
281 return new ObjectId(cls, inner, koid.hasSubclasses());
282 case JavaTypes.OBJECT:
283 return new ObjectId(cls, koid.getIdObject(),
284 koid.hasSubclasses());
285 case JavaTypes.DATE:
286 return new DateId(cls, ((DateId) oid).getId(),
287 koid.hasSubclasses());
288 case JavaTypes.BIGDECIMAL:
289 return new BigDecimalId(cls, ((BigDecimalId) oid).getId(),
290 koid.hasSubclasses());
291 case JavaTypes.BIGINTEGER:
292 return new BigIntegerId(cls, ((BigIntegerId) oid).getId(),
293 koid.hasSubclasses());
294 default:
295 throw new InternalException();
296 }
297 }
298
299 // create a new pc instance of the right type, set its key fields
300 // to the original oid values, then copy its key fields to a new
301 // oid instance
302 if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())
303 && !hasPCPrimaryKeyFields(meta)) {
304 Class type = meta.getDescribedType();
305 PersistenceCapable pc = PCRegistry.newInstance(type, null, oid,
306 false);
307 Object copy = pc.pcNewObjectIdInstance();
308 pc.pcCopyKeyFieldsToObjectId(copy);
309 return copy;
310 }
311
312 Object copy = (!meta.isObjectIdTypeShared()) ? oid
313 : ((ObjectId) oid).getId();
314 copy = copy(copy, meta, meta.getPrimaryKeyFields());
315 if (meta.isObjectIdTypeShared())
316 copy = new ObjectId(meta.getDescribedType(), copy,
317 ((OpenJPAId) oid).hasSubclasses());
318 return copy;
319 }
320
321 /**
322 * Return true if any of the given type's primary key fields are
323 * persistent objects.
324 */
325 private static boolean hasPCPrimaryKeyFields(ClassMetaData meta) {
326 FieldMetaData[] fmds = meta.getPrimaryKeyFields();
327 for (int i = 0; i < fmds.length; i++)
328 if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
329 return true;
330 return false;
331 }
332
333 /**
334 * Copy the given identity object using reflection.
335 */
336 private static Object copy(Object oid, ClassMetaData meta,
337 FieldMetaData[] fmds) {
338 if (oid == null)
339 return null;
340
341 Class oidType = oid.getClass();
342 Object copy = null;
343 try {
344 copy = AccessController.doPrivileged(
345 J2DoPrivHelper.newInstanceAction(oidType));
346 } catch (Throwable t) {
347 if (t instanceof PrivilegedActionException)
348 t = ((PrivilegedActionException) t).getException();
349 throw new GeneralException(t);
350 }
351
352 Field field;
353 Object val;
354 for (int i = 0; i < fmds.length; i++) {
355 if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
356 continue;
357
358 if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
359 field = Reflection.findField(oidType, fmds[i].getName(),
360 true);
361 Reflection.set(copy, field, Reflection.get(oid, field));
362 } else { // property
363 val = Reflection.get(oid, Reflection.findGetter(oidType,
364 fmds[i].getName(), true));
365 Reflection.set(copy, Reflection.findSetter(oidType, fmds[i].
366 getName(), fmds[i].getObjectIdFieldType(), true), val);
367 }
368 }
369 return copy;
370 }
371
372 /**
373 * Return the given primary key field value from the given oid.
374 */
375 public static Object get(Object oid, FieldMetaData fmd) {
376 if (oid == null)
377 return null;
378 if (oid instanceof OpenJPAId)
379 return ((OpenJPAId) oid).getIdObject();
380
381 ClassMetaData meta = fmd.getDefiningMetaData();
382 Class oidType = oid.getClass();
383 if (meta.getAccessType() == ClassMetaData.ACCESS_FIELD)
384 return Reflection.get(oid, Reflection.findField(oidType,
385 fmd.getName(), true));
386 return Reflection.get(oid, Reflection.findGetter(oidType, fmd.getName(),
387 true));
388 }
389
390 /**
391 * Generate an application id based on the current primary key field state
392 * of the given instance.
393 */
394 public static Object create(PersistenceCapable pc, ClassMetaData meta) {
395 if (pc == null)
396 return null;
397
398 Object oid = pc.pcNewObjectIdInstance();
399 if (oid == null)
400 return null;
401
402 if (!meta.isOpenJPAIdentity()) {
403 pc.pcCopyKeyFieldsToObjectId(oid);
404 return oid;
405 }
406
407 FieldMetaData pk = meta.getPrimaryKeyFields()[0];
408 if (pk.getDeclaredTypeCode() != JavaTypes.OID)
409 return oid;
410
411 // always copy oid object in case field value mutates or becomes
412 // managed
413 ObjectId objid = (ObjectId) oid;
414 ClassMetaData embed = pk.getEmbeddedMetaData();
415 objid.setId(copy(objid.getId(), embed, embed.getFields()));
416 return objid;
417 }
418
419 /**
420 * Assign an application identity object to the given state, or return
421 * false if determining the application identity requires a flush.
422 */
423 public static boolean assign(OpenJPAStateManager sm, StoreManager store,
424 boolean preFlush) {
425 ClassMetaData meta = sm.getMetaData();
426 if (meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
427 throw new InternalException();
428
429 boolean ret;
430 FieldMetaData[] pks = meta.getPrimaryKeyFields();
431 if (meta.isOpenJPAIdentity()
432 && pks[0].getDeclaredTypeCode() == JavaTypes.OID) {
433 OpenJPAStateManager oidsm = new ObjectIdStateManager
434 (sm.fetchObjectField(pks[0].getIndex()), sm, pks[0]);
435 ret = assign(oidsm, store, pks[0].getEmbeddedMetaData().
436 getFields(), preFlush);
437 sm.storeObjectField(pks[0].getIndex(),
438 oidsm.getManagedInstance());
439 } else
440 ret = assign(sm, store, meta.getPrimaryKeyFields(), preFlush);
441 if (!ret)
442 return false;
443
444 // base oid on field values
445 sm.setObjectId(create(sm.getPersistenceCapable(), meta));
446 return true;
447 }
448
449 /**
450 * Assign generated values to given primary key fields.
451 */
452 private static boolean assign(OpenJPAStateManager sm, StoreManager store,
453 FieldMetaData[] pks, boolean preFlush) {
454 for (int i = 0; i < pks.length; i++)
455 // If we are generating values...
456 if (pks[i].getValueStrategy() != ValueStrategies.NONE) {
457 // If a value already exists on this field, throw exception.
458 // This is considered an application coding error.
459 if (!sm.isDefaultValue(pks[i].getIndex()))
460 throw new InvalidStateException(_loc2.get(
461 "existing-value-override-excep", pks[i]
462 .getFullName(false)));
463 // Assign the generated value
464 if (store.assignField(sm, pks[i].getIndex(), preFlush))
465 pks[i].setValueGenerated(true);
466 else
467 return false;
468 }
469 return true;
470 }
471
472 /**
473 * Helper class used to transfer pk values to/from application oids.
474 */
475 private static class PrimaryKeyFieldManager
476 implements FieldManager {
477
478 private Object[] _store = null;
479 private int _index = 0;
480 private ClassMetaData _meta = null;
481
482 public void setMetaData(ClassMetaData meta) {
483 _meta = meta;
484 }
485
486 public Object[] getStore() {
487 return _store;
488 }
489
490 public void setStore(Object[] store) {
491 _store = store;
492 }
493
494 public void storeBooleanField(int field, boolean val) {
495 store((val) ? Boolean.TRUE : Boolean.FALSE);
496 }
497
498 public void storeByteField(int field, byte val) {
499 store(new Byte(val));
500 }
501
502 public void storeCharField(int field, char val) {
503 store(new Character(val));
504 }
505
506 public void storeShortField(int field, short val) {
507 store(new Short(val));
508 }
509
510 public void storeIntField(int field, int val) {
511 store(Numbers.valueOf(val));
512 }
513
514 public void storeLongField(int field, long val) {
515 store(Numbers.valueOf(val));
516 }
517
518 public void storeFloatField(int field, float val) {
519 store(new Float(val));
520 }
521
522 public void storeDoubleField(int field, double val) {
523 store(new Double(val));
524 }
525
526 public void storeStringField(int field, String val) {
527 store(val);
528 }
529
530 public void storeObjectField(int field, Object val) {
531 store(val);
532 }
533
534 public boolean fetchBooleanField(int field) {
535 return (retrieve(field) == Boolean.TRUE) ? true : false;
536 }
537
538 public char fetchCharField(int field) {
539 return ((Character) retrieve(field)).charValue();
540 }
541
542 public byte fetchByteField(int field) {
543 return ((Number) retrieve(field)).byteValue();
544 }
545
546 public short fetchShortField(int field) {
547 return ((Number) retrieve(field)).shortValue();
548 }
549
550 public int fetchIntField(int field) {
551 return ((Number) retrieve(field)).intValue();
552 }
553
554 public long fetchLongField(int field) {
555 return ((Number) retrieve(field)).longValue();
556 }
557
558 public float fetchFloatField(int field) {
559 return ((Number) retrieve(field)).floatValue();
560 }
561
562 public double fetchDoubleField(int field) {
563 return ((Number) retrieve(field)).doubleValue();
564 }
565
566 public String fetchStringField(int field) {
567 return (String) retrieve(field);
568 }
569
570 public Object fetchObjectField(int field) {
571 return retrieve(field);
572 }
573
574 private void store(Object val) {
575 _store[_index++] = val;
576 }
577
578 private Object retrieve(int field) {
579 Object val = _store[_index++];
580 if (_meta != null) {
581 FieldMetaData fmd = _meta.getField(field);
582 if (fmd.getDeclaredTypeCode() != JavaTypes.PC)
583 val = JavaTypes.convert(val, fmd.getDeclaredTypeCode());
584 else
585 val = JavaTypes.convert(val, JavaTypes.getTypeCode(fmd.
586 getObjectIdFieldType()));
587 }
588 return val;
589 }
590 }
591 }