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