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, sequence="IndexEntrySequence")
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 }