001 /* 002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org 003 * Copyright (C) 2011 NightLabs Consulting GmbH 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 package org.cumulus4j.store.fieldmanager; 019 020 import java.lang.reflect.Array; 021 import java.util.ArrayList; 022 import java.util.Collection; 023 import java.util.Collections; 024 import java.util.HashSet; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.Map; 028 import java.util.Set; 029 030 import javax.jdo.PersistenceManager; 031 import javax.jdo.spi.JDOImplHelper; 032 import javax.jdo.spi.PersistenceCapable; 033 034 import org.cumulus4j.store.Cumulus4jPersistenceHandler; 035 import org.cumulus4j.store.Cumulus4jStoreManager; 036 import org.cumulus4j.store.EncryptionHandler; 037 import org.cumulus4j.store.ObjectContainerHelper; 038 import org.cumulus4j.store.crypto.CryptoContext; 039 import org.cumulus4j.store.model.ClassMeta; 040 import org.cumulus4j.store.model.DataEntryDAO; 041 import org.cumulus4j.store.model.EmbeddedObjectContainer; 042 import org.cumulus4j.store.model.FieldMeta; 043 import org.cumulus4j.store.model.FieldMetaRole; 044 import org.cumulus4j.store.model.IndexEntry; 045 import org.cumulus4j.store.model.IndexEntryObjectRelationHelper; 046 import org.cumulus4j.store.model.IndexValue; 047 import org.cumulus4j.store.model.ObjectContainer; 048 import org.datanucleus.ExecutionContext; 049 import org.datanucleus.exceptions.NucleusDataStoreException; 050 import org.datanucleus.identity.IdentityUtils; 051 import org.datanucleus.metadata.AbstractClassMetaData; 052 import org.datanucleus.metadata.AbstractMemberMetaData; 053 import org.datanucleus.metadata.RelationType; 054 import org.datanucleus.state.ObjectProvider; 055 import org.datanucleus.store.fieldmanager.AbstractFieldManager; 056 import org.datanucleus.store.types.SCOUtils; 057 058 /** 059 * Manager for the process of fetching a user object from the datastore, handling the translation from the 060 * DataEntry object into the users own object. 061 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 062 */ 063 public class FetchFieldManager extends AbstractFieldManager 064 { 065 private ObjectProvider op; 066 private CryptoContext cryptoContext; 067 private PersistenceManager pmData; 068 private PersistenceManager pmIndex; 069 private ExecutionContext ec; 070 private ClassMeta classMeta; 071 private AbstractClassMetaData dnClassMetaData; 072 private ObjectContainer objectContainer; 073 074 public FetchFieldManager( 075 ObjectProvider op, 076 CryptoContext cryptoContext, 077 ClassMeta classMeta, 078 AbstractClassMetaData dnClassMetaData, 079 ObjectContainer objectContainer 080 ) 081 { 082 if (op == null) 083 throw new IllegalArgumentException("op == null"); 084 if (cryptoContext == null) 085 throw new IllegalArgumentException("cryptoContext == null"); 086 if (classMeta == null) 087 throw new IllegalArgumentException("classMeta == null"); 088 if (dnClassMetaData == null) 089 throw new IllegalArgumentException("dnClassMetaData == null"); 090 if (objectContainer == null) 091 throw new IllegalArgumentException("objectContainer == null"); 092 093 this.op = op; 094 this.cryptoContext = cryptoContext; 095 this.pmData = cryptoContext.getPersistenceManagerForData(); 096 this.pmIndex = cryptoContext.getPersistenceManagerForIndex(); 097 this.ec = op.getExecutionContext(); 098 this.classMeta = classMeta; 099 this.dnClassMetaData = dnClassMetaData; 100 this.objectContainer = objectContainer; 101 } 102 103 protected EncryptionHandler getEncryptionHandler() 104 { 105 return ((Cumulus4jStoreManager) ec.getStoreManager()).getEncryptionHandler(); 106 } 107 108 private long getFieldID(int fieldNumber) 109 { 110 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 111 112 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 113 if (fieldMeta == null) 114 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 115 116 return fieldMeta.getFieldID(); 117 } 118 119 @Override 120 public boolean fetchBooleanField(int fieldNumber) { 121 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 122 return value == null ? false : (Boolean)value; 123 } 124 125 @Override 126 public byte fetchByteField(int fieldNumber) { 127 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 128 return value == null ? 0 : (Byte)value; 129 } 130 131 @Override 132 public char fetchCharField(int fieldNumber) { 133 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 134 return value == null ? 0 : (Character)value; 135 } 136 137 @Override 138 public double fetchDoubleField(int fieldNumber) { 139 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 140 return value == null ? 0 : (Double)value; 141 } 142 143 @Override 144 public float fetchFloatField(int fieldNumber) { 145 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 146 return value == null ? 0 : (Float)value; 147 } 148 149 @Override 150 public int fetchIntField(int fieldNumber) { 151 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 152 return value == null ? 0 : (Integer)value; 153 } 154 155 @Override 156 public long fetchLongField(int fieldNumber) { 157 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 158 return value == null ? 0 : (Long)value; 159 } 160 161 @Override 162 public short fetchShortField(int fieldNumber) { 163 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 164 return value == null ? 0 : (Short)value; 165 } 166 167 @Override 168 public String fetchStringField(int fieldNumber) { 169 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 170 return (String)value; 171 } 172 173 private long thisDataEntryID = -1; 174 175 protected long getThisDataEntryID() 176 { 177 if (thisDataEntryID < 0) 178 thisDataEntryID = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntryID(classMeta, op.getExternalObjectId().toString()); 179 180 return thisDataEntryID; 181 } 182 183 @Override 184 public Object fetchObjectField(int fieldNumber) 185 { 186 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 187 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 188 if (fieldMeta == null) 189 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 190 191 Set<Long> mappedByDataEntryIDs = null; 192 if (mmd.getMappedBy() != null) { 193 ClassMeta fieldOrElementTypeClassMeta = fieldMeta.getFieldOrElementTypeClassMeta(ec); 194 195 // IndexEntry indexEntry = IndexEntryObjectRelationHelper.getIndexEntry(cryptoContext, pmIndex, fieldMeta.getMappedByFieldMeta(ec), classMeta, getThisDataEntryID()); 196 List<IndexEntry> indexEntries = IndexEntryObjectRelationHelper.getIndexEntriesIncludingSubClasses(cryptoContext, pmIndex, fieldMeta.getMappedByFieldMeta(ec), fieldOrElementTypeClassMeta, getThisDataEntryID()); 197 if (indexEntries.isEmpty()) 198 mappedByDataEntryIDs = Collections.emptySet(); 199 else { 200 for (IndexEntry indexEntry : indexEntries) { 201 IndexValue indexValue = getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 202 if (mappedByDataEntryIDs == null) 203 mappedByDataEntryIDs = new HashSet<Long>(indexValue.getDataEntryIDs()); 204 else 205 mappedByDataEntryIDs.addAll(indexValue.getDataEntryIDs()); 206 } 207 } 208 } 209 210 RelationType relationType = mmd.getRelationType(ec.getClassLoaderResolver()); 211 212 if (relationType == RelationType.NONE) 213 return fetchObjectFieldWithRelationTypeNone(fieldNumber, mmd, fieldMeta); 214 else if (RelationType.isRelationSingleValued(relationType)) 215 return fetchObjectFieldWithRelationTypeSingleValue(fieldNumber, mmd, fieldMeta, mappedByDataEntryIDs); 216 else if (RelationType.isRelationMultiValued(relationType)) 217 { 218 // Collection/Map/Array 219 if (mmd.hasCollection()) 220 return fetchObjectFieldWithRelationTypeCollection(fieldNumber, mmd, fieldMeta, mappedByDataEntryIDs); 221 else if (mmd.hasMap()) 222 return fetchObjectFieldWithRelationTypeMap(fieldNumber, mmd, fieldMeta, mappedByDataEntryIDs); 223 else if (mmd.hasArray()) 224 return fetchObjectFieldWithRelationTypeArray(fieldNumber, mmd, fieldMeta, mappedByDataEntryIDs); 225 else 226 throw new IllegalStateException("Unexpected multi-valued relationType: " + relationType); 227 } 228 else 229 throw new IllegalStateException("Unexpected relationType: " + relationType); 230 } 231 232 /** 233 * Fetch related objects that are not persistence-capable. 234 * The related objects might be single-valued, arrays, collections or maps. 235 */ 236 protected Object fetchObjectFieldWithRelationTypeNone(int fieldNumber, AbstractMemberMetaData mmd, FieldMeta fieldMeta) 237 { 238 if (mmd.hasCollection()) 239 { 240 Collection<Object> collection; 241 @SuppressWarnings("unchecked") 242 Class<? extends Collection<Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 243 try { 244 collection = instanceType.newInstance(); 245 } catch (InstantiationException e) { 246 throw new NucleusDataStoreException(e.getMessage(), e); 247 } catch (IllegalAccessException e) { 248 throw new NucleusDataStoreException(e.getMessage(), e); 249 } 250 251 Object array = objectContainer.getValue(fieldMeta.getFieldID()); 252 if (array != null) { 253 for (int idx = 0; idx < Array.getLength(array); ++idx) { 254 Object element = Array.get(array, idx); 255 collection.add(element); 256 } 257 } 258 return op.wrapSCOField(fieldNumber, collection, false, false, true); 259 } 260 261 if (mmd.hasMap()) 262 { 263 Map<?,?> map = (Map<?,?>) objectContainer.getValue(fieldMeta.getFieldID()); 264 return op.wrapSCOField(fieldNumber, map, false, false, true); 265 } 266 267 // Arrays are stored 'as is', thus no conversion necessary. 268 return objectContainer.getValue(getFieldID(fieldNumber)); 269 } 270 271 /** 272 * Fetch a single related object (1-1-relationship). 273 * The related object is persistence-capable. 274 */ 275 protected Object fetchObjectFieldWithRelationTypeSingleValue(int fieldNumber, AbstractMemberMetaData mmd, FieldMeta fieldMeta, Set<Long> mappedByDataEntryIDs) 276 { 277 if (mmd.isEmbedded()) { 278 Object value = objectContainer.getValue(fieldMeta.getFieldID()); 279 if (value == null) 280 return null; 281 282 if (!(value instanceof EmbeddedObjectContainer)) 283 throw new IllegalStateException("field claims to be embedded, but persistent field value is not an EmbeddedObjectContainer! fieldID=" + fieldMeta.getFieldID() + " fieldName=" + fieldMeta.getFieldName() + " value=" + value); 284 285 EmbeddedObjectContainer embeddedObjectContainer = (EmbeddedObjectContainer) value; 286 ClassMeta embeddedClassMeta = fieldMeta.getEmbeddedClassMeta(); 287 return createPCFromEmbeddedObjectContainer(fieldNumber, fieldMeta, embeddedClassMeta, embeddedObjectContainer); 288 } 289 else { 290 if (mmd.getMappedBy() != null) { 291 if (mappedByDataEntryIDs.isEmpty()) 292 return null; 293 294 if (mappedByDataEntryIDs.size() != 1) 295 throw new IllegalStateException("There are multiple objects referencing a 1-1-mapped-by-relationship! Expected 0 or 1! fieldMeta=" + fieldMeta + " dataEntryIDsForMappedBy=" + mappedByDataEntryIDs); 296 297 long dataEntryID = mappedByDataEntryIDs.iterator().next(); 298 return getObjectFromDataEntryID(dataEntryID); 299 } 300 301 Object valueID = objectContainer.getValue(fieldMeta.getFieldID()); 302 return ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, valueID); 303 } 304 } 305 306 protected Object createPCFromEmbeddedObjectContainer(int fieldNumber, FieldMeta fieldMeta, ClassMeta embeddedClassMeta, EmbeddedObjectContainer embeddedObjectContainer) 307 { 308 if (fieldMeta == null) 309 throw new IllegalArgumentException("fieldMeta == null"); 310 if (embeddedClassMeta == null) 311 throw new IllegalArgumentException("embeddedClassMeta == null"); 312 313 if (embeddedObjectContainer == null) // we allow null values in embedded lists - or shouldn't we? 314 return null; 315 316 // TODO the newest DN version has a StateManagerFactory that makes the following more convenient (I think) - maybe switch later?! 317 // ClassMeta embeddedClassMeta = ((Cumulus4jStoreManager)ec.getStoreManager()).getClassMeta(ec, embeddedObjectContainer.getClassID(), true); 318 // ClassMeta embeddedClassMeta = fieldMeta.getEmbeddedClassMeta(); 319 Class<?> embeddedClass = ec.getClassLoaderResolver().classForName(embeddedClassMeta.getClassName()); 320 321 AbstractClassMetaData embeddedDNClassMeta = embeddedClassMeta.getDataNucleusClassMetaData(ec); 322 PersistenceCapable pc = JDOImplHelper.getInstance().newInstance(embeddedClass, null); 323 324 ObjectProvider embeddedOP = ec.getNucleusContext().getObjectProviderFactory().newForEmbedded(ec, pc, false, op, fieldNumber); 325 embeddedOP.replaceFields( 326 embeddedDNClassMeta.getAllMemberPositions(), 327 new FetchFieldManager(embeddedOP, cryptoContext, embeddedClassMeta, embeddedDNClassMeta, embeddedObjectContainer) 328 ); 329 return embeddedOP.getObject(); 330 } 331 332 /** 333 * Fetch an array of related objects (1-n-relationship). 334 * The related objects are persistence-capable. 335 */ 336 protected Object fetchObjectFieldWithRelationTypeArray(int fieldNumber, AbstractMemberMetaData mmd, FieldMeta fieldMeta, Set<Long> mappedByDataEntryIDs) 337 { 338 Class<?> elementType = ec.getClassLoaderResolver().classForName(mmd.getArray().getElementType()); 339 340 Object array; 341 342 if (mmd.getArray().isEmbeddedElement()) { 343 Object value = objectContainer.getValue(fieldMeta.getFieldID()); 344 if (!(value instanceof EmbeddedObjectContainer[])) 345 throw new IllegalStateException("field claims to be embedded, but persistent field value is not an array of EmbeddedObjectContainer! fieldID=" + fieldMeta.getFieldID() + " fieldName=" + fieldMeta.getFieldName() + " value=" + value); 346 347 EmbeddedObjectContainer[] embeddedObjectContainers = (EmbeddedObjectContainer[]) value; 348 int arrayLength = embeddedObjectContainers.length; 349 array = Array.newInstance(elementType, arrayLength); 350 ClassMeta embeddedClassMeta = fieldMeta.getSubFieldMeta(FieldMetaRole.arrayElement).getEmbeddedClassMeta(); 351 for (int i = 0; i < arrayLength; ++i) { 352 Object pc = createPCFromEmbeddedObjectContainer(fieldNumber, fieldMeta, embeddedClassMeta, embeddedObjectContainers[i]); 353 Array.set(array, i, pc); 354 } 355 } 356 else { 357 if (mmd.getMappedBy() != null) { 358 int arrayLength = mappedByDataEntryIDs.size(); 359 array = Array.newInstance(elementType, arrayLength); 360 Iterator<Long> it = mappedByDataEntryIDs.iterator(); 361 for (int i = 0; i < arrayLength; ++i) { 362 Long dataEntryID = it.next(); 363 Object element = getObjectFromDataEntryID(dataEntryID); 364 Array.set(array, i, element); 365 } 366 } 367 else { 368 Object ids = objectContainer.getValue(fieldMeta.getFieldID()); 369 if (ids == null) 370 array = null; 371 else { 372 if (((Cumulus4jPersistenceHandler)ec.getStoreManager().getPersistenceHandler()).useReferentialIntegrity()) { 373 // Directly fill the array. 374 int arrayLength = Array.getLength(ids); 375 array = Array.newInstance(elementType, arrayLength); 376 for (int i = 0; i < arrayLength; ++i) { 377 Object id = Array.get(ids, i); 378 Object element = ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, id); 379 Array.set(array, i, element); 380 } 381 } 382 else { 383 // https://sourceforge.net/tracker/?func=detail&aid=3515529&group_id=517465&atid=2102914 384 // First fill a list and then transfer everything into an array, because there might 385 // be elements missing (orphaned references). 386 int arrayLength = Array.getLength(ids); 387 ArrayList<Object> tmpList = new ArrayList<Object>(); 388 for (int i = 0; i < arrayLength; ++i) { 389 Object id = Array.get(ids, i); 390 Object element = ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, id); 391 if (element != null) 392 tmpList.add(element); 393 } 394 array = Array.newInstance(elementType, tmpList.size()); 395 array = tmpList.toArray((Object[]) array); 396 } 397 } 398 } 399 } 400 401 return array; 402 } 403 404 /** 405 * Fetch a {@link Collection} (<code>List</code>, <code>Set</code>, etc.) of 406 * related objects (1-n-relationship). 407 * The related objects are persistence-capable. 408 */ 409 protected Object fetchObjectFieldWithRelationTypeCollection(int fieldNumber, AbstractMemberMetaData mmd, FieldMeta fieldMeta, Set<Long> mappedByDataEntryIDs) { 410 Collection<Object> collection; 411 @SuppressWarnings("unchecked") 412 Class<? extends Collection<Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 413 try { 414 collection = instanceType.newInstance(); 415 } catch (InstantiationException e) { 416 throw new NucleusDataStoreException(e.getMessage(), e); 417 } catch (IllegalAccessException e) { 418 throw new NucleusDataStoreException(e.getMessage(), e); 419 } 420 421 if (mmd.getCollection().isEmbeddedElement()) { 422 Object value = objectContainer.getValue(fieldMeta.getFieldID()); 423 if (!(value instanceof EmbeddedObjectContainer[])) 424 throw new IllegalStateException("field claims to be embedded, but persistent field value is not an array of EmbeddedObjectContainer! fieldID=" + fieldMeta.getFieldID() + " fieldName=" + fieldMeta.getFieldName() + " value=" + value); 425 426 EmbeddedObjectContainer[] embeddedObjectContainers = (EmbeddedObjectContainer[]) value; 427 ClassMeta embeddedClassMeta = fieldMeta.getSubFieldMeta(FieldMetaRole.collectionElement).getEmbeddedClassMeta(); 428 for (EmbeddedObjectContainer embeddedObjectContainer : embeddedObjectContainers) { 429 Object pc = createPCFromEmbeddedObjectContainer(fieldNumber, fieldMeta, embeddedClassMeta, embeddedObjectContainer); 430 collection.add(pc); 431 } 432 } 433 else { 434 if (mmd.getMappedBy() != null) { 435 for (Long mappedByDataEntryID : mappedByDataEntryIDs) { 436 Object element = getObjectFromDataEntryID(mappedByDataEntryID); 437 collection.add(element); 438 } 439 } 440 else { 441 Object ids = objectContainer.getValue(fieldMeta.getFieldID()); 442 if (ids != null) { 443 for (int idx = 0; idx < Array.getLength(ids); ++idx) { 444 Object id = Array.get(ids, idx); 445 Object element = ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, id); 446 if (element != null) // orphaned reference - https://sourceforge.net/tracker/?func=detail&aid=3515529&group_id=517465&atid=2102914 447 collection.add(element); 448 } 449 } 450 } 451 } 452 453 return op.wrapSCOField(fieldNumber, collection, false, false, true); 454 } 455 456 /** 457 * Fetch a {@link Map} of related objects (1-n-relationship). 458 * The related objects are persistence-capable. 459 */ 460 protected Object fetchObjectFieldWithRelationTypeMap(int fieldNumber, AbstractMemberMetaData mmd, FieldMeta fieldMeta, Set<Long> mappedByDataEntryIDs) 461 { 462 Map<Object, Object> map; 463 @SuppressWarnings("unchecked") 464 Class<? extends Map<Object, Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 465 try { 466 map = instanceType.newInstance(); 467 } catch (InstantiationException e) { 468 throw new NucleusDataStoreException(e.getMessage(), e); 469 } catch (IllegalAccessException e) { 470 throw new NucleusDataStoreException(e.getMessage(), e); 471 } 472 473 boolean keyIsPersistent = mmd.getMap().keyIsPersistent(); 474 boolean valueIsPersistent = mmd.getMap().valueIsPersistent(); 475 476 if (mmd.getMappedBy() != null) { 477 FieldMeta oppositeFieldMetaKey = fieldMeta.getSubFieldMeta(FieldMetaRole.mapKey).getMappedByFieldMeta(ec); 478 FieldMeta oppositeFieldMetaValue = fieldMeta.getSubFieldMeta(FieldMetaRole.mapValue).getMappedByFieldMeta(ec); 479 480 for (Long mappedByDataEntryID : mappedByDataEntryIDs) { 481 Object element = getObjectFromDataEntryID(mappedByDataEntryID); 482 ObjectProvider elementOP = ec.findObjectProvider(element); 483 if (elementOP == null) 484 throw new IllegalStateException("executionContext.findObjectProvider(element) returned null for " + element); 485 486 Object key; 487 if (keyIsPersistent) 488 key = element; 489 else 490 key = elementOP.provideField(oppositeFieldMetaKey.getDataNucleusAbsoluteFieldNumber(ec)); 491 492 Object value; 493 if (valueIsPersistent) 494 value = element; 495 else 496 value = elementOP.provideField(oppositeFieldMetaValue.getDataNucleusAbsoluteFieldNumber(ec)); 497 498 map.put(key, value); 499 } 500 } 501 else { 502 Map<?,?> idMap = (Map<?,?>) objectContainer.getValue(fieldMeta.getFieldID()); 503 if (idMap != null) { 504 for (Map.Entry<?, ?> me : idMap.entrySet()) { 505 Object k = me.getKey(); 506 Object v = me.getValue(); 507 508 if (keyIsPersistent) { 509 k = ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, k); 510 if (k == null) // orphaned reference - https://sourceforge.net/tracker/?func=detail&aid=3515529&group_id=517465&atid=2102914 511 continue; 512 } 513 514 if (valueIsPersistent) { 515 v = ObjectContainerHelper.referenceToEntity(cryptoContext, pmData, v); 516 if (v == null) // orphaned reference - https://sourceforge.net/tracker/?func=detail&aid=3515529&group_id=517465&atid=2102914 517 continue; 518 } 519 520 map.put(k, v); 521 } 522 } 523 } 524 525 return op.wrapSCOField(fieldNumber, map, false, false, true); 526 } 527 528 protected Object getObjectFromDataEntryID(long dataEntryID) 529 { 530 String idStr = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntry(dataEntryID).getObjectID(); 531 return IdentityUtils.getObjectFromIdString( 532 idStr, classMeta.getDataNucleusClassMetaData(ec), ec, true 533 ); 534 } 535 }