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.model;
019
020 import javax.jdo.JDOHelper;
021 import javax.jdo.PersistenceManager;
022 import javax.jdo.annotations.Column;
023 import javax.jdo.annotations.IdGeneratorStrategy;
024 import javax.jdo.annotations.IdentityType;
025 import javax.jdo.annotations.Index;
026 import javax.jdo.annotations.Indices;
027 import javax.jdo.annotations.NotPersistent;
028 import javax.jdo.annotations.NullValue;
029 import javax.jdo.annotations.PersistenceCapable;
030 import javax.jdo.annotations.Persistent;
031 import javax.jdo.annotations.PrimaryKey;
032 import javax.jdo.annotations.Queries;
033 import javax.jdo.annotations.Query;
034 import javax.jdo.annotations.Sequence;
035 import javax.jdo.annotations.SequenceStrategy;
036 import javax.jdo.annotations.Unique;
037 import javax.jdo.annotations.Version;
038 import javax.jdo.annotations.VersionStrategy;
039 import javax.jdo.listener.StoreCallback;
040
041 import org.cumulus4j.store.datastoreversion.command.IntroduceKeyStoreRefID;
042
043 /**
044 * Persistent container holding an entity's data in <b>encrypted</b> form.
045 *
046 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
047 */
048 @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true")
049 @Version(strategy=VersionStrategy.VERSION_NUMBER)
050 @Sequence(name="DataEntrySequence", datastoreSequence="DataEntrySequence", initialValue=0, strategy=SequenceStrategy.CONTIGUOUS)
051 @Unique(members={"keyStoreRefID", "classMeta_classID", "objectID"})
052 @Indices({
053 @Index(members={"keyStoreRefID", "classMeta_classID"}),
054 @Index(members={"classMeta_classID"})
055 })
056 @Queries({
057 @Query(
058 name=DataEntry.NamedQueries.getDataEntryByClassMetaClassIDAndObjectID,
059 value="SELECT UNIQUE WHERE this.keyStoreRefID == :keyStoreRefID && this.classMeta_classID == :classMeta_classID && this.objectID == :objectID"
060 ),
061 @Query(
062 name=DataEntry.NamedQueries.getDataEntryIDByClassMetaClassIDAndObjectID,
063 value="SELECT UNIQUE this.dataEntryID WHERE this.keyStoreRefID == :keyStoreRefID && this.classMeta_classID == :classMeta_classID && this.objectID == :objectID"
064 ),
065 @Query(
066 name=DataEntry.NamedQueries.getDataEntryIDsByClassMetaClassIDAndObjectIDNegated,
067 value="SELECT this.dataEntryID WHERE this.keyStoreRefID == :keyStoreRefID && this.classMeta_classID == :classMeta_classID && this.objectID != :notThisObjectID"
068 )
069 })
070 public class DataEntry
071 implements StoreCallback
072 {
073 protected static class NamedQueries {
074 public static final String getDataEntryByClassMetaClassIDAndObjectID = "getDataEntryByClassMetaClassIDAndObjectID";
075 public static final String getDataEntryIDByClassMetaClassIDAndObjectID = "getDataEntryIDByClassMetaClassIDAndObjectID";
076 public static final String getDataEntryIDsByClassMetaClassIDAndObjectIDNegated = "getDataEntryIDsByClassMetaClassIDAndObjectIDNegated";
077 }
078
079 @PrimaryKey
080 @Persistent(valueStrategy=IdGeneratorStrategy.NATIVE, sequence="DataEntrySequence")
081 private Long dataEntryID;
082
083 @Persistent(nullValue=NullValue.EXCEPTION)
084 @Column(defaultValue="0")
085 private int keyStoreRefID;
086
087 // @Persistent(nullValue=NullValue.EXCEPTION)
088 @NotPersistent
089 private ClassMeta classMeta;
090
091 @Column(name="classMeta_classID_oid") // for downward-compatibility
092 private long classMeta_classID;
093
094 @Persistent(nullValue=NullValue.EXCEPTION)
095 @Column(length=255)
096 private String objectID;
097
098 private long keyID = -1;
099
100 private byte[] value;
101
102 /**
103 * Internal constructor. This exists only for JDO and should not be used by application code!
104 */
105 protected DataEntry() { }
106
107 /**
108 * Create an instance of <code>DataEntry</code>.
109 * @param classMeta the type of the entity persisted in this container (which must be the entity's concrete type -
110 * <b>not</b> the root-type of the inheritance tree!). See {@link #getClassMeta()} for further details.
111 * @param keyStoreRefID TODO
112 * @param objectID the <code>String</code>-representation of the entity's identifier (aka OID or object-ID).
113 * See {@link #getObjectID()} for further details.
114 */
115 public DataEntry(ClassMeta classMeta, int keyStoreRefID, String objectID) {
116 this.classMeta = classMeta;
117 this.classMeta_classID = classMeta.getClassID();
118 this.keyStoreRefID = keyStoreRefID;
119 this.objectID = objectID;
120
121 if (this.classMeta_classID < 0)
122 throw new IllegalStateException("classMeta not persisted yet: " + classMeta);
123 }
124
125 /**
126 * Get the single primary key field (= object-identifier) of <code>DataEntry</code>.
127 * @return the object-identifier (= primary key).
128 */
129 public long getDataEntryID() {
130 return dataEntryID == null ? -1 : dataEntryID;
131 }
132
133 /**
134 * <p>
135 * Get the type of the entity persisted in this container.
136 * </p>
137 * <p>
138 * Note, that this is the concrete type of the persisted object and <b>not</b> the root-type of the
139 * persistable hierarchy. For example, if <code>bbb</code> is persisted and <code>bbb</code> is an instance of
140 * class <code>BBB</code> which extends <code>AAA</code>
141 * and both classes are persistable, this will point to class <code>BBB</code> (and <b>not</b> <code>AAA</code>).
142 * </p>
143 * <p>
144 * Therefore, if you want to query all instances of a certain type including subclasses, you have to
145 * ask for the sub-classes via {@link org.datanucleus.store.StoreManager#getSubClassesForClass(String, boolean, org.datanucleus.ClassLoaderResolver)}
146 * first and then query for all these classes individually.
147 * </p>
148 * @return the type of the entity.
149 */
150 public ClassMeta getClassMeta() {
151 if (classMeta == null) {
152 if (classMeta_classID < 0)
153 return null;
154
155 classMeta = new ClassMetaDAO(getPersistenceManager()).getClassMeta(classMeta_classID, true);
156 }
157 return classMeta;
158 }
159
160 protected PersistenceManager getPersistenceManager() {
161 PersistenceManager pm = JDOHelper.getPersistenceManager(this);
162 if (pm == null) {
163 throw new IllegalStateException("JDOHelper.getPersistenceManager(this) returned null! " + this);
164 }
165 return pm;
166 }
167
168 /**
169 * Get the numeric identifier of the key store. The key store's String-ID is mapped to this numeric ID
170 * via {@link KeyStoreRef} instances.
171 * @return the numeric identifier of the key store.
172 */
173 public int getKeyStoreRefID() {
174 return keyStoreRefID;
175 }
176
177 /**
178 * Set the numeric identifier of the key store.
179 * @param keyStoreRefID the numeric identifier of the key store.
180 * @deprecated Never call this method! It exists only for downward compatibility
181 * (needs to be accessible by {@link IntroduceKeyStoreRefID}).
182 */
183 @Deprecated
184 public void setKeyStoreRefID(int keyStoreRefID) {
185 this.keyStoreRefID = keyStoreRefID;
186 }
187
188 /**
189 * <p>
190 * Get the <code>String</code>-representation of the entity's identifier.
191 * </p>
192 * <p>
193 * For JDO, please read the following (and related) documentation:
194 * </p>
195 * <ul>
196 * <li><a target="_blank" href="http://www.datanucleus.org/products/accessplatform_3_0/jdo/application_identity.html">JDO Mapping / Identity / Application Identity</a></li>
197 * <li><a target="_blank" href="http://www.datanucleus.org/products/accessplatform_3_0/jdo/datastore_identity.html">JDO Mapping / Identity / Datastore Identity</a></li>
198 * </ul>
199 * <p>
200 * For JPA, please read the following (and related) documentation:
201 * </p>
202 * <ul>
203 * <li><a target="_blank" href="http://www.datanucleus.org/products/accessplatform_3_0/jpa/application_identity.html">JPA Mapping / Identity / Application Identity</a></li>
204 * <li><a target="_blank" href="http://www.datanucleus.org/products/accessplatform_3_0/jpa/datastore_identity.html">JPA Mapping / Identity / Datastore Identity</a></li>
205 * </ul>
206 *
207 * @return the OID in String-form
208 * (e.g. the result of <code><a target="_blank" href="http://db.apache.org/jdo/api30/apidocs/javax/jdo/JDOHelper.html#getObjectId%28java.lang.Object%29">JDOHelper.getObjectId</a>(entity).toString()</code>
209 * when using JDO).
210 */
211 public String getObjectID() {
212 return objectID;
213 }
214
215 /**
216 * Get the identifier of the encryption-key used to encrypt the {@link #getValue() value}.
217 * @return the encryption-key used to encrypt this <code>DataEntry</code>'s contents.
218 * @see #setKeyID(long)
219 */
220 public long getKeyID() {
221 return keyID;
222 }
223
224 /**
225 * Set the identifier of the encryption-key used to encrypt the {@link #getValue() value}.
226 * @param keyID the encryption-key used to encrypt this <code>DataEntry</code>'s contents.
227 * @see #getKeyID()
228 */
229 public void setKeyID(long keyID)
230 {
231 if (keyID < 0)
232 throw new IllegalArgumentException("keyID < 0");
233
234 this.keyID = keyID;
235 }
236
237 /**
238 * Get the <b>encrypted</b> data of an entity. The entity is transformed ("made flat") into an {@link ObjectContainer}
239 * which is then serialised using Java native serialisation and finally encrypted.
240 * @return the <b>encrypted</b> serialised data of an {@link ObjectContainer} holding the entity's data.
241 * @see #setValue(byte[])
242 */
243 public byte[] getValue() {
244 return value;
245 }
246
247 /**
248 * Set the <b>encrypted</b> data of an entity.
249 * @param value the <b>encrypted</b> serialised data of an {@link ObjectContainer} holding the entity's data.
250 * @see #getValue()
251 */
252 public void setValue(byte[] value) {
253 this.value = value;
254 }
255
256 @Override
257 public int hashCode() {
258 final long dataEntryID = getDataEntryID();
259 return (int) (dataEntryID ^ (dataEntryID >>> 32));
260 }
261
262 @Override
263 public boolean equals(Object obj) {
264 if (this == obj) return true;
265 if (obj == null) return false;
266 if (getClass() != obj.getClass()) return false;
267 DataEntry other = (DataEntry) obj;
268 return this.getDataEntryID() < 0 ? false : this.getDataEntryID() == other.getDataEntryID();
269 }
270
271 @Override
272 public void jdoPreStore()
273 {
274 // // We replace 'this.classMeta' by a persistent version, because it is a detached object
275 // // which slows down the storing process immensely, as it is unnecessarily attached.
276 // // Attaching an object is an expensive operation and we neither want nor need to
277 // // update the ClassMeta object when persisting a new DataEntry.
278 // PersistenceManager pm = JDOHelper.getPersistenceManager(this);
279 // Object classMetaID = JDOHelper.getObjectId(classMeta);
280 // if (classMetaID == null)
281 // throw new IllegalStateException("JDOHelper.getObjectId(classMeta) returned null! this=" + this + " classMeta=" + classMeta);
282 //
283 // classMeta = (ClassMeta) pm.getObjectById(classMetaID);
284 }
285 }