Design
Designing an ACDP database involves coding subclasses for theCustomDatabase and the
   
   CustomTable classes, and optionally for the 
   
   SimpleType class if there is a need for one or more custom
   column types.
   Furthermore, data encryption and decryption can be enabled by providing an
   implementation of the ICipherFactory interface.
   The remaining sections of this page are
A Sample World
In an educational institution students attend courses and teachers instruct courses. You can contact a student and a teacher using their contact details. The following UML diagram, created with the UMLet tool, shows the entities, their attributes and the associations.The black diamond () indicates a composition. Since the contact details are rather complex, by dedicating them their own entity, we avoid the repetition of contact related attributes in the Student and Teacher entities, including any code for checking the validity of values, for example whether a given value is a valid phone number.
Coding Table Classes
Translating the world depicted in the UML diagram above into a collection of ACDP database tables is straightforward: Each entity has a direct correspondence to an ACDP table and a link from one entity to the same or another entity corresponds to either a reference column type or an array of references column type, depending on the cardinality of the link.For each table class we need to define the columns of the table as well as the characteristic sequence of columns, also known as the table definition:
- Create a
     
Columninstance with theCLcolumn factory class for each column of the table and assign each column instance to a separate variable. - Define a constructor and invoke the
     
initializemethod of the super class with the column instances declared in the first step. 
   import static acdp.design.ST.Nulls.NO_NULL;
   import static acdp.design.ST.Nulls.NULLABLE;
   import static acdp.design.ST.OutrowStringLength.SMALL;
   
   final class CourseTable extends CustomTable {
      static final Column<String> ID = CL.ofString(NO_NULL, SMALL);
      static final Column<String> NAME = CL.ofString(NO_NULL, SMALL);
      static final Column<Ref> TEACHER = CL.ofRef();
   
      CourseTable() {
         initialize(ID, NAME, TEACHER);
      }
   }
   
   final class StudentTable extends CustomTable {
      static final Column<Integer> STUDENT_NUMBER = CL.ofInteger(NO_NULL);
      static final Column<String> NAME = CL.ofString(NO_NULL, SMALL);
      static final Column<Ref> CONTACT = CL.ofRef();
      static final Column<Ref[]> COURSES = CL.ofArrayOfRef(30);
      
      StudentTable() {
         initialize(STUDENT_NUMBER, NAME, CONTACT, COURSES);
      }
   }
   
   final class TeacherTable extends CustomTable {
      static final Column<String> NAME = CL.ofString(NO_NULL, SMALL);
      static final Column<Float> SALARY = CL.ofFloat(NULLABLE);
      static final Column<Ref> CONTACT = CL.ofRef();
      static final Column<Ref[]> COURSES = CL.ofArrayOfRef(45);
   
      TeacherTable() {
         initialize(NAME, SALARY, CONTACT, COURSES);
      }
   }
   
   final class ContactTable extends CustomTable {
      static final Column<String> PHONE_NUMBER = CL.ofString(
                                     Charset.forName("US-ASCII"), NULLABLE, 12);
      static final Column<String> EMAIL = CL.ofString(NO_NULL, SMALL);
      static final Column<String> STREET = CL.ofString(NO_NULL, SMALL);
      static final Column<String> CITY = CL.ofString(NO_NULL, SMALL);
      static final Column<String> STATE = CL.ofString(NO_NULL, SMALL);
      static final Column<Integer> POSTAL_CODE = CL.ofInteger(NO_NULL);
      static final Column<String> COUNTRY = CL.ofString(NULLABLE, SMALL);
      
      ContactTable() {
         initialize(PHONE_NUMBER, EMAIL, STREET, CITY, STATE, POSTAL_CODE, COUNTRY);
      }
   }
   Remarks:
   - 
     The column variable declarations include the 
staticmodifier. This is possible because we do not intend to reuse any of the table classes in our database or some other database. (See the description of theCustomTableclass for details.) - 
     Always include the 
finalmodifier in the declaration of a column variable. Here, we included thefinalmodifier in the declaration of the table classes too. A more elaborated table class may be derived from a more general (abstract) table super class. - 
     Note that we have not yet assigned names to the tables and columns.
     We have also not yet specified the target tables of the reference column
     types that appear in the table classes Courses, Student and Teacher.
     This will be done later in Chapter 
