001    package org.cumulus4j.store;
002    
003    import java.util.HashMap;
004    import java.util.Map;
005    
006    import javax.jdo.FetchPlan;
007    import javax.jdo.PersistenceManager;
008    
009    import org.cumulus4j.crypto.CryptoRegistry;
010    import org.cumulus4j.store.model.EncryptionCoordinateSet;
011    
012    /**
013     * <p>
014     * Manager for {@link EncryptionCoordinateSet} instances.
015     * </p><p>
016     * There exists one <code>EncryptionCoordinateSetManager</code> instance per {@link Cumulus4jStoreManager}.
017     * The <code>EncryptionCoordinateSet</code>s held by this manager are detached (with all properties)
018     * and thus kept across all transactions.
019     * </p>
020     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
021     */
022    public class EncryptionCoordinateSetManager
023    {
024            private Map<Integer, EncryptionCoordinateSet> encryptionCoordinateSetID2EncryptionCoordinateSet = new HashMap<Integer, EncryptionCoordinateSet>();
025    
026            private Map<String, EncryptionCoordinateSet> encryptionCoordinateString2EncryptionCoordinateSet = new HashMap<String, EncryptionCoordinateSet>();
027    
028            private static String getEncryptionCoordinateString(String cipherTransformation, String macAlgorithm)
029            {
030                    return cipherTransformation + "::" + macAlgorithm;
031            }
032            private static String getEncryptionCoordinateString(EncryptionCoordinateSet encryptionCoordinateSet)
033            {
034                    return getEncryptionCoordinateString(encryptionCoordinateSet.getCipherTransformation(), encryptionCoordinateSet.getMACAlgorithm());
035            }
036    
037            /**
038             * Create an instance.
039             */
040            public EncryptionCoordinateSetManager() { }
041    
042            /**
043             * Get the {@link EncryptionCoordinateSet} identified by the given <code>encryptionCoordinateSetID</code>.
044             * If no such <code>EncryptionCoordinateSet</code> exists, <code>null</code> is returned.
045             * @param persistenceManagerConnection the connection to the underlying datastore(s).
046             * @param encryptionCoordinateSetID {@link EncryptionCoordinateSet#getEncryptionCoordinateSetID() identifier} of the
047             * <code>EncryptionCoordinateSet</code> to be retrieved.
048             * @return the {@link EncryptionCoordinateSet} identified by the given <code>encryptionCoordinateSetID</code> or <code>null</code>.
049             */
050            public synchronized EncryptionCoordinateSet getEncryptionCoordinateSet(PersistenceManagerConnection persistenceManagerConnection, int encryptionCoordinateSetID)
051            {
052                    EncryptionCoordinateSet encryptionCoordinateSet = encryptionCoordinateSetID2EncryptionCoordinateSet.get(encryptionCoordinateSetID);
053                    if (encryptionCoordinateSet == null) {
054                            PersistenceManager pm = persistenceManagerConnection.getDataPM();
055                            encryptionCoordinateSet = EncryptionCoordinateSet.getEncryptionCoordinateSet(pm, encryptionCoordinateSetID);
056                            if (encryptionCoordinateSet != null) {
057                                    pm.getFetchPlan().setMaxFetchDepth(-1);
058                                    pm.getFetchPlan().setGroup(FetchPlan.ALL);
059                                    encryptionCoordinateSet = pm.detachCopy(encryptionCoordinateSet);
060                                    encryptionCoordinateSetID2EncryptionCoordinateSet.put(
061                                                    encryptionCoordinateSet.getEncryptionCoordinateSetID(), encryptionCoordinateSet
062                                    );
063                                    encryptionCoordinateString2EncryptionCoordinateSet.put(
064                                                    getEncryptionCoordinateString(encryptionCoordinateSet), encryptionCoordinateSet
065                                    );
066                            }
067                    }
068                    return encryptionCoordinateSet;
069            }
070    
071            /**
072             * <p>
073             * Get the {@link EncryptionCoordinateSet} identified by the given properties.
074             * </p><p>
075             * If it does not yet exist in the in-memory-cache,
076             * it is looked up in the datastore. If it is found there, it is detached, cached and returned. If it does not exist in the
077             * datastore either, it is - if <code>create == true</code> - created, persisted, detached, cached and returned; if
078             * <code>create == false</code>, <code>null</code> is returned instead.
079             * </p><p>
080             * The <code>EncryptionCoordinateSet</code> instances are only held in the
081             * {@link PersistenceManagerConnection#getDataPM() data-datastore} (not in the index-datastore). This might change in the future
082             * (in case replication becomes necessary).
083             * </p>
084             *
085             * @param create whether to create a new instance, if it does not yet exist. If <code>true</code>, a new instance
086             * will be created, persisted, detached, cached and returned, if it does not yet exist. If <code>false</code>, <code>null</code>
087             * will be returned instead.
088             * @param persistenceManagerConnection the connection to the underlying datastore(s).
089             * @param cipherTransformation the transformation (as passed to {@link CryptoRegistry#createCipher(String)}) used to encrypt and
090             * decrypt the persistent data (or index).
091             * @param macAlgorithm the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> algorithm (as passed to {@link CryptoRegistry#createMACCalculator(String, boolean)})
092             * used to verify peristent records for integrity. Might be {@link EncryptionCoordinateSet#MAC_ALGORITHM_NONE} to deactivate
093             * the MAC calculation.
094             * @return the <code>EncryptionCoordinateSet</code> (detached) matching the given properties. If <code>create == true</code>, this
095             * is never <code>null</code>. If <code>create == false</code> and there does not yet exist an appropriate
096             * <code>EncryptionCoordinateSet</code>, this is <code>null</code>.
097             */
098            protected EncryptionCoordinateSet _createOrGetEncryptionCoordinateSet(boolean create, PersistenceManagerConnection persistenceManagerConnection, String cipherTransformation, String macAlgorithm)
099            {
100                    String encryptionCoordinateString = getEncryptionCoordinateString(cipherTransformation, macAlgorithm);
101                    EncryptionCoordinateSet encryptionCoordinateSet = encryptionCoordinateString2EncryptionCoordinateSet.get(encryptionCoordinateString);
102                    if (encryptionCoordinateSet == null) {
103                            PersistenceManager pm = persistenceManagerConnection.getDataPM();
104    
105                            if (create)
106                                    encryptionCoordinateSet = EncryptionCoordinateSet.createEncryptionCoordinateSet(pm, cipherTransformation, macAlgorithm);
107                            else
108                                    encryptionCoordinateSet = EncryptionCoordinateSet.getEncryptionCoordinateSet(pm, cipherTransformation, macAlgorithm);
109    
110                            if (encryptionCoordinateSet != null) {
111                                    pm.getFetchPlan().setMaxFetchDepth(-1);
112                                    pm.getFetchPlan().setGroup(FetchPlan.ALL);
113                                    encryptionCoordinateSet = pm.detachCopy(encryptionCoordinateSet);
114                                    encryptionCoordinateSetID2EncryptionCoordinateSet.put(
115                                                    encryptionCoordinateSet.getEncryptionCoordinateSetID(), encryptionCoordinateSet
116                                    );
117                                    encryptionCoordinateString2EncryptionCoordinateSet.put(
118                                                    getEncryptionCoordinateString(encryptionCoordinateSet), encryptionCoordinateSet
119                                    );
120                            }
121                    }
122                    return encryptionCoordinateSet;
123            }
124    
125            /**
126             * <p>
127             * Get the {@link EncryptionCoordinateSet} identified by the given properties.
128             * </p><p>
129             * If there is no appropriate <code>EncryptionCoordinateSet</code> (neither in the in-memory-cache nor in the datastore),
130             * <code>null</code> is returned.
131             * </p><p>
132             * This method delegates to {@link #_createOrGetEncryptionCoordinateSet(boolean, PersistenceManagerConnection, String, String)} with
133             * <code>create == false</code>.
134             * </p>
135             *
136             * @param persistenceManagerConnection the connection to the underlying datastore(s).
137             * @param cipherTransformation the transformation (as passed to {@link CryptoRegistry#createCipher(String)}) used to encrypt and
138             * decrypt the persistent data (or index).
139             * @param macAlgorithm the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> algorithm (as passed to {@link CryptoRegistry#createMACCalculator(String, boolean)})
140             * used to verify peristent records for integrity. Might be {@link EncryptionCoordinateSet#MAC_ALGORITHM_NONE} to deactivate
141             * the MAC calculation.
142             * @return the <code>EncryptionCoordinateSet</code> (detached) matching the given properties or <code>null</code>.
143             */
144            public synchronized EncryptionCoordinateSet getEncryptionCoordinateSet(PersistenceManagerConnection persistenceManagerConnection, String cipherTransformation, String macAlgorithm)
145            {
146                    return _createOrGetEncryptionCoordinateSet(false, persistenceManagerConnection, cipherTransformation, macAlgorithm);
147            }
148    
149            /**
150             * <p>
151             * Get the {@link EncryptionCoordinateSet} identified by the given properties.
152             * </p><p>
153             * If there is no appropriate <code>EncryptionCoordinateSet</code> (neither in the in-memory-cache nor in the datastore),
154             * it is created and persisted.
155             * </p><p>
156             * This method delegates to {@link #_createOrGetEncryptionCoordinateSet(boolean, PersistenceManagerConnection, String, String)} with
157             * <code>create == true</code>.
158             * </p>
159             *
160             * @param persistenceManagerConnection the connection to the underlying datastore(s).
161             * @param cipherTransformation the transformation (as passed to {@link CryptoRegistry#createCipher(String)}) used to encrypt and
162             * decrypt the persistent data (or index).
163             * @param macAlgorithm the <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> algorithm (as passed to {@link CryptoRegistry#createMACCalculator(String, boolean)})
164             * used to verify peristent records for integrity. Might be {@link EncryptionCoordinateSet#MAC_ALGORITHM_NONE} to deactivate
165             * the MAC calculation.
166             * @return the <code>EncryptionCoordinateSet</code> (detached) matching the given properties; never <code>null</code>.
167             */
168            public synchronized EncryptionCoordinateSet createEncryptionCoordinateSet(PersistenceManagerConnection persistenceManagerConnection, String cipherTransformation, String macAlgorithm)
169            {
170                    return _createOrGetEncryptionCoordinateSet(true, persistenceManagerConnection, cipherTransformation, macAlgorithm);
171            }
172    }