/*
* Copyright (c) 2010-2015 Evolveum
*
* 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 com.evolveum.midpoint.repo.api;
import java.util.Collection;
import java.util.List;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.*;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;
import javax.xml.namespace.QName;
/**
* <p>Identity Repository Interface.</p>
* <p>
* <ul>
* <li>Status: public</li>
* <li>Stability: stable</li>
* </ul>
* @version 3.1.1
* @author Radovan Semancik
* </p><p>
* This service provides repository for objects that are commonly found
* in identity management deployments. It is used for storage and retrieval
* of objects. It also supports modifications (relative changes), searching
* and basic coordination.
* </p><p>
* Supported object types:
* <ul>
* <li>All object types from Common Schema</li>
* <li>All object types from Identity Schema</li>
* <li>All object types from IDM Model Schema</li>
* </ul>
* </p><p>
* Identity repository may add some kind of basic logic in addition to a
* pure storage of data. E.g. it may check referential consistency,
* validate schema, etc.
* </p><p>
* The implementation may store the objects and properties in any suitable
* way and it is not required to check any schema beyond the basic common schema
* structures. However, the implementation MAY be able to check additional
* schema definitions, e.g. to check for mandatory and allowed properties
* and property types. This may be either explicit (e.g. implementation checking
* against provided XML schema) or implicit, conforming to the constraints of
* the underlying storage (e.g. LDAP schema enforced by underlying directory server).
* One way or another, the implementation may fail to store the objects that violate
* the schema. The method how the schemas are "loaded" to the implementation is not
* defined by this interface. This interface even cannot "reveal" the schema to its
* users (at least not now). Therefore clients of this interface must be prepared to
* handle schema violation errors.
* </p><p>
* The implementation is not required to index the data or provide any other
* optimizations. This depends on the specific implementation, its configuration
* and the underlying storage system. Qualitative constraints (such as performance)
* are NOT defined by this interface definition.
* </p>
* <h1>Naming Conventions</h1>
* <p>
* operations should be named as <operation><objectType> e.g. addUser,
* modifyAccount, searchObjects. The operations that returns single object
* instance or works on single object should be named in singular (e.g. addUser).
* The operation that return multiple instances should be named in plural (e.g. listObjects).
* Operations names should be unified as well:
* <ul>
* <li>add, modify, delete - writing to repository, single object, need OID</li>
* <li>get - retrieving single object by OID</li>
* <li>list - returning all objects, no or fixed search criteria</li>
* <li>search - returning subset of objects with flexible search criteria</li>
* </ul>
* </p>
* <h1>Notes</h1>
* <p>
* The definition of this interface is somehow "fuzzy" at places. E.g.
* allowing schema-aware implementation but not mandating it, recommending
* to remove duplicates, but tolerating them, etc. The reason for this is
* to have better fit to the underlying storage mechanisms and therefore
* more efficient and simpler implementation. It may complicate the clients
* if the code needs to be generic and fit each and every implementation of
* this interface. However, such code will be quite rare. Most of the custom code
* will be developed to work on a specific storage (e.g. Oracle DB or LDAP)
* and therefore can be made slightly implementation-specific. Changing the
* storage in a running IDM system is extremely unlikely.
* </p>
* <h1>TODO</h1>
* <p>
* <ul>
* <li>TODO: Atomicity, consistency</li>
* <li>TODO: security constraints</li>
* <li>TODO: inherently thread-safe</li>
* <li>TODO: note about distributed storage systems and weak/eventual consistency</li>
* <li>TODO: task coordination</li>
* </ul>
* </p>
*/
public interface RepositoryService {
String CLASS_NAME_WITH_DOT = RepositoryService.class.getName() + ".";
String GET_OBJECT = CLASS_NAME_WITH_DOT + "getObject";
@Deprecated
String LIST_ACCOUNT_SHADOW = CLASS_NAME_WITH_DOT + "listAccountShadowOwner";
String ADD_OBJECT = CLASS_NAME_WITH_DOT + "addObject";
String DELETE_OBJECT = CLASS_NAME_WITH_DOT + "deleteObject";
@Deprecated
String CLAIM_TASK = CLASS_NAME_WITH_DOT + "claimTask";
@Deprecated
String RELEASE_TASK = CLASS_NAME_WITH_DOT + "releaseTask";
String SEARCH_OBJECTS = CLASS_NAME_WITH_DOT + "searchObjects";
String SEARCH_CONTAINERS = CLASS_NAME_WITH_DOT + "searchContainers";
String LIST_RESOURCE_OBJECT_SHADOWS = CLASS_NAME_WITH_DOT + "listResourceObjectShadows";
String MODIFY_OBJECT = CLASS_NAME_WITH_DOT + "modifyObject";
String COUNT_OBJECTS = CLASS_NAME_WITH_DOT + "countObjects";
String GET_VERSION = CLASS_NAME_WITH_DOT + "getVersion";
String SEARCH_OBJECTS_ITERATIVE = CLASS_NAME_WITH_DOT + "searchObjectsIterative";
String SEARCH_SHADOW_OWNER = CLASS_NAME_WITH_DOT + "searchShadowOwner";
String ADVANCE_SEQUENCE = CLASS_NAME_WITH_DOT + "advanceSequence";
String RETURN_UNUSED_VALUES_TO_SEQUENCE = CLASS_NAME_WITH_DOT + "returnUnusedValuesToSequence";
String EXECUTE_QUERY_DIAGNOSTICS = CLASS_NAME_WITH_DOT + "executeQueryDiagnostics";
/**
* Returns object for provided OID.
*
* Must fail if object with the OID does not exists.
*
* @param oid
* OID of the object to get
* @param parentResult
* parent OperationResult (in/out)
* @return Object fetched from repository
*
* @throws ObjectNotFoundException
* requested object does not exist
* @throws SchemaException
* error dealing with storage schema
* @throws IllegalArgumentException
* wrong OID format, etc.
*/
<T extends ObjectType> PrismObject<T> getObject(Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult parentResult)
throws ObjectNotFoundException, SchemaException;
// <T extends ObjectType> PrismObject<T> getContainerValue(Class<T> type, String oid, long id,
// Collection<SelectorOptions<GetOperationOptions>> options,
// OperationResult parentResult)
// throws ObjectNotFoundException, SchemaException;
/**
* Returns object version for provided OID.
*
* Must fail if object with the OID does not exists.
*
* This is a supposed to be a very lightweight and cheap operation. It is used to support
* efficient caching of expensive objects.
*
* @param oid
* OID of the object to get
* @param parentResult
* parent OperationResult (in/out)
* @return Object version
*
* @throws ObjectNotFoundException
* requested object does not exist
* @throws SchemaException
* error dealing with storage schema
* @throws IllegalArgumentException
* wrong OID format, etc.
*/
<T extends ObjectType> String getVersion(Class<T> type,String oid, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException;
/**
* <p>Add new object.</p>
* <p>
* The OID provided in the input message may be empty. In that case the OID
* will be assigned by the implementation of this method and it will be
* provided as return value.
* </p><p>
* This operation should fail if such object already exists (if object with
* the provided OID already exists).
* </p><p>
* The operation may fail if provided OID is in an unusable format for the
* storage. Generating own OIDs and providing them to this method is not
* recommended for normal operation.
* </p><p>
* Should be atomic. Should not allow creation of two objects with the same
* OID (even if created in parallel).
* </p><p>
* The operation may fail if the object to be created does not conform to
* the underlying schema of the storage system or the schema enforced by the
* implementation.
* </p><p>
* Note: no need for explicit type parameter here. The object parameter contains the information.
* </p>
*
* @param object
* object to create
* @param parentResult
* parent OperationResult (in/out)
* @return OID assigned to the created object
*
* @throws ObjectAlreadyExistsException
* object with specified identifiers already exists, cannot add
* @throws SchemaException
* error dealing with storage schema, e.g. schema violation
* @throws IllegalArgumentException
* wrong OID format, etc.
*/
<T extends ObjectType> String addObject(PrismObject<T> object, RepoAddOptions options, OperationResult parentResult)
throws ObjectAlreadyExistsException, SchemaException;
/**
* <p>Search for objects in the repository.</p>
* <p>If no search criteria specified, list of all objects of specified type is returned.</p>
* <p>
* Searches through all object types.
* Returns a list of objects that match search criteria.
* </p><p>
* Returns empty list if object type is correct but there are no objects of
* that type. The ordering of the results is not significant and may be arbitrary
* unless sorting in the paging is used.
* </p><p>
* Should fail if object type is wrong. Should fail if unknown property is
* specified in the query.
* </p>
*
* @param query
* search query
* @param parentResult
* parent OperationResult (in/out)
* @return all objects of specified type that match search criteria (subject
* to paging)
*
* @throws IllegalArgumentException
* wrong object type
* @throws SchemaException
* unknown property used in search query
*/
@NotNull
<T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult)
throws SchemaException;
/**
* Search for "sub-object" structures, i.e. containers.
* Currently, only one type of search is available: certification case search.
*/
<T extends Containerable> SearchResultList<T> searchContainers(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult) throws SchemaException;
/**
* <p>Search for objects in the repository in an iterative fashion.</p>
* <p>Searches through all object types. Calls a specified handler for each object found.
* If no search criteria specified, list of all objects of specified type is returned.</p>
* <p>
* Searches through all object types.
* Returns a list of objects that match search criteria.
* </p><p>
* Returns empty list if object type is correct but there are no objects of
* that type. The ordering of the results is not significant and may be arbitrary
* unless sorting in the paging is used.
* </p><p>
* Should fail if object type is wrong. Should fail if unknown property is
* specified in the query.
* </p>
*
* @param query
* search query
* @param handler
* result handler
* @param strictlySequential
* takes care not to skip any object nor to process objects more than once;
* currently requires paging NOT to be used - uses its own paging
* @param parentResult
* parent OperationResult (in/out)
* @return all objects of specified type that match search criteria (subject
* to paging)
*
* @throws IllegalArgumentException
* wrong object type
* @throws SchemaException
* unknown property used in search query
*/
<T extends ObjectType> SearchResultMetadata searchObjectsIterative(Class<T> type, ObjectQuery query,
ResultHandler<T> handler, Collection<SelectorOptions<GetOperationOptions>> options, boolean strictlySequential,
OperationResult parentResult)
throws SchemaException;
/**
* <p>Returns the number of objects that match specified criteria.</p>
* <p>If no search criteria specified, count of all objects of specified type is returned.</p>
* <p>
* Should fail if object type is wrong. Should fail if unknown property is
* specified in the query.
* </p>
*
* @param query
* search query
* @param parentResult
* parent OperationResult (in/out)
* @return count of objects of specified type that match search criteria (subject
* to paging)
*
* @throws IllegalArgumentException
* wrong object type
* @throws SchemaException
* unknown property used in search query
*/
<T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query, OperationResult parentResult)
throws SchemaException;
<T extends ObjectType> int countObjects(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult parentResult) throws SchemaException;
boolean isAnySubordinate(String upperOrgOid, Collection<String> lowerObjectOids) throws SchemaException;
<O extends ObjectType> boolean isDescendant(PrismObject<O> object, String orgOid) throws SchemaException;
<O extends ObjectType> boolean isAncestor(PrismObject<O> object, String oid) throws SchemaException;
/**
* <p>Modifies object using relative change description.</p>
* Must fail if user with
* provided OID does not exists. Must fail if any of the described changes
* cannot be applied. Should be atomic.
* </p><p>
* If two or more modify operations are executed in parallel, the operations
* should be merged. In case that the operations are in conflict (e.g. one
* operation adding a value and the other removing the same value), the
* result is not deterministic.
* </p><p>
* The operation may fail if the modified object does not conform to the
* underlying schema of the storage system or the schema enforced by the
* implementation.
* </p>
*
* TODO: optimistic locking
*
* @param parentResult
* parent OperationResult (in/out)
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws SchemaException
* resulting object would violate the schema
* @throws ObjectAlreadyExistsException
* if resulting object would have name which already exists in another object of the same type
* @throws IllegalArgumentException
* wrong OID format, described change is not applicable
*/
<T extends ObjectType> void modifyObject(Class<T> type, String oid, Collection<? extends ItemDelta> modifications, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException;
<T extends ObjectType> void modifyObject(Class<T> type, String oid, Collection<? extends ItemDelta> modifications, RepoModifyOptions options, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException;
/**
* <p>Deletes object with specified OID.</p>
* <p>
* Must fail if object with specified OID does not exists. Should be atomic.
* </p>
*
* @param oid
* OID of object to delete
* @param parentResult
* parent OperationResult (in/out)
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws IllegalArgumentException
* wrong OID format, described change is not applicable
*/
<T extends ObjectType> void deleteObject(Class<T> type, String oid, OperationResult parentResult) throws ObjectNotFoundException;
/**
* <p>Returns the User object representing owner of specified account (account
* shadow).</p>
* <p>
* May return null if there is no owner specified for the account.
* </p><p>
* May only be called with OID of AccountShadow object.
* </p><p>
* Implements the backward "owns" association between account shadow and
* user. Forward association is implemented by property "account" of user
* object.
* </p><p>
* This is a "list" operation even though it may return at most one owner.
* However the operation implies searching the repository for an owner,
* which may be less efficient that following a direct association. Hence it
* is called "list" to indicate that there may be non-negligible overhead.
* </p>
*
* @param accountOid
* OID of account shadow
* @param parentResult
* parentResult parent OperationResult (in/out)
* @return User object representing owner of specified account
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws IllegalArgumentException
* wrong OID format
*/
@Deprecated
PrismObject<UserType> listAccountShadowOwner(String accountOid, OperationResult parentResult)
throws ObjectNotFoundException;
/**
* <p>Returns the object representing owner of specified shadow.</p>
* <p>
* Implements the backward "owns" association between account shadow and
* user. Forward association is implemented by linkRef reference in subclasses
* of FocusType.
* </p
* <p>
* Returns null if there is no owner for the shadow.
* </p>
* <p>
* This is a "search" operation even though it may return at most one owner.
* However the operation implies searching the repository for an owner,
* which may be less efficient that following a direct association. Hence it
* is called "search" to indicate that there may be non-negligible overhead.
* </p>
* <p>
* This method should not die even if the specified shadow does not exist.
* Even if the shadow is gone, it still may be used in some linkRefs. This
* method should be able to find objects with such linkeRefs otherwise we
* will not be able to do proper cleanup.
* </p>
*
* @param shadowOid
* OID of shadow
* @param parentResult
* parentResult parent OperationResult (in/out)
* @return Object representing owner of specified account (subclass of FocusType)
*
* @throws IllegalArgumentException
* wrong OID format
*/
<F extends FocusType> PrismObject<F> searchShadowOwner(String shadowOid, Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult);
/**
* <p>Search for resource object shadows of a specified type that belong to the
* specified resource.</p>
* <p>
* Returns a list of such object shadows or empty list
* if nothing was found.
* </p><p>
* Implements the backward "has" association between resource and resource
* object shadows. Forward association is implemented by property "resource"
* of resource object shadow.
* </p><p>
* May only be called with OID of Resource object.
* </p>
*
* @param resourceOid
* OID of resource definition (ResourceType)
* @param parentResult
* parentResult parent OperationResult (in/out)
* @return resource object shadows of a specified type from specified
* resource
*
* @throws ObjectNotFoundException
* specified object does not exist
* @throws SchemaException
* found object is not type of {@link ShadowType}
* @throws IllegalArgumentException
* wrong OID format
*/
<T extends ShadowType> List<PrismObject<T>> listResourceObjectShadows(String resourceOid,
Class<T> resourceObjectShadowType, OperationResult parentResult) throws ObjectNotFoundException,
SchemaException;
/**
*
* This operation is guaranteed to be atomic. If two threads or even two nodes request a value from
* the same sequence at the same time then different values will be returned.
*
* @param oid sequence OID
* @param parentResult Operation result
* @return next unallocated counter value
* @throws ObjectNotFoundException the sequence does not exist
* @throws SchemaException the sequence cannot produce a value (e.g. maximum counter reached)
*/
long advanceSequence(String oid, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
*
* The sequence may ignore the values, e.g. if value re-use is disabled or when the list of
* unused values is full. In such a case the values will be ignored silently and no error is indicated.
*
* @param oid sequence OID
* @param unusedValues values to return
* @param parentResult Operation result
*/
void returnUnusedValuesToSequence(String oid, Collection<Long> unusedValues, OperationResult parentResult) throws ObjectNotFoundException, SchemaException;
/**
* Provide repository run-time configuration and diagnostic information.
*/
RepositoryDiag getRepositoryDiag();
/**
* Runs a short, non-descructive repository self test.
* This methods should never throw a (checked) exception. All the results
* should be recorded under the provided result structure (including fatal errors).
*
* This should implement ONLY self-tests that are IMPLEMENTATION-SPECIFIC. It must not
* implement self-tests that are generic and applies to all repository implementations.
* Such self-tests must be implemented in higher layers.
*
* If the repository has no self-tests then the method should return immediately
* without changing the result structure. It must not throw an exception in this case.
*/
void repositorySelfTest(OperationResult parentResult);
/**
* Checks a closure for consistency, repairing any problems found.
* This methods should never throw a (checked) exception. All the results
* should be in the returned result structure (including fatal errors).
*
* The current implementation expects closure to be of reasonable size - so
* it could be fetched into main memory as well as recomputed online
* (perhaps up to ~250K entries). In future, this method will be reimplemented.
*
* BEWARE, this method locks out the M_ORG_CLOSURE table, so org-related operations
* would wait until it completes.
*
* TODO this method is SQL service specific; it should be generalized/fixed somehow.
*/
void testOrgClosureConsistency(boolean repairIfNecessary, OperationResult testResult);
/**
* A bit of hack - execute arbitrary query, e.g. hibernate query in case of SQL repository.
* Use with all the care!
*
* @param request Diagnostics request
* @param result Operation result
* @return diagnostics response
*/
RepositoryQueryDiagResponse executeQueryDiagnostics(RepositoryQueryDiagRequest request, OperationResult result);
<O extends ObjectType> boolean selectorMatches(ObjectSelectorType objectSelector, PrismObject<O> object,
Trace logger, String logMessagePrefix) throws SchemaException;
/**
* Returns matching rule supported by the repository for a given data type (String, PolyString, ...), for
* originally intended matching rule.
*
* New matching rule must NOT be less selective than the original one. I.e. if values V1, V2 would not match
* under the original one, they must not also match under the replacement. Therefore it is safe to replace
* distinguishedName with stringIgnoreCase (but not e.g. the other way around; nor exchangeEmailAddresses
* can be replaced by stringIgnoreCase, because the prefix part is case sensitive).
*
* The assumption is that for unsupported matching rules the repository will store normalized values. And it
* will normalize any values that are used in queries. This is the obligation of the client. So, theoretically,
* it is safe to replace any such matching rule with default (exact) matching rule. But if we replace it with
* something that does not return false positives (i.e. something that is not less sensitive), we get some
* resiliency w.r.t. non-normalized values in repository. TODO TODO TODO think again
*
* If the original matching rule is not supported by the given data type (e.g. trying to use exchangeEmailAddress
* on PolyString), the result may be arbitrary. TODO think again also about this
*/
QName getApproximateSupportedMatchingRule(Class<?> dataType, QName originalMatchingRule);
void applyFullTextSearchConfiguration(FullTextSearchConfigurationType fullTextSearch);
FullTextSearchConfigurationType getFullTextSearchConfiguration();
void postInit(OperationResult result) throws SchemaException;
}