Creation
when we prepare the table classes for processing by ACDP's Setup Tool. - 
     The 
     
CLcolumn factory class provides factory methods for columns with a type corresponding to a built-in Java type, including arrays. TheNO_NULLorNULLABLEargument creates a column type that prohibits or allows thenullvalue to be stored in the column. Thenullvalue is always allowed as a value in a column of an array, a reference or an array of references column type. - 
     Invoking 
CL.ofString()returns a column with a nullable string column type of a length comparable to the length of an ordinary JavaStringinstance. - 
     A column of a string column type created with the
     
SMALLargument allows for strings with a length of 255 characters at most. - 
     The 
PHONE_NUMBERcolumn in the Contact table is created by an invocation ofCL.ofString(Charset.forName("US-ASCII"), NULLABLE, 12). This allows for a phone number with a length of 12 characters at most. The really special thing about this column type, however, is its so called inrow storage scheme which allows the particularly efficient storage of values. - A student can attend at most 30 courses and a teacher instructs at most 45 courses.
 
CL.ofArray(int, SimpleType) method with
   any built-in or custom non-array element type.
   For example, CL.ofArray(73, ST.beDouble(NO_NULL)) creates an
   array with a maximum of 73 Double values that are not allowed
   to be null.
   (The ST
   class is a factory class for ACDP's built-in non-array column types.)
Coding the Database Class
Coding the database class includes defining the tables as well as a constructor which, when invoked, opens the database.
   final class EduDB extends CustomDatabase {
      static final CourseTable COURSE_TABLE = new CourseTable();
      static final StudentTable STUDENT_TABLE = new StudentTable();
      static final TeacherTable TEACHER_TABLE = new TeacherTable();
      static final ContactTable CONTACT_TABLE = new ContactTable();
   
      EduDB(Path mainFile, int opMode, boolean writeProtect,
                                                   int consistencyNumber) {
         open(mainFile, opMode, writeProtect, null, consistencyNumber,
                 COURSE_TABLE, STUDENT_TABLE, TEACHER_TABLE, CONTACT_TABLE);
      }
   }
   Remarks:
   - 
     The table variable declarations include the 
staticmodifier. This is possible because we do not intend to reuse any of the table classes in our or some other database. (See the description of theCustomDatabaseclass for details.) - 
     Always include the 
finalmodifier in the declaration of a table variable. Here, we included thefinalmodifier in the declaration of the database class too. A more elaborated database class may be derived from a more general (abstract) database super class. - 
     Note that we have not yet assigned a name to the database.
     This will be done later in Chapter 
Creation
when we prepare the database class for processing by ACDP's Setup Tool. - 
     The arguments of the 
openmethod are explained in ChapterOpening
. 
Coding a Custom Column Type
Suppose we want to store the salary of a teacher as a value of typejava.math.BigInteger rather than java.lang.Float.
   Since ACDP has no such built-in type, we must code our own
   BigInteger column type.
   
   All non-array column types are derived from the
   
   SimpleType class.
   A class that extends the SimpleType class must implement the
   
   toBytes(T) and the
   fromBytes(byte[], int, int) methods which
   convert a value of type T, for example BigInteger,
   into a byte array and vice versa.
   
   Moreover, implementers of a custom column type must implement a
   public and static factory method annotated
   with the
   
   TypeFromDesc annotation which creates an instance of the
   column type from its so called type descriptor.
   The type descriptor is a String that uniquely identifies the
   column type.
   It contains enough information so that an instance of the column type can be
   created from it.
   
BigInteger column type looks as follows:
   
   public final class BigIntegerType extends SimpleType<BigInteger> {
      private static final String TD_PREFIX = "BigInteger";
   
      @TypeFromDesc
      public static final BigIntegerType createType(String typeDesc) throws
                                                           CreationException {
         final TypeDesc td = new TypeDesc(typeDesc);
         if (!td.prefix.equals(TD_PREFIX) || td.scheme != Scheme.INROW ||
                                                               !td.variable) {
            throw new CreationException("Illegal type descriptor");
         }
         return new BigIntegerType(td.limit, Nulls.get(td.nullable));
      }
   
      BigIntegerType(int limit, Nulls nulls) throws IllegalArgumentException {
         super(BigInteger.class, Scheme.INROW, nulls.value(), limit, true);
      }
      @Override
      protected final String typeDescPrefix() {
         return TD_PREFIX;
      }
   
      @Override
      protected final byte[] toBytes(BigInteger val) throws NullPointerException {
         return val.toByteArray();
      }
      @Override
      public final BigInteger fromBytes(byte[] bytes, int offset,
                                     int len) throws IndexOutOfBoundsException {
         return new BigInteger(Arrays.copyOfRange(bytes, offset, offset + len));
      }
   }
   Remarks:
   - 
      The 
limitparameter of the constructor lets us create columns wich accept integer values of an arbitrary size. Recall that Java'sintandlongtypes are 4 and 8 bytes in size, respectively. - 
      The 
nullsparameter of the constructor specifies whether the column type allows thenullvalue. - 
      The second and fifth arguments of the super constructor invocation tell
      us that the 
BigIntegercolumn type uses the efficient inrow storage scheme and expects the byte array returned by thetoBytesmethod to be of a variable length. - 
      The type descriptor starts with a prefix which by default starts
      with the uppercase letter 
Lfollowed by the qualified name of the column type's class name. Overriding thetypeDescPrefix()method lets you specify your own prefix. Such a custom prefix must start with an uppercase letter because the type descriptor of a custom column type must start with an uppercase letter. - 
      Note that the 
createType(String)factory method throws aCreationExceptionif the argument is not a valid type descriptor for theBigIntegercolumn type. 
BigInteger custom column type to replace
   the Float built-in column type in the Salary column of the
   Teacher table.
   Simply replace the line of code
      static final Column<Float> SALARY = CL.ofFloat(NULLABLE);
   in the TeacherTable class with
      static final Column<BigInteger> SALARY = CL.create(
                          new BigIntegerType(20, NULLABLE));
   The Salary column is now a column of type BigInteger with a
   maximum size in its two's-complement binary representation of 20 bytes.
   The column accepts the null value.
Coding a Cipher Factory
Data stored in an ACDP database is not encrypted by default. If encryption is required then we need to provide an implementation of theICipherFactory interface.
   Such a class defines two methods: A factory method returning an instance of
   Java's javax.crypto.Cipher class and a method that initializes
   the cipher.
   That said, the strength of encryption of data stored in an ACDP database
   is not a property of ACDP itself but is completely governed by the strength
   of encryption the Cipher instance returned by the
   createCipher method is able to deliver.
   Here is a ready-to-play-with cipher factory class which unrealistically discloses the secret key in the source code:
   final class CipherFactory implements ICipherFactory {
      private final IvParameterSpec iv = new IvParameterSpec(new byte[] {
                                 114, -8, 22, -67, -71, 30, 118, -103,
                                 51, -45, -110, -65, 16, -127, -73, 103 });
      private final Key key = new SecretKeySpec(new byte[] { 114, -8, 23,
                                   -67, -71, 30, 118, -103, 51, -45, -110,
                                   -65, 16, -127, -73, 103 }, "AES");
      @Override
      public final Cipher createCipher() throws NoSuchAlgorithmException,
                                                  NoSuchPaddingException {
         // Example with padding: AES/CBC/PKCS5Padding
         return Cipher.getInstance("AES/CTR/NoPadding");
      }
      @Override
      public final void initCipher(Cipher cipher, boolean encrypt) throws
                  InvalidKeyException, InvalidAlgorithmParameterException {
         cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
                                                                   key, iv);
      }
   }
   When we speak of an ACDP database we usually mean the writable WRtype. However, as described in Section
Creating an RO Database from a WR Databaseof Chapter
Creation, there exists a second type: the readonly
ROtype. The cipher for a WR database must be a byte-oriented stream cipher or must behave that way. With the
NoPadding attribute, the createCipher
   method of the cipher factory from above returns such a cipher.
   The cipher factory can therefore be used for a WR database.
   (No such restriction applies for an RO database.)
   For details consult the description of the
   
   ICipherFactory interface.