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 }