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; 019 020 import java.util.Arrays; 021 import java.util.Map; 022 023 import javax.jdo.PersistenceManager; 024 025 import org.cumulus4j.store.crypto.CryptoContext; 026 import org.cumulus4j.store.fieldmanager.FetchFieldManager; 027 import org.cumulus4j.store.fieldmanager.StoreFieldManager; 028 import org.cumulus4j.store.model.ClassMeta; 029 import org.cumulus4j.store.model.DataEntry; 030 import org.cumulus4j.store.model.DataEntryDAO; 031 import org.cumulus4j.store.model.EmbeddedClassMeta; 032 import org.cumulus4j.store.model.EmbeddedFieldMeta; 033 import org.cumulus4j.store.model.EmbeddedObjectContainer; 034 import org.cumulus4j.store.model.FieldMeta; 035 import org.cumulus4j.store.model.ObjectContainer; 036 import org.datanucleus.ExecutionContext; 037 import org.datanucleus.exceptions.NucleusObjectNotFoundException; 038 import org.datanucleus.metadata.AbstractClassMetaData; 039 import org.datanucleus.metadata.AbstractMemberMetaData; 040 import org.datanucleus.state.ObjectProvider; 041 import org.datanucleus.store.AbstractPersistenceHandler; 042 import org.datanucleus.store.connection.ManagedConnection; 043 import org.slf4j.Logger; 044 import org.slf4j.LoggerFactory; 045 046 /** 047 * Handler for all persistence calls from the StoreManager, communicating with the backend datastore(s). 048 * Manages all inserts/updates/deletes/fetches/locates of the users own objects and translates them 049 * into inserts/updates/deletes/fetches/locates of Cumulus4J model objects. 050 */ 051 public class Cumulus4jPersistenceHandler extends AbstractPersistenceHandler 052 { 053 private static final Logger logger = LoggerFactory.getLogger(Cumulus4jPersistenceHandler.class); 054 055 private Cumulus4jStoreManager storeManager; 056 private EncryptionCoordinateSetManager encryptionCoordinateSetManager; 057 private KeyStoreRefManager keyStoreRefManager; 058 private EncryptionHandler encryptionHandler; 059 060 private IndexEntryAction addIndexEntryAction; 061 private IndexEntryAction removeIndexEntryAction; 062 063 private static <T> T assertNotNull(String objectName, T object) { 064 if (object == null) 065 throw new IllegalArgumentException(objectName + " == null"); 066 067 return object; 068 } 069 070 public Cumulus4jPersistenceHandler(Cumulus4jStoreManager storeManager) { 071 super(assertNotNull("storeManager", storeManager)); 072 this.storeManager = storeManager; 073 this.encryptionCoordinateSetManager = storeManager.getEncryptionCoordinateSetManager(); 074 this.keyStoreRefManager = storeManager.getKeyStoreRefManager(); 075 this.encryptionHandler = storeManager.getEncryptionHandler(); 076 077 this.addIndexEntryAction = new IndexEntryAction.Add(this); 078 this.removeIndexEntryAction = new IndexEntryAction.Remove(this); 079 } 080 081 public Cumulus4jStoreManager getStoreManager() { 082 return storeManager; 083 } 084 085 @Override 086 public void close() { 087 // No resources require to be closed here. 088 } 089 090 @Override 091 public void deleteObject(ObjectProvider op) { 092 // Check if read-only so update not permitted 093 storeManager.assertReadOnlyForUpdateOfObject(op); 094 095 ExecutionContext ec = op.getExecutionContext(); 096 ManagedConnection mconn = storeManager.getConnection(ec); 097 try { 098 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection(); 099 PersistenceManager pmData = pmConn.getDataPM(); 100 CryptoContext cryptoContext = new CryptoContext(encryptionCoordinateSetManager, keyStoreRefManager, ec, pmConn); 101 getStoreManager().getDatastoreVersionManager().applyOnce(cryptoContext); 102 103 Object object = op.getObject(); 104 Object objectID = op.getExternalObjectId(); 105 String objectIDString = objectID.toString(); 106 final ClassMeta classMeta = storeManager.getClassMeta(ec, object.getClass()); 107 DataEntry dataEntry = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntry(classMeta, objectIDString); 108 // if (dataEntry == null) 109 // throw new NucleusObjectNotFoundException("Object does not exist in datastore: class=" + classMeta.getClassName() + " oid=" + objectIDString); 110 111 if (dataEntry != null) { 112 // decrypt object-container in order to identify index entries for deletion 113 ObjectContainer objectContainer = encryptionHandler.decryptDataEntry(cryptoContext, dataEntry); 114 if (objectContainer != null) { 115 AbstractClassMetaData dnClassMetaData = storeManager.getMetaDataManager().getMetaDataForClass(object.getClass(), ec.getClassLoaderResolver()); 116 117 deleteObjectIndex(cryptoContext, classMeta, dataEntry, objectContainer, dnClassMetaData); 118 } 119 pmData.deletePersistent(dataEntry); 120 } 121 122 } finally { 123 mconn.release(); 124 } 125 } 126 127 protected void deleteObjectIndex( 128 CryptoContext cryptoContext, final ClassMeta classMeta, DataEntry dataEntry, 129 ObjectContainer objectContainer, AbstractClassMetaData dnClassMetaData 130 ) 131 { 132 for (Map.Entry<Long, ?> me : objectContainer.getFieldID2value().entrySet()) { 133 long fieldID = me.getKey(); 134 Object fieldValue = me.getValue(); 135 FieldMeta fieldMeta = classMeta.getFieldMeta(fieldID); 136 deleteObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, fieldValue); 137 } 138 } 139 140 protected void deleteObjectIndex( 141 CryptoContext cryptoContext, ClassMeta classMeta, DataEntry dataEntry, 142 FieldMeta fieldMeta, EmbeddedObjectContainer embeddedObjectContainer 143 ) 144 { 145 ClassMeta embeddedClassMeta = storeManager.getClassMeta(cryptoContext.getExecutionContext(), embeddedObjectContainer.getClassID(), true); 146 EmbeddedClassMeta ecm = (EmbeddedClassMeta) embeddedClassMeta; 147 for (Map.Entry<Long, ?> me : embeddedObjectContainer.getFieldID2value().entrySet()) { 148 long embeddedFieldID = me.getKey(); 149 Object embeddedFieldValue = me.getValue(); 150 EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) ecm.getFieldMeta(embeddedFieldID); 151 deleteObjectIndex(cryptoContext, embeddedClassMeta, dataEntry, embeddedFieldMeta, embeddedFieldValue); 152 } 153 } 154 155 protected void deleteObjectIndex( 156 CryptoContext cryptoContext, ClassMeta classMeta, DataEntry dataEntry, 157 FieldMeta fieldMeta, Object fieldValue 158 ) 159 { 160 if (fieldValue instanceof EmbeddedObjectContainer) { 161 EmbeddedObjectContainer embeddedObjectContainer = (EmbeddedObjectContainer) fieldValue; 162 deleteObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, embeddedObjectContainer); 163 } 164 else if (fieldValue instanceof EmbeddedObjectContainer[]) { 165 EmbeddedObjectContainer[] embeddedObjectContainers = (EmbeddedObjectContainer[]) fieldValue; 166 for (EmbeddedObjectContainer embeddedObjectContainer : embeddedObjectContainers) { 167 if (embeddedObjectContainer != null) 168 deleteObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, embeddedObjectContainer); 169 } 170 } 171 else { 172 // AbstractMemberMetaData dnMemberMetaData = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldMeta.getDataNucleusAbsoluteFieldNumber()); 173 AbstractMemberMetaData dnMemberMetaData = fieldMeta.getDataNucleusMemberMetaData(cryptoContext.getExecutionContext()); 174 175 // sanity checks 176 if (dnMemberMetaData == null) 177 throw new IllegalStateException("dnMemberMetaData == null!!! class == \"" + classMeta.getClassName() + "\" fieldMeta.dataNucleusAbsoluteFieldNumber == " + fieldMeta.getDataNucleusAbsoluteFieldNumber() + " fieldMeta.fieldName == \"" + fieldMeta.getFieldName() + "\""); 178 179 if (!fieldMeta.getFieldName().equals(dnMemberMetaData.getName())) 180 throw new IllegalStateException("Meta data inconsistency!!! class == \"" + classMeta.getClassName() + "\" fieldMeta.dataNucleusAbsoluteFieldNumber == " + fieldMeta.getDataNucleusAbsoluteFieldNumber() + " fieldMeta.fieldName == \"" + fieldMeta.getFieldName() + "\" != dnMemberMetaData.name == \"" + dnMemberMetaData.getName() + "\""); 181 182 removeIndexEntryAction.perform(cryptoContext, dataEntry.getDataEntryID(), fieldMeta, dnMemberMetaData, classMeta, fieldValue); 183 } 184 } 185 186 @Override 187 public void fetchObject(ObjectProvider op, int[] fieldNumbers) 188 { 189 ExecutionContext ec = op.getExecutionContext(); 190 ManagedConnection mconn = storeManager.getConnection(ec); 191 try { 192 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection(); 193 PersistenceManager pmData = pmConn.getDataPM(); 194 CryptoContext cryptoContext = new CryptoContext(encryptionCoordinateSetManager, keyStoreRefManager, ec, pmConn); 195 getStoreManager().getDatastoreVersionManager().applyOnce(cryptoContext); 196 197 Object object = op.getObject(); 198 Object objectID = op.getExternalObjectId(); 199 String objectIDString = objectID.toString(); 200 final ClassMeta classMeta = storeManager.getClassMeta(ec, object.getClass()); 201 AbstractClassMetaData dnClassMetaData = storeManager.getMetaDataManager().getMetaDataForClass(object.getClass(), ec.getClassLoaderResolver()); 202 203 // TODO Maybe we should load ALL *SIMPLE* fields, because the decryption happens on a per-row-level and thus 204 // loading only some fields makes no sense performance-wise. However, maybe DataNucleus already optimizes 205 // calls to this method. It makes definitely no sense to load 1-n- or 1-1-fields and it makes no sense to 206 // optimize things that already are optimal. Hence we have to analyze first, how often this method is really 207 // called in normal operation. 208 // Marco. 209 210 DataEntry dataEntry = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntry(classMeta, objectIDString); 211 if (dataEntry == null) 212 throw new NucleusObjectNotFoundException("Object does not exist in datastore: class=" + classMeta.getClassName() + " oid=" + objectIDString); 213 214 ObjectContainer objectContainer = encryptionHandler.decryptDataEntry(cryptoContext, dataEntry); 215 216 op.replaceFields(fieldNumbers, new FetchFieldManager(op, cryptoContext, classMeta, dnClassMetaData, objectContainer)); 217 if (op.getVersion() == null) // null-check prevents overwriting in case this method is called multiple times (for different field-numbers) - TODO necessary? 218 op.setVersion(objectContainer.getVersion()); 219 } finally { 220 mconn.release(); 221 } 222 } 223 224 @Override 225 public Object findObject(ExecutionContext ec, Object id) { 226 // Since we don't manage the memory instantiation of objects this just returns null. 227 return null; 228 } 229 230 @Override 231 public void insertObjects(ObjectProvider ... ops) { 232 boolean error = true; 233 ObjectContainerHelper.enterTemporaryReferenceScope(); 234 try { 235 super.insertObjects(ops); 236 237 error = false; 238 } finally { 239 ObjectContainerHelper.exitTemporaryReferenceScope(error); 240 } 241 } 242 243 @Override 244 public void deleteObjects(ObjectProvider... ops) { 245 boolean error = true; 246 ObjectContainerHelper.enterTemporaryReferenceScope(); 247 try { 248 super.deleteObjects(ops); 249 250 error = false; 251 } finally { 252 ObjectContainerHelper.exitTemporaryReferenceScope(error); 253 } 254 } 255 256 257 @Override 258 public void insertObject(ObjectProvider op) 259 { 260 // Check if read-only so update not permitted 261 storeManager.assertReadOnlyForUpdateOfObject(op); 262 263 if (op.getEmbeddedOwners() != null && op.getEmbeddedOwners().length > 0) { 264 return; // don't handle embedded objects here! 265 } 266 267 ExecutionContext ec = op.getExecutionContext(); 268 ManagedConnection mconn = storeManager.getConnection(ec); 269 try { 270 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection(); 271 PersistenceManager pmData = pmConn.getDataPM(); 272 CryptoContext cryptoContext = new CryptoContext(encryptionCoordinateSetManager, keyStoreRefManager, ec, pmConn); 273 getStoreManager().getDatastoreVersionManager().applyOnce(cryptoContext); 274 275 boolean error = true; 276 ObjectContainerHelper.enterTemporaryReferenceScope(); 277 try { 278 Object object = op.getObject(); 279 Object objectID = op.getExternalObjectId(); 280 if (objectID == null) { 281 throw new IllegalStateException("op.getExternalObjectId() returned null! Maybe Cumulus4jStoreManager.isStrategyDatastoreAttributed(...) is incorrect?"); 282 } 283 final ClassMeta classMeta = storeManager.getClassMeta(ec, object.getClass()); 284 285 AbstractClassMetaData dnClassMetaData = storeManager.getMetaDataManager().getMetaDataForClass(object.getClass(), ec.getClassLoaderResolver()); 286 287 ObjectContainer objectContainer = new ObjectContainer(); 288 String objectIDString = objectID.toString(); 289 290 // We have to persist the DataEntry before the call to provideFields(...), because the InsertFieldManager recursively 291 // persists other fields which might back-reference (=> mapped-by) and thus need this DataEntry to already exist. 292 // TO DO Try to make this persistent afterwards and solve the problem by only allocating the ID before [keeping it in memory] (see Cumulus4jStoreManager#nextDataEntryID(), which is commented out currently). 293 // Even though reducing the INSERT + UPDATE to one single INSERT in the handling of IndexEntry made 294 // things faster, it seems not to have a performance benefit here. But we should still look at this 295 // again later. 296 // Marco. 297 // 298 // 2012-02-02: Refactored this because of a Heisenbug with optimistic transactions. At the same time solved 299 // the above to do. Marco :-) 300 301 // This performs reachability on this input object so that all related objects are persisted. 302 op.provideFields( 303 dnClassMetaData.getAllMemberPositions(), 304 new StoreFieldManager(op, cryptoContext, pmData, classMeta, dnClassMetaData, cryptoContext.getKeyStoreRefID(), objectContainer)); 305 objectContainer.setVersion(op.getTransactionalVersion()); 306 307 // The DataEntry might already have been written by ObjectContainerHelper.entityToReference(...), 308 // if it was needed for a reference. We therefore check, if it already exists (and update it then instead of insert). 309 boolean persistDataEntry = false; 310 DataEntry dataEntry = ObjectContainerHelper.getTemporaryReferenceDataEntry(cryptoContext, pmData, objectIDString); 311 if (dataEntry != null) 312 logger.trace("insertObject: Found temporary-reference-DataEntry for: {}", objectIDString); 313 else { 314 persistDataEntry = true; 315 dataEntry = new DataEntry(classMeta, cryptoContext.getKeyStoreRefID(), objectIDString); 316 logger.trace("insertObject: Created new DataEntry for: {}", objectIDString); 317 } 318 319 encryptionHandler.encryptDataEntry(cryptoContext, dataEntry, objectContainer); 320 321 // persist data 322 if (persistDataEntry) { 323 dataEntry = pmData.makePersistent(dataEntry); 324 logger.trace("insertObject: Persisted new non-embedded DataEntry for: {}", objectIDString); 325 } 326 327 insertObjectIndex(op, cryptoContext, classMeta, dnClassMetaData, objectContainer, dataEntry); 328 329 error = false; 330 } finally { 331 ObjectContainerHelper.exitTemporaryReferenceScope(error); 332 } 333 } finally { 334 mconn.release(); 335 } 336 } 337 338 protected void insertObjectIndex( 339 ObjectProvider op, CryptoContext cryptoContext, 340 ClassMeta classMeta, AbstractClassMetaData dnClassMetaData, 341 ObjectContainer objectContainer, DataEntry dataEntry 342 ) 343 { 344 // persist index 345 for (Map.Entry<Long, ?> me : objectContainer.getFieldID2value().entrySet()) { 346 long fieldID = me.getKey(); 347 Object fieldValue = me.getValue(); 348 FieldMeta fieldMeta = classMeta.getFieldMeta(fieldID); 349 if (fieldMeta == null) 350 throw new IllegalStateException("fieldMeta not found: " + classMeta + ": fieldID=" + fieldID); 351 352 insertObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, fieldValue); 353 } 354 } 355 356 protected void insertObjectIndex( 357 CryptoContext cryptoContext, ClassMeta classMeta, DataEntry dataEntry, 358 FieldMeta fieldMeta, EmbeddedObjectContainer embeddedObjectContainer 359 ) 360 { 361 ClassMeta embeddedClassMeta = storeManager.getClassMeta(cryptoContext.getExecutionContext(), embeddedObjectContainer.getClassID(), true); 362 EmbeddedClassMeta ecm = (EmbeddedClassMeta) embeddedClassMeta; 363 for (Map.Entry<Long, ?> me : embeddedObjectContainer.getFieldID2value().entrySet()) { 364 long embeddedFieldID = me.getKey(); 365 Object embeddedFieldValue = me.getValue(); 366 EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) ecm.getFieldMeta(embeddedFieldID); 367 if (embeddedFieldMeta == null) 368 throw new IllegalStateException("fieldMeta not found: " + classMeta + ": embeddedFieldID=" + embeddedFieldID); 369 370 insertObjectIndex(cryptoContext, embeddedClassMeta, dataEntry, embeddedFieldMeta, embeddedFieldValue); 371 } 372 } 373 374 protected void insertObjectIndex( 375 CryptoContext cryptoContext, ClassMeta classMeta, DataEntry dataEntry, 376 FieldMeta fieldMeta, Object fieldValue 377 ) 378 { 379 if (cryptoContext == null) 380 throw new IllegalArgumentException("cryptoContext == null"); 381 382 if (classMeta == null) 383 throw new IllegalArgumentException("classMeta == null"); 384 385 if (dataEntry == null) 386 throw new IllegalArgumentException("dataEntry == null"); 387 388 if (fieldMeta == null) 389 throw new IllegalArgumentException("fieldMeta == null"); 390 391 if (fieldValue instanceof EmbeddedObjectContainer) { 392 EmbeddedObjectContainer embeddedObjectContainer = (EmbeddedObjectContainer) fieldValue; 393 insertObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, embeddedObjectContainer); 394 } 395 else if (fieldValue instanceof EmbeddedObjectContainer[]) { 396 EmbeddedObjectContainer[] embeddedObjectContainers = (EmbeddedObjectContainer[]) fieldValue; 397 for (EmbeddedObjectContainer embeddedObjectContainer : embeddedObjectContainers) { 398 if (embeddedObjectContainer != null) 399 insertObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, embeddedObjectContainer); 400 } 401 } 402 else { 403 AbstractMemberMetaData dnMemberMetaData = fieldMeta.getDataNucleusMemberMetaData(cryptoContext.getExecutionContext()); 404 405 // sanity checks 406 if (dnMemberMetaData == null) 407 throw new IllegalStateException("dnMemberMetaData == null!!! class == \"" + classMeta.getClassName() + "\" fieldMeta.dataNucleusAbsoluteFieldNumber == " + fieldMeta.getDataNucleusAbsoluteFieldNumber() + " fieldMeta.fieldName == \"" + fieldMeta.getFieldName() + "\""); 408 409 if (!fieldMeta.getFieldName().equals(dnMemberMetaData.getName())) 410 throw new IllegalStateException("Meta data inconsistency!!! class == \"" + classMeta.getClassName() + "\" fieldMeta.dataNucleusAbsoluteFieldNumber == " + fieldMeta.getDataNucleusAbsoluteFieldNumber() + " fieldMeta.fieldName == \"" + fieldMeta.getFieldName() + "\" != dnMemberMetaData.name == \"" + dnMemberMetaData.getName() + "\""); 411 412 addIndexEntryAction.perform(cryptoContext, dataEntry.getDataEntryID(), fieldMeta, dnMemberMetaData, classMeta, fieldValue); 413 } 414 } 415 416 @Override 417 public void locateObject(ObjectProvider op) 418 { 419 ExecutionContext ec = op.getExecutionContext(); 420 ManagedConnection mconn = storeManager.getConnection(op.getExecutionContext()); 421 try { 422 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection(); 423 PersistenceManager pmData = pmConn.getDataPM(); 424 CryptoContext cryptoContext = new CryptoContext(encryptionCoordinateSetManager, keyStoreRefManager, ec, pmConn); 425 getStoreManager().getDatastoreVersionManager().applyOnce(cryptoContext); 426 427 ClassMeta classMeta = storeManager.getClassMeta(op.getExecutionContext(), op.getObject().getClass()); 428 Object objectID = op.getExternalObjectId(); 429 String objectIDString = objectID.toString(); 430 431 DataEntry dataEntry = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntry(classMeta, objectIDString); 432 if (dataEntry == null) 433 throw new NucleusObjectNotFoundException("Object does not exist in datastore: class=" + classMeta.getClassName() + " oid=" + objectIDString); 434 } finally { 435 mconn.release(); 436 } 437 } 438 439 @Override 440 public void updateObject(ObjectProvider op, int[] fieldNumbers) 441 { 442 // Check if read-only so update not permitted 443 storeManager.assertReadOnlyForUpdateOfObject(op); 444 445 if (op.getEmbeddedOwners() != null && op.getEmbeddedOwners().length > 0) { 446 return; // don't handle embedded objects here! 447 } 448 449 ExecutionContext ec = op.getExecutionContext(); 450 ManagedConnection mconn = storeManager.getConnection(ec); 451 try { 452 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection(); 453 PersistenceManager pmData = pmConn.getDataPM(); 454 CryptoContext cryptoContext = new CryptoContext(encryptionCoordinateSetManager, keyStoreRefManager, ec, pmConn); 455 getStoreManager().getDatastoreVersionManager().applyOnce(cryptoContext); 456 457 boolean error = true; 458 ObjectContainerHelper.enterTemporaryReferenceScope(); 459 try { 460 461 Object object = op.getObject(); 462 Object objectID = op.getExternalObjectId(); 463 String objectIDString = objectID.toString(); 464 final ClassMeta classMeta = storeManager.getClassMeta(ec, object.getClass()); 465 AbstractClassMetaData dnClassMetaData = storeManager.getMetaDataManager().getMetaDataForClass(object.getClass(), ec.getClassLoaderResolver()); 466 467 DataEntry dataEntry = new DataEntryDAO(pmData, cryptoContext.getKeyStoreRefID()).getDataEntry(classMeta, objectIDString); 468 if (dataEntry == null) 469 throw new NucleusObjectNotFoundException("Object does not exist in datastore: class=" + classMeta.getClassName() + " oid=" + objectIDString); 470 471 ObjectContainer objectContainerOld = encryptionHandler.decryptDataEntry(cryptoContext, dataEntry); 472 ObjectContainer objectContainerNew = objectContainerOld.clone(); 473 474 // This performs reachability on this input object so that all related objects are persisted 475 op.provideFields(fieldNumbers, new StoreFieldManager(op, cryptoContext, pmData, classMeta, dnClassMetaData, cryptoContext.getKeyStoreRefID(), objectContainerNew)); 476 objectContainerNew.setVersion(op.getTransactionalVersion()); 477 478 // update persistent data 479 encryptionHandler.encryptDataEntry(cryptoContext, dataEntry, objectContainerNew); 480 481 // update persistent index 482 for (int fieldNumber : fieldNumbers) { 483 AbstractMemberMetaData dnMemberMetaData = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 484 if (dnMemberMetaData == null) 485 throw new IllegalStateException("dnMemberMetaData == null!!! class == \"" + classMeta.getClassName() + "\" fieldNumber == " + fieldNumber); 486 487 if (dnMemberMetaData.getMappedBy() != null) 488 continue; // TODO is this sufficient to take 'mapped-by' into account? 489 490 FieldMeta fieldMeta = classMeta.getFieldMeta(dnMemberMetaData.getClassName(), dnMemberMetaData.getName()); 491 if (fieldMeta == null) 492 throw new IllegalStateException("fieldMeta == null!!! class == \"" + classMeta.getClassName() + "\" dnMemberMetaData.className == \"" + dnMemberMetaData.getClassName() + "\" dnMemberMetaData.name == \"" + dnMemberMetaData.getName() + "\""); 493 494 Object fieldValueOld = objectContainerOld.getValue(fieldMeta.getFieldID()); 495 Object fieldValueNew = objectContainerNew.getValue(fieldMeta.getFieldID()); 496 497 if (!fieldsEqual(fieldValueOld, fieldValueNew)){ 498 499 // removeIndexEntryAction.perform(cryptoContext, dataEntryID, fieldMeta, dnMemberMetaData, classMeta, fieldValueOld); 500 // addIndexEntryAction.perform( cryptoContext, dataEntryID, fieldMeta, dnMemberMetaData, classMeta, fieldValueNew); 501 deleteObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, fieldValueOld); 502 insertObjectIndex(cryptoContext, classMeta, dataEntry, fieldMeta, fieldValueNew); 503 } 504 } 505 506 error = false; 507 } finally { 508 ObjectContainerHelper.exitTemporaryReferenceScope(error); 509 } 510 } finally { 511 mconn.release(); 512 } 513 } 514 515 private static boolean fieldsEqual(Object obj0, Object obj1) { 516 if (obj0 instanceof Object[] && obj1 instanceof Object[]) 517 return obj0 == obj1 || Arrays.equals((Object[])obj0, (Object[])obj1); 518 return obj0 == obj1 || (obj0 != null && obj0.equals(obj1)); 519 } 520 521 // TODO what happened to this method? Was it moved or renamed? I don't find it. 522 // @Override 523 public boolean useReferentialIntegrity() { 524 // https://sourceforge.net/tracker/?func=detail&aid=3515527&group_id=517465&atid=2102914 525 // return super.useReferentialIntegrity(); 526 return false; 527 } 528 529 /** 530 * Get the {@link IndexEntryAction} used to add an index-entry. 531 * @return the {@link IndexEntryAction} used to add an index-entry. Never <code>null</code>. 532 */ 533 public IndexEntryAction getAddIndexEntryAction() { 534 return addIndexEntryAction; 535 } 536 537 /** 538 * Get the {@link IndexEntryAction} used to remove an index-entry. 539 * @return the {@link IndexEntryAction} used to remove an index-entry. Never <code>null</code>. 540 */ 541 public IndexEntryAction getRemoveIndexEntryAction() { 542 return removeIndexEntryAction; 543 } 544 }