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 }