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 java.util.HashMap;
021 import java.util.Map;
022
023 import javax.jdo.PersistenceManager;
024
025 import org.cumulus4j.store.EncryptionHandler;
026
027 /**
028 * <p>
029 * Factory for creating (or looking up) specific {@link IndexEntry} implementations.
030 * </p><p>
031 * It is optional to implement a specific factory. For most use cases, it is sufficient to
032 * use the {@link DefaultIndexEntryFactory} (which is used, if the extension specifies the
033 * attribute <code>index-entry-type</code>), but you can alternatively specify a custom
034 * factory via the extension-attribute <code>index-entry-factory-type</code>.
035 * </p><p>
036 * If you specify a custom
037 * factory, you must omit (or leave empty) the <code>index-entry-type</code>!
038 * </p>
039 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
040 */
041 public abstract class IndexEntryFactory
042 {
043 /**
044 * Get the concrete implementation class (sub-class) of {@link IndexEntry} managed by this factory.
045 * @return the concrete implementation class of {@link IndexEntry} managed by this factory.
046 */
047 public abstract Class<? extends IndexEntry> getIndexEntryClass();
048
049 /**
050 * Get an {@link IndexEntry} for the specified unique key fields or <code>null</code>, if no such instance
051 * exists.
052 * @param pmIndex the backend-<code>PersistenceManager</code>. Must not be <code>null</code>.
053 * @param fieldMeta the meta-data of the field to query. Must not be <code>null</code>.
054 * @param indexKey the indexed value to search for. Might be <code>null</code> (<code>null</code> can be indexed).
055 * @return the matching {@link IndexEntry} or <code>null</code>.
056 */
057 public IndexEntry getIndexEntry(PersistenceManager pmIndex, FieldMeta fieldMeta, Object indexKey)
058 {
059 if (pmIndex == null)
060 throw new IllegalArgumentException("pm == null");
061
062 if (fieldMeta == null)
063 throw new IllegalArgumentException("fieldMeta == null");
064
065 Class<? extends IndexEntry> indexEntryClass = getIndexEntryClass();
066 javax.jdo.Query q = pmIndex.newQuery(indexEntryClass);
067 q.setUnique(true);
068 q.setFilter(
069 "this.fieldMeta == :fieldMeta && " +
070 "this.indexKey == :indexKey"
071 );
072 Map<String, Object> params = new HashMap<String, Object>();
073 params.put("fieldMeta", fieldMeta);
074 params.put("indexKey", indexKey);
075 return indexEntryClass.cast(q.executeWithMap(params));
076 }
077
078 /**
079 * Get an existing {@link IndexEntry} just like {@link #getIndexEntry(PersistenceManager, FieldMeta, Object)}
080 * or create one, if it does not yet exist.
081 * @param pmIndex the backend-<code>PersistenceManager</code>. Must not be <code>null</code>.
082 * @param fieldMeta the meta-data of the field to query. Must not be <code>null</code>.
083 * @param indexKey the indexed value to search for. Might be <code>null</code> (<code>null</code> can be indexed).
084 * @return the matching {@link IndexEntry} (never <code>null</code>).
085 */
086 public IndexEntry createIndexEntry(PersistenceManager pmIndex, FieldMeta fieldMeta, Object indexKey)
087 {
088 IndexEntry result = getIndexEntry(pmIndex, fieldMeta, indexKey);
089 if (result == null) {
090 try {
091 result = getIndexEntryClass().newInstance();
092 } catch (InstantiationException e) {
093 throw new RuntimeException(e);
094 } catch (IllegalAccessException e) {
095 throw new RuntimeException(e);
096 }
097 result.setFieldMeta(fieldMeta);
098 result.setIndexKey(indexKey);
099
100 // We persist *after* setting all values, because that improves performance:
101 // This way, there is only one INSERT instead of one INSERT AND one UPDATE for each new
102 // index entry. The MovieQueryTest.importDataCsv() is around 10% faster when using MySQL
103 // (approximately 60 sec vs. 66 sec).
104 // However, when dumping the plaintexts for debugging, we need the indexEntryID already *before*
105 // encryption. Hence, we persist here, if the DEBUG_DUMP flag is set.
106 // Marco :-)
107 if (EncryptionHandler.DEBUG_DUMP)
108 result = pmIndex.makePersistent(result);
109 }
110
111 return result;
112 }
113 }