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.HashMap; 021 import java.util.Map; 022 023 import javax.jdo.PersistenceManager; 024 025 import org.cumulus4j.store.model.ClassMeta; 026 import org.cumulus4j.store.model.DataEntry; 027 import org.cumulus4j.store.model.ObjectContainer; 028 import org.datanucleus.identity.IdentityUtils; 029 import org.datanucleus.metadata.AbstractClassMetaData; 030 import org.datanucleus.store.ExecutionContext; 031 import org.slf4j.Logger; 032 import org.slf4j.LoggerFactory; 033 034 /** 035 * Helper class for replacing object-references when storing a 1-1- or 1-n- or m-n-relationship 036 * inside an {@link ObjectContainer}. 037 * 038 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 039 */ 040 public final class ObjectContainerHelper 041 { 042 private static final Logger logger = LoggerFactory.getLogger(ObjectContainerHelper.class); 043 044 /** 045 * If <code>false</code>, store object-ID in {@link ObjectContainer}. 046 * If <code>true</code>, store {@link DataEntry#getDataEntryID() dataEntryID} in {@link ObjectContainer}. 047 */ 048 private static final boolean USE_DATA_ENTRY_ID = true; 049 050 private ObjectContainerHelper() { } 051 052 private static final class TemporaryReferenceDataEntry { 053 public String objectID; 054 public ClassMeta classMeta; 055 } 056 057 private static final String PM_DATA_KEY_TEMPORARY_REFERENCE_DATA_ENTRY_MAP = "temporaryReferenceDataEntryMap"; 058 059 private static void registerTemporaryReferenceDataEntry(PersistenceManager pmData, DataEntry dataEntry) 060 { 061 @SuppressWarnings("unchecked") 062 Map<String, TemporaryReferenceDataEntry> objectID2tempRefMap = (Map<String, TemporaryReferenceDataEntry>) pmData.getUserObject(PM_DATA_KEY_TEMPORARY_REFERENCE_DATA_ENTRY_MAP); 063 if (objectID2tempRefMap == null) { 064 objectID2tempRefMap = new HashMap<String, TemporaryReferenceDataEntry>(); 065 pmData.putUserObject(PM_DATA_KEY_TEMPORARY_REFERENCE_DATA_ENTRY_MAP, objectID2tempRefMap); 066 } 067 068 TemporaryReferenceDataEntry trde = new TemporaryReferenceDataEntry(); 069 trde.objectID = dataEntry.getObjectID(); 070 trde.classMeta = dataEntry.getClassMeta(); 071 objectID2tempRefMap.put(trde.objectID, trde); 072 } 073 074 public static DataEntry popTemporaryReferenceDataEntry(PersistenceManager pmData, String objectIDString) 075 { 076 @SuppressWarnings("unchecked") 077 Map<String, TemporaryReferenceDataEntry> objectID2tempRefMap = (Map<String, TemporaryReferenceDataEntry>) pmData.getUserObject(PM_DATA_KEY_TEMPORARY_REFERENCE_DATA_ENTRY_MAP); 078 if (objectID2tempRefMap == null) 079 return null; 080 081 TemporaryReferenceDataEntry trde = objectID2tempRefMap.remove(objectIDString); 082 if (trde == null) 083 return null; 084 085 DataEntry dataEntry = DataEntry.getDataEntry(pmData, trde.classMeta, objectIDString); 086 return dataEntry; 087 } 088 089 @SuppressWarnings("unused") 090 public static Object entityToReference(ExecutionContext ec, PersistenceManager pmData, Object entity) 091 { 092 if (entity == null) 093 return null; 094 095 Cumulus4jStoreManager storeManager = (Cumulus4jStoreManager) ec.getStoreManager(); 096 Object objectID = ec.getApiAdapter().getIdForObject(entity); 097 if (objectID == null) 098 throw new IllegalStateException("executionContext.getApiAdapter().getIdForObject(entity) returned null for " + entity); 099 100 storeManager.setClassNameForObjectID(objectID, entity.getClass().getName()); 101 102 if (USE_DATA_ENTRY_ID) { 103 ClassMeta classMeta = storeManager.getClassMeta(ec, entity.getClass()); 104 String objectIDString = objectID.toString(); 105 Long dataEntryID = DataEntry.getDataEntryID(pmData, classMeta, objectIDString); 106 if (dataEntryID == null) { 107 // Referenced entity not yet persisted => Create a temporarily empty DataEntry. It should be 108 // filled later when Cumulus4jPersistenceHandler.insertObject(...) is called for this entity. 109 // 110 // TODO If we ever stumble over empty DataEntry objects in the database, we should add a sanity check, 111 // which checks at the end of a flush(...) or commit(...) whether all of the DataEntry objects created here 112 // were actually post-processed by a call to Cumulus4jPersistenceHandler.insertObject(...). Marco :-) 113 DataEntry dataEntry = pmData.makePersistent(new DataEntry(classMeta, objectIDString)); 114 dataEntryID = dataEntry.getDataEntryID(); 115 registerTemporaryReferenceDataEntry(pmData, dataEntry); 116 logger.trace("entityToReference: Created temporary-reference-DataEntry for: {}", objectIDString); 117 // throw new IllegalStateException("DataEntry.getDataEntryID(...) returned null for entity=\"" + entity + "\" with objectID=\"" + objectID + "\""); 118 } 119 120 return dataEntryID; 121 } 122 123 return objectID; 124 } 125 126 @SuppressWarnings("unused") 127 public static Object referenceToEntity(ExecutionContext ec, PersistenceManager pmData, Object reference) 128 { 129 if (reference == null) 130 return null; 131 132 if (USE_DATA_ENTRY_ID) { 133 DataEntry dataEntry = DataEntry.getDataEntry(pmData, ((Long)reference).longValue()); 134 if (dataEntry == null) 135 throw new IllegalStateException("DataEntry.getDataEntry(...) returned null for reference=\"" + reference + "\"!"); 136 137 AbstractClassMetaData cmd = dataEntry.getClassMeta().getDataNucleusClassMetaData(ec); 138 return IdentityUtils.getObjectFromIdString(dataEntry.getObjectID(), cmd, ec, true); 139 } 140 141 return ec.findObject(reference, true, true, null); 142 } 143 144 @SuppressWarnings("unused") 145 public static Long referenceToDataEntryID(ExecutionContext ec, PersistenceManager pmData, Object reference) 146 { 147 if (reference == null) 148 return null; 149 150 if (USE_DATA_ENTRY_ID) 151 return (Long)reference; 152 153 Cumulus4jStoreManager storeManager = (Cumulus4jStoreManager) ec.getStoreManager(); 154 String clazzName = storeManager.getClassNameForObjectID(reference, ec.getClassLoaderResolver(), ec); 155 Class<?> clazz = ec.getClassLoaderResolver().classForName(clazzName); 156 ClassMeta classMeta = storeManager.getClassMeta(ec, clazz); 157 return DataEntry.getDataEntryID(pmData, classMeta, reference.toString()); 158 } 159 }