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.HashMap; 023 import java.util.Map; 024 025 import javax.jdo.PersistenceManager; 026 027 import org.cumulus4j.store.ObjectContainerHelper; 028 import org.cumulus4j.store.model.ClassMeta; 029 import org.cumulus4j.store.model.FieldMeta; 030 import org.cumulus4j.store.model.ObjectContainer; 031 import org.datanucleus.exceptions.NucleusDataStoreException; 032 import org.datanucleus.metadata.AbstractClassMetaData; 033 import org.datanucleus.metadata.AbstractMemberMetaData; 034 import org.datanucleus.metadata.Relation; 035 import org.datanucleus.store.ExecutionContext; 036 import org.datanucleus.store.ObjectProvider; 037 import org.datanucleus.store.fieldmanager.AbstractFieldManager; 038 import org.datanucleus.store.types.sco.SCO; 039 import org.datanucleus.store.types.sco.SCOUtils; 040 041 /** 042 * Manager for the process of persisting a user object into the datastore, handling the translation from the 043 * users own object into the DataEntry object. 044 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 045 */ 046 public class StoreFieldManager extends AbstractFieldManager 047 { 048 private ObjectProvider op; 049 private PersistenceManager pmData; 050 private ExecutionContext ec; 051 private ClassMeta classMeta; 052 private AbstractClassMetaData dnClassMetaData; 053 private ObjectContainer objectContainer; 054 055 public StoreFieldManager( 056 ObjectProvider op, 057 PersistenceManager pmData, 058 ClassMeta classMeta, 059 AbstractClassMetaData dnClassMetaData, 060 ObjectContainer objectContainer // populated by this class 061 ) 062 { 063 this.op = op; 064 this.pmData = pmData; 065 this.ec = op.getExecutionContext(); 066 this.classMeta = classMeta; 067 this.dnClassMetaData = dnClassMetaData; 068 this.objectContainer = objectContainer; 069 } 070 071 private long getFieldID(int fieldNumber) 072 { 073 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 074 075 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 076 if (fieldMeta == null) 077 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 078 079 return fieldMeta.getFieldID(); 080 } 081 082 @Override 083 public void storeBooleanField(int fieldNumber, boolean value) { 084 objectContainer.setValue(getFieldID(fieldNumber), value); 085 } 086 087 @Override 088 public void storeByteField(int fieldNumber, byte value) { 089 objectContainer.setValue(getFieldID(fieldNumber), value); 090 } 091 092 @Override 093 public void storeCharField(int fieldNumber, char value) { 094 objectContainer.setValue(getFieldID(fieldNumber), value); 095 } 096 097 @Override 098 public void storeDoubleField(int fieldNumber, double value) { 099 objectContainer.setValue(getFieldID(fieldNumber), value); 100 } 101 102 @Override 103 public void storeFloatField(int fieldNumber, float value) { 104 objectContainer.setValue(getFieldID(fieldNumber), value); 105 } 106 107 @Override 108 public void storeIntField(int fieldNumber, int value) { 109 objectContainer.setValue(getFieldID(fieldNumber), value); 110 } 111 112 @Override 113 public void storeLongField(int fieldNumber, long value) { 114 objectContainer.setValue(getFieldID(fieldNumber), value); 115 } 116 117 @Override 118 public void storeShortField(int fieldNumber, short value) { 119 objectContainer.setValue(getFieldID(fieldNumber), value); 120 } 121 122 @Override 123 public void storeStringField(int fieldNumber, String value) { 124 objectContainer.setValue(getFieldID(fieldNumber), value); 125 } 126 127 @Override 128 public void storeObjectField(int fieldNumber, Object value) 129 { 130 AbstractMemberMetaData mmd = dnClassMetaData.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber); 131 132 FieldMeta fieldMeta = classMeta.getFieldMeta(mmd.getClassName(), mmd.getName()); 133 if (fieldMeta == null) 134 throw new IllegalStateException("Unknown field! class=" + dnClassMetaData.getFullClassName() + " fieldNumber=" + fieldNumber + " fieldName=" + mmd.getName()); 135 136 if (value == null) { 137 objectContainer.setValue(fieldMeta.getFieldID(), null); 138 return; 139 } 140 141 int relationType = mmd.getRelationType(ec.getClassLoaderResolver()); 142 143 // Replace any SCO field that isn't already a wrapper, with its wrapper object 144 boolean[] secondClassMutableFieldFlags = dnClassMetaData.getSCOMutableMemberFlags(); 145 if (secondClassMutableFieldFlags[fieldNumber] && !(value instanceof SCO)) 146 value = op.wrapSCOField(fieldNumber, value, false, true, true); 147 148 if (relationType == Relation.NONE) 149 { 150 if (mmd.hasCollection()) { 151 // Replace the special DN collection by a simple array. 152 Collection<?> collection = (Collection<?>)value; 153 Object[] values = collection.toArray(new Object[collection.size()]); 154 objectContainer.setValue(fieldMeta.getFieldID(), values); 155 } 156 else if (mmd.hasMap()) { 157 // replace the special DN Map by a simple HashMap. 158 Map<?,?> valueMap = (Map<?, ?>) value; 159 160 Map<Object, Object> map; 161 @SuppressWarnings("unchecked") 162 Class<? extends Map<Object, Object>> instanceType = SCOUtils.getContainerInstanceType(mmd.getType(), mmd.getOrderMetaData() != null); 163 try { 164 map = instanceType.newInstance(); 165 } catch (InstantiationException e) { 166 throw new NucleusDataStoreException(e.getMessage(), e); 167 } catch (IllegalAccessException e) { 168 throw new NucleusDataStoreException(e.getMessage(), e); 169 } 170 171 map.putAll(valueMap); 172 173 objectContainer.setValue(fieldMeta.getFieldID(), map); 174 } 175 else // arrays are not managed (no special DN instances) and thus stored 'as is'... 176 objectContainer.setValue(fieldMeta.getFieldID(), value); 177 } 178 else if (Relation.isRelationSingleValued(relationType)) 179 { 180 // Persistable object - persist the related object and store the identity in the cell 181 Object valuePC = ec.persistObjectInternal(value, op, fieldNumber, -1); 182 if (mmd.getMappedBy() == null) { 183 Object valueID = ObjectContainerHelper.entityToReference(ec, pmData, valuePC); 184 objectContainer.setValue(fieldMeta.getFieldID(), valueID); 185 } 186 } 187 else if (Relation.isRelationMultiValued(relationType)) 188 { 189 // Collection/Map/Array 190 if (mmd.hasCollection()) 191 { 192 Collection<?> collection = (Collection<?>)value; 193 Object[] ids = mmd.getMappedBy() != null ? null : new Object[collection.size()]; 194 int idx = -1; 195 for (Object element : collection) { 196 Object elementPC = ec.persistObjectInternal(element, op, fieldNumber, -1); 197 if (ids != null) { 198 Object elementID = ObjectContainerHelper.entityToReference(ec, pmData, elementPC); 199 ids[++idx] = elementID; 200 } 201 } 202 203 if (ids != null) 204 objectContainer.setValue(fieldMeta.getFieldID(), ids); 205 } 206 else if (mmd.hasMap()) 207 { 208 boolean keyIsPersistent = mmd.getMap().keyIsPersistent(); 209 boolean valueIsPersistent = mmd.getMap().valueIsPersistent(); 210 211 Map<?,?> valueMap = (Map<?, ?>) value; 212 Map<Object,Object> idMap = mmd.getMappedBy() != null ? null : new HashMap<Object, Object>(valueMap.size()); 213 for (Map.Entry<?, ?> me : valueMap.entrySet()) { 214 Object k = me.getKey(); 215 Object v = me.getValue(); 216 217 if (keyIsPersistent) { 218 Object kpc = ec.persistObjectInternal(k, op, fieldNumber, -1); 219 220 if (idMap != null) 221 k = ObjectContainerHelper.entityToReference(ec, pmData, kpc); 222 } 223 224 if (valueIsPersistent) { 225 Object vpc = ec.persistObjectInternal(v, op, fieldNumber, -1); 226 227 if (idMap != null) 228 v = ObjectContainerHelper.entityToReference(ec, pmData, vpc); 229 } 230 231 if (idMap != null) 232 idMap.put(k, v); 233 } 234 235 if (idMap != null) 236 objectContainer.setValue(fieldMeta.getFieldID(), idMap); 237 } 238 else if (mmd.hasArray()) 239 { 240 if (mmd.getMappedBy() != null) 241 throw new UnsupportedOperationException("NYI"); 242 243 Object[] ids = new Object[Array.getLength(value)]; 244 for (int i=0;i<Array.getLength(value);i++) 245 { 246 Object element = Array.get(value, i); 247 Object elementPC = ec.persistObjectInternal(element, op, fieldNumber, -1); 248 Object elementID = ObjectContainerHelper.entityToReference(ec, pmData, elementPC); 249 ids[i] = elementID; 250 } 251 objectContainer.setValue(fieldMeta.getFieldID(), ids); 252 } 253 } 254 else 255 throw new IllegalStateException("Unexpected relationType: " + relationType); 256 } 257 258 }