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 }