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 javax.jdo.annotations.Column;
021 import javax.jdo.annotations.IdentityType;
022 import javax.jdo.annotations.NullValue;
023 import javax.jdo.annotations.PersistenceCapable;
024 import javax.jdo.annotations.Persistent;
025 import javax.jdo.annotations.PrimaryKey;
026
027 import org.cumulus4j.store.Cumulus4jIncrementGenerator;
028 import org.cumulus4j.store.crypto.CryptoContext;
029
030 /**
031 * Persistent sequence entity used by {@link Cumulus4jIncrementGenerator}.
032 * <p>
033 * Objects are cached by DataNucleus via their primary key. Accessing an object via its OID therefore does not
034 * require any query, if the object is already cached. Therefore, this class encodes the
035 * {@link CryptoContext#getKeyStoreRefID() keyStoreRefID} and the <code>sequenceName</code> together in one
036 * single {@link #getSequenceID() sequenceID}, which is the (single-field) primary key for this class.
037 * <p>
038 * We do not use a composite primary key, because this is not supported by all underlying databases. The chosen
039 * strategy is thus the most portable and fastest.
040 *
041 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
042 * @since 1.1.0
043 */
044 @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true")
045 public class Sequence2
046 {
047 @PrimaryKey
048 @Persistent(nullValue=NullValue.EXCEPTION)
049 @Column(length=255)
050 private String sequenceID;
051
052 private long nextValue = 1;
053
054 /**
055 * Default constructor. Should never be used by actual code! It exists only to fulfill the JDO requirements.
056 */
057 protected Sequence2() { }
058
059 protected static String createSequenceID(int keyStoreRefID, String sequenceName) {
060 return Integer.toString(keyStoreRefID) + '.' + sequenceName;
061 }
062
063 /**
064 * Constructor creating a <code>Sequence</code> with the given primary key.
065 * @param sequenceName the name of the sequence; must not be <code>null</code>.
066 */
067 protected Sequence2(int keyStoreRefID, String sequenceName)
068 {
069 if (sequenceName == null)
070 throw new IllegalArgumentException("sequenceName == null");
071
072 this.sequenceID = createSequenceID(keyStoreRefID, sequenceName);
073 }
074
075 public String getSequenceID() {
076 return sequenceID;
077 }
078
079 protected String[] splitSequenceID() {
080 int dotIndex = sequenceID.indexOf('.');
081 if (dotIndex < 0)
082 throw new IllegalStateException(String.format("sequenceID \"%s\" does not contain a dot ('.')!", sequenceID));
083
084 String[] result = new String[2];
085 result[0] = sequenceID.substring(0, dotIndex);
086 result[1] = sequenceID.substring(dotIndex + 1);
087 return result;
088 }
089
090 public int getKeyStoreRefID() {
091 String keyStoreRefIDStr = splitSequenceID()[0];
092 try {
093 int keyStoreRefID = Integer.parseInt(keyStoreRefIDStr);
094 return keyStoreRefID;
095 } catch (NumberFormatException x) {
096 throw new IllegalStateException(
097 String.format(
098 "First part of sequenceID \"%s\" is \"%s\", which is not a valid integer: %s",
099 sequenceID, keyStoreRefIDStr, x.toString()
100 ),
101 x
102 );
103 }
104 }
105
106 /**
107 * Get the name of the sequence.
108 * @return the name of the sequence.
109 */
110 public String getSequenceName() {
111 return splitSequenceID()[1];
112 }
113
114 /**
115 * Get the next value (i.e. the first unused value) for this sequence.
116 * @return the next value (i.e. the first unused value) for this sequence.
117 */
118 public long getNextValue() {
119 return nextValue;
120 }
121
122 /**
123 * Set the next value (i.e. the first unused value) for this sequence.
124 * @param nextValue the next value (i.e. the first unused value) for this sequence.
125 */
126 public void setNextValue(long nextValue) {
127 this.nextValue = nextValue;
128 }
129 }