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.Collection; 022 import java.util.Collections; 023 import java.util.Iterator; 024 import java.util.Map; 025 import java.util.Set; 026 027 import javax.jdo.PersistenceManager; 028 029 import org.cumulus4j.store.Cumulus4jStoreManager; 030 import org.cumulus4j.store.EncryptionHandler; 031 import org.cumulus4j.store.ObjectContainerHelper; 032 import org.cumulus4j.store.crypto.CryptoContext; 033 import org.cumulus4j.store.model.ClassMeta; 034 import org.cumulus4j.store.model.DataEntry; 035 import org.cumulus4j.store.model.FieldMeta; 036 import org.cumulus4j.store.model.FieldMetaRole; 037 import org.cumulus4j.store.model.IndexEntry; 038 import org.cumulus4j.store.model.IndexEntryObjectRelationHelper; 039 import org.cumulus4j.store.model.IndexValue; 040 import org.cumulus4j.store.model.ObjectContainer; 041 import org.datanucleus.exceptions.NucleusDataStoreException; 042 import org.datanucleus.identity.IdentityUtils; 043 import org.datanucleus.metadata.AbstractClassMetaData; 044 import org.datanucleus.metadata.AbstractMemberMetaData; 045 import org.datanucleus.metadata.Relation; 046 import org.datanucleus.store.ExecutionContext; 047 import org.datanucleus.store.ObjectProvider; 048 import org.datanucleus.store.fieldmanager.AbstractFieldManager; 049 import org.datanucleus.store.types.sco.SCOUtils; 050 051 /** 052 * Manager for the process of fetching a user object from the datastore, handling the translation from the 053 * DataEntry object into the users own object. 054 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 055 */ 056 public class FetchFieldManager extends AbstractFieldManager 057 { 058 private ObjectProvider op; 059 private CryptoContext cryptoContext; 060 private PersistenceManager pmData; 061 private PersistenceManager pmIndex; 062 private ExecutionContext ec; 063 private ClassMeta classMeta; 064 private AbstractClassMetaData dnClassMetaData; 065 private ObjectContainer objectContainer; 066 067 public FetchFieldManager( 068 ObjectProvider op, 069 CryptoContext cryptoContext, 070 ClassMeta classMeta, 071 AbstractClassMetaData dnClassMetaData, 072 ObjectContainer objectContainer 073 ) 074 { 075 this.op = op; 076 this.cryptoContext = cryptoContext; 077 this.pmData = cryptoContext.getPersistenceManagerForData(); 078 this.pmIndex = cryptoContext.getPersistenceManagerForIndex(); 079 this.ec = op.getExecutionContext(); 080 this.classMeta = classMeta; 081 this.dnClassMetaData = dnClassMetaData; 082 this.objectContainer = objectContainer; 083 } 084 085 protected EncryptionHandler getEncryptionHandler() 086 { 087 return ((Cumulus4jStoreManager) ec.getStoreManager()).getEncryptionHandler(); 088 } 089 090 private long getFieldID(int fieldNumber) 091 { 092 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 093 094 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 095 if (fieldMeta == null) 096 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 097 098 return fieldMeta.getFieldID(); 099 } 100 101 @Override 102 public boolean fetchBooleanField(int fieldNumber) { 103 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 104 return value == null ? false : (Boolean)value; 105 } 106 107 @Override 108 public byte fetchByteField(int fieldNumber) { 109 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 110 return value == null ? 0 : (Byte)value; 111 } 112 113 @Override 114 public char fetchCharField(int fieldNumber) { 115 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 116 return value == null ? 0 : (Character)value; 117 } 118 119 @Override 120 public double fetchDoubleField(int fieldNumber) { 121 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 122 return value == null ? 0 : (Double)value; 123 } 124 125 @Override 126 public float fetchFloatField(int fieldNumber) { 127 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 128 return value == null ? 0 : (Float)value; 129 } 130 131 @Override 132 public int fetchIntField(int fieldNumber) { 133 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 134 return value == null ? 0 : (Integer)value; 135 } 136 137 @Override 138 public long fetchLongField(int fieldNumber) { 139 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 140 return value == null ? 0 : (Long)value; 141 } 142 143 @Override 144 public short fetchShortField(int fieldNumber) { 145 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 146 return value == null ? 0 : (Short)value; 147 } 148 149 @Override 150 public String fetchStringField(int fieldNumber) { 151 Object value = objectContainer.getValue(getFieldID(fieldNumber)); 152 return (String)value; 153 } 154 155 private long thisDataEntryID = -1; 156 157 protected long getThisDataEntryID() 158 { 159 if (thisDataEntryID < 0) 160 thisDataEntryID = DataEntry.getDataEntryID(pmData, classMeta, op.getObjectId().toString()); 161 162 return thisDataEntryID; 163 } 164 165 @Override 166 public Object fetchObjectField(int fieldNumber) 167 { 168 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 169 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 170 if (fieldMeta == null) 171 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 172 173 Set<Long> mappedByDataEntryIDs = null; 174 if (mmd.getMappedBy() != null) { 175 IndexEntry indexEntry = IndexEntryObjectRelationHelper.getIndexEntry(pmIndex, fieldMeta.getMappedByFieldMeta(ec), getThisDataEntryID()); 176 if (indexEntry == null) 177 mappedByDataEntryIDs = Collections.emptySet(); 178 else { 179 IndexValue indexValue = getEncryptionHandler().decryptIndexEntry(cryptoContext, indexEntry); 180 mappedByDataEntryIDs = indexValue.getDataEntryIDs(); 181 } 182 } 183 184 int relationType = mmd.getRelationType(ec.getClassLoaderResolver()); 185 186 if (relationType == Relation.NONE) 187 { 188 if (mmd.hasCollection()) 189 { 190 Collection<Object> collection; 191 @SuppressWarnings("unchecked") 192 Class<? extends Collection<Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 193 try { 194 collection = instanceType.newInstance(); 195 } catch (InstantiationException e) { 196 throw new NucleusDataStoreException(e.getMessage(), e); 197 } catch (IllegalAccessException e) { 198 throw new NucleusDataStoreException(e.getMessage(), e); 199 } 200 201 Object array = objectContainer.getValue(fieldMeta.getFieldID()); 202 for (int idx = 0; idx < Array.getLength(array); ++idx) { 203 Object element = Array.get(array, idx); 204 collection.add(element); 205 } 206 return op.wrapSCOField(fieldNumber, collection, false, false, true); 207 } 208 209 if (mmd.hasMap()) 210 { 211 Map<?,?> map = (Map<?,?>) objectContainer.getValue(fieldMeta.getFieldID()); 212 return op.wrapSCOField(fieldNumber, map, false, false, true); 213 } 214 215 // Arrays are stored 'as is', thus no conversion necessary. 216 return objectContainer.getValue(getFieldID(fieldNumber)); 217 } 218 else if (Relation.isRelationSingleValued(relationType)) 219 { 220 if (mmd.getMappedBy() != null) { 221 if (mappedByDataEntryIDs.isEmpty()) 222 return null; 223 224 if (mappedByDataEntryIDs.size() != 1) 225 throw new IllegalStateException("There are multiple objects referencing a 1-1-mapped-by-relationship! Expected 0 or 1! fieldMeta=" + fieldMeta + " dataEntryIDsForMappedBy=" + mappedByDataEntryIDs); 226 227 long dataEntryID = mappedByDataEntryIDs.iterator().next(); 228 return getObjectFromDataEntryID(dataEntryID); 229 } 230 231 Object valueID = objectContainer.getValue(fieldMeta.getFieldID()); 232 return ObjectContainerHelper.referenceToEntity(ec, pmData, valueID); 233 } 234 else if (Relation.isRelationMultiValued(relationType)) 235 { 236 // Collection/Map/Array 237 if (mmd.hasCollection()) 238 { 239 Collection<Object> collection; 240 @SuppressWarnings("unchecked") 241 Class<? extends Collection<Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 242 try { 243 collection = instanceType.newInstance(); 244 } catch (InstantiationException e) { 245 throw new NucleusDataStoreException(e.getMessage(), e); 246 } catch (IllegalAccessException e) { 247 throw new NucleusDataStoreException(e.getMessage(), e); 248 } 249 250 if (mmd.getMappedBy() != null) { 251 for (Long mappedByDataEntryID : mappedByDataEntryIDs) { 252 Object element = getObjectFromDataEntryID(mappedByDataEntryID); 253 collection.add(element); 254 } 255 } 256 else { 257 Object ids = objectContainer.getValue(fieldMeta.getFieldID()); 258 for (int idx = 0; idx < Array.getLength(ids); ++idx) { 259 Object id = Array.get(ids, idx); 260 Object element = ObjectContainerHelper.referenceToEntity(ec, pmData, id); 261 collection.add(element); 262 } 263 } 264 return op.wrapSCOField(fieldNumber, collection, false, false, true); 265 } 266 else if (mmd.hasMap()) 267 { 268 Map<Object, Object> map; 269 @SuppressWarnings("unchecked") 270 Class<? extends Map<Object, Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 271 try { 272 map = instanceType.newInstance(); 273 } catch (InstantiationException e) { 274 throw new NucleusDataStoreException(e.getMessage(), e); 275 } catch (IllegalAccessException e) { 276 throw new NucleusDataStoreException(e.getMessage(), e); 277 } 278 279 boolean keyIsPersistent = mmd.getMap().keyIsPersistent(); 280 boolean valueIsPersistent = mmd.getMap().valueIsPersistent(); 281 282 if (mmd.getMappedBy() != null) { 283 FieldMeta oppositeFieldMetaKey = fieldMeta.getSubFieldMeta(FieldMetaRole.mapKey).getMappedByFieldMeta(ec); 284 FieldMeta oppositeFieldMetaValue = fieldMeta.getSubFieldMeta(FieldMetaRole.mapValue).getMappedByFieldMeta(ec); 285 286 for (Long mappedByDataEntryID : mappedByDataEntryIDs) { 287 Object element = getObjectFromDataEntryID(mappedByDataEntryID); 288 ObjectProvider elementOP = ec.findObjectProvider(element); 289 if (elementOP == null) 290 throw new IllegalStateException("executionContext.findObjectProvider(element) returned null for " + element); 291 292 Object key; 293 if (keyIsPersistent) 294 key = element; 295 else 296 key = elementOP.provideField(oppositeFieldMetaKey.getDataNucleusAbsoluteFieldNumber()); 297 298 Object value; 299 if (valueIsPersistent) 300 value = element; 301 else 302 value = elementOP.provideField(oppositeFieldMetaValue.getDataNucleusAbsoluteFieldNumber()); 303 304 map.put(key, value); 305 } 306 } 307 else { 308 Map<?,?> idMap = (Map<?,?>) objectContainer.getValue(fieldMeta.getFieldID()); 309 for (Map.Entry<?, ?> me : idMap.entrySet()) { 310 Object k = me.getKey(); 311 Object v = me.getValue(); 312 313 if (keyIsPersistent) 314 k = ObjectContainerHelper.referenceToEntity(ec, pmData, k); 315 316 if (valueIsPersistent) 317 v = ObjectContainerHelper.referenceToEntity(ec, pmData, v); 318 319 map.put(k, v); 320 } 321 } 322 323 return op.wrapSCOField(fieldNumber, map, false, false, true); 324 } 325 else if (mmd.hasArray()) 326 { 327 Class<?> elementType = ec.getClassLoaderResolver().classForName(mmd.getArray().getElementType()); 328 329 Object array; 330 if (mmd.getMappedBy() != null) { 331 int arrayLength = mappedByDataEntryIDs.size(); 332 array = Array.newInstance(elementType, arrayLength); 333 Iterator<Long> it = mappedByDataEntryIDs.iterator(); 334 for (int i = 0; i < arrayLength; ++i) { 335 Long dataEntryID = it.next(); 336 Object element = getObjectFromDataEntryID(dataEntryID); 337 Array.set(array, i, element); 338 } 339 } 340 else { 341 Object ids = objectContainer.getValue(fieldMeta.getFieldID()); 342 int arrayLength = Array.getLength(ids); 343 array = Array.newInstance(elementType, arrayLength); 344 for (int i = 0; i < arrayLength; ++i) { 345 Object id = Array.get(ids, i); 346 Object element = ObjectContainerHelper.referenceToEntity(ec, pmData, id); 347 Array.set(array, i, element); 348 } 349 } 350 return array; 351 } 352 else 353 throw new IllegalStateException("Unexpected multi-valued relationType: " + relationType); 354 } 355 else 356 throw new IllegalStateException("Unexpected relationType: " + relationType); 357 } 358 359 private Object getObjectFromDataEntryID(long dataEntryID) 360 { 361 String idStr = DataEntry.getDataEntry(pmData, dataEntryID).getObjectID(); 362 return IdentityUtils.getObjectFromIdString( 363 idStr, classMeta.getDataNucleusClassMetaData(ec), ec, true 364 ); 365 } 366 }