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.IdGeneratorStrategy; 023 import javax.jdo.annotations.IdentityType; 024 import javax.jdo.annotations.Inheritance; 025 import javax.jdo.annotations.InheritanceStrategy; 026 import javax.jdo.annotations.NullValue; 027 import javax.jdo.annotations.PersistenceCapable; 028 import javax.jdo.annotations.Persistent; 029 import javax.jdo.annotations.PrimaryKey; 030 import javax.jdo.annotations.Version; 031 import javax.jdo.annotations.VersionStrategy; 032 import javax.jdo.listener.StoreCallback; 033 034 /** 035 * <p> 036 * Persistent index information with <b>encrypted</b> pointers to {@link DataEntry}s. 037 * </p> 038 * <p> 039 * Since the index is type-specific, there are sub-classes for each data type. One 040 * {@link IndexEntry} instance is used for each distinct value of one certain field. 041 * Therefore, the field (represented by the property {@link #getFieldMeta() fieldMeta}) 042 * and the value together form a unique key of <code>IndexEntry</code> - thus the value 043 * is represented by the property {@link #getIndexKey() indexKey}. 044 * </p> 045 * <p> 046 * Example: 047 * </p> 048 * <pre> 049 * // persistent class: 050 * @PersistenceCapable 051 * class Person 052 * { 053 * public Person(String firstName, String lastName) { 054 * this.firstName = firstName; 055 * this.lastName = lastName; 056 * } 057 * 058 * private String firstName; 059 * private String lastName; 060 * 061 * // ... 062 * } 063 * 064 * class SomeTest 065 * { 066 * @Test 067 * public void persistPersons() 068 * { 069 * pm.makePersistent(new Person("Alice", "Müller")); 070 * pm.makePersistent(new Person("Alice", "Meier")); 071 * } 072 * } 073 * </pre> 074 * <p> 075 * After running this test, there would be three instances of {@link IndexEntryStringShort} in the database 076 * indexing the values "Alice", "Müller" and "Meier". Note, that "Alice" occurs only once in the index, even though 077 * there are two <code>Person</code> instances using it. The two persons would be referenced from the one index-entry 078 * via {@link #getIndexValue()}. 079 * </p> 080 * 081 * 082 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 083 */ 084 @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true") 085 @Inheritance(strategy=InheritanceStrategy.SUBCLASS_TABLE) 086 @Version(strategy=VersionStrategy.VERSION_NUMBER) 087 //@Unique(members={"fieldMeta", "indexKeyDouble", "indexKeyLong", "indexKeyString"}) 088 public abstract class IndexEntry 089 implements StoreCallback 090 { 091 @PrimaryKey 092 @Persistent(valueStrategy=IdGeneratorStrategy.NATIVE) 093 private long indexEntryID = -1; 094 095 @Persistent(nullValue=NullValue.EXCEPTION) 096 private FieldMeta fieldMeta; 097 098 private long keyID = -1; 099 100 /** DataEntryIDs for this indexed key. */ 101 private byte[] indexValue; 102 103 /** 104 * Get the single primary key field (= object-identifier) of this <code>IndexEntry</code>. 105 * 106 * @return the object-identifier (= primary key). 107 */ 108 public long getIndexEntryID() { 109 return indexEntryID; 110 } 111 112 /** 113 * <p> 114 * Get the descriptor of the indexed field. 115 * </p> 116 * <p> 117 * Every <code>IndexEntry</code> instance belongs to one field or a part of the field (e.g. a <code>Map</code>'s key). 118 * </p> 119 * @return the descriptor of the indexed field. 120 */ 121 public FieldMeta getFieldMeta() { 122 return fieldMeta; 123 } 124 125 protected void setFieldMeta(FieldMeta fieldMeta) { 126 if (this.fieldMeta != null && !this.fieldMeta.equals(fieldMeta)) 127 throw new IllegalStateException("The property fieldMeta cannot be modified after being set once!"); 128 129 this.fieldMeta = fieldMeta; 130 } 131 132 /** 133 * Get the value which is indexed by this instance. It serves as 2nd part of the unique key together 134 * with the property {@link #getFieldMeta() fieldMeta}. 135 * @return the key. 136 */ 137 public abstract Object getIndexKey(); 138 139 protected abstract void setIndexKey(Object indexKey); 140 141 /** 142 * Get the identifier of the encryption-key used to encrypt the {@link #getIndexValue() indexValue}. 143 * @return the encryption-key used to encrypt this <code>IndexEntry</code>'s contents. 144 * @see #setKeyID(long) 145 */ 146 public long getKeyID() { 147 return keyID; 148 } 149 150 /** 151 * Set the identifier of the encryption-key used to encrypt the {@link #getIndexValue() indexValue}. 152 * @param keyID the encryption-key used to encrypt this <code>IndexEntry</code>'s contents. 153 * @see #getKeyID() 154 */ 155 public void setKeyID(long keyID) 156 { 157 if (keyID < 0) 158 throw new IllegalArgumentException("keyID < 0"); 159 160 this.keyID = keyID; 161 } 162 163 /** 164 * Get the <b>encrypted</b> pointers to {@link DataEntry}. After decrypting 165 * this byte array, you can pass it to {@link IndexValue#IndexValue(byte[])}. 166 * 167 * @return the <b>encrypted</b> pointers to {@link DataEntry}s. 168 */ 169 public byte[] getIndexValue() { 170 return indexValue; 171 } 172 173 public void setIndexValue(byte[] indexValue) { 174 this.indexValue = indexValue; 175 } 176 177 @Override 178 public int hashCode() { 179 return (int) (indexEntryID ^ (indexEntryID >>> 32)); 180 } 181 182 @Override 183 public boolean equals(Object obj) { 184 if (this == obj) return true; 185 if (obj == null) return false; 186 if (getClass() != obj.getClass()) return false; 187 IndexEntry other = (IndexEntry) obj; 188 return this.indexEntryID == other.indexEntryID; 189 } 190 191 @Override 192 public void jdoPreStore() 193 { 194 // See: DataEntry#jdoPreStore() - the same applies here to 'this.fieldMeta'. 195 PersistenceManager pm = JDOHelper.getPersistenceManager(this); 196 Object fieldMetaID = JDOHelper.getObjectId(fieldMeta); 197 fieldMeta = (FieldMeta) pm.getObjectById(fieldMetaID); 198 } 199 }