Changeset 3977


Ignore:
Timestamp:
2010-04-06 13:37:14 (3 years ago)
Author:
evert
Message:

Introduced record types and record type groups for non-versioned, versioned, and versioned mutable fields.

Location:
projects/lily/trunk
Files:
15 added
1 deleted
18 edited

Legend:

Unmodified
Added
Removed
  • projects/lily/trunk/indexer/src/main/java/org/lilycms/indexer/conf/IndexerConfBuilder.java

    r3950 r3977  
    163163            List<Element> indexFieldEls = FIELD_CHILDREN.get().evalAsNativeElementList(caseEl); 
    164164            for (Element indexFieldEl : indexFieldEls) { 
    165                 mapping.indexFieldBindings.add(buildIndexFieldBinding(indexFieldEl)); 
     165                mapping.indexFieldBindings.add(buildIndexFieldBinding(indexFieldEl, true)); 
    166166            } 
    167167 
     
    181181            List<Element> indexFieldEls = FIELD_CHILDREN.get().evalAsNativeElementList(caseEl); 
    182182            for (Element indexFieldEl : indexFieldEls) { 
    183                 mapping.indexFieldBindings.add(buildIndexFieldBinding(indexFieldEl)); 
     183                mapping.indexFieldBindings.add(buildIndexFieldBinding(indexFieldEl, false)); 
    184184            } 
    185185 
     
    188188    } 
    189189 
    190     private IndexFieldBinding buildIndexFieldBinding(Element indexFieldEl) throws Exception { 
     190    private IndexFieldBinding buildIndexFieldBinding(Element indexFieldEl, boolean versioned) throws Exception { 
    191191        String name = DocumentHelper.getAttribute(indexFieldEl, "name", false); 
    192192        String ref = DocumentHelper.getAttribute(indexFieldEl, "ref", false); 
     
    211211 
    212212 
    213         Value value = buildValue(DocumentHelper.getElementChild(indexFieldEl, "value", true)); 
     213        Value value = buildValue(DocumentHelper.getElementChild(indexFieldEl, "value", true), versioned); 
    214214 
    215215        return new IndexFieldBinding(field, value); 
    216216    } 
    217217 
    218     private Value buildValue(Element valueEl) throws Exception { 
     218    private Value buildValue(Element valueEl, boolean versioned) throws Exception { 
    219219        Element fieldEl = DocumentHelper.getElementChild(valueEl, "field", true); 
    220220        String name = DocumentHelper.getAttribute(fieldEl, "name", true); 
    221         return new Value(name); 
     221        return new Value(name, versioned); 
    222222    } 
    223223 
  • projects/lily/trunk/indexer/src/main/java/org/lilycms/indexer/conf/Value.java

    r3952 r3977  
    11package org.lilycms.indexer.conf; 
    22 
    3 import org.apache.hadoop.hbase.util.Bytes; 
     3import java.util.Set; 
     4 
    45import org.lilycms.repository.api.FieldNotFoundException; 
    56import org.lilycms.repository.api.Record; 
    67 
    7 import java.util.Set; 
    8  
    98public class Value { 
    109    private String fieldName; 
     10    private final boolean versioned; 
    1111 
    12     public Value(String fieldName) { 
     12    public Value(String fieldName, boolean versioned) { 
    1313        this.fieldName = fieldName; 
     14        this.versioned = versioned; 
    1415    } 
    1516 
    1617    public String eval(Record record) { 
    1718        try { 
    18             return (String)record.getField(fieldName); 
     19            if (versioned) { 
     20                return (String)record.getVersionableField(fieldName); 
     21            } else { 
     22                return (String)record.getNonVersionableField(fieldName); 
     23            } 
    1924        } catch (FieldNotFoundException e) { 
    2025            // TODO 
  • projects/lily/trunk/indexer/src/test/java/org/lilycms/indexer/test/IndexerTest.java

    r3959 r3977  
    2424import org.lilycms.queue.api.QueueListener; 
    2525import org.lilycms.queue.api.QueueMessage; 
     26import org.lilycms.repository.api.FieldGroup; 
    2627import org.lilycms.repository.api.IdGenerator; 
    2728import org.lilycms.repository.api.Record; 
     
    3132import org.lilycms.repository.api.TypeManager; 
    3233import org.lilycms.repository.api.ValueType; 
    33 import org.lilycms.repository.impl.FieldDescriptorImpl; 
    3434import org.lilycms.repository.impl.HBaseRepository; 
    3535import org.lilycms.repository.impl.HBaseTypeManager; 
    3636import org.lilycms.repository.impl.IdGeneratorImpl; 
    37 import org.lilycms.repository.impl.RecordImpl; 
    38 import org.lilycms.repository.impl.RecordTypeImpl; 
    3937import org.lilycms.testfw.TestHelper; 
    4038 
     
    6967    public void testIndexer() throws Exception { 
    7068        IdGenerator idGenerator = new IdGeneratorImpl(); 
    71         TypeManager typeManager = new HBaseTypeManager(idGenerator, RecordTypeImpl.class, FieldDescriptorImpl.class, TEST_UTIL.getConfiguration()); 
    72         Repository repository = new HBaseRepository(typeManager, idGenerator, RecordImpl.class, TEST_UTIL.getConfiguration()); 
     69        TypeManager typeManager = new HBaseTypeManager(idGenerator, TEST_UTIL.getConfiguration()); 
     70        Repository repository = new HBaseRepository(typeManager, idGenerator, TEST_UTIL.getConfiguration()); 
    7371        SolrServer solrServer = SOLR_TEST_UTIL.getSolrServer(); 
    7472        TestLilyQueue queue = new TestLilyQueue(); 
     
    7674 
    7775        // Create a record type 
     76        ValueType stringValueType = typeManager.getValueType("STRING", false, false); 
     77        typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("field1", stringValueType, "field1GN")); 
     78        typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("field2", stringValueType, "field2GN")); 
     79        typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("field3", stringValueType, "field3GN")); 
     80        FieldGroup fieldGroup1 = typeManager.newFieldGroup("fieldGroup1"); 
     81        fieldGroup1.setFieldGroupEntry(typeManager.newFieldGroupEntry("field1", Long.valueOf(1), true, "alias1")); 
     82        fieldGroup1.setFieldGroupEntry(typeManager.newFieldGroupEntry("field2", Long.valueOf(1), true, "alias2")); 
     83        fieldGroup1.setFieldGroupEntry(typeManager.newFieldGroupEntry("field3", Long.valueOf(1), true, "alias3")); 
     84        fieldGroup1 = typeManager.createFieldGroup(fieldGroup1); 
     85 
     86        typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("nvfield1", stringValueType, "nvfield1GN")); 
     87        FieldGroup fieldGroup2 = typeManager.newFieldGroup("fieldGroup2"); 
     88        fieldGroup2.setFieldGroupEntry(typeManager.newFieldGroupEntry("nvfield1", Long.valueOf(1), true, "nvalias1")); 
     89        fieldGroup2 = typeManager.createFieldGroup(fieldGroup2); 
     90         
    7891        RecordType recordType = typeManager.newRecordType("RecordType1"); 
    79         ValueType stringValueType = typeManager.getValueType("STRING", false, false); 
    80         recordType.addFieldDescriptor(typeManager.newFieldDescriptor("field1", stringValueType, true, true)); 
    81         recordType.addFieldDescriptor(typeManager.newFieldDescriptor("field2", stringValueType, true, true)); 
    82         recordType.addFieldDescriptor(typeManager.newFieldDescriptor("field3", stringValueType, true, true)); 
    83         recordType.addFieldDescriptor(typeManager.newFieldDescriptor("nvfield1", stringValueType, true, false)); 
    84         typeManager.createRecordType(recordType); 
    85  
    86         // TODO need to re-retrieve the record type because its version property is not updated 
    87         recordType = typeManager.getRecordType("RecordType1"); 
     92        recordType.setVersionableFieldGroupId(fieldGroup1.getId()); 
     93        recordType.setVersionableFieldGroupVersion(fieldGroup1.getVersion()); 
     94        recordType.setNonVersionableFieldGroupId(fieldGroup2.getId()); 
     95        recordType.setNonVersionableFieldGroupVersion(fieldGroup2.getVersion()); 
     96        recordType = typeManager.createRecordType(recordType); 
    8897 
    8998        // Create a document 
    90         Record record = repository.newRecord(); 
     99        Record record = repository.newRecord(idGenerator.newRecordId()); 
    91100        record.setRecordType("RecordType1", recordType.getVersion()); 
    92         record.setField("field1", "apple"); 
    93         record.setField("field2", "pear"); 
    94         record.setField("field3", "orange"); 
    95         record.setField("nvfield1", "banana"); 
     101        record.setVersionableField("field1", "apple"); 
     102        record.setVersionableField("field2", "pear"); 
     103        record.setVersionableField("field3", "orange"); 
     104        record.setNonVersionableField("nvfield1", "banana"); 
    96105        repository.create(record); 
    97106 
     
    114123        record = repository.newRecord(record.getId()); 
    115124        record.setRecordType(recordType.getId(), recordType.getVersion()); 
    116         record.setField("field1", "peach"); 
     125        record.setVersionableField("field1", "peach"); 
    117126        repository.update(record); 
    118127        record = repository.read(record.getId()); 
  • projects/lily/trunk/linkmgmt/src/main/java/org/lilycms/linkmgmt/RecordLinkExtractor.java

    r3966 r3977  
    1414        //  run over all fields, check using the typemanager if it is a link field, if so put 
    1515        //  the resolved link into the collector. 
    16         for(Map.Entry<String, Object> field : record.getFields().entrySet()) { 
     16        for(Map.Entry<String, Object> field : record.getVersionableFields().entrySet()) { 
    1717        } 
    1818 
  • projects/lily/trunk/linkmgmt/src/test/java/org/lilycms/linkmgmt/test/LinkManagerTest.java

    r3966 r3977  
    11package org.lilycms.linkmgmt.test; 
     2 
     3import static org.junit.Assert.*; 
     4 
     5import java.util.HashSet; 
     6import java.util.Set; 
    27 
    38import org.apache.hadoop.hbase.HBaseTestingUtility; 
     
    510import org.junit.BeforeClass; 
    611import org.junit.Test; 
    7 import static org.junit.Assert.*; 
    812import org.lilycms.hbaseindex.IndexManager; 
    913import org.lilycms.linkmgmt.FieldedLink; 
     
    1317import org.lilycms.repository.api.Repository; 
    1418import org.lilycms.repository.api.TypeManager; 
    15 import org.lilycms.repository.impl.*; 
     19import org.lilycms.repository.impl.HBaseRepository; 
     20import org.lilycms.repository.impl.HBaseTypeManager; 
     21import org.lilycms.repository.impl.IdGeneratorImpl; 
    1622import org.lilycms.testfw.TestHelper; 
    17  
    18 import java.util.HashSet; 
    19 import java.util.Set; 
    2023 
    2124public class LinkManagerTest { 
     
    3841    public void test() throws Exception { 
    3942        IdGenerator idGenerator = new IdGeneratorImpl(); 
    40         TypeManager typeManager = new HBaseTypeManager(idGenerator, RecordTypeImpl.class, FieldDescriptorImpl.class, TEST_UTIL.getConfiguration()); 
    41         Repository repository = new HBaseRepository(typeManager, idGenerator, RecordImpl.class, TEST_UTIL.getConfiguration()); 
     43        TypeManager typeManager = new HBaseTypeManager(idGenerator, TEST_UTIL.getConfiguration()); 
     44        Repository repository = new HBaseRepository(typeManager, idGenerator, TEST_UTIL.getConfiguration()); 
    4245        IdGenerator ids = repository.getIdGenerator(); 
    4346        IndexManager indexManager = new IndexManager(TEST_UTIL.getConfiguration()); 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/FieldDescriptor.java

    r3955 r3977  
    3131public interface FieldDescriptor { 
    3232    /** 
    33      * The id of the {@link FieldDescriptor} is also the id to be used by the corresponding {@link Field}   
     33     * The id of the {@link FieldDescriptor} is also the id to be used by the corresponding {@link Field} 
     34     * The id is system generated and unique.   
    3435     * @return the id of the {@link FieldDescriptor} 
    3536     */ 
    3637    String getId(); 
    3738 
     39    /** 
     40     * Set the version of the {@link FieldDescriptor} 
     41     */ 
     42    void setVersion(Long version); 
     43     
    3844    /** 
    3945     * @return the version of the {@link FieldDescriptor} 
     
    4248 
    4349    /** 
     50     * Set the global unique name of the {@link FieldDescriptor}. 
     51     * The name can be chose by the user. 
     52     */ 
     53    void setGlobalName(String name); 
     54     
     55    /** 
     56     * @return the global unique name of the {@link FieldDescriptor} 
     57     */ 
     58    String getGlobalName(); 
     59     
     60    /** 
     61     * Set the {@link ValueType} of the {@link Field} 
     62     * @param valueType 
     63     */ 
     64    void setValueType(ValueType valueType); 
     65     
     66    /** 
    4467     * @return the {@link ValueType} of the {@link Field} 
    4568     */ 
    4669    ValueType getValueType(); 
    4770 
    48     /** 
    49      * @return if the {@link Field} is mandatory 
    50      */ 
    51     boolean isMandatory(); 
     71    boolean equals(Object obj); 
    5272 
    53     /** 
    54      * @return if the {@link Field} is versionable 
    55      */ 
    56     boolean isVersionable(); 
    57      
    58     boolean equals(Object obj); 
     73    FieldDescriptor clone(); 
    5974} 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/PrimitiveValueType.java

    r3959 r3977  
    5050     */ 
    5151    Class getType(); 
     52     
     53    @Override 
     54    boolean equals(Object obj); 
    5255} 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/Record.java

    r3955 r3977  
    1616package org.lilycms.repository.api; 
    1717 
     18import java.util.List; 
    1819import java.util.Map; 
    19 import java.util.Set; 
    2020 
    2121/** 
     
    3434 * Fields to be deleted need to be added explicitly with their fieldId 
    3535 *  
    36  * <p> 
    37  * A record can either be an ordinary record or a variant record, in which case 
    38  * the variantProperties need to be provided, and the {@link RecordId} should 
    39  * point to the master record. 
    4036 */ 
    4137public interface Record { 
     
    4844    Long getVersion(); 
    4945 
    50     void setRecordType(String recordTypeId, long recordTypeVersion); 
     46    void setRecordType(String id, Long version); 
    5147 
    5248    String getRecordTypeId(); 
    5349 
    54     long getRecordTypeVersion(); 
     50    Long getRecordTypeVersion(); 
     51     
     52    void setNonVersionableRecordType(String id, Long version); 
     53     
     54    String getNonVersionableRecordTypeId(); 
     55     
     56    Long getNonVersionableRecordTypeVersion(); 
     57     
     58    void setVersionableRecordType(String id, Long version); 
     59     
     60    String getVersionableRecordTypeId(); 
     61     
     62    Long getVersionableRecordTypeVersion(); 
     63     
     64    void setVersionableMutableRecordType(String id, Long version); 
     65     
     66    String getVersionableMutableRecordTypeId(); 
     67     
     68    Long getVersionableMutableRecordTypeVersion(); 
    5569 
    56     void setField(String fieldId, Object value); 
     70    void setNonVersionableField(String fieldId, Object value); 
    5771 
    58     Object getField(String fieldId) throws FieldNotFoundException; 
     72    Object getNonVersionableField(String fieldId) throws FieldNotFoundException; 
    5973 
    60     Map<String, Object> getFields(); 
     74    void setVersionableField(String fieldId, Object value); 
    6175 
    62     void deleteField(String fieldId); 
     76    Object getVersionableField(String fieldId) throws FieldNotFoundException; 
    6377 
    64     Set<String> getDeleteFields(); 
     78    void setVersionableMutableField(String fieldId, Object value); 
     79 
     80    Object getVersionableMutableField(String fieldId) throws FieldNotFoundException; 
     81     
     82    Map<String, Object> getNonVersionableFields(); 
     83 
     84    Map<String, Object> getVersionableFields(); 
     85 
     86    Map<String, Object> getVersionableMutableFields(); 
     87     
     88    void addNonVersionableFieldsToDelete(List<String> fieldIds); 
     89     
     90    void removeNonVersionableFieldsToDelete(List<String> fieldIds); 
     91 
     92    List<String> getNonVersionableFieldsToDelete(); 
     93     
     94    void addVersionableFieldsToDelete(List<String> fieldIds); 
     95 
     96    void removeVersionableFieldsToDelete(List<String> fieldIds); 
     97     
     98    List<String> getVersionableFieldsToDelete(); 
     99     
     100    void addVersionableMutableFieldsToDelete(List<String> fieldIds); 
     101 
     102    void removeVersionableMutableFieldsToDelete(List<String> fieldIds); 
     103     
     104    List<String> getVersionableMutableFieldsToDelete(); 
     105     
     106    Record clone(); 
    65107     
    66108    boolean equals(Object obj); 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/RecordType.java

    r3955 r3977  
    1616package org.lilycms.repository.api; 
    1717 
    18 import java.util.Collection; 
    1918 
    2019/** 
     
    3635     
    3736    Long getVersion(); 
     37     
     38    void setNonVersionableFieldGroupId(String id); 
     39  
     40    void setNonVersionableFieldGroupVersion(Long version); 
     41     
     42    void setVersionableFieldGroupId(String id); 
     43     
     44    void setVersionableFieldGroupVersion(Long version); 
     45     
     46    void setVersionableMutableFieldGroupId(String id); 
     47     
     48    void setVersionableMutableFieldGroupVersion(Long version); 
     49     
     50    String getNonVersionableFieldGroupId(); 
     51     
     52    Long getNonVersionableFieldGroupVersion(); 
     53     
     54    String getVersionableFieldGroupId(); 
     55     
     56    Long getVersionableFieldGroupVersion(); 
     57     
     58    String getVersionableMutableFieldGroupId(); 
     59     
     60    Long getVersionableMutableFieldGroupVersion(); 
    3861 
    39     void addFieldDescriptor(FieldDescriptor fieldDescriptor); 
    40  
    41     void removeFieldDescriptor(String removeFieldDescriptorId); 
    42  
    43     FieldDescriptor getFieldDescriptor(String fieldDescriptorId); 
    44  
    45     Collection<FieldDescriptor> getFieldDescriptors(); 
    46  
     62    RecordType clone(); 
     63     
    4764    boolean equals(Object obj); 
    4865} 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/Repository.java

    r3952 r3977  
    1616package org.lilycms.repository.api; 
    1717 
     18import java.util.List; 
     19 
    1820 
    1921/** 
     
    2426     * Creates a new {@link Record} object. 
    2527     */ 
    26     Record newRecord() throws RepositoryException; 
     28    Record newRecord(); 
    2729 
    2830    /** 
     
    3032     * filled in. 
    3133     */ 
    32     Record newRecord(RecordId recordId) throws RepositoryException; 
     34    Record newRecord(RecordId recordId); 
    3335 
    3436    /** 
     
    4850     * @throws InvalidRecordException 
    4951     *             if an empty record is being created 
     52     * @throws FieldDescriptorNotFoundException  
     53     * @throws FieldGroupNotFoundException  
     54     * @throws RecordTypeNotFoundException  
    5055     * @throws RepostioryException 
    5156     *             TBD 
    5257     */ 
    53     void create(Record record) throws RecordExistsException, RecordNotFoundException, InvalidRecordException, 
    54                     RepositoryException; 
     58    Record create(Record record) throws RecordExistsException, RecordNotFoundException, InvalidRecordException, 
     59                    RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
    5560 
    5661    /** 
     
    7580     * @throws RepositoryException 
    7681     *             TBD 
     82     * @throws FieldDescriptorNotFoundException  
     83     * @throws FieldGroupNotFoundException  
     84     * @throws RecordTypeNotFoundException  
    7785     */ 
    78     void update(Record record) throws RecordNotFoundException, InvalidRecordException, RepositoryException; 
     86    Record update(Record record) throws RecordNotFoundException, InvalidRecordException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
    7987 
    80     /** 
    81      * Read the latest version of a {@link Record} from the repository. 
    82      *  
    83      * @param recordId 
    84      *            the id of the {@link Record} to read 
    85      * @param fieldIds 
    86      *            the fields to read from the {@link Record}, an empty list 
    87      *            results in reading all fields 
    88      * @throws RecordNotFoundException 
    89      *             if the {@link Record} does not exist 
    90      * @throws RepositoryException 
    91      *             TBD 
    92      */ 
    93     Record read(RecordId recordId, String... fieldIds) throws RecordNotFoundException, RepositoryException; 
     88    Record read(RecordId recordId) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
    9489 
    95     /** 
    96      * Read a specific version of a {@link Record} 
    97      *  
    98      * @param version 
    99      *            the versionNumber to read 
    100      *  
    101      */ 
    102     Record read(RecordId recordId, Long version, String... fieldIds) throws RecordNotFoundException, 
    103                     RepositoryException; 
     90    Record read(RecordId recordId, List<String> nonVersionableFieldIds, List<String> versionableFieldIds, List<String> versionableMutableFieldIds) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
     91 
     92    Record read(RecordId recordId, Long version) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
     93 
     94    Record read(RecordId recordId, Long version, List<String> nonVersionableFieldIds, List<String> versionableFieldIds, List<String> versionableMutableFieldIds) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
    10495 
    10596    /** 
  • projects/lily/trunk/repository/api/src/main/java/org/lilycms/repository/api/TypeManager.java

    r3955 r3977  
    1616package org.lilycms.repository.api; 
    1717 
     18import java.util.List; 
     19 
    1820 
    1921/** 
     
    2628     * Creates a new {@link RecordType} object. 
    2729     */ 
    28     RecordType newRecordType(String recordTypeId) throws RepositoryException; 
     30    RecordType newRecordType(String recordTypeId); 
    2931 
    3032    /** 
    31      * Creates a new {@link FieldDescriptor} object. 
     33     * Creates a {@link RecordType} on the repository with the properties defined in the {@link RecordType} object. 
     34     * @throws RecordTypeExistsException when a recordType with the same id already exists on the repository  
     35     * @throws FieldGroupNotFoundException when the recordType refers to a non-existing {@link FieldGroup} 
     36     * @throws RepositoryException when an unexpected exception occurs on the repository 
    3237     */ 
    33     FieldDescriptor newFieldDescriptor(String fieldDescriptorId, ValueType valueType, boolean mandatory, boolean versionable) throws RepositoryException; 
     38    RecordType createRecordType(RecordType recordType) throws RecordTypeExistsException, FieldGroupNotFoundException, RepositoryException; 
    3439     
    3540    /** 
    36      * Creates a new {@link FieldDescriptor} object. 
     41     * Retrieves the latest version of a {@link RecordType} from the repository. 
     42     * @throws RecordTypeNotFoundException when the recordType does not exist 
     43     * @throws RepositoryException when an unexpected exception occurs on the repository 
    3744     */ 
    38     FieldDescriptor newFieldDescriptor(String fieldDescriptorId, Long version, ValueType valueType, boolean mandatory, boolean versionable) throws RepositoryException; 
    39      
    40     /** 
    41      * Creates a {@link RecordType} on the repository with the properties defined in the {@link RecordType} object. 
    42      */ 
    43     void createRecordType(RecordType recordType) throws RepositoryException; 
    44  
    45     /** 
    46      * Retrieves the latest version of a {@link RecordType} from the repository. 
    47      */ 
    48     RecordType getRecordType(String recordTypeId) throws RepositoryException; 
    49  
    50     /** 
    51      * Retrieves a specific version of a {@link RecordType} from the repository. 
    52      * If no version is given, the latest version is retrieved. 
    53      */ 
    54     RecordType getRecordType(String recordTypeId, Long recordTypeVersion) throws RepositoryException; 
     45    RecordType getRecordType(String recordTypeId, Long recordTypeVersion) throws RecordTypeNotFoundException, RepositoryException; 
    5546 
    5647    /** 
     
    5950     * be deleted it should be left out of the {@link RecordType}'s list of 
    6051     * {@link FieldDescriptor}s. 
     52     * @throws RecordTypeNotFoundException when the recordType to be updated does not exist  
     53     * @throws FieldGroupNotFoundException when a {@link FieldGroup} referred to by the recordType does not exist 
     54     * @throws RepositoryException when an unexpected exception occurs on the repository 
    6155     */ 
    62     void updateRecordType(RecordType recordType) throws RepositoryException; 
     56    RecordType updateRecordType(RecordType recordType) throws  RecordTypeNotFoundException, FieldGroupNotFoundException, RepositoryException; 
    6357     
     58    /** 
     59     * Removes fieldGroups from the recordType. If no fieldGroup existed it is ignored. 
     60     * @param recordTypeId the id of the {@link RecordType} 
     61     * @param nonVersionable if the non-versionable fieldGroup should be removed 
     62     * @param versionable if the versionable fieldGroup should be removed 
     63     * @param versionableMutable if the versionable-mutable fieldGroup should be removed 
     64     * @return a {@link RecordType} with an updated version number and the requested fieldGroups removed 
     65     * @throws RecordTypeNotFoundException when the recordType does not exist 
     66     * @throws RepositoryException when an unexpected exception occurs on the repository 
     67     */ 
     68    RecordType removeFieldGroups(String recordTypeId, boolean nonVersionable, boolean versionable, boolean versionableMutable) throws RecordTypeNotFoundException, RepositoryException; 
     69     
     70    /** 
     71     * Creates a new {@link FieldGroup} object. 
     72     */ 
     73    FieldGroup newFieldGroup(String id); 
     74     
     75    FieldGroupEntry newFieldGroupEntry(String fieldDescriptorId, Long fieldDescriptorVersion, boolean mandatory, String alias); 
     76     
     77    /** 
     78     * Creates a {@link FieldGroup} on the repository with the properties defined in the {@link FieldGroup} object. 
     79     * @return a {@link FieldGroup} object containing the updated version number of the fieldGroup 
     80     * @throws FieldGroupExistsException when a fieldGroup with the same id already exists on the repository  
     81     * @throws FieldDescriptorNotFoundException when the fieldGroup refers to a non-existing {@link FieldDescriptor}  
     82     * @throws RepositoryException when an unexpected exception occurs on the repository 
     83     */ 
     84    FieldGroup createFieldGroup(FieldGroup fieldGroup) throws FieldGroupExistsException, FieldDescriptorNotFoundException, RepositoryException; 
     85     
     86    /** 
     87     * Updates a {@link FieldGroup} on the repository with the properties defined in the {@link FieldGroup} object. 
     88     * @return a {@link FieldGroup} object containing the updated version number of the fieldGroup 
     89     * @throws FieldGroupNotFoundException when the fieldGroup to update does not exist 
     90     * @throws FieldDescriptorNotFoundException when the updated fieldGroup refers to a non-existing {@link FieldDescriptor} 
     91     * @throws RepositoryException when an unexpected exception occurs on the repository 
     92     */ 
     93    FieldGroup updateFieldGroup(FieldGroup fieldGroup) throws FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException; 
     94     
     95    /** 
     96     * Removes fieldDescriptors from fieldGroup. This operation is performed towards the latest version of the fieldGroup. 
     97     * FieldDescriptors that are not known by the fieldGroup are ignored. 
     98     * @param fieldGroupId the id of the {@link FieldGroup} to perform this operation on 
     99     * @param fieldDescriptorIds a list of {@link FieldDescriptors} to remove 
     100     * @return a new {@link FieldGroup} with an updated version number and the remaining fieldDescriptors 
     101     * @throws FieldGroupNotFoundException when the fieldGroup does not exist 
     102     * @throws RepositoryException when an unexpected exception occurs in the repository 
     103     */ 
     104    FieldGroup removeFieldDescriptors(String fieldGroupId, List<String> fieldDescriptorIds) throws FieldGroupNotFoundException, RepositoryException; 
     105     
     106    /** 
     107     * Gets a {@link FieldGroup} from the repository 
     108     * @param version is the version of the {@link FieldGroup} to get. If null, the latest existing version is taken. 
     109     * @return a {@link FieldGroup} object  
     110     * @throws FieldGroupNotFoundException when no fieldGroup with id and version exists 
     111     * @throws RepositoryException when an unexpected exception occurs on the repository 
     112     */ 
     113    FieldGroup getFieldGroup(String id, Long version) throws FieldGroupNotFoundException, RepositoryException; 
     114     
     115    // Field Descriptors 
     116    /** 
     117     * Creates a new {@link FieldDescriptor} object. 
     118     */ 
     119    FieldDescriptor newFieldDescriptor(String id, ValueType valueType, String globalName); 
     120     
     121    /** 
     122     * Creates a {@link FieldDescriptor} on the repository with the properties defined in the {@link FieldDescriptor} object. 
     123     * @return a {@link FieldDescriptor} object containing the updated version number of the fieldDescriptor 
     124     * @throws FieldDescriptorExistsException if a fieldDescriptor with the same id already exists on the repository  
     125     * @throws RepositoryException when an unexpected exception occurs on the repository 
     126     */ 
     127    FieldDescriptor createFieldDescriptor(FieldDescriptor fieldDescriptor) throws FieldDescriptorExistsException, RepositoryException; 
     128 
     129    /** 
     130     * Updates a {@link FieldDescriptor} on the repository with the properties defined in the {@link FieldDescriptor} object. 
     131     * The version number in the given fieldDescriptor is ignored, a new version number will be assigned. 
     132     * @return a {@link FieldDescriptor} object containing the new version number of the fieldDescriptor 
     133     * @throws FieldDescriptorNotFoundException when no fieldDescriptor with id and version exists 
     134     * @throws FieldDescriptorUpdateException an exception occured while updating the FieldDescriptor  
     135     * @throws RepositoryException when an unexpected exception occurs on the repository 
     136     */ 
     137    FieldDescriptor updateFieldDescriptor(FieldDescriptor fieldDescriptor) throws FieldDescriptorNotFoundException, FieldDescriptorUpdateException, RepositoryException;  
     138     
     139    /** 
     140     * Gets a {@link FieldDescriptor} from the repository 
     141     * @param version is the version of the {@link FieldDescriptor} to get. If null, the latest existing version is taken. 
     142     * @return a {@link FieldDescriptor} object  
     143     * @throws FieldDescriptorNotFoundException when no fieldDescriptor with id and version exists 
     144     * @throws RepositoryException when an unexpected exception occurs on the repository 
     145     */ 
     146    FieldDescriptor getFieldDescriptor(String id, Long version) throws FieldDescriptorNotFoundException, RepositoryException; 
     147 
     148     
     149    // Value Types 
     150     
     151    /** 
     152     * A new {@link PrimitiveValueType} should be registered by calling this method before it can be used. 
     153     */ 
     154    void registerPrimitiveValueType(PrimitiveValueType primitiveValueType); 
     155 
    64156    /** 
    65157     * This method should be called to get a {@link ValueType} instance  
     
    69161     */ 
    70162    ValueType getValueType(String primitiveValueTypeName, boolean multiValue, boolean hierarchical); 
    71      
    72     /** 
    73      * A new {@link PrimitiveValueType} should be registered by calling this method before it can be used. 
    74      */ 
    75     void registerPrimitiveValueType(PrimitiveValueType primitiveValueType); 
    76163} 
  • projects/lily/trunk/repository/impl/src/main/java/org/lilycms/repository/impl/FieldDescriptorImpl.java

    r3952 r3977  
    2222public class FieldDescriptorImpl implements FieldDescriptor { 
    2323 
    24     private final String fieldDescriptorId; 
     24    private final String id; 
    2525    private Long version; 
    26     private final boolean mandatory; 
    27     private final boolean versionable; 
    28     private final ValueType valueType; 
     26    private ValueType valueType; 
     27    private String globalName; 
    2928 
    3029    /** 
     
    3231     * @use {@link TypeManager#newFieldDescriptor} instead 
    3332     */ 
    34     public FieldDescriptorImpl(String fieldDescriptorId, ValueType valueType, boolean mandatory, boolean versionable) { 
    35         this(fieldDescriptorId, null, valueType, mandatory, versionable); 
     33    public FieldDescriptorImpl(String id, ValueType valueType, String globalName) { 
     34        this.id = id; 
     35        this.valueType = valueType; 
     36        this.globalName = globalName; 
    3637    } 
    3738 
    38     /** 
    39      * This constructor should not be called directly. 
    40      * @use {@link TypeManager#newFieldDescriptor} instead 
    41      */ 
    42     public FieldDescriptorImpl(String fieldDescriptorId, Long version, ValueType valueType, boolean mandatory, boolean versionable) { 
    43         this.fieldDescriptorId = fieldDescriptorId; 
    44         this.version = version; 
    45         this.valueType = valueType; 
    46         this.mandatory = mandatory; 
    47         this.versionable = versionable; 
     39    public String getGlobalName() { 
     40        return globalName; 
     41    } 
     42 
     43    public String getId() { 
     44        return id; 
    4845    } 
    4946 
     
    5249    } 
    5350 
    54     public String getId() { 
    55         return fieldDescriptorId; 
    56     } 
    57  
    5851    public Long getVersion() { 
    5952        return version; 
    6053    } 
    61      
    62     public void setVersion(long version) { 
     54 
     55    public void setGlobalName(String name) { 
     56        this.globalName = name; 
     57    } 
     58 
     59    public void setValueType(ValueType valueType) { 
     60        this.valueType = valueType; 
     61    } 
     62 
     63    public void setVersion(Long version) { 
    6364        this.version = version; 
    6465    } 
    65  
    66     public boolean isMandatory() { 
    67         return mandatory; 
    68     } 
    69  
    70     public boolean isVersionable() { 
    71         return versionable; 
     66     
     67    public FieldDescriptor clone() { 
     68        FieldDescriptorImpl clone = new FieldDescriptorImpl(this.id, this.valueType, this.globalName); 
     69        clone.version = this.version; 
     70        return clone; 
    7271    } 
    7372 
     
    7675        final int prime = 31; 
    7776        int result = 1; 
    78         result = prime * result + ((fieldDescriptorId == null) ? 0 : fieldDescriptorId.hashCode()); 
     77        result = prime * result + ((globalName == null) ? 0 : globalName.hashCode()); 
     78        result = prime * result + ((id == null) ? 0 : id.hashCode()); 
    7979        result = prime * result + ((valueType == null) ? 0 : valueType.hashCode()); 
    80         result = prime * result + (mandatory ? 1231 : 1237); 
    8180        result = prime * result + ((version == null) ? 0 : version.hashCode()); 
    82         result = prime * result + (versionable ? 1231 : 1237); 
    8381        return result; 
    8482    } 
     
    9391            return false; 
    9492        FieldDescriptorImpl other = (FieldDescriptorImpl) obj; 
    95         if (fieldDescriptorId == null) { 
    96             if (other.fieldDescriptorId != null) 
     93        if (globalName == null) { 
     94            if (other.globalName != null) 
    9795                return false; 
    98         } else if (!fieldDescriptorId.equals(other.fieldDescriptorId)) 
     96        } else if (!globalName.equals(other.globalName)) 
     97            return false; 
     98        if (id == null) { 
     99            if (other.id != null) 
     100                return false; 
     101        } else if (!id.equals(other.id)) 
    99102            return false; 
    100103        if (valueType == null) { 
     
    103106        } else if (!valueType.equals(other.valueType)) 
    104107            return false; 
    105         if (mandatory != other.mandatory) 
    106             return false; 
    107108        if (version == null) { 
    108109            if (other.version != null) 
    109110                return false; 
    110111        } else if (!version.equals(other.version)) 
    111             return false; 
    112         if (versionable != other.versionable) 
    113112            return false; 
    114113        return true; 
     
    117116    @Override 
    118117    public String toString() { 
    119         return "FieldDescriptorImpl [fieldDescriptorId=" + fieldDescriptorId + ", version=" + version + ", valueType=" 
    120                         + valueType + ", mandatory=" + mandatory + ", versionable=" + versionable + "]"; 
     118        return "FieldDescriptorImpl [id=" + id + ", version=" + version + ", globalName=" + globalName 
     119                        + ", valueType=" + valueType + "]"; 
    121120    } 
    122      
    123121} 
  • projects/lily/trunk/repository/impl/src/main/java/org/lilycms/repository/impl/HBaseRepository.java

    r3967 r3977  
    1717 
    1818import java.io.IOException; 
    19 import java.lang.reflect.Constructor; 
     19import java.util.ArrayList; 
     20import java.util.List; 
     21import java.util.Map; 
    2022import java.util.NavigableMap; 
    21 import java.util.Set; 
    2223import java.util.Map.Entry; 
    2324 
     
    3435import org.apache.hadoop.hbase.util.Bytes; 
    3536import org.lilycms.repository.api.FieldDescriptor; 
     37import org.lilycms.repository.api.FieldDescriptorNotFoundException; 
     38import org.lilycms.repository.api.FieldGroup; 
     39import org.lilycms.repository.api.FieldGroupEntry; 
     40import org.lilycms.repository.api.FieldGroupNotFoundException; 
    3641import org.lilycms.repository.api.IdGenerator; 
    3742import org.lilycms.repository.api.InvalidRecordException; 
     
    4146import org.lilycms.repository.api.RecordNotFoundException; 
    4247import org.lilycms.repository.api.RecordType; 
     48import org.lilycms.repository.api.RecordTypeNotFoundException; 
    4349import org.lilycms.repository.api.Repository; 
    4450import org.lilycms.repository.api.RepositoryException; 
     
    4652import org.lilycms.repository.api.ValueType; 
    4753import org.lilycms.util.ArgumentValidator; 
     54import org.lilycms.util.Pair; 
    4855 
    4956public class HBaseRepository implements Repository { 
    5057 
    51     private static final byte[] SYSTEM_COLUMN_FAMILY = Bytes.toBytes("systemCF"); 
    52     private static final byte[] VERSIONABLE_SYSTEM_COLUMN_FAMILY = Bytes.toBytes("versionableSystemCF"); 
    53     private static final byte[] VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("versionableCF"); 
    54     private static final byte[] NON_VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("nonVersionableCF"); 
    55     private static final byte[] CURRENT_VERSION_COLUMN_NAME = Bytes.toBytes("currentVersion"); 
     58    private static final byte[] NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY = Bytes.toBytes("NonVersionableSystemCF"); 
     59    private static final byte[] VERSIONABLE_SYSTEM_COLUMN_FAMILY = Bytes.toBytes("VersionableSystemCF"); 
     60    private static final byte[] NON_VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("NonVersionableCF"); 
     61    private static final byte[] VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("VersionableCF"); 
     62    private static final byte[] VERSIONABLE_MUTABLE_COLUMN_FAMILY = Bytes.toBytes("VersionableMutableCF"); 
     63    private static final byte[] CURRENT_VERSION_COLUMN_NAME = Bytes.toBytes("$CurrentVersion"); 
    5664    private static final byte[] RECORDTYPEID_COLUMN_NAME = Bytes.toBytes("$RecordTypeId"); 
    5765    private static final byte[] RECORDTYPEVERSION_COLUMN_NAME = Bytes.toBytes("$RecordTypeVersion"); 
     66    private static final byte[] NON_VERSIONABLE_RECORDTYPEID_COLUMN_NAME = Bytes.toBytes("$NonVersionableRecordTypeId"); 
     67    private static final byte[] NON_VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME = Bytes.toBytes("$NonVersionableRecordTypeVersion"); 
     68    private static final byte[] VERSIONABLE_RECORDTYPEID_COLUMN_NAME = Bytes.toBytes("$VersionableRecordTypeId"); 
     69    private static final byte[] VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME = Bytes.toBytes("$VersionableRecordTypeVersion"); 
     70    private static final byte[] VERSIONABLE_MUTABLE_RECORDTYPEID_COLUMN_NAME = Bytes.toBytes("$VersionableMutableRecordTypeId"); 
     71    private static final byte[] VERSIONABLE_MUTABLE_RECORDTYPEVERSION_COLUMN_NAME = Bytes.toBytes("$VersionableMutableRecordTypeVersion"); 
    5872    private static final String RECORD_TABLE = "recordTable"; 
    5973    private HTable recordTable; 
    6074    private final TypeManager typeManager; 
    6175    private final IdGenerator idGenerator; 
    62     private Class<Record> recordClass; 
    63  
    64     public HBaseRepository(TypeManager typeManager, IdGenerator idGenerator, Class recordClass,  
     76 
     77    public HBaseRepository(TypeManager typeManager, IdGenerator idGenerator,  
    6578                    Configuration configuration) throws IOException { 
    6679        this.typeManager = typeManager; 
    6780        this.idGenerator = idGenerator; 
    68         this.recordClass = recordClass; 
    6981        try { 
    7082            recordTable = new HTable(configuration, RECORD_TABLE); 
     
    7284            HBaseAdmin admin = new HBaseAdmin(configuration); 
    7385            HTableDescriptor tableDescriptor = new HTableDescriptor(RECORD_TABLE); 
    74             tableDescriptor.addFamily(new HColumnDescriptor(SYSTEM_COLUMN_FAMILY)); 
     86            tableDescriptor.addFamily(new HColumnDescriptor(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY)); 
    7587            tableDescriptor.addFamily(new HColumnDescriptor(VERSIONABLE_SYSTEM_COLUMN_FAMILY, HConstants.ALL_VERSIONS, 
    7688                            "none", false, true, HConstants.FOREVER, false)); 
     89            tableDescriptor.addFamily(new HColumnDescriptor(NON_VERSIONABLE_COLUMN_FAMILY)); 
    7790            tableDescriptor.addFamily(new HColumnDescriptor(VERSIONABLE_COLUMN_FAMILY, HConstants.ALL_VERSIONS, "none", 
    7891                            false, true, HConstants.FOREVER, false)); 
    79             tableDescriptor.addFamily(new HColumnDescriptor(NON_VERSIONABLE_COLUMN_FAMILY)); 
     92            tableDescriptor.addFamily(new HColumnDescriptor(VERSIONABLE_MUTABLE_COLUMN_FAMILY, HConstants.ALL_VERSIONS, 
     93                            "none", false, true, HConstants.FOREVER, false)); 
    8094            admin.createTable(tableDescriptor); 
    8195            recordTable = new HTable(configuration, RECORD_TABLE); 
     
    86100        return idGenerator; 
    87101    } 
    88      
    89     public Record newRecord() throws RepositoryException { 
    90         try { 
    91             return (Record) recordClass.newInstance(); 
    92         } catch (Exception e) { 
    93             throw new RepositoryException("Exception occured while creating new Record object", e); 
    94         } 
    95     } 
    96  
    97     public Record newRecord(RecordId recordId) throws RepositoryException { 
    98         try { 
    99             Constructor<Record> constructor = recordClass.getConstructor(RecordId.class); 
    100             return constructor.newInstance(recordId); 
    101         } catch (Exception e) { 
    102             throw new RepositoryException("Exception occured while creating new Record object <" + recordId + ">", e); 
    103         } 
    104     } 
    105  
    106     public void create(Record record) throws RecordExistsException, RecordNotFoundException, InvalidRecordException, RepositoryException { 
     102 
     103    public Record newRecord() { 
     104        return new RecordImpl(); 
     105    } 
     106 
     107    public Record newRecord(RecordId recordId) { 
     108        ArgumentValidator.notNull(recordId, "recordId"); 
     109        return new RecordImpl(recordId); 
     110    } 
     111 
     112    public Record create(Record record) throws RecordExistsException, RecordNotFoundException, InvalidRecordException, 
     113                    RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, 
     114                    RepositoryException { 
    107115        ArgumentValidator.notNull(record, "record"); 
    108         if (record.getFields().isEmpty()) { 
     116        if (record.getRecordTypeId() == null) { 
     117            throw new InvalidRecordException(record, "The recordType cannot be null for a record to be created."); 
     118        } 
     119        if (record.getNonVersionableFields().isEmpty() && record.getVersionableFields().isEmpty() 
     120                        && record.getVersionableMutableFields().isEmpty()) { 
    109121            throw new InvalidRecordException(record, "Creating an empty record is not allowed"); 
    110122        } 
    111123 
    112         RecordId recordId = record.getId(); 
     124        Record newRecord = record.clone(); 
     125 
     126        RecordId recordId = newRecord.getId(); 
    113127        if (recordId == null) { 
    114128            recordId = idGenerator.newRecordId(); 
    115             record.setId(recordId); 
     129            newRecord.setId(recordId); 
    116130        } else { 
    117131            RecordId masterRecordId = recordId.getMaster(); 
    118             if (masterRecordId != null) { 
     132            if (!recordId.equals(masterRecordId)) { 
    119133                Get get = new Get(masterRecordId.toBytes()); 
    120134                Result masterResult; 
     
    122136                    masterResult = recordTable.get(get); 
    123137                } catch (IOException e) { 
    124                     throw new RepositoryException("Exception occured while checking existence of master record <"+ recordId + ">", e); 
     138                    throw new RepositoryException("Exception occured while checking existence of master record <" 
     139                                    + recordId + ">", e); 
    125140                } 
    126141                if (masterResult.isEmpty()) { 
    127                     throw new RecordNotFoundException(record); 
    128                 }  
    129             } 
    130         } 
    131  
    132         Get get = new Get(recordId.toBytes()); 
    133         Result result; 
     142                    throw new RecordNotFoundException(newRecord); 
     143                } 
     144            } 
     145        } 
     146        byte[] rowId = recordId.toBytes(); 
    134147        try { 
    135             result = recordTable.get(get); 
    136         } catch (IOException e) { 
    137             throw new RepositoryException("Exception occured while checking if record <" + recordId + "> already exists", e); 
    138         } 
    139         if (!result.isEmpty()) { 
    140             throw new RecordExistsException(record); 
    141         } 
    142         Put put; 
    143         try { 
    144             put = createPut(record, Long.valueOf(1)); 
     148            if (recordTable.exists(new Get(rowId))) { 
     149                throw new RecordExistsException(newRecord); 
     150            } 
     151            Record dummyOriginalRecord = newRecord(); 
     152            dummyOriginalRecord.setVersion(Long.valueOf(1)); 
     153            Put put = new Put(record.getId().toBytes());  
     154            putRecord(newRecord, dummyOriginalRecord, Long.valueOf(1), put); 
    145155            recordTable.put(put); 
    146156        } catch (IOException e) { 
    147             throw new RepositoryException("Exception occured while creating record <" + recordId +"> in HBase table", e); 
    148         } 
    149         record.setVersion(Long.valueOf(1)); 
    150     } 
    151  
    152     public Record read(RecordId recordId, String... fieldIds) throws RecordNotFoundException, RepositoryException { 
    153         return read(recordId, null, fieldIds); 
    154     } 
    155  
    156     public Record read(RecordId recordId, Long version, String... fieldIds) 
    157                     throws RecordNotFoundException, RepositoryException { 
     157            throw new RepositoryException("Exception occured while creating record <" + recordId + "> in HBase table", 
     158                            e); 
     159        } 
     160        newRecord.setVersion(Long.valueOf(1)); 
     161        return newRecord; 
     162    } 
     163 
     164    public Record update(Record record) throws RecordNotFoundException, InvalidRecordException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
     165        ArgumentValidator.notNull(record, "record"); 
     166        if (record.getRecordTypeId() == null) { 
     167            throw new InvalidRecordException(record, "The recordType cannot be null for a record to be updated."); 
     168        } 
     169        Get get = new Get(record.getId().toBytes()); 
     170        try { 
     171            if (!recordTable.exists(get)) { 
     172                throw new RecordNotFoundException(record); 
     173            } 
     174        } catch (IOException e) { 
     175            throw new RepositoryException("Exception occured while retrieving original record <" + record.getId() 
     176                            + "> from HBase table", e); 
     177        } 
     178        Record newRecord = record.clone(); 
     179        Record originalRecord = read(newRecord.getId()); 
     180         
     181        Put put = new Put(newRecord.getId().toBytes()); 
     182        if (putRecord(newRecord, originalRecord, originalRecord.getVersion() + 1, put)) { 
     183            try { 
     184                recordTable.put(put); 
     185            } catch (IOException e) { 
     186                throw new RepositoryException("Exception occured while putting updated record <" + record.getId() 
     187                                + "> on HBase table", e); 
     188            } 
     189        } 
     190        return newRecord; 
     191    } 
     192     
     193    private boolean putRecord(Record record, Record originalRecord, Long version, Put put) throws RecordTypeNotFoundException, FieldGroupNotFoundException, 
     194                    FieldDescriptorNotFoundException, RepositoryException { 
     195        String recordTypeId = record.getRecordTypeId(); 
     196        Long recordTypeVersion = record.getRecordTypeVersion(); 
     197 
     198        RecordType recordType = typeManager.getRecordType(recordTypeId, recordTypeVersion); 
     199 
     200        boolean changed = false; 
     201        boolean versionableChanged = false; 
     202        if (putNonVersionableFields(record, originalRecord, recordType, put)) { 
     203            changed = true; 
     204        } 
     205        if (putVersionableFields(record, originalRecord, recordType, version, put)) { 
     206            changed = true; 
     207            versionableChanged = true; 
     208        } 
     209        if (putVersionableMutableFields(record, originalRecord, recordType, version, put)) { 
     210            changed = true; 
     211            versionableChanged = true; 
     212        } 
     213        if (!versionableChanged) { 
     214            version = originalRecord.getVersion(); 
     215        } 
     216        if (changed) { 
     217            recordTypeId = recordType.getId(); 
     218            recordTypeVersion = recordType.getVersion(); 
     219            put.add(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEID_COLUMN_NAME, Bytes.toBytes(recordTypeId)); 
     220            put.add(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEVERSION_COLUMN_NAME, Bytes.toBytes(recordTypeVersion)); 
     221            record.setRecordType(recordTypeId, recordTypeVersion); 
     222            put.add(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     223        } 
     224        record.setVersion(version); 
     225        return changed; 
     226    } 
     227    private boolean putNonVersionableFields(Record record, Record originalRecord, RecordType recordType, Put put) throws FieldGroupNotFoundException, FieldDescriptorNotFoundException, 
     228    RepositoryException { 
     229        if (putFieldGroupFields(record.getNonVersionableFields(), record.getNonVersionableFieldsToDelete(), originalRecord.getNonVersionableFields(), recordType.getNonVersionableFieldGroupId(), recordType 
     230                .getNonVersionableFieldGroupVersion(), NON_VERSIONABLE_COLUMN_FAMILY, null, put)) { 
     231        put.add(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, NON_VERSIONABLE_RECORDTYPEID_COLUMN_NAME, Bytes.toBytes(recordType.getId())); 
     232        put.add(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, NON_VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME, Bytes.toBytes(recordType.getVersion())); 
     233        record.setNonVersionableRecordType(recordType.getId(), recordType.getVersion()); 
     234        return true; 
     235        } 
     236        return false; 
     237    } 
     238     
     239    private boolean putVersionableFields(Record record, Record originalRecord, RecordType recordType, Long version, 
     240                    Put put) throws FieldGroupNotFoundException, FieldDescriptorNotFoundException, 
     241                    RepositoryException { 
     242        if (putFieldGroupFields(record.getVersionableFields(), record.getVersionableFieldsToDelete(), originalRecord.getVersionableFields(), recordType.getVersionableFieldGroupId(), recordType 
     243                        .getVersionableFieldGroupVersion(), VERSIONABLE_COLUMN_FAMILY, version, put)) { 
     244            put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_RECORDTYPEID_COLUMN_NAME, version, Bytes.toBytes(recordType.getId())); 
     245            put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME, version, Bytes.toBytes(recordType.getVersion())); 
     246            record.setVersionableRecordType(recordType.getId(), recordType.getVersion()); 
     247            return true; 
     248        } 
     249        return false; 
     250    } 
     251     
     252    private boolean putVersionableMutableFields(Record record, Record originalRecord,  
     253                    RecordType recordType, Long version, Put put) throws FieldGroupNotFoundException, 
     254                    FieldDescriptorNotFoundException, RepositoryException { 
     255        if (putFieldGroupFields(record.getVersionableMutableFields(), record.getVersionableMutableFieldsToDelete(), originalRecord.getVersionableMutableFields(), recordType.getVersionableMutableFieldGroupId(), 
     256                        recordType.getVersionableMutableFieldGroupVersion(), VERSIONABLE_MUTABLE_COLUMN_FAMILY, 
     257                        version, put)) { 
     258            put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_MUTABLE_RECORDTYPEID_COLUMN_NAME, version, Bytes.toBytes(recordType.getId())); 
     259            put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_MUTABLE_RECORDTYPEVERSION_COLUMN_NAME, version, Bytes.toBytes(recordType.getVersion())); 
     260            record.setVersionableMutableRecordType(recordType.getId(), recordType.getVersion()); 
     261            return true; 
     262        } 
     263        return false; 
     264    } 
     265 
     266    private boolean putFieldGroupFields(Map<String, Object> fields, List<String> fieldsToDelete, Map<String, Object> originalFields, String fieldGroupId, Long fieldGroupVersion, 
     267                    byte[] columnFamily, Long version, Put put) throws FieldGroupNotFoundException, 
     268                    FieldDescriptorNotFoundException, RepositoryException { 
     269        boolean changed = false; 
     270        // Update fields 
     271        for (Entry<String, Object> field : fields.entrySet()) { 
     272            String fieldId = field.getKey(); 
     273            Object newValue = field.getValue(); 
     274            Object originalValue = originalFields.get(fieldId); 
     275            if (((newValue == null) && (originalValue != null)) || !newValue.equals(originalValue)) { 
     276                byte[] fieldIdAsBytes = Bytes.toBytes(fieldId); 
     277                FieldGroup fieldGroup = typeManager.getFieldGroup(fieldGroupId, fieldGroupVersion); 
     278                FieldGroupEntry fieldGroupEntry = fieldGroup.getFieldGroupEntry(fieldId); 
     279                FieldDescriptor fieldDescriptor = typeManager.getFieldDescriptor(fieldGroupEntry.getFieldDescriptorId(), 
     280                                fieldGroupEntry.getFieldDescriptorVersion()); 
     281                ValueType valueType = fieldDescriptor.getValueType(); 
     282     
     283                // TODO validate with Class#isAssignableFrom() 
     284                byte[] fieldValue = valueType.toBytes(field.getValue()); 
     285                byte[] prefixedValue = EncodingUtil.prefixValue(fieldValue, EncodingUtil.EXISTS_FLAG); 
     286     
     287                if (version != null) { 
     288                    put.add(columnFamily, fieldIdAsBytes, version, prefixedValue); 
     289                } else { 
     290                    put.add(columnFamily, fieldIdAsBytes, prefixedValue); 
     291                } 
     292                changed = true; 
     293            } 
     294        } 
     295        // Delete fields 
     296        for (String fieldToDelete : fieldsToDelete) { 
     297            if (originalFields.get(fieldToDelete) != null) { 
     298                if (version != null) { 
     299                    put.add(columnFamily, Bytes.toBytes(fieldToDelete), version, new byte[]{EncodingUtil.DELETE_FLAG}); 
     300                } else { 
     301                    put.add(columnFamily, Bytes.toBytes(fieldToDelete), new byte[]{EncodingUtil.DELETE_FLAG}); 
     302                } 
     303                changed = true; 
     304            } 
     305        } 
     306        return changed; 
     307    } 
     308 
     309    public Record read(RecordId recordId) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
     310        return read(recordId, null); 
     311    } 
     312 
     313    public Record read(RecordId recordId, List<String> nonVersionableFieldIds, List<String> versionableFieldIds, List<String> versionableMutableFieldIds) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
     314        return read(recordId, null, nonVersionableFieldIds, versionableFieldIds, versionableMutableFieldIds); 
     315    } 
     316 
     317    public Record read(RecordId recordId, Long version) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
     318        return read(recordId, version, null, null, null); 
     319    } 
     320 
     321    public Record read(RecordId recordId, Long version, List<String> nonVersionableFieldIds, List<String> versionableFieldIds, List<String> versionableMutableFieldIds) throws RecordNotFoundException, RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
    158322        ArgumentValidator.notNull(recordId, "recordId"); 
    159323        Record record = newRecord(); 
    160324        record.setId(recordId); 
    161         record.setVersion(version); 
    162325 
    163326        Get get = new Get(recordId.toBytes()); 
     
    165328            get.setMaxVersions(); 
    166329        } 
    167         if (fieldIds.length != 0) { 
    168             addFieldsToGet(get, record, fieldIds); 
    169         } 
     330        addFieldsToGet(get, nonVersionableFieldIds, versionableFieldIds, versionableMutableFieldIds); 
    170331        Result result; 
    171332        try { 
     333            if (!recordTable.exists(get)) { 
     334                throw new RecordNotFoundException(record); 
     335            } 
    172336            result = recordTable.get(get); 
    173337        } catch (IOException e) { 
    174             throw new RepositoryException("Exception occured while retrieving record <" + recordId +"> from HBase table", e); 
    175         } 
    176         if (result.isEmpty()) { 
    177             throw new RecordNotFoundException(record); 
    178         } 
    179         long currentVersion = Bytes.toLong(result.getValue(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME)); 
     338            throw new RepositoryException("Exception occured while retrieving record <" + recordId 
     339                            + "> from HBase table", e); 
     340        } 
     341        long currentVersion = Bytes.toLong(result.getValue(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     342                        CURRENT_VERSION_COLUMN_NAME)); 
    180343        if (version != null) { 
    181344            if (currentVersion < version) { 
    182345                throw new RecordNotFoundException(record); 
    183346            } 
     347            record.setVersion(version); 
    184348        } else { 
    185349            record.setVersion(currentVersion); 
    186350        } 
    187351 
    188         extractFields(result, version, record); 
     352        if (extractFields(result, version, record)) { 
     353            Pair<String, Long> recordTypePair = extractRecordType(result, record); 
     354            record.setRecordType(recordTypePair.getV1(), recordTypePair.getV2()); 
     355        } 
    189356        return record; 
    190357    } 
    191358 
    192     private void addFieldsToGet(Get get, Record record, String... fieldIds) throws RecordNotFoundException, RepositoryException { 
    193         String recordTypeId; 
    194         Long recordTypeVersion; 
    195         Get recordTypeGet = new Get(record.getId().toBytes()); 
    196         Long version = record.getVersion(); 
    197         if (version != null) { 
    198             get.getMaxVersions(); 
    199         } 
    200         get.addColumn(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME); 
    201         get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEID_COLUMN_NAME); 
    202         get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEVERSION_COLUMN_NAME); 
    203         Result recordTypeResult; 
    204         try { 
    205             recordTypeResult = recordTable.get(recordTypeGet); 
    206         } catch (IOException e) { 
    207             throw new RepositoryException("Exception occured while retrieving record <"+ record.getId() +"> from HBase table", e); 
    208         } 
    209         if (recordTypeResult.isEmpty()) { 
    210             throw new RecordNotFoundException(record); 
    211         } 
    212  
     359    private Pair<String, Long> extractRecordType(Result result, Record record) { 
     360        return new Pair<String, Long>(Bytes.toString(result.getValue(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     361                        RECORDTYPEID_COLUMN_NAME)), Bytes.toLong(result.getValue(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     362                                        RECORDTYPEVERSION_COLUMN_NAME))); 
     363    } 
     364 
     365    private Pair<String, Long> extractNonVersionableRecordType(Result result, Record record) { 
     366        return new Pair<String, Long>(Bytes.toString(result.getValue(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     367                        NON_VERSIONABLE_RECORDTYPEID_COLUMN_NAME)), Bytes.toLong(result.getValue(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     368                                        NON_VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME))); 
     369    } 
     370 
     371    private Pair<String, Long> extractVersionableRecordType(Result result, Long version, Record record) { 
    213372        if (version == null) { 
    214             recordTypeId = Bytes.toString(recordTypeResult.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
    215                             RECORDTYPEID_COLUMN_NAME)); 
    216             recordTypeVersion = Bytes.toLong(recordTypeResult.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
    217                             RECORDTYPEID_COLUMN_NAME)); 
     373            // Get latest version 
     374            return new Pair<String, Long>(Bytes.toString(result.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     375                            VERSIONABLE_RECORDTYPEID_COLUMN_NAME)), Bytes.toLong(result.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     376                                            VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME))); 
     377             
    218378        } else { 
    219             NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = recordTypeResult 
    220                             .getMap(); 
    221             NavigableMap<byte[], NavigableMap<Long, byte[]>> allVersionsSystemColumnFamilyMap = allVersionsMap 
    222                             .get(VERSIONABLE_SYSTEM_COLUMN_FAMILY); 
    223             recordTypeId = Bytes.toString(allVersionsSystemColumnFamilyMap.get(RECORDTYPEID_COLUMN_NAME).ceilingEntry( 
    224                             version).getValue()); 
    225             recordTypeVersion = Bytes.toLong(allVersionsSystemColumnFamilyMap.get(RECORDTYPEVERSION_COLUMN_NAME) 
    226                             .ceilingEntry(version).getValue()); 
    227         } 
    228  
    229         RecordType recordType = typeManager.getRecordType(recordTypeId, recordTypeVersion); 
    230  
    231         get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEID_COLUMN_NAME); 
    232         get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEVERSION_COLUMN_NAME); 
    233         for (String fieldId : fieldIds) { 
    234             byte[] columnFamily = recordType.getFieldDescriptor(fieldId).isVersionable() ? VERSIONABLE_COLUMN_FAMILY 
    235                             : NON_VERSIONABLE_COLUMN_FAMILY; 
    236             get.addColumn(columnFamily, Bytes.toBytes(fieldId)); 
    237         } 
    238     } 
    239  
    240     private void extractFields(Result result, Long version, Record record) throws RepositoryException { 
    241         if (version != null) { 
    242             NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> mapWithVersions = result.getMap(); 
    243             extractRecordTypeInfoOnVersion(version, record, mapWithVersions.get(VERSIONABLE_SYSTEM_COLUMN_FAMILY)); 
    244             extractVersionableFieldsOnVersion(version, record, mapWithVersions.get(VERSIONABLE_COLUMN_FAMILY)); 
     379            // Get on version 
     380            NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = result.getMap(); 
     381            NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableSystemCFversions = allVersionsMap.get(VERSIONABLE_SYSTEM_COLUMN_FAMILY); 
     382            return extractVersionRecordType(version, versionableSystemCFversions, 
     383                            VERSIONABLE_RECORDTYPEID_COLUMN_NAME, VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME); 
     384        } 
     385    } 
     386 
     387    private Pair<String, Long> extractVersionableMutableRecordType(Result result, Long version, Record record) { 
     388        if (version == null) { 
     389            // Get latest version 
     390            return new Pair<String, Long>(Bytes.toString(result.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     391                            VERSIONABLE_MUTABLE_RECORDTYPEID_COLUMN_NAME)), Bytes.toLong(result.getValue(VERSIONABLE_SYSTEM_COLUMN_FAMILY, 
     392                            VERSIONABLE_MUTABLE_RECORDTYPEVERSION_COLUMN_NAME))); 
    245393        } else { 
    246             extractLatestRecordTypeInfo(result, record); 
    247             extractLatestVersionableFields(result, record); 
    248         } 
    249         extractNonVersionableFields(result, record); 
    250     } 
    251  
    252     public void update(Record record) throws RecordNotFoundException, InvalidRecordException, RepositoryException { 
    253         ArgumentValidator.notNull(record, "record"); 
    254         Get get = new Get(record.getId().toBytes()); 
    255         Result result; 
    256         try { 
    257             result = recordTable.get(get); 
    258         } catch (IOException e) { 
    259             throw new RepositoryException("Exception occured while retrieving original record <" + record.getId() +"> from HBase table", e); 
    260         } 
    261         if (result.isEmpty()) { 
    262             throw new RecordNotFoundException(record); 
    263         } 
    264         // TODO lock row 
    265         NavigableMap<byte[], byte[]> systemFamilyMap = result.getFamilyMap(SYSTEM_COLUMN_FAMILY); 
    266         long version = Bytes.toLong(systemFamilyMap.get(CURRENT_VERSION_COLUMN_NAME)); 
    267         if (record.getFields().isEmpty() && record.getDeleteFields().isEmpty()) { 
    268             throw new InvalidRecordException(record, "No fields to update or delete"); 
    269         } 
    270         long newVersion = version + 1; 
    271         try { 
    272             recordTable.put(createPut(record, newVersion)); 
    273         } catch (IOException e) { 
    274             throw new RepositoryException("Exception occured while putting updated record <" + record.getId() +"> on HBase table", e); 
    275         } 
    276         record.setVersion(newVersion); 
     394            // Get on version 
     395            NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = result.getMap(); 
     396            NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableSystemCFversions = allVersionsMap.get(VERSIONABLE_SYSTEM_COLUMN_FAMILY); 
     397            return extractVersionRecordType(version, versionableSystemCFversions, 
     398                            VERSIONABLE_MUTABLE_RECORDTYPEID_COLUMN_NAME, VERSIONABLE_MUTABLE_RECORDTYPEVERSION_COLUMN_NAME); 
     399        } 
     400    } 
     401 
     402    private Pair<String, Long> extractVersionRecordType(Long version, 
     403                    NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableSystemCFversions, 
     404                    byte[] recordTypeIdColumnName, byte[] recordTypeVersionColumnName) { 
     405        Entry<Long, byte[]> ceilingEntry = versionableSystemCFversions.get(recordTypeIdColumnName).ceilingEntry(version); 
     406        String recordTypeId = null; 
     407        if (ceilingEntry != null) { 
     408            recordTypeId = Bytes.toString(ceilingEntry.getValue()); 
     409        }  
     410        Long recordTypeVersion = null; 
     411        ceilingEntry = versionableSystemCFversions.get(recordTypeVersionColumnName).ceilingEntry(version); 
     412        if (ceilingEntry != null) { 
     413            recordTypeVersion = Bytes.toLong(ceilingEntry.getValue()); 
     414        }  
     415        Pair<String, Long> recordType = new Pair<String, Long>(recordTypeId, recordTypeVersion); 
     416        return recordType; 
     417    } 
     418     
     419    private List<Pair<String, Object>> extractFields(NavigableMap<byte[], byte[]> familyMap) throws FieldDescriptorNotFoundException, RepositoryException { 
     420        List<Pair<String, Object>> fields = new ArrayList<Pair<String, Object>>(); 
     421        if (familyMap != null) { 
     422            for (Entry<byte[], byte[]> entry : familyMap.entrySet()) { 
     423                Pair<String, Object> field = extractField(entry.getKey(), entry.getValue()); 
     424                if (field != null) { 
     425                    fields.add(field); 
     426                } 
     427            } 
     428        } 
     429        return fields; 
     430    } 
     431     
     432    private List<Pair<String, Object>> extractVersionFields(Long version, NavigableMap<byte[], NavigableMap<Long, byte[]>> mapWithVersions) throws FieldDescriptorNotFoundException, RepositoryException { 
     433        List<Pair<String, Object>> fields = new ArrayList<Pair<String, Object>>(); 
     434        if (mapWithVersions != null) { 
     435            for (Entry<byte[], NavigableMap<Long, byte[]>> columnWithAllVersions : mapWithVersions.entrySet()) { 
     436                NavigableMap<Long, byte[]> allValueVersions = columnWithAllVersions.getValue(); 
     437                Entry<Long, byte[]> ceilingEntry = allValueVersions.ceilingEntry(version); 
     438                if (ceilingEntry != null) { 
     439                    Pair<String, Object> field = extractField(columnWithAllVersions.getKey(), ceilingEntry.getValue()); 
     440                    if (field != null) { 
     441                        fields.add(field); 
     442                    } 
     443                } 
     444            } 
     445        } 
     446        return fields; 
     447    } 
     448 
     449    private Pair<String, Object> extractField(byte[] key, byte[] prefixedValue) throws FieldDescriptorNotFoundException, RepositoryException { 
     450        if (EncodingUtil.isDeletedField(prefixedValue)) { 
     451            return null; 
     452        } 
     453        String fieldId = Bytes.toString(key); 
     454        ValueType valueType = typeManager.getFieldDescriptor(fieldId, null).getValueType(); 
     455        Object value = valueType.fromBytes(EncodingUtil.stripPrefix(prefixedValue)); 
     456        return new Pair<String, Object>(fieldId, value); 
     457    } 
     458     
     459 
     460    private void addFieldsToGet(Get get, List<String> nonVersionableFieldIds, List<String> versionableFieldIds, List<String> versionableMutableFieldIds) 
     461                    throws RecordNotFoundException, RepositoryException { 
     462        boolean added = false; 
     463        added |= addFieldsToGet(get, nonVersionableFieldIds, NON_VERSIONABLE_COLUMN_FAMILY); 
     464        added |= addFieldsToGet(get, versionableFieldIds, VERSIONABLE_COLUMN_FAMILY); 
     465        added |= addFieldsToGet(get, versionableMutableFieldIds, VERSIONABLE_MUTABLE_COLUMN_FAMILY); 
     466        if (added) { 
     467            addSystemColumnsToGet(get); 
     468        } 
     469    } 
     470     
     471 
     472    private boolean addFieldsToGet(Get get, List<String> fieldIds, byte[] columnFamily) { 
     473        boolean added = false; 
     474        if (fieldIds != null) { 
     475            for (String fieldId : fieldIds) { 
     476                get.addColumn(columnFamily, Bytes.toBytes(fieldId)); 
     477            } 
     478            added = true; 
     479        }  
     480        return added; 
     481    } 
     482 
     483    private void addSystemColumnsToGet(Get get) { 
     484        get.addColumn(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME); 
     485        get.addColumn(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEID_COLUMN_NAME); 
     486        get.addColumn(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEVERSION_COLUMN_NAME); 
     487        get.addColumn(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, NON_VERSIONABLE_RECORDTYPEID_COLUMN_NAME); 
     488        get.addColumn(NON_VERSIONABLE_SYSTEM_COLUMN_FAMILY, NON_VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME); 
     489        get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_RECORDTYPEID_COLUMN_NAME); 
     490        get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_RECORDTYPEVERSION_COLUMN_NAME); 
     491        get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_MUTABLE_RECORDTYPEID_COLUMN_NAME); 
     492        get.addColumn(VERSIONABLE_SYSTEM_COLUMN_FAMILY, VERSIONABLE_MUTABLE_RECORDTYPEVERSION_COLUMN_NAME); 
     493    } 
     494 
     495    private boolean extractFields(Result result, Long version, Record record) throws RecordTypeNotFoundException, FieldGroupNotFoundException, FieldDescriptorNotFoundException, RepositoryException { 
     496        boolean retrieved = false; 
     497        retrieved |= extractNonVersionableFields(result, record); 
     498        retrieved |= extractVersionableFields(result, version, record); 
     499        retrieved |= extractVersionableMutableFields(result, version, record); 
     500        return retrieved; 
     501    } 
     502 
     503    private boolean extractNonVersionableFields(Result result, Record record) throws RecordTypeNotFoundException, 
     504    RepositoryException, FieldGroupNotFoundException, FieldDescriptorNotFoundException { 
     505        boolean retrieved = false; 
     506        Pair<String, Long> recordTypePair = extractNonVersionableRecordType(result, record); 
     507        String recordTypeId = recordTypePair.getV1(); 
     508        Long recordTypeVersion = recordTypePair.getV2(); 
     509        // If there is no recordType, there can't be any fields 
     510        if (recordTypeId != null) { 
     511            List<Pair<String, Object>> fields = extractFields(result.getFamilyMap(NON_VERSIONABLE_COLUMN_FAMILY)); 
     512            if (!fields.isEmpty()) { 
     513                for (Pair<String, Object> field : fields) { 
     514                    record.setNonVersionableField(field.getV1(), field.getV2());  
     515                } 
     516                record.setNonVersionableRecordType(recordTypeId, recordTypeVersion); 
     517                retrieved = true; 
     518            } 
     519        } 
     520        return retrieved; 
     521    } 
     522 
     523    private boolean extractVersionableFields(Result result, Long version, Record record) 
     524    throws RecordTypeNotFoundException, RepositoryException, FieldGroupNotFoundException, 
     525    FieldDescriptorNotFoundException { 
     526        boolean retrieved = false; 
     527        Pair<String, Long> recordTypePair = extractVersionableRecordType(result, version, record); 
     528        String recordTypeId = recordTypePair.getV1(); 
     529        Long recordTypeVersion = recordTypePair.getV2(); 
     530     // If there is no recordType, there can't be any fields 
     531        if (recordTypeId != null) { 
     532            List<Pair<String, Object>> fields; 
     533            if (version == null) { 
     534                fields = extractFields(result.getFamilyMap(VERSIONABLE_COLUMN_FAMILY)); 
     535            } else { 
     536                fields = extractVersionFields(version, result.getMap().get(VERSIONABLE_COLUMN_FAMILY)); 
     537            } 
     538            if (!fields.isEmpty()) { 
     539                for (Pair<String, Object> field : fields) { 
     540                    record.setVersionableField(field.getV1(), field.getV2());  
     541                } 
     542                record.setVersionableRecordType(recordTypeId, recordTypeVersion); 
     543                retrieved = true; 
     544            } 
     545        } 
     546        return retrieved; 
     547    } 
     548 
     549    private boolean extractVersionableMutableFields(Result result, Long version, Record record) 
     550                    throws RecordTypeNotFoundException, RepositoryException, FieldGroupNotFoundException, 
     551                    FieldDescriptorNotFoundException { 
     552        boolean retrieved = false; 
     553        Pair<String, Long> recordTypePair = extractVersionableMutableRecordType(result, version, record); 
     554        String recordTypeId = recordTypePair.getV1(); 
     555        Long recordTypeVersion = recordTypePair.getV2(); 
     556     // If there is no recordType, there can't be any fields 
     557        if (recordTypeId != null) { 
     558            List<Pair<String, Object>> fields; 
     559            if (version == null) { 
     560                fields = extractFields(result.getFamilyMap(VERSIONABLE_MUTABLE_COLUMN_FAMILY)); 
     561            } else { 
     562                fields = extractVersionFields(version, result.getMap().get(VERSIONABLE_MUTABLE_COLUMN_FAMILY)); 
     563            } 
     564            if (!fields.isEmpty()) { 
     565                for (Pair<String, Object> field : fields) { 
     566                    record.setVersionableMutableField(field.getV1(), field.getV2());  
     567                } 
     568                record.setVersionableMutableRecordType(recordTypeId, recordTypeVersion); 
     569                retrieved = true; 
     570            } 
     571        } 
     572        return retrieved; 
    277573    } 
    278574 
     
    283579            recordTable.delete(delete); 
    284580        } catch (IOException e) { 
    285             throw new RepositoryException("Exception occured while deleting record <"+recordId+"> from HBase table", e); 
    286         } 
    287  
    288     } 
    289  
    290     private Put createPut(Record record, Long version) throws RepositoryException { 
    291         String recordTypeId = record.getRecordTypeId(); 
    292         long recordTypeVersion = record.getRecordTypeVersion(); 
    293         RecordType recordType = typeManager.getRecordType(recordTypeId, recordTypeVersion); 
    294  
    295         Put put = new Put(record.getId().toBytes()); 
    296         put.add(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
    297         put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEID_COLUMN_NAME, version, Bytes.toBytes(recordTypeId)); 
    298         put.add(VERSIONABLE_SYSTEM_COLUMN_FAMILY, RECORDTYPEVERSION_COLUMN_NAME, version, Bytes 
    299                         .toBytes(recordTypeVersion)); 
    300         putFields(record, version, recordType, put); 
    301         putDeleteFields(record, version, recordType, put); 
    302         return put; 
    303     } 
    304  
    305     private void putFields(Record record, Long version, RecordType recordType, Put put) { 
    306         for (Entry<String, Object> field : record.getFields().entrySet()) { 
    307             String fieldId = field.getKey(); 
    308             byte[] fieldIdAsBytes = Bytes.toBytes(fieldId); 
    309             FieldDescriptor fieldDescriptor = recordType.getFieldDescriptor(fieldId); 
    310             ValueType valueType = fieldDescriptor.getValueType(); 
    311             // TODO validate with Class#isAssignableFrom() 
    312             byte[] fieldValue = valueType.toBytes(field.getValue()); 
    313             byte[] prefixedValue = EncodingUtil.prefixValue(fieldValue, EncodingUtil.EXISTS_FLAG); 
    314              
    315             if (fieldDescriptor.isVersionable()) { 
    316                 put.add(VERSIONABLE_COLUMN_FAMILY, fieldIdAsBytes, version, prefixedValue); 
    317             } else { 
    318                 put.add(NON_VERSIONABLE_COLUMN_FAMILY, fieldIdAsBytes, prefixedValue); 
    319             } 
    320         } 
    321     } 
    322  
    323     private void putDeleteFields(Record record, Long version, RecordType recordType, Put put) { 
    324         for (String deleteFieldId : record.getDeleteFields()) { 
    325             FieldDescriptor fieldDescriptor = recordType.getFieldDescriptor(deleteFieldId); 
    326             if (fieldDescriptor.isVersionable()) { 
    327                 put.add(VERSIONABLE_COLUMN_FAMILY, Bytes.toBytes(deleteFieldId), version, 
    328                                 new byte[] { EncodingUtil.DELETE_FLAG }); 
    329             } else { 
    330                 put.add(NON_VERSIONABLE_COLUMN_FAMILY, Bytes.toBytes(deleteFieldId), 
    331                                 new byte[] { EncodingUtil.DELETE_FLAG }); 
    332             } 
    333         } 
    334     } 
    335  
    336     private void extractVersionableFieldsOnVersion(Long version, Record record, 
    337                     NavigableMap<byte[], NavigableMap<Long, byte[]>> mapWithVersions) throws RepositoryException { 
    338         if (mapWithVersions != null) { 
    339             Set<Entry<byte[], NavigableMap<Long, byte[]>>> columnSetWithAllVersions = mapWithVersions.entrySet(); 
    340             for (Entry<byte[], NavigableMap<Long, byte[]>> columnWithAllVersions : columnSetWithAllVersions) { 
    341                 NavigableMap<Long, byte[]> allValueVersions = columnWithAllVersions.getValue(); 
    342                 Entry<Long, byte[]> ceilingEntry = allValueVersions.ceilingEntry(version); 
    343                 if (ceilingEntry != null) { 
    344                     extractField(record, columnWithAllVersions.getKey(), ceilingEntry.getValue()); 
    345                 } 
    346             } 
    347         } 
    348     } 
    349  
    350     private void extractRecordTypeInfoOnVersion(Long version, Record record, 
    351                     NavigableMap<byte[], NavigableMap<Long, byte[]>> mapWithVersions) { 
    352         NavigableMap<Long, byte[]> recordTypeIdVersions = mapWithVersions.get(RECORDTYPEID_COLUMN_NAME); 
    353         Entry<Long, byte[]> recordTypeIdEntry = recordTypeIdVersions.ceilingEntry(version); 
    354         NavigableMap<Long, byte[]> recordTypeVersionVersions = mapWithVersions.get(RECORDTYPEVERSION_COLUMN_NAME); 
    355         Entry<Long, byte[]> recordTypeVersionEntry = recordTypeVersionVersions.ceilingEntry(version); 
    356         record.setRecordType(Bytes.toString(recordTypeIdEntry.getValue()), Bytes.toLong(recordTypeVersionEntry 
    357                         .getValue())); 
    358     } 
    359  
    360     private void extractLatestRecordTypeInfo(Result result, Record record) { 
    361         NavigableMap<byte[], byte[]> familyMap = result.getFamilyMap(VERSIONABLE_SYSTEM_COLUMN_FAMILY); 
    362         record.setRecordType(Bytes.toString(familyMap.get(RECORDTYPEID_COLUMN_NAME)), Bytes.toLong(familyMap 
    363                         .get(RECORDTYPEVERSION_COLUMN_NAME))); 
    364     } 
    365  
    366     private void extractLatestVersionableFields(Result result, Record record) throws RepositoryException { 
    367         NavigableMap<byte[], byte[]> familyMap = result.getFamilyMap(VERSIONABLE_COLUMN_FAMILY); 
    368         extractFields(record, familyMap); 
    369     } 
    370  
    371     private void extractNonVersionableFields(Result result, Record record) throws RepositoryException { 
    372         NavigableMap<byte[], byte[]> familyMap = result.getFamilyMap(NON_VERSIONABLE_COLUMN_FAMILY); 
    373         extractFields(record, familyMap); 
    374     } 
    375  
    376     private void extractFields(Record record, NavigableMap<byte[], byte[]> familyMap) throws RepositoryException { 
    377         if (familyMap != null) { 
    378             for (Entry<byte[], byte[]> entry : familyMap.entrySet()) { 
    379                 extractField(record, entry.getKey(), entry.getValue()); 
    380             } 
    381         } 
    382     } 
    383  
    384     private void extractField(Record record, byte[] key, byte[] prefixedValue) throws RepositoryException { 
    385         if (!EncodingUtil.isDeletedField(prefixedValue)) { 
    386             RecordType recordType = typeManager.getRecordType(record.getRecordTypeId()); 
    387             String fieldId = Bytes.toString(key); 
    388             FieldDescriptor fieldDescriptor = recordType.getFieldDescriptor(fieldId); 
    389             Object value = fieldDescriptor.getValueType().fromBytes(EncodingUtil.stripPrefix(prefixedValue)); 
    390             record.setField(fieldId, value); 
    391         } 
     581            throw new RepositoryException( 
     582                            "Exception occured while deleting record <" + recordId + "> from HBase table", e); 
     583        } 
     584 
    392585    } 
    393586} 
  • projects/lily/trunk/repository/impl/src/main/java/org/lilycms/repository/impl/HBaseTypeManager.java

    r3959 r3977  
    1717 
    1818import java.io.IOException; 
    19 import java.lang.reflect.Constructor; 
    20 import java.util.ArrayList; 
    2119import java.util.Collection; 
    2220import java.util.HashMap; 
    23 import java.util.HashSet; 
    2421import java.util.List; 
    2522import java.util.Map; 
    2623import java.util.NavigableMap; 
    27 import java.util.Set; 
    2824import java.util.Map.Entry; 
    2925 
     
    3935import org.apache.hadoop.hbase.util.Bytes; 
    4036import org.lilycms.repository.api.FieldDescriptor; 
     37import org.lilycms.repository.api.FieldDescriptorExistsException; 
     38import org.lilycms.repository.api.FieldDescriptorNotFoundException; 
     39import org.lilycms.repository.api.FieldDescriptorUpdateException; 
     40import org.lilycms.repository.api.FieldGroup; 
     41import org.lilycms.repository.api.FieldGroupEntry; 
     42import org.lilycms.repository.api.FieldGroupExistsException; 
     43import org.lilycms.repository.api.FieldGroupNotFoundException; 
    4144import org.lilycms.repository.api.IdGenerator; 
    4245import org.lilycms.repository.api.PrimitiveValueType; 
    4346import org.lilycms.repository.api.RecordType; 
     47import org.lilycms.repository.api.RecordTypeExistsException; 
     48import org.lilycms.repository.api.RecordTypeNotFoundException; 
    4449import org.lilycms.repository.api.RepositoryException; 
    4550import org.lilycms.repository.api.TypeManager; 
    4651import org.lilycms.repository.api.ValueType; 
    4752import org.lilycms.util.ArgumentValidator; 
     53import org.lilycms.util.Pair; 
    4854 
    4955public class HBaseTypeManager implements TypeManager { 
    5056 
    5157    private static final String TYPE_TABLE = "typeTable"; 
    52     private static final byte[] SYSTEM_COLUMN_FAMILY = Bytes.toBytes("systemCF"); 
    53     private static final byte[] SYSTEM_VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("systemVersionableCF"); 
    54     private static final byte[] FIELDDESCRIPTOR_COLUMN_FAMILY = Bytes.toBytes("fieldDescriptorCF"); 
    55     private static final byte[] CURRENT_VERSION_COLUMN_NAME = Bytes.toBytes("currentVersion"); 
    56     private static final byte[] VALUE_TYPE_COLUMN_NAME = Bytes.toBytes("valueType"); 
    57     private static final byte[] MANDATORY_COLUMN_NAME = Bytes.toBytes("mandatory"); 
    58     private static final byte[] VERSIONABLE_COLUMN_NAME = Bytes.toBytes("versionable"); 
     58    private static final byte[] NON_VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("nonVersionableCF"); 
     59    private static final byte[] VERSIONABLE_COLUMN_FAMILY = Bytes.toBytes("versionableCF"); 
     60 
     61    private static final byte[] CURRENT_VERSION_COLUMN_NAME = Bytes.toBytes("$currentVersion"); 
     62 
     63    private static final byte[] RECORDTYPE_NONVERSIONABLEFIELDGROUP_COLUMN_NAME = Bytes.toBytes("$nonVersionableFG"); 
     64    private static final byte[] RECORDTYPE_VERSIONABLEFIELDGROUP_COLUMN_NAME = Bytes.toBytes("$versionableFG"); 
     65    private static final byte[] RECORDTYPE_VERSIONABLEMUTABLEFIELDGROUP_COLUMN_NAME = Bytes 
     66                    .toBytes("$versionableMutableFG"); 
     67    private static final byte[] FIELDDESCRIPTOR_GLOBALNAME_COLUMN_NAME = Bytes.toBytes("$globalName"); 
     68    private static final byte[] FIELDDESCRIPTOR_VALUETYPE_COLUMN_NAME = Bytes.toBytes("$valueType"); 
    5969 
    6070    private HTable typeTable; 
    6171    private IdGenerator idGenerator; 
    62     private Class<RecordType> recordTypeClass; 
    63     private Class<FieldDescriptor> fieldDescriptorClass; 
    64  
    65     public HBaseTypeManager(IdGenerator idGenerator, Class recordTypeClass, Class fieldDescriptorClass, Configuration configuration) 
    66                     throws IOException { 
     72 
     73    public HBaseTypeManager(IdGenerator idGenerator, Configuration configuration) throws IOException { 
    6774        this.idGenerator = idGenerator; 
    68         this.recordTypeClass = recordTypeClass; 
    69         this.fieldDescriptorClass = fieldDescriptorClass; 
    7075        try { 
    7176            typeTable = new HTable(configuration, TYPE_TABLE); 
     
    7378            HBaseAdmin admin = new HBaseAdmin(configuration); 
    7479            HTableDescriptor tableDescriptor = new HTableDescriptor(TYPE_TABLE); 
    75             tableDescriptor.addFamily(new HColumnDescriptor(SYSTEM_COLUMN_FAMILY)); 
    76             tableDescriptor.addFamily(new HColumnDescriptor(SYSTEM_VERSIONABLE_COLUMN_FAMILY, HConstants.ALL_VERSIONS, 
    77                             "none", false, true, HConstants.FOREVER, false)); 
    78             tableDescriptor.addFamily(new HColumnDescriptor(FIELDDESCRIPTOR_COLUMN_FAMILY, HConstants.ALL_VERSIONS, 
    79                             "none", false, true, HConstants.FOREVER, false)); 
     80            tableDescriptor.addFamily(new HColumnDescriptor(NON_VERSIONABLE_COLUMN_FAMILY)); 
     81            tableDescriptor.addFamily(new HColumnDescriptor(VERSIONABLE_COLUMN_FAMILY, HConstants.ALL_VERSIONS, "none", 
     82                            false, true, HConstants.FOREVER, false)); 
    8083            admin.createTable(tableDescriptor); 
    8184            typeTable = new HTable(configuration, TYPE_TABLE); 
     
    8487    } 
    8588 
    86     public RecordType newRecordType(String recordTypeId) throws RepositoryException { 
     89    public RecordType newRecordType(String recordTypeId) { 
     90        ArgumentValidator.notNull(recordTypeId, "recordTypeId"); 
     91        return new RecordTypeImpl(recordTypeId); 
     92    } 
     93 
     94    public RecordType createRecordType(RecordType recordType) throws RecordTypeExistsException, 
     95                    FieldGroupNotFoundException, RepositoryException { 
     96        ArgumentValidator.notNull(recordType, "recordType"); 
     97        RecordType newRecordType = recordType.clone(); 
     98        Long recordTypeVersion = Long.valueOf(1); 
     99        byte[] rowId = Bytes.toBytes(recordType.getId()); 
    87100        try { 
    88             Constructor<RecordType> constructor = recordTypeClass.getConstructor(String.class); 
    89             return constructor.newInstance(recordTypeId); 
    90         } catch (Exception e) { 
    91             throw new RepositoryException("Exception occured while creating new RecordType object", e); 
    92         } 
    93     } 
    94  
    95     public FieldDescriptor newFieldDescriptor(String fieldDescriptorId, ValueType valueType, boolean mandatory, 
    96                     boolean versionable) throws RepositoryException { 
    97         try { 
    98             Constructor<FieldDescriptor> constructor = fieldDescriptorClass.getConstructor(String.class, 
    99                             ValueType.class, boolean.class, boolean.class); 
    100             return constructor.newInstance(fieldDescriptorId, valueType, mandatory, versionable); 
    101         } catch (Exception e) { 
    102             throw new RepositoryException("Exception occured while creating new FieldDescriptor object", e); 
    103         } 
    104     } 
    105  
    106     public FieldDescriptor newFieldDescriptor(String fieldDescriptorId, Long version, ValueType valueType, 
    107                     boolean mandatory, boolean versionable) throws RepositoryException { 
    108         try { 
    109             Constructor<FieldDescriptor> constructor = fieldDescriptorClass.getConstructor(String.class, Long.class, 
    110                             ValueType.class, boolean.class, boolean.class); 
    111             return constructor.newInstance(fieldDescriptorId, version, valueType, mandatory, versionable); 
    112         } catch (Exception e) { 
    113             throw new RepositoryException("Exception occured while creating new FieldDescriptor object", e); 
    114         } 
    115     } 
    116  
    117     // TODO return a new RecordType object 
    118     public void createRecordType(RecordType recordType) throws RepositoryException { 
    119         ArgumentValidator.notNull(recordType, "recordType"); 
    120         List<Put> puts = new ArrayList<Put>(); 
    121         String recordTypeId = recordType.getId(); 
    122         Put put = new Put(EncodingUtil.generateRecordTypeRowKey(recordTypeId)); 
    123         long recordTypeVersion = 1; 
    124         recordType.setVersion(recordTypeVersion); 
    125         put.add(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(recordTypeVersion)); 
    126         Collection<FieldDescriptor> fieldDescriptors = recordType.getFieldDescriptors(); 
    127         for (FieldDescriptor fieldDescriptor : fieldDescriptors) { 
    128             puts.add(addFieldDescriptor(fieldDescriptor, recordTypeId, recordTypeVersion, put)); 
    129         } 
    130         puts.add(put); 
    131         try { 
    132             typeTable.put(puts); 
     101            if (typeTable.exists(new Get(rowId))) { 
     102                throw new RecordTypeExistsException(recordType); 
     103            } 
     104 
     105            Put put = new Put(rowId); 
     106            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(recordTypeVersion)); 
     107            String fieldGroupId = recordType.getNonVersionableFieldGroupId(); 
     108            if (fieldGroupId != null) { 
     109                newRecordType.setNonVersionableFieldGroupVersion(putFieldGroupOnRecordType(recordTypeVersion, put, 
     110                                fieldGroupId, recordType.getNonVersionableFieldGroupVersion(), 
     111                                RECORDTYPE_NONVERSIONABLEFIELDGROUP_COLUMN_NAME)); 
     112            } 
     113 
     114            fieldGroupId = recordType.getVersionableFieldGroupId(); 
     115            if (fieldGroupId != null) { 
     116                newRecordType.setVersionableFieldGroupVersion(putFieldGroupOnRecordType(recordTypeVersion, put, 
     117                                fieldGroupId, recordType.getVersionableFieldGroupVersion(), 
     118                                RECORDTYPE_VERSIONABLEFIELDGROUP_COLUMN_NAME)); 
     119            } 
     120 
     121            fieldGroupId = recordType.getVersionableMutableFieldGroupId(); 
     122            if (fieldGroupId != null) { 
     123                newRecordType.setVersionableMutableFieldGroupVersion(putFieldGroupOnRecordType(recordTypeVersion, put, 
     124                                fieldGroupId, recordType.getVersionableMutableFieldGroupVersion(), 
     125                                RECORDTYPE_VERSIONABLEMUTABLEFIELDGROUP_COLUMN_NAME)); 
     126            } 
     127 
     128            typeTable.put(put); 
    133129        } catch (IOException e) { 
    134130            throw new RepositoryException("Exception occured while creating recordType <" + recordType.getId() 
    135131                            + "> on HBase", e); 
    136132        } 
    137     } 
    138  
    139     private Put addFieldDescriptor(FieldDescriptor fieldDescriptor, String recordTypeId, Long recordTypeVersion, 
    140                     Put recordTypePut) throws RepositoryException { 
    141         String fieldDescriptorId = fieldDescriptor.getId(); 
    142         Put put = new Put(EncodingUtil.generateFieldDescriptorRowKey(recordTypeId, fieldDescriptorId)); 
    143  
    144         long fieldDescriptorVersion = getLatestFieldDescriptorVersion(recordTypeId, fieldDescriptorId) + 1; 
    145         put.add(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(fieldDescriptorVersion)); 
    146         put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, VALUE_TYPE_COLUMN_NAME, fieldDescriptorVersion, fieldDescriptor 
    147                         .getValueType().toBytes()); 
    148         put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, MANDATORY_COLUMN_NAME, fieldDescriptorVersion, Bytes 
    149                         .toBytes(fieldDescriptor.isMandatory())); 
    150         put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, VERSIONABLE_COLUMN_NAME, fieldDescriptorVersion, Bytes 
    151                         .toBytes(fieldDescriptor.isVersionable())); 
    152  
    153         recordTypePut.add(FIELDDESCRIPTOR_COLUMN_FAMILY, Bytes.toBytes(fieldDescriptorId), recordTypeVersion, 
    154                         EncodingUtil.prefixValue(Bytes.toBytes(fieldDescriptorVersion), EncodingUtil.EXISTS_FLAG)); 
    155  
    156         return put; 
    157     } 
    158  
    159     public void updateRecordType(RecordType recordType) throws RepositoryException { 
     133        newRecordType.setVersion(recordTypeVersion); 
     134        return newRecordType; 
     135    } 
     136 
     137    public RecordType updateRecordType(RecordType recordType) throws RecordTypeNotFoundException, 
     138                    FieldGroupNotFoundException, RepositoryException { 
    160139        ArgumentValidator.notNull(recordType, "recordType"); 
    161         List<Put> puts = new ArrayList<Put>(); 
    162         String recordTypeId = recordType.getId(); 
    163         Put recordTypePut = new Put(EncodingUtil.generateRecordTypeRowKey(recordTypeId)); 
    164         RecordType originalRecordType = getRecordType(recordTypeId); 
    165         long newRecordTypeVersion = originalRecordType.getVersion() + 1; 
     140        RecordType newRecordType = recordType.clone(); 
     141        String id = recordType.getId(); 
     142        Put put = new Put(Bytes.toBytes(id)); 
     143 
     144        RecordType latestRecordType = getRecordType(id, null); 
     145        Long latestRecordTypeVersion = latestRecordType.getVersion(); 
     146        Long newRecordTypeVersion = latestRecordTypeVersion + 1; 
     147 
    166148        boolean recordTypeChanged = false; 
    167         Map<String, FieldDescriptor> fieldDescriptors = new HashMap<String, FieldDescriptor>(); 
    168         for (FieldDescriptor fieldDescriptor : recordType.getFieldDescriptors()) { 
    169             fieldDescriptors.put(fieldDescriptor.getId(), fieldDescriptor); 
    170         } 
    171         Collection<FieldDescriptor> originalFieldDescriptors = originalRecordType.getFieldDescriptors(); 
    172         Set<FieldDescriptor> originalFieldDescriptorSet = new HashSet<FieldDescriptor>(originalFieldDescriptors); 
    173         for (FieldDescriptor originalFieldDescriptor : originalFieldDescriptors) { 
    174             String fieldDescriptorId = originalFieldDescriptor.getId(); 
    175             FieldDescriptor fieldDescriptor = fieldDescriptors.get(fieldDescriptorId); 
    176             if (fieldDescriptor != null) { 
    177                 Put updateFieldDescriptorPut = updateFieldDescriptor(fieldDescriptor, originalFieldDescriptor, 
    178                                 recordTypeId, newRecordTypeVersion, recordTypePut); 
    179                 if (updateFieldDescriptorPut != null) { 
    180                     recordTypeChanged = true; 
    181                     puts.add(updateFieldDescriptorPut); 
    182                 } 
    183                 fieldDescriptors.remove(fieldDescriptorId); 
    184                 originalFieldDescriptorSet.remove(originalFieldDescriptor); 
    185             } 
    186  
    187         } 
    188         if (!fieldDescriptors.isEmpty()) { 
     149        // non-versionable field group 
     150        Pair<Boolean, Long> updateResult = updateFieldGroupOnRecordType(put, newRecordTypeVersion, recordType 
     151                        .getNonVersionableFieldGroupId(), recordType.getNonVersionableFieldGroupVersion(), 
     152                        latestRecordType.getNonVersionableFieldGroupId(), latestRecordType 
     153                                        .getNonVersionableFieldGroupVersion(), 
     154                        RECORDTYPE_NONVERSIONABLEFIELDGROUP_COLUMN_NAME); 
     155        if (updateResult.getV1()) { 
     156            recordTypeChanged =true; 
     157            newRecordType.setNonVersionableFieldGroupVersion(updateResult.getV2()); 
     158        } 
     159 
     160        // versionable field group 
     161        updateResult = updateFieldGroupOnRecordType(put, newRecordTypeVersion, recordType.getVersionableFieldGroupId(), 
     162                        recordType.getVersionableFieldGroupVersion(), latestRecordType.getVersionableFieldGroupId(), 
     163                        latestRecordType.getVersionableFieldGroupVersion(), 
     164                        RECORDTYPE_VERSIONABLEFIELDGROUP_COLUMN_NAME); 
     165        if (updateResult.getV1()) { 
    189166            recordTypeChanged = true; 
    190             for (FieldDescriptor fieldDescriptor : fieldDescriptors.values()) { 
    191                 puts.add(addFieldDescriptor(fieldDescriptor, recordTypeId, newRecordTypeVersion, recordTypePut)); 
    192             } 
    193         } 
    194         if (!originalFieldDescriptorSet.isEmpty()) { 
     167            newRecordType.setVersionableFieldGroupVersion(updateResult.getV2()); 
     168        } 
     169 
     170        // versionable mutable field group 
     171        updateResult = updateFieldGroupOnRecordType(put, newRecordTypeVersion, recordType 
     172                        .getVersionableMutableFieldGroupId(), recordType.getVersionableMutableFieldGroupVersion(), 
     173                        latestRecordType.getVersionableMutableFieldGroupId(), latestRecordType 
     174                                        .getVersionableMutableFieldGroupVersion(), 
     175                        RECORDTYPE_VERSIONABLEMUTABLEFIELDGROUP_COLUMN_NAME); 
     176        if (updateResult.getV1()) { 
    195177            recordTypeChanged = true; 
    196             for (FieldDescriptor fieldDescriptor : originalFieldDescriptorSet) { 
    197                 removeFieldDescriptor(fieldDescriptor, newRecordTypeVersion, recordTypePut); 
    198             } 
     178            newRecordType.setVersionableMutableFieldGroupVersion(updateResult.getV2()); 
    199179        } 
    200180 
    201181        if (recordTypeChanged) { 
    202             recordTypePut.add(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(newRecordTypeVersion)); 
    203             puts.add(recordTypePut); 
     182            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(newRecordTypeVersion)); 
    204183            try { 
    205                 typeTable.put(puts); 
     184                typeTable.put(put); 
    206185            } catch (IOException e) { 
    207                 throw new RepositoryException("Exception occured while writing updated recordType <" + recordTypeId 
    208                                 + "> to HBase table", e); 
    209             } 
    210         } 
    211     } 
    212  
    213     private Put updateFieldDescriptor(FieldDescriptor fieldDescriptor, FieldDescriptor originalFieldDescriptor, 
    214                     String recordTypeId, Long recordTypeVersion, Put recordTypePut) throws RepositoryException { 
    215         boolean fieldDescriptorChanged = false; 
    216         long fieldDescriptorVersion = 0; 
    217         String fieldDescriptorId = fieldDescriptor.getId(); 
    218         Put put = new Put(EncodingUtil.generateFieldDescriptorRowKey(recordTypeId, fieldDescriptorId)); 
    219  
    220         if (!originalFieldDescriptor.getValueType().equals(fieldDescriptor.getValueType())) { 
    221             if (fieldDescriptorChanged == false) { 
    222                 fieldDescriptorChanged = true; 
    223                 fieldDescriptorVersion = getLatestFieldDescriptorVersion(recordTypeId, fieldDescriptorId) + 1; 
    224             } 
    225             put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, VALUE_TYPE_COLUMN_NAME, fieldDescriptorVersion, fieldDescriptor 
    226                             .getValueType().toBytes()); 
    227         } 
    228         if (fieldDescriptor.isMandatory() != originalFieldDescriptor.isMandatory()) { 
    229             if (fieldDescriptorChanged == false) { 
    230                 fieldDescriptorChanged = true; 
    231                 fieldDescriptorVersion = getLatestFieldDescriptorVersion(recordTypeId, fieldDescriptorId) + 1; 
    232             } 
    233             put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, MANDATORY_COLUMN_NAME, fieldDescriptorVersion, Bytes 
    234                             .toBytes(fieldDescriptor.isMandatory())); 
    235         } 
    236         if (fieldDescriptor.isVersionable() != originalFieldDescriptor.isVersionable()) { 
    237             if (fieldDescriptorChanged == false) { 
    238                 fieldDescriptorChanged = true; 
    239                 fieldDescriptorVersion = getLatestFieldDescriptorVersion(recordTypeId, fieldDescriptorId) + 1; 
    240             } 
    241             put.add(SYSTEM_VERSIONABLE_COLUMN_FAMILY, VERSIONABLE_COLUMN_NAME, fieldDescriptorVersion, Bytes 
    242                             .toBytes(fieldDescriptor.isVersionable())); 
    243         } 
    244         if (fieldDescriptorChanged) { 
    245             put.add(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(fieldDescriptorVersion)); 
    246             recordTypePut.add(FIELDDESCRIPTOR_COLUMN_FAMILY, Bytes.toBytes(fieldDescriptorId), recordTypeVersion, 
    247                             EncodingUtil.prefixValue(Bytes.toBytes(fieldDescriptorVersion), EncodingUtil.EXISTS_FLAG)); 
    248             return put; 
    249         } 
    250         return null; 
    251     } 
    252  
    253     private void removeFieldDescriptor(FieldDescriptor fieldDescriptor, Long recordTypeVersion, Put recordTypePut) { 
    254         recordTypePut.add(FIELDDESCRIPTOR_COLUMN_FAMILY, Bytes.toBytes(fieldDescriptor.getId()), recordTypeVersion, 
    255                         new byte[] { EncodingUtil.DELETE_FLAG }); 
    256     } 
    257  
    258     public RecordType getRecordType(String recordTypeId) throws RepositoryException { 
     186                throw new RepositoryException("Exception occured while updating recordType <" + recordType.getId() 
     187                                + "> on HBase", e); 
     188            } 
     189            newRecordType.setVersion(newRecordTypeVersion); 
     190        } else { 
     191            newRecordType.setVersion(latestRecordTypeVersion); 
     192        } 
     193        return newRecordType; 
     194    } 
     195 
     196    private Pair<Boolean, Long> updateFieldGroupOnRecordType(Put put, Long newRecordTypeVersion, String fieldGroupId, 
     197                    Long fieldGroupVersion, String latestFieldGroupId, Long latestFieldGroupVersion, 
     198                    byte[] fieldGroupColumnName) throws FieldGroupNotFoundException, RepositoryException { 
     199 
     200        if ((fieldGroupId == null) 
     201                        || (fieldGroupId.equals(latestFieldGroupId) && (latestFieldGroupVersion 
     202                                        .equals(fieldGroupVersion)))) { 
     203            return new Pair<Boolean, Long>(false, null); 
     204        } else { 
     205            Long version = putFieldGroupOnRecordType(newRecordTypeVersion, put, fieldGroupId, fieldGroupVersion, 
     206                            fieldGroupColumnName); 
     207            return new Pair<Boolean, Long>(true, version); 
     208        } 
     209    } 
     210 
     211    private Long putFieldGroupOnRecordType(Long recordTypeVersion, Put put, String fieldGroupId, 
     212                    Long fieldGroupVersion, byte[] fieldGroupColumnName) throws FieldGroupNotFoundException, 
     213                    RepositoryException { 
     214        // Validate if the fieldGroup exists and use the latest version if none 
     215        // was given. 
     216        Long newFieldGroupVersion = getFieldGroup(fieldGroupId, fieldGroupVersion).getVersion(); 
     217        put.add(VERSIONABLE_COLUMN_FAMILY, fieldGroupColumnName, recordTypeVersion, encodeFieldGroup(fieldGroupId, 
     218                        newFieldGroupVersion)); 
     219        return newFieldGroupVersion; 
     220    } 
     221     
     222    public RecordType removeFieldGroups(String recordTypeId, boolean nonVersionable, boolean versionable, boolean versionableMutable) throws RecordTypeNotFoundException, RepositoryException { 
     223        RecordType recordType = getRecordType(recordTypeId, null); 
     224        Long version = recordType.getVersion() + 1; 
     225        Put put = new Put(Bytes.toBytes(recordTypeId)); 
     226        boolean changed = false; 
     227        if (nonVersionable) { 
     228            if (recordType.getNonVersionableFieldGroupId() != null) { 
     229                put.add(VERSIONABLE_COLUMN_FAMILY, RECORDTYPE_NONVERSIONABLEFIELDGROUP_COLUMN_NAME, new byte[]{EncodingUtil.DELETE_FLAG}); 
     230                recordType.setNonVersionableFieldGroupId(null); 
     231                recordType.setNonVersionableFieldGroupVersion(null); 
     232                changed = true; 
     233            } 
     234        } 
     235        if (versionable) { 
     236            if (recordType.getVersionableFieldGroupId() != null) { 
     237                put.add(VERSIONABLE_COLUMN_FAMILY, RECORDTYPE_VERSIONABLEFIELDGROUP_COLUMN_NAME, new byte[]{EncodingUtil.DELETE_FLAG}); 
     238                recordType.setVersionableFieldGroupId(null); 
     239                recordType.setVersionableFieldGroupVersion(null); 
     240                changed = true; 
     241            } 
     242        } 
     243        if (versionableMutable) { 
     244            if (recordType.getVersionableMutableFieldGroupId() != null) { 
     245                put.add(VERSIONABLE_COLUMN_FAMILY, RECORDTYPE_VERSIONABLEMUTABLEFIELDGROUP_COLUMN_NAME, new byte[]{EncodingUtil.DELETE_FLAG}); 
     246                recordType.setVersionableMutableFieldGroupId(null); 
     247                recordType.setVersionableMutableFieldGroupVersion(null); 
     248                changed = true; 
     249            } 
     250        } 
     251        if (changed) { 
     252            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     253            try { 
     254                typeTable.put(put); 
     255            } catch (IOException e) { 
     256                throw new RepositoryException("Exception occured while removing fieldGroups from recordType <" + recordTypeId 
     257                                + "> on HBase", e); 
     258            } 
     259            recordType.setVersion(version); 
     260        } 
     261        return recordType; 
     262    } 
     263 
     264    public RecordType getRecordType(String recordTypeId, Long version) throws RecordTypeNotFoundException, 
     265                    RepositoryException { 
    259266        ArgumentValidator.notNull(recordTypeId, "recordTypeId"); 
    260         Get get = new Get(EncodingUtil.generateRecordTypeRowKey(recordTypeId)); 
     267        Get get = new Get(Bytes.toBytes(recordTypeId)); 
     268        if (version != null) { 
     269            get.setMaxVersions(); 
     270        } 
    261271        Result result; 
    262272        try { 
     273            if (!typeTable.exists(get)) { 
     274                throw new RecordTypeNotFoundException(recordTypeId, null); 
     275            } 
    263276            result = typeTable.get(get); 
    264277        } catch (IOException e) { 
     
    266279                            + "> from HBase table", e); 
    267280        } 
    268         NavigableMap<byte[], byte[]> systemFamilyMap = result.getFamilyMap(SYSTEM_COLUMN_FAMILY); 
    269         long version = Bytes.toLong(systemFamilyMap.get(CURRENT_VERSION_COLUMN_NAME)); 
    270  
    271281        RecordType recordType = newRecordType(recordTypeId); 
    272         recordType.setVersion(version); 
    273  
    274         NavigableMap<byte[], byte[]> fieldDescriptorsFamilyMap = result.getFamilyMap(FIELDDESCRIPTOR_COLUMN_FAMILY); 
    275         Set<Entry<byte[], byte[]>> fieldDescriptorEntries = fieldDescriptorsFamilyMap.entrySet(); 
    276         for (Entry<byte[], byte[]> fieldDescriptorEntry : fieldDescriptorEntries) { 
    277             if (!EncodingUtil.isDeletedField(fieldDescriptorEntry.getValue())) { 
    278                 recordType.addFieldDescriptor(getFieldDescriptor(recordTypeId, Bytes.toString(fieldDescriptorEntry 
    279                                 .getKey()), Bytes.toLong(EncodingUtil.stripPrefix(fieldDescriptorEntry.getValue())))); 
    280             } 
     282        Long currentVersion = Bytes.toLong(result.getValue(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME)); 
     283        if (version != null) { 
     284            if (currentVersion < version) { 
     285                throw new RecordTypeNotFoundException(recordTypeId, version); 
     286            } 
     287            recordType.setVersion(version); 
     288        } else { 
     289            recordType.setVersion(currentVersion); 
     290        } 
     291        Pair<String, Long> fieldGroup = extractFieldGroup(result, version, 
     292                        RECORDTYPE_NONVERSIONABLEFIELDGROUP_COLUMN_NAME); 
     293        if (fieldGroup != null) { 
     294            recordType.setNonVersionableFieldGroupId(fieldGroup.getV1()); 
     295            recordType.setNonVersionableFieldGroupVersion(fieldGroup.getV2()); 
     296        } 
     297        fieldGroup = extractFieldGroup(result, version, RECORDTYPE_VERSIONABLEFIELDGROUP_COLUMN_NAME); 
     298        if (fieldGroup != null) { 
     299            recordType.setVersionableFieldGroupId(fieldGroup.getV1()); 
     300            recordType.setVersionableFieldGroupVersion(fieldGroup.getV2()); 
     301        } 
     302        fieldGroup = extractFieldGroup(result, version, RECORDTYPE_VERSIONABLEMUTABLEFIELDGROUP_COLUMN_NAME); 
     303        if (fieldGroup != null) { 
     304            recordType.setVersionableMutableFieldGroupId(fieldGroup.getV1()); 
     305            recordType.setVersionableMutableFieldGroupVersion(fieldGroup.getV2()); 
    281306        } 
    282307        return recordType; 
    283308    } 
    284309 
    285     public RecordType getRecordType(String recordTypeId, Long recordTypeVersion) throws RepositoryException { 
    286         if (recordTypeVersion == null) { 
    287             return getRecordType(recordTypeId); 
    288         } 
    289         ArgumentValidator.notNull(recordTypeId, "recordTypeId"); 
    290         Get get = new Get(EncodingUtil.generateRecordTypeRowKey(recordTypeId)); 
    291         get.setMaxVersions(); 
     310    private Pair<String, Long> extractFieldGroup(Result result, Long version, byte[] fieldGroupColumnName) { 
     311        byte[] fieldGroupBytes = null; 
     312        Pair<String, Long> fieldGroup = null; 
     313        if (version != null) { 
     314            NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = result.getMap(); 
     315            NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableVersionsMap = allVersionsMap 
     316                            .get(VERSIONABLE_COLUMN_FAMILY); 
     317            NavigableMap<Long, byte[]> fieldGroupVersions = versionableVersionsMap.get(fieldGroupColumnName); 
     318            if (fieldGroupVersions != null) { 
     319                Entry<Long, byte[]> ceilingEntry = fieldGroupVersions.ceilingEntry(version); 
     320                if (ceilingEntry != null) { 
     321                    fieldGroupBytes = ceilingEntry.getValue(); 
     322                } 
     323            } 
     324        } else { 
     325            fieldGroupBytes = result.getValue(VERSIONABLE_COLUMN_FAMILY, fieldGroupColumnName); 
     326        } 
     327        if (fieldGroupBytes != null) { 
     328            fieldGroup = decodeFieldGroup(fieldGroupBytes); 
     329        } 
     330        return fieldGroup; 
     331    } 
     332 
     333    private byte[] encodeFieldGroup(String fieldGroupId, Long fieldGroupVersion) { 
     334        byte[] bytes = Bytes.toBytes(fieldGroupVersion); 
     335        bytes = EncodingUtil.prefixValue(Bytes.add(bytes, Bytes.toBytes(fieldGroupId)), EncodingUtil.EXISTS_FLAG); 
     336        return bytes; 
     337    } 
     338 
     339    private Pair<String, Long> decodeFieldGroup(byte[] fieldGroupBytes) { 
     340        if (EncodingUtil.isDeletedField(fieldGroupBytes)) { 
     341            return null; 
     342        } 
     343        byte[] encodedFieldGroupBytes = EncodingUtil.stripPrefix(fieldGroupBytes); 
     344        Long version = Bytes.toLong(encodedFieldGroupBytes, 0, Bytes.SIZEOF_LONG); 
     345        String fieldGroupId = Bytes.toString(encodedFieldGroupBytes, Bytes.SIZEOF_LONG, encodedFieldGroupBytes.length 
     346                        - Bytes.SIZEOF_LONG); 
     347        return new Pair<String, Long>(fieldGroupId, version); 
     348    } 
     349 
     350    // Field Groups 
     351 
     352    public FieldGroup newFieldGroup(String id) { 
     353        ArgumentValidator.notNull(id, "id"); 
     354        return new FieldGroupImpl(id); 
     355    } 
     356 
     357    public FieldGroupEntry newFieldGroupEntry(String fieldDescriptorId, Long fieldDescriptorVersion, boolean mandatory, 
     358                    String alias) { 
     359        ArgumentValidator.notNull(fieldDescriptorId, "fieldDescriptorId"); 
     360        ArgumentValidator.notNull(mandatory, "mandatory"); 
     361        ArgumentValidator.notNull(alias, "alias"); 
     362        return new FieldGroupEntryImpl(fieldDescriptorId, fieldDescriptorVersion, mandatory, alias); 
     363    } 
     364 
     365    public FieldGroup createFieldGroup(FieldGroup fieldGroup) throws FieldGroupExistsException, 
     366                    FieldDescriptorNotFoundException, RepositoryException { 
     367        ArgumentValidator.notNull(fieldGroup, "fieldGroup"); 
     368        FieldGroup result; 
     369        String id = fieldGroup.getId(); 
     370        byte[] rowId = Bytes.toBytes(id); 
     371        Long version = Long.valueOf(1); 
     372        result = fieldGroup.clone(); 
     373        try { 
     374            if (typeTable.exists(new Get(rowId))) { 
     375                throw new FieldGroupExistsException(fieldGroup); 
     376            } 
     377            Put put = new Put(rowId); 
     378            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     379            Collection<FieldGroupEntry> fieldGroupEntries = fieldGroup.getFieldGroupEntries(); 
     380            for (FieldGroupEntry fieldGroupEntry : fieldGroupEntries) { 
     381                result.setFieldGroupEntry(putFieldGroupEntry(version, put, fieldGroupEntry)); 
     382            } 
     383            typeTable.put(put); 
     384        } catch (IOException e) { 
     385            throw new RepositoryException("Exception occured while creating fieldGroup <" + fieldGroup.getId() 
     386                            + "> version: <" + version + "> on HBase", e); 
     387        } 
     388        result.setVersion(version); 
     389        return result; 
     390    } 
     391 
     392    public FieldGroup updateFieldGroup(FieldGroup fieldGroup) throws FieldGroupNotFoundException, 
     393                    FieldDescriptorNotFoundException, RepositoryException { 
     394        ArgumentValidator.notNull(fieldGroup, "fieldGroup"); 
     395        String id = fieldGroup.getId(); 
     396        FieldGroup latestFieldGroup = getFieldGroup(id, null); 
     397        Long version = latestFieldGroup.getVersion() + 1; 
     398        byte[] rowId = Bytes.toBytes(id); 
     399        Put put = new Put(rowId); 
     400        boolean updateNeeded = false; 
     401        FieldGroup updatedFieldGroup = fieldGroup.clone(); 
     402 
     403        for (FieldGroupEntry fieldGroupEntry : fieldGroup.getFieldGroupEntries()) { 
     404            FieldGroupEntry latestFieldGroupEntry = latestFieldGroup.getFieldGroupEntry(fieldGroupEntry 
     405                            .getFieldDescriptorId()); 
     406            if (!fieldGroupEntry.equals(latestFieldGroupEntry)) { 
     407                updatedFieldGroup.setFieldGroupEntry(putFieldGroupEntry(version, put, fieldGroupEntry)); 
     408                updateNeeded = true; 
     409            } 
     410        } 
     411 
     412        if (updateNeeded) { 
     413            updatedFieldGroup.setVersion(version); 
     414            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     415            try { 
     416                typeTable.put(put); 
     417            } catch (IOException e) { 
     418                throw new RepositoryException("Exception occured while updating fieldGroup <" + fieldGroup.getId() 
     419                                + "> version: <" + version + "> on HBase", e); 
     420            } 
     421        } else { 
     422            updatedFieldGroup.setVersion(latestFieldGroup.getVersion()); 
     423        } 
     424        return updatedFieldGroup; 
     425    } 
     426 
     427    private FieldGroupEntry putFieldGroupEntry(Long version, Put put, FieldGroupEntry fieldGroupEntry) 
     428                    throws FieldDescriptorNotFoundException, RepositoryException { 
     429        FieldGroupEntry newFieldGroupEntry = fieldGroupEntry.clone(); 
     430        // Retrieve fieldDescriptor to check it exists and get the latest 
     431        // version number if none was given. 
     432        Long fieldDescriptorVersion = getFieldDescriptor(fieldGroupEntry.getFieldDescriptorId(), 
     433                        fieldGroupEntry.getFieldDescriptorVersion()).getVersion(); 
     434        newFieldGroupEntry.setFieldDescriptorVersion(fieldDescriptorVersion); 
     435        put.add(VERSIONABLE_COLUMN_FAMILY, Bytes.toBytes(fieldGroupEntry.getFieldDescriptorId()), version, 
     436                        encodeFieldGroupEntry(newFieldGroupEntry)); 
     437        return newFieldGroupEntry; 
     438    } 
     439     
     440    public FieldGroup removeFieldDescriptors(String fieldGroupId, List<String> fieldDescriptorIds) throws FieldGroupNotFoundException, RepositoryException{ 
     441        FieldGroup fieldGroup = getFieldGroup(fieldGroupId, null); 
     442        Put put = new Put(Bytes.toBytes(fieldGroupId)); 
     443        Long version = fieldGroup.getVersion() + 1; 
     444        boolean changed = false; 
     445        for (String fieldDescriptorId : fieldDescriptorIds) { 
     446            if (fieldGroup.getFieldGroupEntry(fieldDescriptorId) == null) { 
     447                // Ignore 
     448            } else { 
     449                put.add(VERSIONABLE_COLUMN_FAMILY, Bytes.toBytes(fieldDescriptorId), version, new byte[]{EncodingUtil.DELETE_FLAG}); 
     450                fieldGroup.removeFieldGroupEntry(fieldDescriptorId); 
     451                changed = true; 
     452            } 
     453        } 
     454        if (changed) { 
     455            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     456            try { 
     457            typeTable.put(put); 
     458            } catch (IOException e) { 
     459                throw new RepositoryException("Exception occured while removing fieldDescriptor <"+fieldGroupId+"> from fieldGroup <" + fieldGroup.getId() 
     460                                + "> on HBase", e); 
     461            } 
     462            fieldGroup.setVersion(version); 
     463        } 
     464        return fieldGroup; 
     465    } 
     466 
     467    public FieldGroup getFieldGroup(String id, Long version) throws FieldGroupNotFoundException, RepositoryException { 
     468        FieldGroup fieldGroup = new FieldGroupImpl(id); 
     469        byte[] rowId = Bytes.toBytes(id); 
     470        Get get = new Get(rowId); 
     471        if (version != null) { 
     472            get.setMaxVersions(); 
     473        } 
    292474        Result result; 
    293475        try { 
     476            if (!typeTable.exists(get)) { 
     477                throw new FieldGroupNotFoundException(id, null); 
     478            } 
    294479            result = typeTable.get(get); 
    295480        } catch (IOException e) { 
    296             throw new RepositoryException("Exception occured while retrieving recordType <" + recordTypeId 
    297                             + "> from HBase table", e); 
    298         } 
    299         RecordType recordType = newRecordType(recordTypeId); 
    300         NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allFamiliesMap = result.getMap(); 
    301         NavigableMap<byte[], NavigableMap<Long, byte[]>> fieldDescriptorsVersionedMap = allFamiliesMap 
    302                         .get(FIELDDESCRIPTOR_COLUMN_FAMILY); 
    303         Set<Entry<byte[], NavigableMap<Long, byte[]>>> fieldDescriptorsEntrySet = fieldDescriptorsVersionedMap 
    304                         .entrySet(); 
    305         for (Entry<byte[], NavigableMap<Long, byte[]>> fieldDescriptorEntry : fieldDescriptorsEntrySet) { 
    306             String fieldDescriptorId = Bytes.toString(fieldDescriptorEntry.getKey()); 
    307             Entry<Long, byte[]> ceilingEntry = fieldDescriptorEntry.getValue().ceilingEntry(recordTypeVersion); 
    308             if (ceilingEntry != null && !EncodingUtil.isDeletedField(ceilingEntry.getValue())) { 
    309                 long fieldDescriptorVersion = Bytes.toLong(EncodingUtil.stripPrefix(ceilingEntry.getValue())); 
    310                 recordType.addFieldDescriptor(getFieldDescriptor(recordTypeId, fieldDescriptorId, 
    311                                 fieldDescriptorVersion)); 
    312             } 
    313         } 
    314         recordType.setVersion(recordTypeVersion); 
    315         return recordType; 
    316     } 
    317  
    318     private FieldDescriptor getFieldDescriptor(String recordTypeId, String fieldDescriptorId, 
    319                     Long fieldDescriptorVersion) throws RepositoryException { 
    320         Get get = new Get(EncodingUtil.generateFieldDescriptorRowKey(recordTypeId, fieldDescriptorId)); 
    321         get.setMaxVersions(); 
     481            throw new RepositoryException("Exception occured while retrieving fieldGroup <" + id + "> version: <" 
     482                            + version + "> from HBase", e); 
     483        } 
     484        long latestVersion = Bytes.toLong(result.getValue(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME)); 
     485        if (version != null) { 
     486            if (version > latestVersion) { 
     487                throw new FieldGroupNotFoundException(id, version); 
     488            } 
     489            NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = result.getMap(); 
     490            NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableVersionsMap = allVersionsMap 
     491                            .get(VERSIONABLE_COLUMN_FAMILY); 
     492            for (Entry<byte[], NavigableMap<Long, byte[]>> entry : versionableVersionsMap.entrySet()) { 
     493                String fieldDescriptorId = Bytes.toString(entry.getKey()); 
     494                Entry<Long, byte[]> ceilingEntry = entry.getValue().ceilingEntry(version); 
     495                if (ceilingEntry != null) { 
     496                    FieldGroupEntry decodedFieldGroupEntry = decodeFieldGroupEntry(ceilingEntry.getValue(), fieldDescriptorId); 
     497                    if (decodedFieldGroupEntry != null) { 
     498                        fieldGroup.setFieldGroupEntry(decodedFieldGroupEntry); 
     499                    } 
     500                } 
     501            } 
     502            fieldGroup.setVersion(version); 
     503        } else { 
     504            NavigableMap<byte[], byte[]> versionableFamilyMap = result.getFamilyMap(VERSIONABLE_COLUMN_FAMILY); 
     505            for (Entry<byte[], byte[]> entry : versionableFamilyMap.entrySet()) { 
     506                String fieldDescriptorId = Bytes.toString(entry.getKey()); 
     507                FieldGroupEntry decodeFieldGroupEntry = decodeFieldGroupEntry(entry.getValue(), fieldDescriptorId); 
     508                if (decodeFieldGroupEntry != null) { 
     509                    fieldGroup.setFieldGroupEntry(decodeFieldGroupEntry); 
     510                } 
     511            } 
     512            fieldGroup.setVersion(latestVersion); 
     513        } 
     514 
     515        return fieldGroup; 
     516    } 
     517 
     518    // TODO move to some encoder/decoder 
     519    /** 
     520     * Encoding the fields: FD-version, mandatory, alias 
     521     */ 
     522    private byte[] encodeFieldGroupEntry(FieldGroupEntry fieldGroupEntry) { 
     523        // TODO check if we can use nio instead 
     524        byte[] bytes = new byte[0]; 
     525        bytes = Bytes.add(bytes, Bytes.toBytes(fieldGroupEntry.getFieldDescriptorVersion())); 
     526        bytes = Bytes.add(bytes, Bytes.toBytes(fieldGroupEntry.isMandatory())); 
     527        bytes = Bytes.add(bytes, Bytes.toBytes(fieldGroupEntry.getAlias())); 
     528        return EncodingUtil.prefixValue(bytes, EncodingUtil.EXISTS_FLAG); 
     529    } 
     530 
     531    private FieldGroupEntry decodeFieldGroupEntry(byte[] bytes, String fieldDescriptorId) { 
     532        if (EncodingUtil.isDeletedField(bytes)) { 
     533            return null; 
     534        } 
     535        byte[] encodedBytes = EncodingUtil.stripPrefix(bytes); 
     536        int offset = 0; 
     537        long fieldDescriptorVersion = Bytes.toLong(encodedBytes, 0); 
     538        offset = offset + Bytes.SIZEOF_LONG; 
     539        byte[] booleanBytes = new byte[Bytes.SIZEOF_BOOLEAN]; 
     540        Bytes.putBytes(booleanBytes, 0, encodedBytes, offset, Bytes.SIZEOF_BOOLEAN); 
     541        boolean mandatory = Bytes.toBoolean(booleanBytes); 
     542        offset = offset + Bytes.SIZEOF_BOOLEAN; 
     543        String alias = Bytes.toString(encodedBytes, offset, encodedBytes.length - offset); 
     544        return new FieldGroupEntryImpl(fieldDescriptorId, fieldDescriptorVersion, mandatory, alias); 
     545    } 
     546 
     547    public FieldDescriptor newFieldDescriptor(String id, ValueType valueType, String globalName) { 
     548        ArgumentValidator.notNull(id, "id"); 
     549        ArgumentValidator.notNull(valueType, "valueType"); 
     550        ArgumentValidator.notNull(globalName, "globalName"); 
     551        return new FieldDescriptorImpl(id, valueType, globalName); 
     552    } 
     553 
     554    public FieldDescriptor createFieldDescriptor(FieldDescriptor fieldDescriptor) 
     555                    throws FieldDescriptorExistsException, RepositoryException { 
     556        ArgumentValidator.notNull(fieldDescriptor, "fieldDescriptor"); 
     557        FieldDescriptor result; 
     558        byte[] rowId = Bytes.toBytes(fieldDescriptor.getId()); 
     559        Long version = Long.valueOf(1); 
     560        try { 
     561            if (typeTable.exists(new Get(rowId))) { 
     562                throw new FieldDescriptorExistsException(fieldDescriptor); 
     563            } 
     564            Put put = new Put(rowId); 
     565            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     566            put.add(NON_VERSIONABLE_COLUMN_FAMILY, FIELDDESCRIPTOR_VALUETYPE_COLUMN_NAME, fieldDescriptor 
     567                            .getValueType().toBytes()); 
     568            put.add(VERSIONABLE_COLUMN_FAMILY, FIELDDESCRIPTOR_GLOBALNAME_COLUMN_NAME, version, Bytes 
     569                            .toBytes(fieldDescriptor.getGlobalName())); 
     570            typeTable.put(put); 
     571        } catch (IOException e) { 
     572            throw new RepositoryException("Exception occured while creating fieldDescriptor <" 
     573                            + fieldDescriptor.getId() + "> version: <" + version + "> on HBase", e); 
     574        } 
     575        result = fieldDescriptor.clone(); 
     576        result.setVersion(version); 
     577        return result; 
     578    } 
     579 
     580    public FieldDescriptor updateFieldDescriptor(FieldDescriptor fieldDescriptor) 
     581                    throws FieldDescriptorNotFoundException, FieldDescriptorUpdateException, RepositoryException { 
     582        FieldDescriptor latestFieldDescriptor = getFieldDescriptor(fieldDescriptor.getId(), null); 
     583        if (!fieldDescriptor.getValueType().equals(latestFieldDescriptor.getValueType())) { 
     584            throw new FieldDescriptorUpdateException("Changing the valueType of a fieldDescriptor <" 
     585                            + fieldDescriptor.getId() + "> is not allowed; old<" + latestFieldDescriptor.getValueType() 
     586                            + "> new<" + fieldDescriptor.getValueType() + ">"); 
     587        } 
     588        Long version = latestFieldDescriptor.getVersion(); 
     589        if (!fieldDescriptor.getGlobalName().equals(latestFieldDescriptor.getGlobalName())) { 
     590            version = version + 1; 
     591            Put put = new Put(Bytes.toBytes(fieldDescriptor.getId())); 
     592            put.add(VERSIONABLE_COLUMN_FAMILY, FIELDDESCRIPTOR_GLOBALNAME_COLUMN_NAME, version, Bytes 
     593                            .toBytes(fieldDescriptor.getGlobalName())); 
     594            put.add(NON_VERSIONABLE_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME, Bytes.toBytes(version)); 
     595            try { 
     596                typeTable.put(put); 
     597            } catch (IOException e) { 
     598                throw new RepositoryException("Exception occured while updating fieldDescriptor <" 
     599                                + fieldDescriptor.getId() + "> on HBase", e); 
     600            } 
     601        } 
     602        FieldDescriptor newFieldDescriptor = fieldDescriptor.clone(); 
     603        newFieldDescriptor.setVersion(version); 
     604        return newFieldDescriptor; 
     605    } 
     606 
     607    public FieldDescriptor getFieldDescriptor(String id, Long version) throws FieldDescriptorNotFoundException, 
     608                    RepositoryException { 
     609        ArgumentValidator.notNull(id, "id"); 
    322610        Result result; 
     611        Get get = new Get(Bytes.toBytes(id)); 
     612        if (version != null) { 
     613            get.setMaxVersions(); 
     614        } 
    323615        try { 
     616            if (!typeTable.exists(get)) { 
     617                throw new FieldDescriptorNotFoundException(id, null); 
     618            } 
    324619            result = typeTable.get(get); 
    325620        } catch (IOException e) { 
    326             throw new RepositoryException("Exception occured while retrieving fieldDescriptor <" + fieldDescriptorId 
    327                             + "> for recordType <" + recordTypeId + "> from HBase table", e); 
    328         } 
    329         NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allFamiliesMap = result.getMap(); 
    330         NavigableMap<byte[], NavigableMap<Long, byte[]>> systemVersionableVersionedMap = allFamiliesMap 
    331                         .get(SYSTEM_VERSIONABLE_COLUMN_FAMILY); 
    332         ValueType valueType = ValueTypeImpl.fromBytes(systemVersionableVersionedMap.get(VALUE_TYPE_COLUMN_NAME) 
    333                         .ceilingEntry(fieldDescriptorVersion).getValue(), this); 
    334         boolean mandatory = Bytes.toBoolean(systemVersionableVersionedMap.get(MANDATORY_COLUMN_NAME).ceilingEntry( 
    335                         fieldDescriptorVersion).getValue()); 
    336         boolean versionable = Bytes.toBoolean(systemVersionableVersionedMap.get(VERSIONABLE_COLUMN_NAME).ceilingEntry( 
    337                         fieldDescriptorVersion).getValue()); 
    338  
    339         return newFieldDescriptor(fieldDescriptorId, fieldDescriptorVersion, valueType, mandatory, versionable); 
    340     } 
    341  
    342     private long getLatestFieldDescriptorVersion(String recordTypeId, String fieldDescriptorId) 
    343                     throws RepositoryException { 
    344         Get get = new Get(EncodingUtil.generateFieldDescriptorRowKey(recordTypeId, fieldDescriptorId)); 
    345         get.addColumn(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME); 
    346         Result result; 
    347         try { 
    348             result = typeTable.get(get); 
    349         } catch (IOException e) { 
    350             throw new RepositoryException("Exception occured while retrieving fielddescriptor <" + fieldDescriptorId 
    351                             + "> of recordType <" + recordTypeId + ">", e); 
    352         } 
    353         if (result.isEmpty()) { 
    354             return 0; 
    355         } 
    356         return Bytes.toLong(result.getValue(SYSTEM_COLUMN_FAMILY, CURRENT_VERSION_COLUMN_NAME)); 
    357     } 
     621            throw new RepositoryException("Exception occured while retrieving fieldDescriptor <" + id + "> version: <" 
     622                            + version + "> from HBase", e); 
     623        } 
     624        NavigableMap<byte[], byte[]> nonVersionableColumnFamily = result.getFamilyMap(NON_VERSIONABLE_COLUMN_FAMILY); 
     625        Long currentVersion = Bytes.toLong(nonVersionableColumnFamily.get(CURRENT_VERSION_COLUMN_NAME)); 
     626        Long retrievedVersion; 
     627        String globalName; 
     628        if (version != null) { 
     629            if (version > currentVersion) { 
     630                throw new FieldDescriptorNotFoundException(id, version); 
     631            } 
     632            NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> allVersionsMap = result.getMap(); 
     633            NavigableMap<byte[], NavigableMap<Long, byte[]>> versionableVersionsMap = allVersionsMap 
     634                            .get(VERSIONABLE_COLUMN_FAMILY); 
     635            NavigableMap<Long, byte[]> globalNameVersionsMap = versionableVersionsMap 
     636                            .get(FIELDDESCRIPTOR_GLOBALNAME_COLUMN_NAME); 
     637            globalName = Bytes.toString(globalNameVersionsMap.floorEntry(version).getValue()); 
     638            retrievedVersion = version; 
     639        } else { 
     640            NavigableMap<byte[], byte[]> versionableColumnFamily = result.getFamilyMap(VERSIONABLE_COLUMN_FAMILY); 
     641            globalName = Bytes.toString(versionableColumnFamily.get(FIELDDESCRIPTOR_GLOBALNAME_COLUMN_NAME)); 
     642            retrievedVersion = currentVersion; 
     643        } 
     644        ValueType valueType = ValueTypeImpl.fromBytes(nonVersionableColumnFamily 
     645                        .get(FIELDDESCRIPTOR_VALUETYPE_COLUMN_NAME), this); 
     646        FieldDescriptor fieldDescriptor = new FieldDescriptorImpl(id, valueType, globalName); 
     647        fieldDescriptor.setVersion(retrievedVersion); 
     648        return fieldDescriptor; 
     649    } 
     650 
     651    // Value Types 
    358652 
    359653    // TODO move to a primitiveValueType registry 
     
    377671        return new ValueTypeImpl(primitiveValueTypes.get(primitiveValueTypeName), multivalue, hierarchy); 
    378672    } 
     673 
    379674} 
  • projects/lily/trunk/repository/impl/src/main/java/org/lilycms/repository/impl/RecordImpl.java

    r3952 r3977  
    1616package org.lilycms.repository.impl; 
    1717 
     18import java.util.ArrayList; 
    1819import java.util.HashMap; 
    19 import java.util.HashSet; 
     20import java.util.List; 
    2021import java.util.Map; 
    21 import java.util.Set; 
    2222 
    2323import org.lilycms.repository.api.FieldNotFoundException; 
     
    2727 
    2828public class RecordImpl implements Record { 
    29     private RecordId recordId; 
    30     private Map<String, Object> fields = new HashMap<String, Object>(); 
    31     private Set<String> deleteFields = new HashSet<String>(); 
     29    private RecordId id; 
     30    private Map<String, Object> nonVersionableFields = new HashMap<String, Object>(); 
     31    private Map<String, Object> versionableFields = new HashMap<String, Object>(); 
     32    private Map<String, Object> versionableMutableFields = new HashMap<String, Object>(); 
    3233    private String recordTypeId; 
    33     private long recordTypeVersion; 
    34     private Map<String, String> variantProperties = new HashMap<String, String>(); 
     34    private Long recordTypeVersion; 
     35    private String nonVersionableRecordTypeId; 
     36    private Long nonVersionableRecordTypeVersion; 
     37    private String versionableRecordTypeId; 
     38    private Long versionableRecordTypeVersion; 
     39    private String versionableMutableRecordTypeId; 
     40    private Long versionableMutableRecordTypeVersion; 
    3541    private Long version; 
     42    private List<String> nonVersionableFieldsToDelete = new ArrayList<String>(); 
     43    private List<String> versionableFieldsToDelete = new ArrayList<String>(); 
     44    private List<String> versionableMutableFieldsToDelete = new ArrayList<String>(); 
    3645 
    3746    /** 
     
    4655     * @use {@link Repository#newRecord} instead 
    4756     */ 
    48     public RecordImpl(RecordId recordId) { 
    49         this.recordId = recordId; 
    50     } 
    51      
    52     public void setId(RecordId recordId) { 
    53         this.recordId = recordId; 
     57    public RecordImpl(RecordId id) { 
     58        this.id = id; 
     59    } 
     60     
     61    public void setId(RecordId id) { 
     62        this.id = id; 
    5463    } 
    5564     
    5665    public RecordId getId() { 
    57         return recordId; 
     66        return id; 
    5867    } 
    5968     
     
    6675    } 
    6776 
    68     public void setRecordType(String recordTypeId, long recordTypeVersion) { 
     77    public void setRecordType(String recordTypeId, Long recordTypeVersion) { 
    6978        this.recordTypeId = recordTypeId; 
    7079        this.recordTypeVersion = recordTypeVersion; 
     
    7584    } 
    7685 
    77     public long getRecordTypeVersion() { 
     86    public Long getRecordTypeVersion() { 
    7887        return recordTypeVersion; 
    7988    } 
    80  
    81     public void setField(String fieldId, Object value) { 
    82         fields.put(fieldId, value); 
    83     } 
    84  
    85     public Object getField(String fieldId) throws FieldNotFoundException { 
    86         Object field = fields.get(fieldId); 
     89     
     90    public void setNonVersionableRecordType(String id, Long version) { 
     91        this.nonVersionableRecordTypeId = id; 
     92        this.nonVersionableRecordTypeVersion = version; 
     93    } 
     94     
     95    public String getNonVersionableRecordTypeId() { 
     96        return nonVersionableRecordTypeId; 
     97    } 
     98     
     99    public Long getNonVersionableRecordTypeVersion() { 
     100        return nonVersionableRecordTypeVersion; 
     101    } 
     102     
     103    public void setVersionableRecordType(String id, Long version) { 
     104        this.versionableRecordTypeId = id; 
     105        this.versionableRecordTypeVersion = version; 
     106    } 
     107     
     108    public String getVersionableRecordTypeId() { 
     109        return versionableRecordTypeId; 
     110    } 
     111     
     112    public Long getVersionableRecordTypeVersion() { 
     113        return versionableRecordTypeVersion; 
     114    } 
     115     
     116    public void setVersionableMutableRecordType(String id, Long version) { 
     117        this.versionableMutableRecordTypeId = id; 
     118        this.versionableMutableRecordTypeVersion = version; 
     119    } 
     120     
     121    public String getVersionableMutableRecordTypeId() { 
     122        return versionableMutableRecordTypeId; 
     123    } 
     124 
     125    public Long getVersionableMutableRecordTypeVersion() { 
     126        return versionableMutableRecordTypeVersion; 
     127    } 
     128     
     129    public void setNonVersionableField(String fieldId, Object value) { 
     130        this.nonVersionableFields.put(fieldId, value); 
     131    } 
     132     
     133    public void setVersionableField(String fieldId, Object value) { 
     134        this.versionableFields.put(fieldId, value); 
     135    } 
     136     
     137    public void setVersionableMutableField(String fieldId, Object value) { 
     138        this.versionableMutableFields.put(fieldId, value); 
     139    } 
     140 
     141    public Object getNonVersionableField(String fieldId) throws FieldNotFoundException { 
     142        Object field = nonVersionableFields.get(fieldId); 
    87143        if (field == null) { 
    88144            throw new FieldNotFoundException(fieldId); 
     
    91147    } 
    92148 
    93     public Map<String, Object> getFields() { 
    94         return fields; 
    95     } 
    96  
    97     public void addVariantProperty(String dimension, String dimensionValue) { 
    98         variantProperties.put(dimension, dimensionValue); 
    99     } 
    100  
    101     public void addVariantProperties(Map<String, String> variantProperties) { 
    102         if (variantProperties != null) { 
    103             this.variantProperties.putAll(variantProperties); 
     149    public Object getVersionableField(String fieldId) throws FieldNotFoundException { 
     150        Object field = versionableFields.get(fieldId); 
     151        if (field == null) { 
     152            throw new FieldNotFoundException(fieldId); 
    104153        } 
    105     } 
    106  
    107     public Map<String, String> getVariantProperties() { 
    108         return variantProperties; 
    109     } 
    110  
    111     public void deleteField(String fieldId) { 
    112         deleteFields.add(fieldId); 
    113     } 
    114  
    115     public Set<String> getDeleteFields() { 
    116         return deleteFields; 
     154        return field; 
     155    } 
     156 
     157    public Object getVersionableMutableField(String fieldId) throws FieldNotFoundException { 
     158        Object field = versionableMutableFields.get(fieldId); 
     159        if (field == null) { 
     160            throw new FieldNotFoundException(fieldId); 
     161        } 
     162        return field; 
     163    } 
     164 
     165    public Map<String, Object> getNonVersionableFields() { 
     166        return nonVersionableFields; 
     167    } 
     168 
     169    public Map<String, Object> getVersionableFields() { 
     170        return versionableFields; 
     171    } 
     172 
     173    public Map<String, Object> getVersionableMutableFields() { 
     174        return versionableMutableFields; 
     175    } 
     176     
     177    public List<String> getNonVersionableFieldsToDelete() { 
     178        return nonVersionableFieldsToDelete; 
     179    } 
     180 
     181    public List<String> getVersionableFieldsToDelete() { 
     182        return versionableFieldsToDelete; 
     183    } 
     184 
     185    public List<String> getVersionableMutableFieldsToDelete() { 
     186        return versionableMutableFieldsToDelete; 
     187    } 
     188 
     189    public void addNonVersionableFieldsToDelete(List<String> fieldIds) { 
     190        this.nonVersionableFieldsToDelete .addAll(fieldIds); 
     191    } 
     192 
     193    public void addVersionableFieldsToDelete(List<String> fieldIds) { 
     194        this.versionableFieldsToDelete.addAll(fieldIds); 
     195    } 
     196 
     197    public void addVersionableMutableFieldsToDelete(List<String> fieldIds) { 
     198        this.versionableMutableFieldsToDelete.addAll(fieldIds); 
     199    } 
     200     
     201    public void removeNonVersionableFieldsToDelete(List<String> fieldIds) { 
     202        this.nonVersionableFieldsToDelete .removeAll(fieldIds); 
     203    } 
     204 
     205    public void removeVersionableFieldsToDelete(List<String> fieldIds) { 
     206        this.versionableFieldsToDelete.removeAll(fieldIds); 
     207    } 
     208 
     209    public void removeVersionableMutableFieldsToDelete(List<String> fieldIds) { 
     210        this.versionableMutableFieldsToDelete.removeAll(fieldIds); 
     211    } 
     212 
     213    public Record clone() { 
     214        RecordImpl record = new RecordImpl(); 
     215        record.id = id; 
     216        record.version = version; 
     217        record.recordTypeId = recordTypeId; 
     218        record.recordTypeVersion = recordTypeVersion; 
     219        record.nonVersionableRecordTypeId = nonVersionableRecordTypeId; 
     220        record.nonVersionableRecordTypeVersion = nonVersionableRecordTypeVersion; 
     221        record.versionableRecordTypeId = versionableRecordTypeId; 
     222        record.versionableRecordTypeVersion = versionableRecordTypeVersion; 
     223        record.versionableMutableRecordTypeId = versionableMutableRecordTypeId; 
     224        record.versionableMutableRecordTypeVersion = versionableMutableRecordTypeVersion; 
     225        record.nonVersionableFields.putAll(nonVersionableFields); 
     226        record.versionableFields.putAll(versionableFields); 
     227        record.versionableMutableFields.putAll(versionableMutableFields); 
     228        record.nonVersionableFieldsToDelete.addAll(nonVersionableFieldsToDelete); 
     229        record.versionableFieldsToDelete.addAll(versionableFieldsToDelete); 
     230        record.versionableMutableFieldsToDelete.addAll(versionableMutableFieldsToDelete); 
     231        return record; 
    117232    } 
    118233 
     
    121236        final int prime = 31; 
    122237        int result = 1; 
    123         result = prime * result + ((deleteFields == null) ? 0 : deleteFields.hashCode()); 
    124         result = prime * result + ((fields == null) ? 0 : fields.hashCode()); 
    125         result = prime * result + ((recordId == null) ? 0 : recordId.hashCode()); 
     238        result = prime * result + ((id == null) ? 0 : id.hashCode()); 
     239        result = prime * result + ((nonVersionableFields == null) ? 0 : nonVersionableFields.hashCode()); 
     240        result = prime * result 
     241                        + ((nonVersionableFieldsToDelete == null) ? 0 : nonVersionableFieldsToDelete.hashCode()); 
     242        result = prime * result + ((nonVersionableRecordTypeId == null) ? 0 : nonVersionableRecordTypeId.hashCode()); 
     243        result = prime * result 
     244                        + ((nonVersionableRecordTypeVersion == null) ? 0 : nonVersionableRecordTypeVersion.hashCode()); 
    126245        result = prime * result + ((recordTypeId == null) ? 0 : recordTypeId.hashCode()); 
    127         result = prime * result + (int) (recordTypeVersion ^ (recordTypeVersion >>> 32)); 
    128         result = prime * result + ((variantProperties == null) ? 0 : variantProperties.hashCode()); 
     246        result = prime * result + ((recordTypeVersion == null) ? 0 : recordTypeVersion.hashCode()); 
    129247        result = prime * result + ((version == null) ? 0 : version.hashCode()); 
     248        result = prime * result + ((versionableFields == null) ? 0 : versionableFields.hashCode()); 
     249        result = prime * result + ((versionableFieldsToDelete == null) ? 0 : versionableFieldsToDelete.hashCode()); 
     250        result = prime * result + ((versionableMutableFields == null) ? 0 : versionableMutableFields.hashCode()); 
     251        result = prime 
     252                        * result 
     253                        + ((versionableMutableFieldsToDelete == null) ? 0 : versionableMutableFieldsToDelete.hashCode()); 
     254        result = prime * result 
     255                        + ((versionableMutableRecordTypeId == null) ? 0 : versionableMutableRecordTypeId.hashCode()); 
     256        result = prime 
     257                        * result 
     258                        + ((versionableMutableRecordTypeVersion == null) ? 0 : versionableMutableRecordTypeVersion 
     259                                        .hashCode()); 
     260        result = prime * result + ((versionableRecordTypeId == null) ? 0 : versionableRecordTypeId.hashCode()); 
     261        result = prime * result 
     262                        + ((versionableRecordTypeVersion == null) ? 0 : versionableRecordTypeVersion.hashCode()); 
    130263        return result; 
    131264    } 
     
    140273            return false; 
    141274        RecordImpl other = (RecordImpl) obj; 
    142         if (deleteFields == null) { 
    143             if (other.deleteFields != null) 
    144                 return false; 
    145         } else if (!deleteFields.equals(other.deleteFields)) 
    146             return false; 
    147         if (fields == null) { 
    148             if (other.fields != null) 
    149                 return false; 
    150         } else if (!fields.equals(other.fields)) 
    151             return false; 
    152         if (recordId == null) { 
    153             if (other.recordId != null) 
    154                 return false; 
    155         } else if (!recordId.equals(other.recordId)) 
     275        if (id == null) { 
     276            if (other.id != null) 
     277                return false; 
     278        } else if (!id.equals(other.id)) 
     279            return false; 
     280        if (nonVersionableFields == null) { 
     281            if (other.nonVersionableFields != null) 
     282                return false; 
     283        } else if (!nonVersionableFields.equals(other.nonVersionableFields)) 
     284            return false; 
     285        if (nonVersionableFieldsToDelete == null) { 
     286            if (other.nonVersionableFieldsToDelete != null) 
     287                return false; 
     288        } else if (!nonVersionableFieldsToDelete.equals(other.nonVersionableFieldsToDelete)) 
     289            return false; 
     290        if (nonVersionableRecordTypeId == null) { 
     291            if (other.nonVersionableRecordTypeId != null) 
     292                return false; 
     293        } else if (!nonVersionableRecordTypeId.equals(other.nonVersionableRecordTypeId)) 
     294            return false; 
     295        if (nonVersionableRecordTypeVersion == null) { 
     296            if (other.nonVersionableRecordTypeVersion != null) 
     297                return false; 
     298        } else if (!nonVersionableRecordTypeVersion.equals(other.nonVersionableRecordTypeVersion)) 
    156299            return false; 
    157300        if (recordTypeId == null) { 
     
    160303        } else if (!recordTypeId.equals(other.recordTypeId)) 
    161304            return false; 
    162         if (recordTypeVersion != other.recordTypeVersion) 
    163             return false; 
    164         if (variantProperties == null) { 
    165             if (other.variantProperties != null) 
    166                 return false; 
    167         } else if (!variantProperties.equals(other.variantProperties)) 
     305        if (recordTypeVersion == null) { 
     306            if (other.recordTypeVersion != null) 
     307                return false; 
     308        } else if (!recordTypeVersion.equals(other.recordTypeVersion)) 
    168309            return false; 
    169310        if (version == null) { 
     
    172313        } else if (!version.equals(other.version)) 
    173314            return false; 
     315        if (versionableFields == null) { 
     316            if (other.versionableFields != null) 
     317                return false; 
     318        } else if (!versionableFields.equals(other.versionableFields)) 
     319            return false; 
     320        if (versionableFieldsToDelete == null) { 
     321            if (other.versionableFieldsToDelete != null) 
     322                return false; 
     323        } else if (!versionableFieldsToDelete.equals(other.versionableFieldsToDelete)) 
     324            return false; 
     325        if (versionableMutableFields == null) { 
     326            if (other.versionableMutableFields != null) 
     327                return false; 
     328        } else if (!versionableMutableFields.equals(other.versionableMutableFields)) 
     329            return false; 
     330        if (versionableMutableFieldsToDelete == null) { 
     331            if (other.versionableMutableFieldsToDelete != null) 
     332                return false; 
     333        } else if (!versionableMutableFieldsToDelete.equals(other.versionableMutableFieldsToDelete)) 
     334            return false; 
     335        if (versionableMutableRecordTypeId == null) { 
     336            if (other.versionableMutableRecordTypeId != null) 
     337                return false; 
     338        } else if (!versionableMutableRecordTypeId.equals(other.versionableMutableRecordTypeId)) 
     339            return false; 
     340        if (versionableMutableRecordTypeVersion == null) { 
     341            if (other.versionableMutableRecordTypeVersion != null) 
     342                return false; 
     343        } else if (!versionableMutableRecordTypeVersion.equals(other.versionableMutableRecordTypeVersion)) 
     344            return false; 
     345        if (versionableRecordTypeId == null) { 
     346            if (other.versionableRecordTypeId != null) 
     347                return false; 
     348        } else if (!versionableRecordTypeId.equals(other.versionableRecordTypeId)) 
     349            return false; 
     350        if (versionableRecordTypeVersion == null) { 
     351            if (other.versionableRecordTypeVersion != null) 
     352                return false; 
     353        } else if (!versionableRecordTypeVersion.equals(other.versionableRecordTypeVersion)) 
     354            return false; 
    174355        return true; 
    175356    } 
     
    177358    @Override 
    178359    public String toString() { 
    179         return "RecordImpl [recordId=" + recordId + ", version=" + version + ", recordTypeId=" + recordTypeId 
    180                         + ", recordTypeVersion=" + recordTypeVersion + ", variantProperties=" + variantProperties 
    181                         + ", fields=" + fields + ", deleteFields=" + deleteFields + "]"; 
    182     } 
    183  
    184      
    185  
     360        return "RecordImpl [id=" + id + ", version=" + version + ", recordTypeId=" + recordTypeId 
     361                        + ", recordTypeVersion=" + recordTypeVersion + ", nonVersionableRecordTypeId=" 
     362                        + nonVersionableRecordTypeId + ", nonVersionableRecordTypeVersion=" 
     363                        + nonVersionableRecordTypeVersion + ", versionableMutableRecordTypeId=" 
     364                        + versionableMutableRecordTypeId + ", versionableMutableRecordTypeVersion=" 
     365                        + versionableMutableRecordTypeVersion + ", versionableRecordTypeId=" + versionableRecordTypeId 
     366                        + ", versionableRecordTypeVersion=" + versionableRecordTypeVersion + ", nonVersionableFields=" 
     367                        + nonVersionableFields + ", nonVersionableFieldsToDelete=" + nonVersionableFieldsToDelete 
     368                        + ", versionableFields=" + versionableFields + ", versionableFieldsToDelete=" 
     369                        + versionableFieldsToDelete + ", versionableMutableFields=" + versionableMutableFields 
     370                        + ", versionableMutableFieldsToDelete=" + versionableMutableFieldsToDelete + "]"; 
     371    } 
    186372} 
  • projects/lily/trunk/repository/impl/src/main/java/org/lilycms/repository/impl/RecordTypeImpl.java

    r3943 r3977  
    1616package org.lilycms.repository.impl; 
    1717 
    18 import java.util.Collection; 
    19 import java.util.HashMap; 
    20 import java.util.Map; 
    21  
    22 import org.lilycms.repository.api.FieldDescriptor; 
    2318import org.lilycms.repository.api.RecordType; 
     19import org.lilycms.repository.api.TypeManager; 
    2420 
    2521public class RecordTypeImpl implements RecordType { 
    2622     
    27     private final String recordTypeId; 
    28     private Map<String, FieldDescriptor> fieldDescriptors = new HashMap<String, FieldDescriptor>(); 
     23    private final String id; 
    2924    private Long version; 
     25    private String nonVersionableFieldGroupId; 
     26    private String versionableFieldGroupId; 
     27    private String versionableMutableFieldGroupId; 
     28    private Long nonVersionableFieldGroupVersion; 
     29    private Long versionableFieldGroupVersion; 
     30    private Long versionableMutableFieldGroupVersion; 
    3031 
    3132    /** 
     
    3334     * @use {@link TypeManager#newRecordType} instead 
    3435     */ 
    35     public RecordTypeImpl(String recordTypeId) { 
    36         this.recordTypeId = recordTypeId; 
     36    public RecordTypeImpl(String id) { 
     37        this.id = id; 
    3738    } 
    3839     
    39     public void addFieldDescriptor(FieldDescriptor fieldDescriptor) { 
    40         fieldDescriptors.put(fieldDescriptor.getId(), fieldDescriptor); 
    41     } 
    42      
    43     public void removeFieldDescriptor(String fieldDescriptorId) { 
    44         fieldDescriptors.remove(fieldDescriptorId); 
    45     } 
    46  
    47     public FieldDescriptor getFieldDescriptor(String fieldDescriptorId) { 
    48         return fieldDescriptors.get(fieldDescriptorId); 
    49     } 
    50      
    51     public Collection<FieldDescriptor> getFieldDescriptors() { 
    52         return fieldDescriptors.values(); 
    53     } 
    54  
    5540    public String getId() { 
    56         return recordTypeId; 
     41        return id; 
    5742    } 
    5843 
     
    6449        this.version = version; 
    6550    } 
     51 
     52    public String getNonVersionableFieldGroupId() { 
     53        return nonVersionableFieldGroupId; 
     54    } 
     55 
     56    public Long getNonVersionableFieldGroupVersion() { 
     57        return nonVersionableFieldGroupVersion; 
     58    } 
     59 
     60    public String getVersionableFieldGroupId() { 
     61        return versionableFieldGroupId; 
     62    } 
     63 
     64 
     65    public Long getVersionableFieldGroupVersion() { 
     66        return versionableFieldGroupVersion; 
     67    } 
     68 
     69    public String getVersionableMutableFieldGroupId() { 
     70        return versionableMutableFieldGroupId; 
     71    } 
     72 
     73    public Long getVersionableMutableFieldGroupVersion() { 
     74        return versionableMutableFieldGroupVersion; 
     75    } 
     76 
     77    public void setNonVersionableFieldGroupId(String id) { 
     78        nonVersionableFieldGroupId = id; 
     79    } 
     80 
     81    public void setNonVersionableFieldGroupVersion(Long version) { 
     82        nonVersionableFieldGroupVersion = version; 
     83    } 
     84 
     85    public void setVersionableFieldGroupId(String id) { 
     86        versionableFieldGroupId = id; 
     87    } 
     88 
     89    public void setVersionableFieldGroupVersion(Long version) { 
     90        versionableFieldGroupVersion = version; 
     91    } 
     92 
     93    public void setVersionableMutableFieldGroupId(String id) { 
     94        versionableMutableFieldGroupId = id; 
     95    } 
     96     
     97    public void setVersionableMutableFieldGroupVersion(Long version) { 
     98        versionableMutableFieldGroupVersion = version; 
     99    } 
     100 
     101    public RecordType clone() { 
     102        RecordTypeImpl clone = new RecordTypeImpl(this.id); 
     103        clone.version = this.version; 
     104        clone.nonVersionableFieldGroupId = this.nonVersionableFieldGroupId; 
     105        clone.nonVersionableFieldGroupVersion = this.nonVersionableFieldGroupVersion; 
     106        clone.versionableFieldGroupId = this.versionableFieldGroupId; 
     107        clone.versionableFieldGroupVersion = this.versionableFieldGroupVersion; 
     108        clone.versionableMutableFieldGroupId = this.versionableMutableFieldGroupId; 
     109        clone.versionableMutableFieldGroupVersion = this.versionableMutableFieldGroupVersion; 
     110        return clone; 
     111    } 
     112 
     113    @Override 
     114    public int hashCode() { 
     115        final int prime = 31; 
     116        int result = 1; 
     117        result = prime * result + ((id == null) ? 0 : id.hashCode()); 
     118        result = prime * result + ((nonVersionableFieldGroupId == null) ? 0 : nonVersionableFieldGroupId.hashCode()); 
     119        result = prime * result 
     120                        + ((nonVersionableFieldGroupVersion == null) ? 0 : nonVersionableFieldGroupVersion.hashCode()); 
     121        result = prime * result + ((version == null) ? 0 : version.hashCode()); 
     122        result = prime * result + ((versionableFieldGroupId == null) ? 0 : versionableFieldGroupId.hashCode()); 
     123        result = prime * result 
     124                        + ((versionableFieldGroupVersion == null) ? 0 : versionableFieldGroupVersion.hashCode()); 
     125        result = prime * result 
     126                        + ((versionableMutableFieldGroupId == null) ? 0 : versionableMutableFieldGroupId.hashCode()); 
     127        result = prime 
     128                        * result 
     129                        + ((versionableMutableFieldGroupVersion == null) ? 0 : versionableMutableFieldGroupVersion 
     130                                        .hashCode()); 
     131        return result; 
     132    } 
     133 
     134    @Override 
     135    public boolean equals(Object obj) { 
     136        if (this == obj) 
     137            return true; 
     138        if (obj == null) 
     139            return false; 
     140        if (getClass() != obj.getClass()) 
     141            return false; 
     142        RecordTypeImpl other = (RecordTypeImpl) obj; 
     143        if (id == null) { 
     144            if (other.id != null) 
     145                return false; 
     146        } else if (!id.equals(other.id)) 
     147            return false; 
     148        if (nonVersionableFieldGroupId == null) { 
     149            if (other.nonVersionableFieldGroupId != null) 
     150                return false; 
     151        } else if (!nonVersionableFieldGroupId.equals(other.nonVersionableFieldGroupId)) 
     152            return false; 
     153        if (nonVersionableFieldGroupVersion == null) { 
     154            if (other.nonVersionableFieldGroupVersion != null) 
     155                return false; 
     156        } else if (!nonVersionableFieldGroupVersion.equals(other.nonVersionableFieldGroupVersion)) 
     157            return false; 
     158        if (version == null) { 
     159            if (other.version != null) 
     160                return false; 
     161        } else if (!version.equals(other.version)) 
     162            return false; 
     163        if (versionableFieldGroupId == null) { 
     164            if (other.versionableFieldGroupId != null) 
     165                return false; 
     166        } else if (!versionableFieldGroupId.equals(other.versionableFieldGroupId)) 
     167            return false; 
     168        if (versionableFieldGroupVersion == null) { 
     169            if (other.versionableFieldGroupVersion != null) 
     170                return false; 
     171        } else if (!versionableFieldGroupVersion.equals(other.versionableFieldGroupVersion)) 
     172            return false; 
     173        if (versionableMutableFieldGroupId == null) { 
     174            if (other.versionableMutableFieldGroupId != null) 
     175                return false; 
     176        } else if (!versionableMutableFieldGroupId.equals(other.versionableMutableFieldGroupId)) 
     177            return false; 
     178        if (versionableMutableFieldGroupVersion == null) { 
     179            if (other.versionableMutableFieldGroupVersion != null) 
     180                return false; 
     181        } else if (!versionableMutableFieldGroupVersion.equals(other.versionableMutableFieldGroupVersion)) 
     182            return false; 
     183        return true; 
     184    } 
     185 
     186    @Override 
     187    public String toString() { 
     188        return "RecordTypeImpl [id=" + id + ", version=" + version + ", nonVersionableFieldGroupId=" 
     189                        + nonVersionableFieldGroupId + ", nonVersionableFieldGroupVersion=" 
     190                        + nonVersionableFieldGroupVersion + ", versionableFieldGroupId=" + versionableFieldGroupId 
     191                        + ", versionableFieldGroupVersion=" + versionableFieldGroupVersion 
     192                        + ", versionableMutableFieldGroupId=" + versionableMutableFieldGroupId 
     193                        + ", versionableMutableFieldGroupVersion=" + versionableMutableFieldGroupVersion + "]"; 
     194    } 
    66195} 
  • projects/lily/trunk/repository/impl/src/test/java/org/lilycms/repository/impl/test/HBaseRepositoryTest.java

    r3955 r3977  
    1616package org.lilycms.repository.impl.test; 
    1717 
    18 import static org.easymock.EasyMock.anyLong; 
    19 import static org.easymock.EasyMock.expect; 
    20 import static org.easymock.EasyMock.isA; 
    21 import static org.easymock.classextension.EasyMock.createControl; 
    22 import static org.junit.Assert.assertEquals; 
    23 import static org.junit.Assert.fail; 
    24  
     18import static org.junit.Assert.*; 
     19 
     20import java.util.Arrays; 
    2521import java.util.HashMap; 
    2622import java.util.Map; 
    2723 
    2824import org.apache.hadoop.hbase.HBaseTestingUtility; 
    29 import org.easymock.classextension.IMocksControl; 
    3025import org.junit.After; 
    3126import org.junit.AfterClass; 
     
    3429import org.junit.Test; 
    3530import org.lilycms.repository.api.FieldDescriptor; 
     31import org.lilycms.repository.api.FieldGroup; 
    3632import org.lilycms.repository.api.FieldNotFoundException; 
    3733import org.lilycms.repository.api.IdGenerator; 
     
    4238import org.lilycms.repository.api.RecordNotFoundException; 
    4339import org.lilycms.repository.api.RecordType; 
     40import org.lilycms.repository.api.RecordTypeNotFoundException; 
    4441import org.lilycms.repository.api.Repository; 
    4542import org.lilycms.repository.api.TypeManager; 
    46 import org.lilycms.repository.impl.FieldDescriptorImpl; 
    4743import org.lilycms.repository.impl.HBaseRepository; 
     44import org.lilycms.repository.impl.HBaseTypeManager; 
    4845import org.lilycms.repository.impl.IdGeneratorImpl; 
    49 import org.lilycms.repository.impl.RecordImpl; 
    50 import org.lilycms.repository.impl.StringValueType; 
    51 import org.lilycms.repository.impl.ValueTypeImpl; 
    5246import org.lilycms.testfw.TestHelper; 
    5347 
     
    5549 
    5650    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 
     51    private static IdGenerator idGenerator = new IdGeneratorImpl(); 
     52    private static TypeManager typeManager; 
     53    private static Repository repository; 
     54    private static FieldDescriptor fieldDescriptor1; 
     55    private static FieldDescriptor fieldDescriptor1B; 
     56    private static FieldDescriptor fieldDescriptor2; 
     57    private static FieldDescriptor fieldDescriptor3; 
     58    private static FieldGroup fieldGroup1; 
     59    private static FieldGroup fieldGroup1B; 
     60    private static FieldGroup fieldGroup2; 
     61    private static FieldGroup fieldGroup3; 
     62    private static RecordType recordType1; 
     63    private static RecordType recordType1B; 
     64    private static RecordType recordType2; 
    5765 
    5866    @BeforeClass 
     
    6068        TestHelper.setupLogging(); 
    6169        TEST_UTIL.startMiniCluster(1); 
     70        typeManager = new HBaseTypeManager(idGenerator, TEST_UTIL.getConfiguration()); 
     71        repository = new HBaseRepository(typeManager, idGenerator, TEST_UTIL.getConfiguration()); 
     72        setupTypes(); 
     73    } 
     74 
     75    private static void setupTypes() throws Exception { 
     76        setupFieldDescriptors(); 
     77        setupFieldGroups(); 
     78        setupRecordTypes(); 
     79    } 
     80 
     81    private static void setupFieldDescriptors() throws Exception { 
     82        fieldDescriptor1 = typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("FD1", typeManager.getValueType("STRING", false, false), "GN1")); 
     83        fieldDescriptor1B = typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("FD1B", typeManager.getValueType("STRING", false, false), "GN1B")); 
     84        fieldDescriptor2 = typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("FD2", typeManager.getValueType("INTEGER", false, false), "GN2")); 
     85        fieldDescriptor3 = typeManager.createFieldDescriptor(typeManager.newFieldDescriptor("FD3", typeManager.getValueType("BOOLEAN", false, false), "GN3")); 
     86    } 
     87 
     88    private static void setupFieldGroups() throws Exception { 
     89        FieldGroup fieldGroup = typeManager.newFieldGroup("FG1"); 
     90        fieldGroup.setFieldGroupEntry(typeManager.newFieldGroupEntry(fieldDescriptor1.getId(), fieldDescriptor1.getVersion(), false, "alias1")); 
     91        fieldGroup1 = typeManager.createFieldGroup(fieldGroup); 
     92        fieldGroup = typeManager.newFieldGroup("FG1"); 
     93        fieldGroup.setFieldGroupEntry(typeManager.newFieldGroupEntry(fieldDescriptor1B.getId(), fieldDescriptor1B.getVersion(), false, "alias1B")); 
     94        fieldGroup1B = typeManager.updateFieldGroup(fieldGroup); 
     95         
     96        fieldGroup = typeManager.newFieldGroup("FG2"); 
     97        fieldGroup.setFieldGroupEntry(typeManager.newFieldGroupEntry(fieldDescriptor2.getId(), fieldDescriptor2.getVersion(), false, "alias2")); 
     98        fieldGroup2 = typeManager.createFieldGroup(fieldGroup); 
     99        fieldGroup = typeManager.newFieldGroup("FG3"); 
     100        fieldGroup.setFieldGroupEntry(typeManager.newFieldGroupEntry(fieldDescriptor3.getId(), fieldDescriptor3.getVersion(), false, "alias3")); 
     101        fieldGroup3 = typeManager.createFieldGroup(fieldGroup); 
     102    } 
     103     
     104    private static void setupRecordTypes() throws Exception { 
     105        recordType1 = typeManager.newRecordType("RT1"); 
     106        recordType1.setNonVersionableFieldGroupId(fieldGroup1.getId()); 
     107        recordType1.setNonVersionableFieldGroupVersion(fieldGroup1.getVersion()); 
     108        recordType1.setVersionableFieldGroupId(fieldGroup2.getId()); 
     109        recordType1.setVersionableFieldGroupVersion(fieldGroup2.getVersion()); 
     110        recordType1.setVersionableMutableFieldGroupId(fieldGroup3.getId()); 
     111        recordType1.setVersionableMutableFieldGroupVersion(fieldGroup3.getVersion()); 
     112        recordType1 = typeManager.createRecordType(recordType1); 
     113         
     114        recordType1B = recordType1.clone(); 
     115        recordType1B.setNonVersionableFieldGroupId(fieldGroup1B.getId()); 
     116        recordType1B.setNonVersionableFieldGroupVersion(fieldGroup1B.getVersion()); 
     117        recordType1B = typeManager.updateRecordType(recordType1B); 
     118         
     119        recordType2 = typeManager.newRecordType("RT2"); 
     120        recordType2.setNonVersionableFieldGroupId(fieldGroup2.getId()); 
     121        recordType2.setNonVersionableFieldGroupVersion(fieldGroup2.getVersion()); 
     122        recordType2.setVersionableFieldGroupId(fieldGroup3.getId()); 
     123        recordType2.setVersionableFieldGroupVersion(fieldGroup3.getVersion()); 
     124        recordType2.setVersionableMutableFieldGroupId(fieldGroup1.getId()); 
     125        recordType2.setVersionableMutableFieldGroupVersion(fieldGroup1.getVersion()); 
     126        recordType2 = typeManager.createRecordType(recordType2); 
    62127    } 
    63128 
     
    67132    } 
    68133 
    69     private Repository repository; 
    70     private IMocksControl control; 
    71     private TypeManager typeManager; 
    72     private RecordType recordType; 
    73     private FieldDescriptor versionableFieldDescriptor; 
    74     private FieldDescriptor nonVersionableFieldDescriptor; 
    75     private IdGenerator idGenerator; 
    76  
    77134    @Before 
    78135    public void setUp() throws Exception { 
    79         control = createControl(); 
    80         typeManager = control.createMock(TypeManager.class); 
    81         expect(typeManager.getValueType("STRING", false, false)).andReturn(new ValueTypeImpl(new StringValueType(), false, false)).anyTimes(); 
    82         recordType = control.createMock(RecordType.class); 
    83         expect(typeManager.getRecordType(isA(String.class), anyLong())).andReturn(recordType).anyTimes(); 
    84         expect(typeManager.getRecordType(isA(String.class))).andReturn(recordType).anyTimes(); 
    85         expect(recordType.getId()).andReturn("dummyRecordType").anyTimes(); 
    86         expect(recordType.getVersion()).andReturn(Long.valueOf(0)).anyTimes(); 
    87          
    88         // Using FieldDescriptorImpl since typeManager is a mock 
    89         versionableFieldDescriptor = new FieldDescriptorImpl("aVersionableFieldDescriptor", Long.valueOf(1), new ValueTypeImpl(new StringValueType(), false, false), true, true); 
    90         nonVersionableFieldDescriptor = new FieldDescriptorImpl("aNonVersionableFieldDescriptor", Long.valueOf(1), new ValueTypeImpl(new StringValueType(), false, false), true, false); 
    91  
    92         idGenerator = new IdGeneratorImpl(); 
    93         repository = new HBaseRepository(typeManager, idGenerator, RecordImpl.class, TEST_UTIL.getConfiguration()); 
    94136    } 
    95137 
    96138    @After 
    97139    public void tearDown() throws Exception { 
    98         control.reset(); 
    99     } 
    100  
    101     @Test 
    102     public void testEmptyRecord() throws Exception { 
    103         control.replay(); 
    104         Record record = generateRecord(); 
    105  
     140    } 
     141 
     142    @Test 
     143    public void testRecordCreateWithoutRecordType() throws Exception { 
     144        Record record = repository.newRecord(idGenerator.newRecordId()); 
     145        try { 
     146            record = repository.create(record); 
     147        } catch (InvalidRecordException expected){ 
     148        } 
     149    } 
     150 
     151    @Test 
     152    public void testRecordUpdateWithoutRecordType() throws Exception { 
     153        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     154        Record updateRecord = repository.newRecord(record.getId()); 
     155        try { 
     156            record = repository.update(updateRecord); 
     157        } catch (InvalidRecordException expected){ 
     158        } 
     159    } 
     160 
     161    @Test 
     162    public void testEmptyRecordCreate() throws Exception { 
     163        Record record = repository.newRecord(idGenerator.newRecordId()); 
     164        record.setRecordType(recordType1.getId(), null); 
     165        try { 
     166            record = repository.create(record); 
     167        } catch (InvalidRecordException expected){ 
     168        } 
     169    } 
     170     
     171    @Test 
     172    public void testCreate() throws Exception { 
     173        RecordId recordId = idGenerator.newRecordId(); 
     174        Record createdRecord = createDefaultRecord(recordId); 
     175         
     176        assertEquals(Long.valueOf(1), createdRecord.getVersion()); 
     177        assertEquals("value1", createdRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     178        assertEquals(123, createdRecord.getVersionableField(fieldDescriptor2.getId())); 
     179        assertTrue((Boolean)createdRecord.getVersionableMutableField(fieldDescriptor3.getId())); 
     180        assertEquals(recordType1.getId(), createdRecord.getRecordTypeId()); 
     181        assertEquals(Long.valueOf(1), createdRecord.getRecordTypeVersion()); 
     182        assertEquals(recordType1.getId(), createdRecord.getNonVersionableRecordTypeId()); 
     183        assertEquals(Long.valueOf(1), createdRecord.getNonVersionableRecordTypeVersion()); 
     184        assertEquals(recordType1.getId(), createdRecord.getVersionableRecordTypeId()); 
     185        assertEquals(Long.valueOf(1), createdRecord.getVersionableRecordTypeVersion()); 
     186        assertEquals(recordType1.getId(), createdRecord.getVersionableMutableRecordTypeId()); 
     187        assertEquals(Long.valueOf(1), createdRecord.getVersionableMutableRecordTypeVersion()); 
     188         
     189        assertEquals(createdRecord, repository.read(recordId)); 
     190    } 
     191     
     192    private Record createDefaultRecord(RecordId recordId) throws Exception { 
     193        Record record = repository.newRecord(recordId); 
     194        record.setRecordType(recordType1.getId(), recordType1.getVersion()); 
     195        record.setNonVersionableField(fieldDescriptor1.getId(), "value1"); 
     196        record.setVersionableField(fieldDescriptor2.getId(), 123); 
     197        record.setVersionableMutableField(fieldDescriptor3.getId(), true); 
     198        return repository.create(record); 
     199    } 
     200     
     201    @Test  
     202    public void testCreateExistingRecordFails() throws Exception { 
     203        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     204         
    106205        try { 
    107206            repository.create(record); 
    108             fail("A record should at least have some fields"); 
    109         } catch (InvalidRecordException expected) { 
    110         } 
    111         control.verify(); 
    112     } 
    113      
    114     @Test 
    115     public void testEmptyUpdateRecord() throws Exception { 
    116         expect(recordType.getFieldDescriptor("aField")).andReturn(versionableFieldDescriptor); 
    117          
    118         control.replay(); 
    119         Record record = generateRecord(new String[]{"aField" , "aValue", "false"}); 
    120         repository.create(record); 
    121          
    122         Record emptyRecord = repository.newRecord(record.getId()); 
    123  
    124         try { 
    125             repository.update(emptyRecord); 
    126             fail("A record should at least have some fields"); 
    127         } catch (Exception expected) { 
    128         } 
    129         control.verify(); 
    130     } 
    131  
    132     @Test 
    133     public void testNonVersionedRecord() throws Exception { 
    134         expect(recordType.getFieldDescriptor("aField")).andReturn(nonVersionableFieldDescriptor).times(2); 
    135          
    136         control.replay(); 
    137         Record record = generateRecord(new String[] { "aField", "aValue"}); 
    138         repository.create(record); 
    139         Record actualRecord = repository.read(record.getId()); 
    140         assertEquals(record, actualRecord); 
    141         control.verify(); 
    142     } 
    143  
    144     @Test 
    145     public void testMultipleFields() throws Exception { 
    146         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).times(4); 
    147  
    148         control.replay(); 
    149         Record record = generateRecord(new String[] { "aField", "aValue"}, new String[] {"aField2", "aValue2"}); 
    150         repository.create(record); 
    151         Record actualRecord = repository.read(record.getId()); 
    152         assertEquals(record, actualRecord); 
    153         control.verify(); 
    154     } 
    155  
    156     @Test 
    157     public void testCreateExistingRecord() throws Exception { 
    158         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor); 
    159  
    160         control.replay(); 
    161         Record record = generateRecord(new String[] { "aField", "aValue"}); 
    162         repository.create(record); 
     207            fail(); 
     208        } catch (RecordExistsException expected) { 
     209        } 
     210    } 
     211     
     212    @Test 
     213    public void testCreateWithNonExistingRecordTypeFails() throws Exception { 
     214        Record record = repository.newRecord(idGenerator.newRecordId()); 
     215        record.setRecordType("nonExistingRecordType", null); 
     216        record.setNonVersionableField(fieldDescriptor1.getId(), "value1"); 
    163217        try { 
    164218            repository.create(record); 
    165             fail("Create of an existing record is not allowed"); 
    166         } catch (RecordExistsException expected) { 
    167         } 
    168         control.verify(); 
    169     } 
    170  
    171     @Test 
    172     public void testUpdateNonExistingRecord() throws Exception { 
    173         control.replay(); 
    174         Record record = repository.newRecord(idGenerator.newRecordId("nonExistingRecordId")); 
    175  
    176         try { 
    177             repository.update(record); 
    178             fail("Cannot update a non-existing document"); 
     219            fail(); 
     220        } catch (RecordTypeNotFoundException expected) { 
     221        } 
     222    } 
     223     
     224    @Test 
     225    public void testCreateUsesLatestRecordType() throws Exception { 
     226        Record record = repository.newRecord(idGenerator.newRecordId()); 
     227        record.setRecordType(recordType1.getId(), null); 
     228        record.setNonVersionableField(fieldDescriptor1.getId(), "value1"); 
     229        Record createdRecord = repository.create(record); 
     230        assertEquals(recordType1.getId(), createdRecord.getRecordTypeId()); 
     231        assertEquals(Long.valueOf(2), createdRecord.getRecordTypeVersion()); 
     232        assertEquals(recordType1.getId(), createdRecord.getNonVersionableRecordTypeId()); 
     233        assertEquals(Long.valueOf(2), createdRecord.getNonVersionableRecordTypeVersion()); 
     234        assertNull(createdRecord.getVersionableRecordTypeId()); 
     235        assertNull(createdRecord.getVersionableRecordTypeVersion()); 
     236        assertNull(createdRecord.getVersionableMutableRecordTypeId()); 
     237        assertNull(createdRecord.getVersionableMutableRecordTypeVersion()); 
     238         
     239        assertEquals(createdRecord, repository.read(record.getId())); 
     240    } 
     241     
     242    @Test 
     243    public void testCreateVariant() throws Exception { 
     244        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     245         
     246        Map<String, String> variantProperties = new HashMap<String, String>(); 
     247        variantProperties.put("dimension1", "dimval1"); 
     248        Record variant = repository.newRecord(idGenerator.newRecordId(record.getId(), variantProperties )); 
     249        variant.setRecordType(recordType1.getId(), null); 
     250        variant.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     251        variant.setVersionableField(fieldDescriptor2.getId(), 567); 
     252        variant.setVersionableMutableField(fieldDescriptor3.getId(), false); 
     253         
     254        Record createdVariant = repository.create(variant); 
     255         
     256        assertEquals(Long.valueOf(1), createdVariant.getVersion()); 
     257        assertEquals("value2", createdVariant.getNonVersionableField(fieldDescriptor1.getId())); 
     258        assertEquals(567, createdVariant.getVersionableField(fieldDescriptor2.getId())); 
     259        assertFalse((Boolean)createdVariant.getVersionableMutableField(fieldDescriptor3.getId())); 
     260         
     261        assertEquals(createdVariant, repository.read(variant.getId())); 
     262    } 
     263     
     264    @Test 
     265    public void testUpdate() throws Exception { 
     266        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     267        Record updateRecord = record.clone(); 
     268        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     269        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     270        updateRecord.setVersionableMutableField(fieldDescriptor3.getId(), false); 
     271         
     272        Record updatedRecord = repository.update(updateRecord); 
     273         
     274        assertEquals(Long.valueOf(2), updatedRecord.getVersion()); 
     275        assertEquals("value2", updatedRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     276        assertEquals(789, updatedRecord.getVersionableField(fieldDescriptor2.getId())); 
     277        assertEquals(false, updatedRecord.getVersionableMutableField(fieldDescriptor3.getId())); 
     278         
     279        assertEquals(updatedRecord, repository.read(record.getId())); 
     280    } 
     281     
     282    @Test 
     283    public void testUpdateOnlyOneField() throws Exception { 
     284        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     285        Record updateRecord = repository.newRecord(record.getId()); 
     286        updateRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     287        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     288         
     289        Record updatedRecord = repository.update(updateRecord); 
     290         
     291        assertEquals(Long.valueOf(1), updatedRecord.getVersion()); 
     292        assertEquals("value2", updatedRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     293        try { 
     294            updatedRecord.getVersionableField(fieldDescriptor2.getId()); 
     295            fail(); 
     296        } catch (FieldNotFoundException expected) { 
     297        } 
     298        try { 
     299            updatedRecord.getVersionableMutableField(fieldDescriptor3.getId()); 
     300            fail(); 
     301        } catch (FieldNotFoundException expected) { 
     302        } 
     303         
     304        updatedRecord = repository.read(record.getId()); 
     305        assertEquals("value2", updatedRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     306        assertEquals(123, updatedRecord.getVersionableField(fieldDescriptor2.getId())); 
     307        assertEquals(true, updatedRecord.getVersionableMutableField(fieldDescriptor3.getId())); 
     308    } 
     309     
     310    @Test 
     311    public void testEmptyUpdate() throws Exception { 
     312        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     313        Record updateRecord = repository.newRecord(record.getId()); 
     314        updateRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     315         
     316        Record updatedRecord = repository.update(updateRecord); 
     317         
     318        assertEquals(Long.valueOf(1), updatedRecord.getVersion()); 
     319        try { 
     320            updatedRecord.getNonVersionableField(fieldDescriptor1.getId()); 
     321            fail(); 
     322        } catch (FieldNotFoundException expected) { 
     323        } 
     324        try { 
     325            updatedRecord.getVersionableField(fieldDescriptor2.getId()); 
     326            fail(); 
     327        } catch (FieldNotFoundException expected) { 
     328        } 
     329        try { 
     330            updatedRecord.getVersionableMutableField(fieldDescriptor3.getId()); 
     331            fail(); 
     332        } catch (FieldNotFoundException expected) { 
     333        } 
     334         
     335        assertEquals(record, repository.read(record.getId())); 
     336    } 
     337     
     338    @Test 
     339    public void testIdempotentUpdate() throws Exception { 
     340        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     341        Record updateRecord = record.clone(); 
     342         
     343        Record updatedRecord = repository.update(updateRecord); 
     344         
     345        assertEquals(Long.valueOf(1), updatedRecord.getVersion()); 
     346        assertEquals("value1", updatedRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     347        assertEquals(123, updatedRecord.getVersionableField(fieldDescriptor2.getId())); 
     348        assertEquals(true, updatedRecord.getVersionableMutableField(fieldDescriptor3.getId())); 
     349         
     350        assertEquals(record, repository.read(record.getId())); 
     351    } 
     352     
     353    @Test 
     354    public void testUpdateIgnoresVersion() throws Exception { 
     355        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     356        Record updateRecord = record.clone(); 
     357        updateRecord.setVersion(Long.valueOf(99)); 
     358        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     359         
     360        Record updatedRecord = repository.update(updateRecord); 
     361         
     362        assertEquals(Long.valueOf(1), updatedRecord.getVersion()); 
     363         
     364        assertEquals(updatedRecord, repository.read(record.getId())); 
     365    } 
     366     
     367    @Test 
     368    public void testUpdateNonVersionable() throws Exception { 
     369        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     370        Record updateRecord = repository.newRecord(record.getId()); 
     371        updateRecord.setRecordType(record.getRecordTypeId(), null); 
     372        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "aNewValue"); 
     373        repository.update(updateRecord); 
     374         
     375        Record readRecord = repository.read(record.getId()); 
     376        assertEquals(Long.valueOf(1), readRecord.getVersion()); 
     377        assertEquals("aNewValue", readRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     378    } 
     379     
     380     
     381    @Test 
     382    public void testReadOlderVersions() throws Exception { 
     383        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     384        Record updateRecord = record.clone(); 
     385        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     386        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     387        updateRecord.setVersionableMutableField(fieldDescriptor3.getId(), false); 
     388         
     389        repository.update(updateRecord); 
     390         
     391        record.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     392        assertEquals(record, repository.read(record.getId(), Long.valueOf(1))); 
     393    } 
     394     
     395    @Test 
     396    public void testReadNonExistingRecord() throws Exception { 
     397        try { 
     398            repository.read(idGenerator.newRecordId()); 
     399            fail(); 
    179400        } catch (RecordNotFoundException expected) { 
    180401        } 
    181         control.verify(); 
    182     } 
    183  
    184     @Test 
    185     public void testUpdateRecord() throws Exception { 
    186         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).times(3); 
    187  
    188         control.replay(); 
    189         Record record = generateRecord(new String[] { "aField", "aValue"}); 
    190         repository.create(record); 
    191  
    192         record.setField("aField", "anotherValue"); 
    193         repository.update(record); 
    194  
    195         assertEquals(record, repository.read(record.getId())); 
    196         control.verify(); 
    197     } 
    198  
    199     @Test 
    200     public void testUpdateRecordWithExtraField() throws Exception { 
    201         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    202  
    203         control.replay(); 
    204         Record record = generateRecord(new String[] { "aField", "aValue"}); 
    205         repository.create(record); 
    206  
    207         // TODO avoid updates of non-changed fields 
    208         record.setField("anotherField", "anotherValue"); 
    209         repository.update(record); 
    210         Record actualRecord = repository.read(record.getId()); 
    211         assertEquals(2, record.getFields().size()); 
    212         assertEquals(record, actualRecord); 
    213         control.verify(); 
    214     } 
    215  
    216     @Test 
    217     public void testReadNonExistingRecord() throws Exception { 
    218         control.replay(); 
    219         try { 
    220             repository.read(idGenerator.newRecordId("readNonExistingRecordId")); 
    221             fail("A RecordNotFoundException should be thrown"); 
     402    } 
     403 
     404    @Test 
     405    public void testReadTooRecentRecord() throws Exception { 
     406        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     407        try { 
     408            repository.read(record.getId(), Long.valueOf(2)); 
     409            fail(); 
    222410        } catch (RecordNotFoundException expected) { 
    223411        } 
    224         control.verify(); 
    225     } 
    226  
    227     @Test 
    228     public void testReadAll() throws Exception { 
    229         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    230  
    231         control.replay(); 
    232         Record record = generateRecord(new String[] { "field1", "value1"}, new String[] { "field2", 
    233                 "value2"}, new String[] { "field3", "value3"}); 
    234         repository.create(record); 
    235         Record actualRecord = repository.read(record.getId()); 
    236         assertEquals(record, actualRecord); 
    237         assertEquals(record.getId(), actualRecord.getId()); 
    238         assertEquals(3, actualRecord.getFields().size()); 
    239         assertEquals("value1", actualRecord.getField("field1")); 
    240         assertEquals("value2", actualRecord.getField("field2")); 
    241         assertEquals("value3", actualRecord.getField("field3")); 
    242         control.verify(); 
    243     } 
    244  
    245     @Test 
    246     public void testRecordDelete() throws Exception { 
    247         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    248  
    249         control.replay(); 
    250         Record record = generateRecord(new String[] { "field1", "value1"}, new String[] { "field2","value2"}, new String[] { "field3", "value3"}); 
    251         repository.create(record); 
    252         RecordId recordId = record.getId(); 
    253         Record actualRecord = repository.read(recordId); 
    254         assertEquals(record, actualRecord); 
    255         repository.delete(recordId); 
    256         try { 
    257             repository.read(recordId); 
    258             fail("A RecordNotFoundException should be thrown"); 
    259         } catch(RecordNotFoundException expected) { 
    260         } 
    261         control.verify(); 
    262     } 
    263  
    264     @Test 
    265     public void testCreateVersionableAndNonVersionable() throws Exception { 
    266         expect(recordType.getFieldDescriptor("field1")).andReturn(nonVersionableFieldDescriptor).times(2); 
    267         expect(recordType.getFieldDescriptor("field2")).andReturn(versionableFieldDescriptor).times(2); 
    268  
    269         control.replay(); 
    270         Record record = generateRecord(new String[] { "field1", "value1",}, new String[] { "field2","value2"}); 
    271         repository.create(record); 
    272         Record actualRecord = repository.read(record.getId()); 
    273         assertEquals(record, actualRecord); 
    274         control.verify(); 
    275     } 
    276  
    277     @Test 
    278     public void testReadVersionableAndNonVersionableField() throws Exception { 
    279         expect(recordType.getFieldDescriptor("field1")).andReturn(nonVersionableFieldDescriptor).times(3); 
    280         expect(recordType.getFieldDescriptor("field2")).andReturn(versionableFieldDescriptor).times(3); 
    281  
    282         control.replay(); 
    283         Record record = generateRecord(new String[] { "field1", "value1"}, new String[] { "field2", 
    284                 "value2"}); 
    285         repository.create(record); 
    286         Record actualRecord = repository.read(record.getId(), "field1", "field2"); 
    287         assertEquals(record, actualRecord); 
    288         control.verify(); 
    289     } 
    290  
    291     @Test 
    292     public void testVersionableField() throws Exception { 
    293         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    294  
    295         control.replay(); 
    296         Record record = generateRecord(new String[] { "versionableField1", "value1"}); 
    297         repository.create(record); 
    298         record.setField("versionableField1", "value2"); 
    299         repository.update(record); 
    300  
    301         Record actualRecord = repository.read(record.getId()); 
    302         assertEquals("value2", actualRecord.getField("versionableField1")); 
    303         assertEquals(Long.valueOf(2), actualRecord.getVersion()); 
    304          
    305         actualRecord = repository.read(record.getId(), Long.valueOf(1)); 
    306         assertEquals("value1", actualRecord.getField("versionableField1")); 
    307         assertEquals(Long.valueOf(1), actualRecord.getVersion()); 
    308         control.verify(); 
    309     } 
    310  
    311     @Test 
    312     public void testVersionableFields() throws Exception { 
    313         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    314          
    315         control.replay(); 
    316         Record record = generateRecord( 
    317                         new String[] { "versionableField1", "f1value1"}, 
    318                         new String[] { "versionableField2", "f2value1"},  
    319                         new String[] { "versionableField3", "f3value1"}); 
    320         repository.create(record); 
    321         Record update1 = generateRecord( 
    322                         new String[] { "versionableField1", "f1value2"},  
    323                         new String[] { "versionableField2", "f2value2"}); 
    324         RecordId recordId = record.getId(); 
    325         update1.setId(recordId); 
    326         repository.update(update1); 
    327          
    328         Record update2 = generateRecord(new String[] { "versionableField1", "f1value3"}); 
    329         update2.setId(recordId); 
    330         repository.update(update2); 
    331          
    332         Record expectedRecord = generateRecord( 
    333                         new String[] { "versionableField1", "f1value3"}, 
    334                         new String[] { "versionableField2", "f2value2"},  
    335                         new String[] { "versionableField3", "f3value1"}); 
    336         expectedRecord.setId(recordId); 
    337         expectedRecord.setVersion(Long.valueOf(3)); 
    338         Record actualRecord = repository.read(recordId); 
    339         assertEquals(expectedRecord, actualRecord); 
    340          
    341         actualRecord = repository.read(recordId, Long.valueOf(3)); 
    342         assertEquals(expectedRecord, actualRecord); 
    343          
    344         expectedRecord = generateRecord( 
    345                         new String[] { "versionableField1", "f1value2"}, 
    346                         new String[] { "versionableField2", "f2value2"},  
    347                         new String[] { "versionableField3", "f3value1"}); 
    348         expectedRecord.setId(recordId); 
    349         expectedRecord.setVersion(Long.valueOf(2)); 
    350         actualRecord = repository.read(recordId, Long.valueOf(2)); 
    351         assertEquals(expectedRecord, actualRecord); 
    352          
    353         expectedRecord = generateRecord( 
    354                         new String[] { "versionableField1", "f1value1"}, 
    355                         new String[] { "versionableField2", "f2value1"},  
    356                         new String[] { "versionableField3", "f3value1"}); 
    357         expectedRecord.setId(recordId); 
    358         expectedRecord.setVersion(Long.valueOf(1)); 
    359         actualRecord = repository.read(recordId, Long.valueOf(1)); 
    360         assertEquals(expectedRecord, actualRecord); 
    361         control.verify(); 
    362     } 
    363      
    364     @Test 
    365     public void testReadNonExistingversion() throws Exception { 
    366         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    367          
    368         control.replay(); 
    369         Record record = generateRecord(new String[] { "versionableField1", "f1value1"}); 
    370         repository.create(record); 
    371         record.setField("versionableField1", "f1value2"); 
    372         repository.update(record); 
    373         try { 
    374             repository.read(record.getId(), Long.valueOf(3)); 
    375         } catch(RecordNotFoundException expected) { 
    376             assertEquals(record.getId(), expected.getRecord().getId()); 
    377             assertEquals(Long.valueOf(3), expected.getRecord().getVersion()); 
    378         } 
    379         control.verify(); 
    380412    } 
    381413     
    382414    @Test 
    383415    public void testReadSpecificFields() throws Exception { 
    384         expect(recordType.getFieldDescriptor("field1")).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    385         expect(recordType.getFieldDescriptor("field2")).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    386         expect(recordType.getFieldDescriptor("field3")).andReturn(versionableFieldDescriptor).anyTimes(); 
    387      
    388         control.replay(); 
    389         Record record = generateRecord(new String[] { "field1", "value1"}, new String[] { "field2", 
    390                 "value2"}, new String[] { "field3", "value3"}); 
    391         repository.create(record); 
    392         Record actualRecord = repository.read(record.getId(), "field1", "field3"); 
    393         assertEquals(record.getId(), actualRecord.getId()); 
    394         assertEquals("value1", actualRecord.getField("field1")); 
    395         try { 
    396             actualRecord.getField("field2"); 
    397             fail("An exception should be thrown because the record does not contain the requested field"); 
    398         } catch (FieldNotFoundException expected) { 
    399         } 
    400         assertEquals("value3", actualRecord.getField("field3")); 
    401         control.verify(); 
    402     } 
    403      
    404     @Test 
    405     public void testDeleteANonVersionableField() throws Exception { 
    406         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(nonVersionableFieldDescriptor).anyTimes(); 
    407  
    408         control.replay(); 
    409         Record record = generateRecord(new String[] {"aField", "f1"}); 
    410         repository.create(record); 
     416        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     417        Record readRecord = repository.read(record.getId(), Arrays.asList(new String[]{fieldDescriptor1.getId()}), Arrays.asList(new String[]{fieldDescriptor2.getId()}), Arrays.asList(new String[]{fieldDescriptor3.getId()})); 
     418        assertEquals(repository.read(record.getId()), readRecord); 
     419    } 
     420     
     421     
     422    @Test 
     423    public void testUpdateWithNewRecordTypeVersion() throws Exception { 
     424        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     425        Record updateRecord = repository.newRecord(record.getId()); 
     426        updateRecord.setRecordType(recordType1B.getId(), recordType1B.getVersion()); 
     427        updateRecord.setNonVersionableField(fieldDescriptor1.getId(), "value2"); 
     428        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     429        updateRecord.setVersionableMutableField(fieldDescriptor3.getId(), false); 
     430         
     431        Record updatedRecord = repository.update(updateRecord); 
     432        assertEquals(recordType1B.getId(),updatedRecord.getRecordTypeId()); 
     433        assertEquals(recordType1B.getVersion(),updatedRecord.getRecordTypeVersion()); 
     434        assertEquals(recordType1B.getId(),updatedRecord.getNonVersionableRecordTypeId()); 
     435        assertEquals(recordType1B.getVersion(),updatedRecord.getNonVersionableRecordTypeVersion()); 
     436        assertEquals(recordType1B.getId(),updatedRecord.getVersionableRecordTypeId()); 
     437        assertEquals(recordType1B.getVersion(),updatedRecord.getVersionableRecordTypeVersion()); 
     438        assertEquals(recordType1B.getId(),updatedRecord.getVersionableMutableRecordTypeId()); 
     439        assertEquals(recordType1B.getVersion(),updatedRecord.getVersionableMutableRecordTypeVersion()); 
     440         
     441        Record recordV1 = repository.read(record.getId(), Long.valueOf(1)); 
     442        assertEquals(recordType1B.getId(),recordV1.getRecordTypeId()); 
     443        assertEquals(recordType1B.getVersion(),recordV1.getRecordTypeVersion()); 
     444        assertEquals(recordType1B.getId(),recordV1.getNonVersionableRecordTypeId()); 
     445        assertEquals(recordType1B.getVersion(),recordV1.getNonVersionableRecordTypeVersion()); 
     446        assertEquals(recordType1.getId(),recordV1.getVersionableRecordTypeId()); 
     447        assertEquals(recordType1.getVersion(),recordV1.getVersionableRecordTypeVersion()); 
     448        assertEquals(recordType1.getId(),recordV1.getVersionableMutableRecordTypeId()); 
     449        assertEquals(recordType1.getVersion(),recordV1.getVersionableMutableRecordTypeVersion()); 
     450    } 
     451     
     452    @Test 
     453    public void testUpdateWithNewRecordTypeVersionOnlyOneFieldUpdated() throws Exception { 
     454        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     455        Record updateRecord = repository.newRecord(record.getId()); 
     456        updateRecord.setRecordType(recordType1B.getId(), recordType1B.getVersion()); 
     457        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     458         
     459        Record updatedRecord = repository.update(updateRecord); 
     460        assertEquals(recordType1B.getId(),updatedRecord.getRecordTypeId()); 
     461        assertEquals(recordType1B.getVersion(),updatedRecord.getRecordTypeVersion()); 
     462        assertEquals(recordType1B.getId(),updatedRecord.getVersionableRecordTypeId()); 
     463        assertEquals(recordType1B.getVersion(),updatedRecord.getVersionableRecordTypeVersion()); 
     464         
     465        Record readRecord = repository.read(record.getId()); 
     466        assertEquals(recordType1B.getId(),updatedRecord.getRecordTypeId()); 
     467        assertEquals(recordType1B.getVersion(),updatedRecord.getRecordTypeVersion()); 
     468        assertEquals(recordType1.getId(),readRecord.getNonVersionableRecordTypeId()); 
     469        assertEquals(recordType1.getVersion(),readRecord.getNonVersionableRecordTypeVersion()); 
     470        assertEquals(recordType1B.getId(),updatedRecord.getVersionableRecordTypeId()); 
     471        assertEquals(recordType1B.getVersion(),updatedRecord.getVersionableRecordTypeVersion()); 
     472        assertEquals(recordType1.getId(),readRecord.getVersionableMutableRecordTypeId()); 
     473        assertEquals(recordType1.getVersion(),readRecord.getVersionableMutableRecordTypeVersion()); 
     474    } 
     475     
     476    @Test 
     477    public void testUpdateWithNewRecordType() throws Exception { 
     478        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     479        Record updateRecord = repository.newRecord(record.getId()); 
     480        updateRecord.setRecordType(recordType2.getId(), recordType2.getVersion()); 
     481        updateRecord.setNonVersionableField(fieldDescriptor2.getId(), 789); 
     482        updateRecord.setVersionableField(fieldDescriptor3.getId(), false); 
     483        updateRecord.setVersionableMutableField(fieldDescriptor1.getId(), "value2"); 
     484         
     485        Record updatedRecord = repository.update(updateRecord); 
     486        assertEquals(recordType2.getId(),updatedRecord.getRecordTypeId()); 
     487        assertEquals(recordType2.getVersion(),updatedRecord.getRecordTypeVersion()); 
     488        assertEquals(recordType2.getId(),updatedRecord.getNonVersionableRecordTypeId()); 
     489        assertEquals(recordType2.getVersion(),updatedRecord.getNonVersionableRecordTypeVersion()); 
     490        assertEquals(recordType2.getId(),updatedRecord.getVersionableRecordTypeId()); 
     491        assertEquals(recordType2.getVersion(),updatedRecord.getVersionableRecordTypeVersion()); 
     492        assertEquals(recordType2.getId(),updatedRecord.getVersionableMutableRecordTypeId()); 
     493        assertEquals(recordType2.getVersion(),updatedRecord.getVersionableMutableRecordTypeVersion()); 
     494         
     495        assertEquals(1, updatedRecord.getNonVersionableFields().size()); 
     496        assertEquals(1, updatedRecord.getVersionableFields().size()); 
     497        assertEquals(1, updatedRecord.getVersionableMutableFields().size()); 
     498 
     499        Record readRecord = repository.read(record.getId()); 
     500        // Nothing got deleted 
     501        assertEquals(2, readRecord.getNonVersionableFields().size()); 
     502        assertEquals(2, readRecord.getVersionableFields().size()); 
     503        assertEquals(2, readRecord.getVersionableMutableFields().size()); 
     504        assertEquals("value1", readRecord.getNonVersionableField(fieldDescriptor1.getId())); 
     505        assertEquals(789, readRecord.getNonVersionableField(fieldDescriptor2.getId())); 
     506        assertEquals(123, readRecord.getVersionableField(fieldDescriptor2.getId())); 
     507        assertFalse((Boolean)readRecord.getVersionableField(fieldDescriptor3.getId())); 
     508        assertTrue((Boolean)readRecord.getVersionableMutableField(fieldDescriptor3.getId())); 
     509        assertEquals("value2", readRecord.getVersionableMutableField(fieldDescriptor1.getId())); 
     510 
     511    } 
     512     
     513    @Test 
     514    public void testDeleteField() throws Exception { 
     515        Record record = createDefaultRecord(idGenerator.newRecordId()); 
    411516        Record deleteRecord = repository.newRecord(record.getId()); 
    412         deleteRecord.setRecordType("dummyRecordType", 1); 
    413         deleteRecord.deleteField("aField"); 
     517        deleteRecord.setRecordType(record.getRecordTypeId(), null); 
     518        deleteRecord.addNonVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor1.getId()})); 
     519        deleteRecord.addVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor2.getId()})); 
     520        deleteRecord.addVersionableMutableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor3.getId()})); 
     521         
    414522        repository.update(deleteRecord); 
    415         Record actualRecord = repository.read(record.getId()); 
    416         try {  
    417             actualRecord.getField("aField"); 
    418             fail("Getting a deleted field from a record should throw a FieldNotFoundException"); 
    419         } catch(FieldNotFoundException expected) { 
    420         } 
    421         control.verify(); 
    422     } 
    423      
    424     @Test 
    425     public void testDeleteAVersionableField() throws Exception { 
    426         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    427  
    428         control.replay(); 
    429         Record record = generateRecord(new String[] {"aField", "f1"}); 
    430         repository.create(record); 
     523        Record readRecord = repository.read(record.getId()); 
     524        assertTrue(readRecord.getNonVersionableFields().isEmpty()); 
     525        assertTrue(readRecord.getVersionableFields().isEmpty()); 
     526        assertTrue(readRecord.getVersionableMutableFields().isEmpty()); 
     527    } 
     528     
     529    @Test 
     530    public void testDeleteFieldsNoLongerInRecordType() throws Exception { 
     531        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     532        Record updateRecord = repository.newRecord(record.getId()); 
     533        updateRecord.setRecordType(recordType2.getId(), recordType2.getVersion()); 
     534        updateRecord.setNonVersionableField(fieldDescriptor2.getId(), 789); 
     535        updateRecord.setVersionableField(fieldDescriptor3.getId(), false); 
     536        updateRecord.setVersionableMutableField(fieldDescriptor1.getId(), "value2"); 
     537         
     538        repository.update(updateRecord); 
     539         
    431540        Record deleteRecord = repository.newRecord(record.getId()); 
    432         deleteRecord.setRecordType("dummyRecordType", 1); 
    433         deleteRecord.deleteField("aField"); 
     541        deleteRecord.setRecordType(recordType2.getId(), recordType2.getVersion()); 
     542        deleteRecord.addNonVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor1.getId()})); 
    434543        repository.update(deleteRecord); 
    435         Record actualRecord = repository.read(record.getId()); 
    436         try {  
    437             actualRecord.getField("aField"); 
    438             fail("Getting a deleted field from a record should throw a FieldNotFoundException"); 
    439         } catch(FieldNotFoundException expected) { 
    440         } 
    441          
    442         //TODO should we throw already at the moment of the read operation? i.e. validate that the requested fields are not null 
    443         actualRecord = repository.read(record.getId(), "aField"); 
    444         try { 
    445             actualRecord.getField("aField"); 
    446             fail("Getting a deleted field from a record should throw a FieldNotFoundException"); 
    447         } catch(FieldNotFoundException expected) { 
    448         } 
    449         control.verify(); 
    450     } 
    451      
    452     @Test 
    453     public void testDeleteAVersionableFieldWithOlderVersions() throws Exception { 
    454         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    455  
    456         control.replay(); 
    457         Record record = generateRecord(new String[] {"aField", "f1"}); 
    458         repository.create(record); 
    459         record.setField("aField", "f2"); 
    460         repository.update(record); 
    461          
     544         
     545        Record readRecord = repository.read(record.getId()); 
     546        assertEquals(Long.valueOf(2), readRecord.getVersion()); 
     547        assertEquals(1, readRecord.getNonVersionableFields().size()); 
     548        try { 
     549            readRecord.getNonVersionableField(fieldDescriptor1.getId()); 
     550            fail(); 
     551        } catch (FieldNotFoundException expected) { 
     552        } 
     553        assertEquals("value2", readRecord.getVersionableMutableField(fieldDescriptor1.getId())); 
     554        assertEquals(789, readRecord.getNonVersionableField(fieldDescriptor2.getId())); 
     555         
     556        deleteRecord.addVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor2.getId()})); 
     557        deleteRecord.addVersionableMutableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor3.getId()})); 
     558        repository.update(deleteRecord); 
     559         
     560        readRecord = repository.read(record.getId()); 
     561        assertEquals(Long.valueOf(3), readRecord.getVersion()); 
     562        assertEquals(1, readRecord.getNonVersionableFields().size()); 
     563        assertEquals(1, readRecord.getVersionableFields().size()); 
     564        assertEquals(1, readRecord.getVersionableMutableFields().size()); 
     565        assertEquals(789, readRecord.getNonVersionableField(fieldDescriptor2.getId())); 
     566        assertEquals(false, readRecord.getVersionableField(fieldDescriptor3.getId())); 
     567        assertEquals("value2", readRecord.getVersionableMutableField(fieldDescriptor1.getId())); 
     568    } 
     569     
     570    @Test 
     571    public void testUpdateAfterDelete() throws Exception { 
     572        Record record = createDefaultRecord(idGenerator.newRecordId()); 
    462573        Record deleteRecord = repository.newRecord(record.getId()); 
    463         deleteRecord.setRecordType("dummyRecordType", 1); 
    464         deleteRecord.deleteField("aField"); 
     574        deleteRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     575        deleteRecord.addVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor2.getId()})); 
    465576        repository.update(deleteRecord); 
    466  
    467         Record actualRecord = repository.read(record.getId()); 
    468         try {  
    469             actualRecord.getField("aField"); 
    470             fail("Getting a deleted field from a record should throw a FieldNotFoundException"); 
    471         } catch(FieldNotFoundException expected) { 
    472         } 
    473         control.verify(); 
    474     } 
    475      
    476     @Test 
    477     public void testCreateVariantRecord() throws Exception { 
    478         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    479  
    480         control.replay(); 
    481         Record record = generateRecord(new String[] {"aField", "f1"}); 
    482         repository.create(record); 
    483          
    484         Map<String, String> variantProperties = new HashMap<String, String>(); 
    485         variantProperties.put("dimension1", "dimensionValue1"); 
    486         Record variantRecord = generateRecord(new String[] {"aVariantField", "vf1"}); 
    487         RecordId variantRecordId = idGenerator.newRecordId(record.getId(), variantProperties); 
    488         variantRecord.setId(variantRecordId); 
    489         repository.create(variantRecord); 
    490          
    491         Record actualVariantRecord = repository.read(variantRecordId); 
    492          
    493         assertEquals(variantRecord, actualVariantRecord); 
    494          
    495         control.verify(); 
    496     } 
    497      
    498     @Test 
    499     public void testCreateVariantRecordWithNonExistingRecord() throws Exception { 
    500         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    501  
    502         control.replay(); 
    503         Map<String, String> variantProperties = new HashMap<String, String>(); 
    504         variantProperties.put("dimension1", "dimensionValue1"); 
    505         Record variantRecord = generateRecord(new String[] {"aVariantField", "vf1"}); 
    506         variantRecord.setId(idGenerator.newRecordId(idGenerator.newRecordId("nonExistingMasterRecordId"), variantProperties )); 
    507         try { 
    508             repository.create(variantRecord); 
     577         
     578        Record updateRecord = repository.newRecord(record.getId()); 
     579        updateRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     580        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     581        repository.update(updateRecord); 
     582         
     583        Record readRecord = repository.read(record.getId()); 
     584        assertEquals(Long.valueOf(3), readRecord.getVersion()); 
     585        assertEquals(789, readRecord.getVersionableField(fieldDescriptor2.getId())); 
     586         
     587        readRecord = repository.read(record.getId(), Long.valueOf(2)); 
     588        try { 
     589            readRecord.getVersionableField(fieldDescriptor2.getId()); 
     590            fail(); 
     591        } catch (FieldNotFoundException expected) { 
     592        } 
     593         
     594        readRecord = repository.read(record.getId(), Long.valueOf(1)); 
     595        assertEquals(123, readRecord.getVersionableField(fieldDescriptor2.getId())); 
     596    } 
     597     
     598    @Test 
     599    public void testDeleteNonVersionableFieldAndUpdateVersionableField() throws Exception { 
     600        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     601        Record updateRecord = repository.newRecord(record.getId()); 
     602        updateRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     603        updateRecord.setVersionableField(fieldDescriptor2.getId(), 999); 
     604        updateRecord.addNonVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor1.getId()})); 
     605        repository.update(updateRecord); 
     606         
     607        Record readRecord = repository.read(record.getId()); 
     608        assertEquals(999, readRecord.getVersionableField(fieldDescriptor2.getId())); 
     609        try { 
     610            readRecord.getNonVersionableField(fieldDescriptor1.getId()); 
     611            fail(); 
     612        } catch (FieldNotFoundException expected) { 
     613        } 
     614         
     615        readRecord = repository.read(record.getId(), Long.valueOf(1)); 
     616        try { 
     617            readRecord.getNonVersionableField(fieldDescriptor1.getId()); 
     618            fail(); 
     619        } catch (FieldNotFoundException expected) { 
     620        } 
     621         
     622    } 
     623     
     624    @Test 
     625    public void testUpdateAndDeleteSameField() throws Exception { 
     626        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     627        Record updateRecord = repository.newRecord(record.getId()); 
     628        updateRecord.setRecordType(record.getRecordTypeId(), record.getRecordTypeVersion()); 
     629        updateRecord.setVersionableField(fieldDescriptor2.getId(), 789); 
     630        updateRecord.addVersionableFieldsToDelete(Arrays.asList(new String[]{fieldDescriptor2.getId()})); 
     631        repository.update(updateRecord); 
     632         
     633        try { 
     634            repository.read(record.getId()).getVersionableField(fieldDescriptor2.getId()); 
     635            fail(); 
     636        } catch (FieldNotFoundException expected) { 
     637        } 
     638    } 
     639     
     640    public void testDeleteRecord() throws Exception { 
     641        Record record = createDefaultRecord(idGenerator.newRecordId()); 
     642        repository.delete(record.getId()); 
     643        try { 
     644            repository.read(record.getId()); 
    509645            fail(); 
    510646        } catch (RecordNotFoundException expected) { 
    511             assertEquals(variantRecord, expected.getRecord()); 
    512         } 
    513         control.verify(); 
    514     } 
    515      
    516     @Test 
    517     public void testReadNonExistingVariantRecord() throws Exception { 
    518         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    519  
    520         control.replay(); 
    521         Record record = generateRecord(new String[] {"aField", "f1"}); 
    522         repository.create(record); 
    523          
    524         Record variantRecord = generateRecord(new String[] {"aVariantField", "vf1"}); 
    525         Map<String, String> variantProperties = new HashMap<String, String>(); 
    526         variantProperties.put("dimension1", "dimensionValue1"); 
    527         RecordId variantRecordId = idGenerator.newRecordId(record.getId(), variantProperties ); 
    528         variantRecord.setId(variantRecordId); 
    529          
    530         try { 
    531             repository.read(variantRecordId); 
    532             fail("Reading a non-existing variant should throw an exception"); 
    533         } catch (RecordNotFoundException expected) { 
    534             assertEquals(variantRecordId, expected.getRecord().getId()); 
    535         } 
    536         repository.create(variantRecord); 
    537          
    538         Map<String, String> variantProperties2 = new HashMap<String, String>(); 
    539         variantProperties2.put("dimension2", "dimensionValue1"); 
    540         RecordId variantRecordId2 = idGenerator.newRecordId(record.getId(), variantProperties2); 
    541         try { 
    542             repository.read(variantRecordId2); 
    543             fail("Reading a non-existing variant should throw an exception"); 
    544         } catch (RecordNotFoundException expected) { 
    545             assertEquals(variantRecordId2, expected.getRecord().getId()); 
    546         } 
    547         control.verify(); 
    548     } 
    549      
    550     @Test 
    551     public void testReadNonExistingVariantRecordVersion() throws Exception { 
    552         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    553  
    554         control.replay(); 
    555         Record record = generateRecord(new String[] {"aField", "f1"}); 
    556         repository.create(record); 
    557          
    558         Record variantRecord = generateRecord(new String[] {"aVariantField", "vf1"}); 
    559         Map<String, String> variantProperties = new HashMap<String, String>(); 
    560         variantProperties.put("dimension1", "dimensionValue1"); 
    561         RecordId variantRecordId = idGenerator.newRecordId(record.getId(), variantProperties ); 
    562         variantRecord.setId(variantRecordId); 
    563         repository.create(variantRecord); 
    564          
    565         try { 
    566             repository.read(variantRecordId, Long.valueOf(2)); 
    567             fail("Reading a non-existing variant should throw an exception"); 
    568         } catch (RecordNotFoundException expected) { 
    569             assertEquals(variantRecordId, expected.getRecord().getId()); 
    570             assertEquals(Long.valueOf(2), expected.getRecord().getVersion()); 
    571         } 
    572          
    573         control.verify(); 
    574     } 
    575      
    576     @Test 
    577     public void testVariantRecordVersions() throws Exception { 
    578         expect(recordType.getFieldDescriptor(isA(String.class))).andReturn(versionableFieldDescriptor).anyTimes(); 
    579  
    580         control.replay(); 
    581         Record record = generateRecord(new String[] {"field1", "f1"}); 
    582         repository.create(record); 
    583         RecordId recordId = record.getId(); 
    584         Map<String, String> variantProperties = new HashMap<String, String>(); 
    585         variantProperties.put("dimension1", "dimensionValue1"); 
    586         RecordId variantRecordId = idGenerator.newRecordId(recordId, variantProperties); 
    587  
    588         Record variantRecord = generateRecord(new String[] {"field1", "vf1"}); 
    589         variantRecord.setId(variantRecordId); 
    590         repository.create(variantRecord); 
    591          
    592         variantRecord = generateRecord(new String[] {"field1", "vf1B"}, new String[] {"field2", "vf2"}); 
    593         variantRecord.setId(variantRecordId); 
    594         repository.update(variantRecord); 
    595  
    596         variantRecord = generateRecord(new String[] {"field2", "vf2B"}, new String[] {"field3", "vf3"}); 
    597         variantRecord.setId(variantRecordId); 
    598         repository.update(variantRecord); 
    599          
    600         variantRecord = generateRecord(); 
    601         variantRecord.setId(variantRecordId); 
    602         variantRecord.deleteField("field1"); 
    603         repository.update(variantRecord); 
    604          
    605         variantRecord = generateRecord(new String[] {"field1", "vf1B"}); 
    606         variantRecord.setId(variantRecordId); 
    607         repository.update(variantRecord); 
    608  
    609         Record actualMasterRecord = repository.read(recordId); 
    610         assertEquals("f1", actualMasterRecord.getField("field1")); 
    611         Record actualVariantRecord = repository.read(variantRecordId, Long.valueOf(1)); 
    612         assertEquals("vf1", actualVariantRecord.getField("field1")); 
    613          
    614         actualVariantRecord = repository.read(variantRecordId, Long.valueOf(2)); 
    615         assertEquals("vf1B", actualVariantRecord.getField("field1")); 
    616         assertEquals("vf2", actualVariantRecord.getField("field2")); 
    617  
    618         actualVariantRecord = repository.read(variantRecordId, Long.valueOf(3)); 
    619         assertEquals("vf1B", actualVariantRecord.getField("field1")); 
    620         assertEquals("vf2B", actualVariantRecord.getField("field2")); 
    621         assertEquals("vf3", actualVariantRecord.getField("field3")); 
    622  
    623         actualVariantRecord = repository.read(variantRecordId, Long.valueOf(4)); 
    624         try {  
    625             actualVariantRecord.getField("field1"); 
    626             fail(); 
    627         } catch (FieldNotFoundException expected) { 
    628         } 
    629          
    630         actualVariantRecord = repository.read(variantRecordId, Long.valueOf(5)); 
    631         assertEquals("vf1B", actualVariantRecord.getField("field1")); 
    632          
    633         try { 
    634             actualVariantRecord = repository.read(variantRecordId, Long.valueOf(6)); 
    635             fail(); 
    636         } catch(RecordNotFoundException expected) { 
    637         } 
    638  
    639         control.verify(); 
    640  
    641     } 
    642  
    643     private Record generateRecord(String[]... fieldsAndValues) throws Exception { 
    644         Record record = repository.newRecord(); 
    645          
    646         record.setRecordType(recordType.getId(), recordType.getVersion()); 
    647         for (String[] fieldInfo : fieldsAndValues) { 
    648             record.setField(fieldInfo[0], fieldInfo[1]); 
    649         } 
    650         return record; 
     647        } 
    651648    } 
    652649} 
  • projects/lily/trunk/repository/impl/src/test/java/org/lilycms/repository/impl/test/ValueTypeTest.java

    r3959 r3977  
    2929import org.junit.Test; 
    3030import org.lilycms.repository.api.FieldDescriptor; 
    31 import org.lilycms.repository.api.FieldNotFoundException; 
     31import org.lilycms.repository.api.FieldGroup; 
    3232import org.lilycms.repository.api.HierarchyPath; 
    33 import org.lilycms.repository.api.InvalidRecordException; 
    3433import org.lilycms.repository.api.PrimitiveValueType; 
    3534import org.lilycms.repository.api.Record; 
    36 import org.lilycms.repository.api.RecordExistsException; 
    37 import org.lilycms.repository.api.RecordNotFoundException; 
    3835import org.lilycms.repository.api.RecordType; 
    39 import org.lilycms.repository.api.RepositoryException; 
    40 import org.lilycms.repository.impl.FieldDescriptorImpl; 
    4136import org.lilycms.repository.impl.HBaseRepository; 
    4237import org.lilycms.repository.impl.HBaseTypeManager; 
    4338import org.lilycms.repository.impl.IdGeneratorImpl; 
    44 import org.lilycms.repository.impl.RecordImpl; 
    45 import org.lilycms.repository.impl.RecordTypeImpl; 
    4639import org.lilycms.testfw.TestHelper; 
    4740 
     
    6962    public void setUp() throws Exception { 
    7063        idGenerator = new IdGeneratorImpl(); 
    71         typeManager = new HBaseTypeManager(idGenerator, RecordTypeImpl.class, FieldDescriptorImpl.class, TEST_UTIL 
    72                         .getConfiguration()); 
    73         repository = new HBaseRepository(typeManager, idGenerator, RecordImpl.class, TEST_UTIL 
    74                         .getConfiguration()); 
     64        typeManager = new HBaseTypeManager(idGenerator, TEST_UTIL.getConfiguration()); 
     65        repository = new HBaseRepository(typeManager, idGenerator, TEST_UTIL.getConfiguration()); 
    7566    } 
    7667 
     
    115106    } 
    116107 
    117     private void runValueTypeTests(String recordTypeId, String primitivaValueType, Object value1, Object value2, Object value3) throws Exception { 
    118         testType(recordTypeId, primitivaValueType, false, false, value1); 
    119         testType(recordTypeId, primitivaValueType, true, false, Arrays.asList(new Object[] { value1, 
     108    private void runValueTypeTests(String recordTypeId, String primitiveValueType, Object value1, Object value2, Object value3) throws Exception { 
     109        testType(recordTypeId, primitiveValueType, false, false, value1); 
     110        testType(recordTypeId, primitiveValueType, true, false, Arrays.asList(new Object[] { value1, 
    120111                        value2 })); 
    121         testType(recordTypeId, primitivaValueType, false, true, new HierarchyPath(new Object[] { value1, 
     112        testType(recordTypeId, primitiveValueType, false, true, new HierarchyPath(new Object[] { value1, 
    122113                        value2 })); 
    123         testType(recordTypeId, primitivaValueType, true, true, Arrays.asList(new HierarchyPath[] { 
     114        testType(recordTypeId, primitiveValueType, true, true, Arrays.asList(new HierarchyPath[] { 
    124115                new HierarchyPath(new Object[] { value1, value2 }), 
    125116                new HierarchyPath(new Object[] { value1, value3 }) })); 
    126117    } 
    127118     
    128     private void testType(String recordId, String valueTypeString, boolean multivalue, boolean hierarchical, 
    129                     Object fieldValue) throws RepositoryException, RecordExistsException, RecordNotFoundException, 
    130                     InvalidRecordException, FieldNotFoundException { 
    131         RecordType recordType = typeManager.newRecordType(recordId); 
    132         FieldDescriptor fieldDescriptor = typeManager.newFieldDescriptor("aFieldId", typeManager.getValueType( 
    133                         valueTypeString, multivalue, hierarchical), true, true); 
    134         recordType.addFieldDescriptor(fieldDescriptor); 
     119    private void testType(String recordTypeId, String valueTypeString, boolean multivalue, boolean hierarchical, 
     120                    Object fieldValue) throws Exception { 
     121        String fieldDescriptorId = valueTypeString+"FieldId"+multivalue+hierarchical; 
     122        FieldDescriptor fieldDescriptor = typeManager.createFieldDescriptor(typeManager.newFieldDescriptor(fieldDescriptorId, typeManager.getValueType( 
     123                        valueTypeString, multivalue, hierarchical), "aGlobalName")); 
     124        String fieldGroupId = valueTypeString+"FieldGroupId"+multivalue+hierarchical; 
     125        FieldGroup fieldGroup = typeManager.newFieldGroup(fieldGroupId); 
     126        fieldGroup.setFieldGroupEntry(typeManager.newFieldGroupEntry(fieldDescriptor.getId(), fieldDescriptor.getVersion(), true, "anAlias")); 
     127        fieldGroup = typeManager.createFieldGroup(fieldGroup); 
     128        RecordType recordType = typeManager.newRecordType(recordTypeId+"RecordTypeId"+multivalue+hierarchical); 
     129        recordType.setNonVersionableFieldGroupId(fieldGroup.getId()); 
     130        recordType.setNonVersionableFieldGroupVersion(fieldGroup.getVersion()); 
    135131        typeManager.createRecordType(recordType); 
    136132 
    137         Record record = repository.newRecord(); 
     133        Record record = repository.newRecord(idGenerator.newRecordId()); 
    138134        record.setRecordType(recordType.getId(), recordType.getVersion()); 
    139         record.setField("aFieldId", fieldValue); 
     135        record.setNonVersionableField(fieldDescriptorId, fieldValue); 
    140136        repository.create(record); 
    141137 
    142138        Record actualRecord = repository.read(record.getId()); 
    143         assertEquals(fieldValue, actualRecord.getField("aFieldId")); 
     139        assertEquals(fieldValue, actualRecord.getNonVersionableField(fieldDescriptorId)); 
    144140    } 
    145141 
Note: See TracChangeset for help on using the changeset viewer.