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