/*
* Copyright 2010 Outerthought bvba
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lilyproject.repository.api;
import java.io.Closeable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.lilyproject.util.Pair;
// IMPORTANT:
// See the note on the requirement TypeException described in the Repository.java file.
/**
* TypeManager provides access to the repository schema. This is where {@link RecordType}s and {@link FieldType}s
* are managed.
*
* <p>For an in-depth description of the repository model, please see the Lily documentation.
*/
public interface TypeManager extends Closeable {
/**
* Instantiates a new RecordType object.
*
* <p>This is only a factory method, nothing is created in the repository.
*/
RecordType newRecordType(QName name) throws TypeException;
/**
* Instantiates a new RecordType object.
*
* <p>This is only a factory method, nothing is created in the repository.
*/
RecordType newRecordType(SchemaId recordTypeId, QName name) throws TypeException;
/**
* Creates a RecordType in the repository.
*
* @throws RecordTypeExistsException when a recordType with the same id already exists on the repository
* @throws RecordTypeNotFoundException when a supertype of the recordType refers to a non-existing {@link RecordType}
* @throws TypeException when the given recordType has no name specified
* @throws FieldTypeNotFoundException
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
RecordType createRecordType(RecordType recordType) throws RepositoryException, InterruptedException;
/**
* Gets a RecordType from the repository.
*
* @param version the version of the record type to return, or null for the latest version.
*
* @throws RecordTypeNotFoundException when the recordType does not exist
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
RecordType getRecordTypeById(SchemaId id, Long version) throws RepositoryException, InterruptedException;
/**
* Gets a RecordType from the repository.
*
* @param version the version of the record type to return, or null for the latest version.
*
* @throws RecordTypeNotFoundException when the recordType does not exist
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
RecordType getRecordTypeByName(QName name, Long version) throws RepositoryException, InterruptedException;
/**
* Gets the set of record types that inherit from the given record type.
*
* <p>This returns subtypes from any level deep, thus the complete type hierarchy.
*
* <p>This is based on information from the latest version of each record type, and ignores the version
* aspect of the record types. For example, if the latest version record type B points to the non-latest version
* of record type A, then when using this method to find the subtypes of A will also return B, even if
* B is not a subtype of the latest version of A, because the latest version of B, where the actual
* extends-pointers are stored, does inherit from A.
*
* <p>If the specified schema id does not exist, this method throws a {@link RecordTypeNotFoundException}.
*
* @return the found sub types, or an empty set if there are none.
*/
Set<SchemaId> findSubtypes(SchemaId recordTypeId) throws InterruptedException, RepositoryException;
/**
* Get the set of record types that directly inherit from the given record type, i.e. only the child
* types but not other descendants.
*
* <p>See {@link #findSubtypes(SchemaId)} for more details.
*
* @return the found sub types, or an empty set if there are none.
*/
Set<SchemaId> findDirectSubtypes(SchemaId recordTypeId) throws InterruptedException, RepositoryException;
/**
* Gets the set of record types that inherit from the given record type.
*
* <p>See {@link #findSubtypes(SchemaId)} for more details.
*/
Set<QName> findSubtypes(QName recordTypeName) throws InterruptedException, RepositoryException;
/**
* Get the set of record types that directly inherit from the given record type, i.e. only the child
* types but not other descendants.
*
* <p>See {@link #findSubtypes(SchemaId)} for more details.
*
* @return the found sub types, or an empty set if there are none.
*/
Set<QName> findDirectSubtypes(QName recordTypeName) throws InterruptedException, RepositoryException;
/**
* Updates an existing record type.
*
* <p>You can provide any RecordType object as argument, either one retrieved from TypeManager, for example
* using {@link #getRecordTypeByName(QName, Long)} or a newly instantiated one, using {@link #newRecordType(QName)}.
*
* <p>The state of the record type will be updated to correspond to the given RecordType object. This also
* concerns the list of fields: any fields that were previously in the record type but are not present in
* the provided RecordType object will be removed. This is different from {@link Record}s, where field deletion
* is explicit.
*
* <p>Upon each update, a new version of the RecordType is created. The number of the created version is available
* from the returned RecordType object.
*
* @throws RecordTypeNotFoundException when the recordType to be updated does not exist
* @throws FieldTypeNotFoundException
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
RecordType updateRecordType(RecordType recordType) throws RepositoryException, InterruptedException;
/**
* Updates an existing record type, and refreshes links in subtypes to point to the new version of
* this record type.
*
* <p>This method is the same as {@link #updateRecordType(RecordType)} but additionally updates subtypes
* if any, as described below, if the refreshSubtypes argument is true.</p>
*
* <p>The supertype links in a record type point to specific versions of parent (supertype) record types.
* When updating such a supertype record type, you thus need to update the links in the subtypes to point
* to the new version of the supertype, and then repeat that recursively (because updating those subtypes
* will need to a new version of those subtypes which means that the subtypes of these subtypes then
* need to be updated). This method does that for you when the argument refreshSubtypes is true.</p>
*
* <p>This method relies on the information provided by {@link #findDirectSubtypes(SchemaId)} (might occasionally
* be wrong in case the schema cache is out of date) and is not transactional: from the moment an update
* to one of the subtype fails, the method stops.</p>
*/
RecordType updateRecordType(RecordType recordType, boolean refreshSubtypes)
throws RepositoryException, InterruptedException;
/**
* Either creates or updates the record type, depending on whether it exists in
* the repository, and depending on the information supplied in the record type.
*
* <p>When the ID is supplied of the record type, this method will always do
* an update. If the name is supplied, it is checked whether it already exists,
* if so the record type is updated, and otherwise it is created.
*
* @return the created or updated record type
*/
RecordType createOrUpdateRecordType(RecordType recordType) throws RepositoryException, InterruptedException;
/**
* Creates or updates a record type.
*
* <p>For an explanation on the createOrUpdate, see {@link #createOrUpdateRecordType(RecordType)}.</p>
*
* <p>For an explanation of the refreshSubtypes argument, see {@link #updateRecordType(RecordType, boolean)}</p>
*/
RecordType createOrUpdateRecordType(RecordType recordType, boolean refreshSubtypes)
throws RepositoryException, InterruptedException;
/**
* Get the list of all record types that exist in the repository. This returns the latest version of
* each record type.
*/
Collection<RecordType> getRecordTypes() throws RepositoryException, InterruptedException;
/**
* Instantiates a new FieldTypeEntry object.
*
* <p>This is only a factory method, nothing is created in the repository.
*
* <p>FieldTypeEntries can be added to {@link RecordType}s.
*/
FieldTypeEntry newFieldTypeEntry(SchemaId fieldTypeId, boolean mandatory);
/**
* Instantiates a new FieldType object.
*
* <p>This is only a factory method, nothing is created in the repository.
*/
FieldType newFieldType(ValueType valueType, QName name, Scope scope);
/**
* Instantiates a new FieldType object.
*
* <p>This is only a factory method, nothing is created in the repository.
*/
FieldType newFieldType(String valueType, QName name, Scope scope) throws RepositoryException, InterruptedException;
/**
* Instantiates a new FieldType object.
*
* <p>This is only a factory method, nothing is created in the repository.
*/
FieldType newFieldType(SchemaId id, ValueType valueType, QName name, Scope scope);
/**
* Creates a FieldType in the repository.
*
* <p>The ID of a field type is assigned by the system. If there is an ID present in the provided FieldType
* object, it will be ignored. The generated ID is available from the returned FieldType object.
*
* @return updated FieldType object
*
* @throws RepositoryException when an unexpected exception occurs on the repository
* @throws FieldTypeExistsException
*/
FieldType createFieldType(FieldType fieldType) throws RepositoryException, InterruptedException;
/**
* Creates a field type in the repository, using the given parameters.
*/
FieldType createFieldType(ValueType valueType, QName name, Scope scope) throws RepositoryException,
InterruptedException;
/**
* Creates a field type in the repository, using the given parameters.
*/
FieldType createFieldType(String valueType, QName name, Scope scope) throws RepositoryException,
InterruptedException;
/**
* Updates an existing FieldType.
*
* <p>You can provide any FieldType object as argument, either retrieved via {@link #getFieldTypeByName} or
* newly instantiated via {@link #newFieldType}.
*
* <p>It is the ID of the field type which serves to identify the field type, so the ID must be present in the
* FieldType object. The QName of the field type can be changed.
*
* @return updated FieldType object
*
* @throws FieldTypeNotFoundException when no fieldType with id and version exists
* @throws FieldTypeUpdateException an exception occurred while updating the FieldType
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
FieldType updateFieldType(FieldType fieldType) throws RepositoryException, InterruptedException;
/**
* Creates or updates a field type, depending on whether it exists in the repository,
* and depending on the information supplied in the field type.
*
* <p>The only case in which an update is performed is when both the ID
* and name are supplied. This is because the name is the only mutable
* property of a field type, and you can obviously only change it if
* there is some other way to identify the field type, namely its ID.
*
* <p>If no create or update needs to be performed, it is still always
* validated that the immutable properties in the supplied field type
* object correspond to those in the repository, if not a
* {@link FieldTypeUpdateException} is produced. These properties could
* also be missing, the returned field type object will always contain
* the full state as stored in the repository.
*
*/
FieldType createOrUpdateFieldType(FieldType fieldType) throws RepositoryException, InterruptedException;
/**
* Gets a FieldType from the repository.
*
* @throws FieldTypeNotFoundException when no fieldType with the given ID exists
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
FieldType getFieldTypeById(SchemaId id) throws RepositoryException, InterruptedException;
/**
* Gets a FieldType from the repository.
*
* @throws FieldTypeNotFoundException when no fieldType with the given name exists
* @throws RepositoryException when an unexpected exception occurs on the repository
*/
FieldType getFieldTypeByName(QName name) throws RepositoryException, InterruptedException;
/**
* Get all {@link FieldTypeEntry}s for a given {@link RecordType}.
* <p>
* Optionally include field types from super-types of the target record type.
*
* @param recordType record type for which field types are to be fetched
* @param includeSupertypes flag to determine if field types from super types of the record type are to be included
* @return
*/
Collection<FieldTypeEntry> getFieldTypesForRecordType(RecordType recordType, boolean includeSupertypes)
throws RepositoryException, InterruptedException;
/**
* Gets the list of all field types that exist in the repository.
*/
Collection<FieldType> getFieldTypes() throws RepositoryException, InterruptedException;
/**
* Provides {@link ValueType} instances. These are used to set to value type of {@link FieldType}s.
*
* <p>The built-in available value types are listed in the following table.
*
* <table>
* <tbody>
* <tr><th>Name</th> <th>Class</th></tr> <th>
* <tr><td>STRING</td> <td>java.lang.String</td></tr>
* <tr><td>INTEGER</td> <td>java.lang.Integer</td></tr>
* <tr><td>LONG</td> <td>java.lang.Long</td></tr>
* <tr><td>DOUBLE</td> <td>java.lang.Double</td></tr>
* <tr><td>DECIMAL</td> <td>java.math.BigDecimal</td></tr>
* <tr><td>BOOLEAN</td> <td>java.lang.Boolean</td></tr>
* <tr><td>DATE</td> <td>org.joda.time.LocalDate</td></tr>
* <tr><td>DATETIME</td> <td>org.joda.time.DateTime</td></tr>
* <tr><td>BLOB</td> <td>org.lilyproject.repository.api.Blob</td></tr>
* <tr><td>LINK</td> <td>org.lilyproject.repository.api.Link</td></tr>
* <tr><td>URI</td> <td>java.net.URI</td></tr>
* <tr><td>LIST</td> <td>java.util.List</td></tr>
* <tr><td>PATH</td> <td>org.lilyproject.repository.api.HierarchyPath</td></tr>
* <tr><td>RECORD</td> <td>org.lilyproject.repository.api.Record</td></tr>
* <tr><td>BYTEARRAY</td><td>org.lilyproject.bytes.api.ByteArray</td></tr>
* </tbody>
* </table>
*
* <p>Some value types accept extra parameters to define the exact value type.
* <p>For List and Path these parameters define the value type of the included values.
* It is mandatory to define this value type.
* It should be specified by putting its name between brackets "<>"
* and if that value type in its turn needs some extra parameters,
* these should be appended again within brackets "<>".
* <br>For example: <code>getValueType("LIST<PATH<STRING>>");</code>
*
* <p>For Record and Link valuetype it is possible to define the {@link RecordType} in the parameters.
* This is not mandatory. It is done by specifying the name of the RecordType in the format
* <code>{namespace}name</code> between brackets "<>".
* <br>For example: <code>getValueType("RECORD<{myNamespace}recordType1>");</code>
*
* @see ValueType
* @param valueType the value type string representation. See table above.
*/
ValueType getValueType(String valueType) throws RepositoryException, InterruptedException;
/**
* Registers custom {@link ValueType}s.
*
* <p><b>TODO:</b> Maybe this should rather move to an SPI interface? Can this replace a built-in primitive
* value type if the name corresponds? Does it make sense to allow registering at any time? Probably implies
* registering on all Lily nodes? This needs more thought.
*/
void registerValueType(String name, ValueTypeFactory valueTypeFactory);
/**
* Returns a record type builder, providing a fluent API to manipulate record types.
*/
RecordTypeBuilder recordTypeBuilder() throws TypeException;
/**
* Returns a field type builder, providing a fluent API to manipulate field types.
*
* <p>Note that field types can also be created in a single statement using
* {@link #createFieldType(String, QName, Scope)}, the field type builder just
* offers a little more flexibility in how to specify name and scope and what
* operations to do (update, createOrUpdate).
*/
FieldTypeBuilder fieldTypeBuilder() throws TypeException;
//
// Schema cache
//
/**
* Returns a snapshot of the FieldTypes cache.
* <p>
* To be used when a consistent snapshot is needed while performing a CRUD
* operation.
*
* @return a snapshot of the FieldTypes cache
* @throws InterruptedException
*/
FieldTypes getFieldTypesSnapshot() throws InterruptedException;
/**
* Returns the list of field types known by the repository.
* <p>
* This method bypasses the cache of the type manager.
*/
List<FieldType> getFieldTypesWithoutCache() throws RepositoryException, InterruptedException;
/**
* Returns the list of record types known by the repository.
* <p>
* This method bypasses the cache of the type manager.
*/
List<RecordType> getRecordTypesWithoutCache() throws RepositoryException, InterruptedException;
/**
* Returns both the list of field types and record types known by the
* repository.
* <p>
* This method bypasses the cache of the type manager.
*/
Pair<List<FieldType>, List<RecordType>> getTypesWithoutCache()
throws RepositoryException, InterruptedException;
/**
* Returns both the list of field types and record types known by the
* repository for a given bucket
* <p>
* This method bypasses the cache of the type manager.
*
* @return a TypeBucket containing the list of field and record types of the
* bucket
*/
TypeBucket getTypeBucketWithoutCache(String bucketId) throws RepositoryException, InterruptedException;
/**
* <b>EXPERT ONLY !</b> Enables the schema cache refreshing system.
* <p>
* When enabled the schema caches will get a trigger to update their data
* whenever a schema update was performed.
*
* @see {@link #disableSchemaCacheRefresh()}
* @throws RepositoryException
* when setting the flag to enabled failed
*/
void enableSchemaCacheRefresh() throws RepositoryException, InterruptedException;
/**
* <b>EXPERT ONLY !</b> Disables the schema cache refreshing system.
* <p>
* When disabling the schema cache refreshing system, a client performing
* schema updates should use a repository which it got through
* <code>LilyClient.getPlainRepository()</code> in order to have all updates
* performed on the same repository and related schema cache.<br/>
* Otherwise updates could be directed to servers lacking needed type
* information (due to the disabled cache refreshing).
*
* @throws RepositoryException
* when setting the flag to disabled failed
*/
void disableSchemaCacheRefresh() throws RepositoryException, InterruptedException;
/**
* <b>EXPERT ONLY !</b> Triggers a forced schema cache refresh.
* <p>
* Even if the schema cache refreshing system is disabled, this call will
* trigger the schema caches to refresh their data.
*
* @see {@link #disableSchemaCacheRefresh()}
* @throws RepositoryException
* when setting the flag to refresh the caches failed
*/
void triggerSchemaCacheRefresh() throws RepositoryException, InterruptedException;
/**
* <b>EXPERT ONLY !</b> Checks if the schema cache refreshing system is
* enabled or disabled
*
* @see {@link #disableSchemaCacheRefresh()}
* @return true when enabled
*/
boolean isSchemaCacheRefreshEnabled() throws RepositoryException, InterruptedException;
}