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;
019    
020    import java.util.Properties;
021    
022    import javax.jdo.PersistenceManager;
023    
024    import org.cumulus4j.store.Cumulus4jConnectionFactory.Cumulus4jManagedConnection;
025    import org.cumulus4j.store.crypto.CryptoContext;
026    import org.cumulus4j.store.model.Sequence2;
027    import org.cumulus4j.store.model.Sequence2DAO;
028    import org.datanucleus.store.ExecutionContext;
029    import org.datanucleus.store.valuegenerator.AbstractDatastoreGenerator;
030    import org.datanucleus.store.valuegenerator.ValueGenerationBlock;
031    import org.datanucleus.store.valuegenerator.ValueGenerator;
032    
033    /**
034     * {@link ValueGenerator} implementation generating values by incrementing a counter.
035     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
036     */
037    public class Cumulus4jIncrementGenerator extends AbstractDatastoreGenerator
038    {
039            private String sequenceName;
040    
041            /**
042             * Create an instance. This is called by DataNucleus.
043             * @param name symbolic name for the generator.
044             * @param props Properties controlling the behaviour of the generator.
045             */
046            public Cumulus4jIncrementGenerator(String name, Properties props) {
047                    super(name, props);
048                    allocationSize = 5;
049    
050                    // TODO Check these names and what we want to use for Cumulus4j (classname or fieldname)
051                    if (properties.getProperty("sequence-name") != null) {
052                            // Specified sequence-name so use that
053                            sequenceName = properties.getProperty("sequence-name");
054                    }
055                    else if (properties.getProperty("field-name") != null) {
056                            // Use field name as the sequence name so we have one sequence per field on the class
057                            sequenceName = properties.getProperty("field-name");
058                    }
059                    else {
060                            // Use actual class name as the sequence name so we have one sequence per class
061                            sequenceName = properties.getProperty("class-name");
062                    }
063            }
064    
065            @Override
066            protected ValueGenerationBlock reserveBlock(long size) {
067                    if (size > Integer.MAX_VALUE)
068                            throw new IllegalStateException("Cannot reserve a block of more than " + Integer.MAX_VALUE + " values!");
069    
070                    Long[] values = new Long[(int)size];
071                    Cumulus4jManagedConnection mconn = (Cumulus4jManagedConnection) connectionProvider.retrieveConnection();
072                    try {
073                            PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection();
074                            PersistenceManager pm = pmConn.getDataPM();
075                            Cumulus4jStoreManager storeManager = (Cumulus4jStoreManager) storeMgr;
076    
077                            CryptoContext cryptoContext = new CryptoContext(
078                                            storeManager.getEncryptionCoordinateSetManager(),
079                                            storeManager.getKeyStoreRefManager(),
080                                            (ExecutionContext) mconn.getPoolKey(), // TODO find a better way! That this is the ExecutionContext is nowhere documented, but it works and unfortunately it is the only way to obtain it I found :-(
081                                            pmConn
082                            );
083                            storeManager.getDatastoreVersionManager().applyOnce(cryptoContext);
084    
085                            pm.currentTransaction().setSerializeRead(true);
086                            try {
087                                    Sequence2 sequence = new Sequence2DAO(pm, cryptoContext.getKeyStoreRefID()).createSequence2(sequenceName);
088                                    long nextValue = sequence.getNextValue();
089                                    for (int idx = 0; idx < values.length; ++idx) {
090                                            values[idx] = nextValue++;
091                                    }
092                                    sequence.setNextValue(nextValue);
093                            } finally {
094                                    pm.currentTransaction().setSerializeRead(false);
095                            }
096                    } finally {
097                            connectionProvider.releaseConnection();
098                    }
099                    return new ValueGenerationBlock(values);
100            }
101    }