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