/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.hawkular.inventory.base.spi;
import java.io.InputStream;
import java.time.Instant;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.hawkular.inventory.api.Query;
import org.hawkular.inventory.api.Relationships;
import org.hawkular.inventory.api.model.AbstractElement;
import org.hawkular.inventory.api.model.Blueprint;
import org.hawkular.inventory.api.model.Entity;
import org.hawkular.inventory.api.model.Hashes;
import org.hawkular.inventory.api.model.StructuredData;
import org.hawkular.inventory.api.paging.Page;
import org.hawkular.inventory.api.paging.Pager;
import org.hawkular.inventory.paths.CanonicalPath;
import org.hawkular.inventory.paths.RelativePath;
/**
* The backend for the base inventory that does all the "low level" stuff like querying the actual inventory store,
* its modifications, etc.
*
* @param <E> the type of the backend-specific objects representing the inventory entities and relationships. It is
* assumed that the backend is "untyped" and stores all different inventory entities using this single type.
* @author Lukas Krejci
* @since 0.1.0
*/
public interface InventoryBackend<E> extends AutoCloseable {
boolean isPreferringBigTransactions();
/**
* Inventory tries to use unique indices in the backend store to ensure certain conditions in the inventory (like
* each entity having a unique canonical path).
* <p>
* If this is not supported by the backend, alternative techniques are employed to ensure at least some level
* consistency (even if not completely as safe as having the unique index ensured by the backend).
*
* @return whether the backend supports unique indices or not
*/
boolean isUniqueIndexSupported();
/**
* Starts a transaction in the backend.
*
* @return a new inventory backend instance that is bound to a new transaction
*/
InventoryBackend<E> startTransaction();
/**
* Tries to find an element at given canonical path.
*
*
* @param discriminator the discriminator to apply on the query for the element
* @param element the canonical path of the element to find
* @return the element
* @throws ElementNotFoundException if the element is not found
*/
E find(Discriminator discriminator, CanonicalPath element) throws ElementNotFoundException;
/**
* Translates the query to the backend-specific representation and runs it, returning a correct page of results
* as prescribed by the provided pager object.
*
* <p>The difference between this method and {@link #traverse(Discriminator, Object, Query, Pager)} is that this method performs
* a graph-wide query, while traverse starts from a single element.
*
* @param discriminator the discriminator to apply on the query
* @param query the query to execute
* @param pager the page to return
* @return a page of results corresponding to the parameters, possibly empty, never null.
*/
Page<E> query(Discriminator discriminator, Query query, Pager pager);
E querySingle(Discriminator discriminator, Query query);
/**
* Translates the query to the backend-specific representation and runs it, returning a correct page of results
* as prescribed by the provided pager object.
*
*
* @param discriminator the discriminator to apply on the query
* @param startingPoint the element which should be the starting point of the traversal
* @param query the query to perform
* @param pager pager to limit the number of results with
* @return the page of results, possibly empty, never null
*/
Page<E> traverse(Discriminator discriminator, E startingPoint, Query query, Pager pager);
E traverseToSingle(Discriminator discriminator, E startingPoint, Query query);
/**
* A variant of the {@link #query(Discriminator, Query, Pager)} method which in addition to querying also converts the results
* using the provided conversion function and, more importantly, filters the results using the provided (possibly
* null) filter function PRIOR TO paging is applied.
*
* <p>Because the total count and the paging is dependent on the filtering it needs to be applied during the
* querying process and not only after the fact be the caller.
*
* @param <T> the type of the returned elements
* @param discriminator the discriminator to apply on the query
* @param query the query to perform
* @param pager the page to retrieve
* @param conversion a conversion function to apply on the elements, never null
* @param filter possibly null filter to filter the results with @return the page of results according to the supplied parameters
*/
<T> Page<T> query(Discriminator discriminator, Query query, Pager pager, Function<E, T> conversion,
Function<T, Boolean> filter);
/**
* Going from the starting poing, this will return an iterator over all elements that are connected to the starting
* point using relationships with provided name and recursively down to the elements connected in the same way to
* them.
*
* @param discriminator the discriminator to apply on the query
* @param startingPoint the starting element
* @param direction any of the valid directions including
* {@link Relationships.Direction#both}.
* @param relationshipNames the names of the relationships to follow when composing the transitive closure
* @return an iterator over the transitive closure, may be "lazy" and evaluate the closure on demand.
*/
Iterator<E> getTransitiveClosureOver(Discriminator discriminator, E startingPoint,
Relationships.Direction direction,
String... relationshipNames);
/**
* Checks whether there exists any relationship in given direction relative to the given entity with given name.
*
* @param discriminator the discriminator to apply on the query
* @param entity the entity in question
* @param direction the direction the relationship should have relative to the entity (
* {@link Relationships.Direction#both} means "any" in this
* context).
* @param relationshipName the name of the relationship to seek
* @return true if there is such relationship, false otherwise
* @see #getRelationships(Discriminator, Object, Relationships.Direction, String...)
*/
boolean hasRelationship(Discriminator discriminator, E entity, Relationships.Direction direction,
String relationshipName);
/**
* Checks whether there exists a relationship with given name between the provided entities.
*
*
* @param discriminator
* @param source the source of the relationship
* @param target the target of the relationship
* @param relationshipName the name of the relationship
* @return true, if such relationship exists, false otherwise
*/
boolean hasRelationship(Discriminator discriminator, E source, E target, String relationshipName);
/**
* Similar to {@link #hasRelationship(Discriminator, Object, Relationships.Direction, String)} but this method actually returns
* the relationship objects.
*
*
* @param discriminator
* @param entity the entity in question
* @param direction the direction in which the relationships should be going
* @param names the names of the relationships to return
* @return the possibly empty set of the relationships, never null
* @see #hasRelationship(Discriminator, Object, Relationships.Direction, String)
*/
Set<E> getRelationships(Discriminator discriminator, E entity, Relationships.Direction direction, String... names);
/**
* Get a single relationship with the provided name between the source and target.
*
*
* @param discriminator
* @param source the source of the relationship
* @param target the target of the relationship
* @param relationshipName the name of the relationship
* @return the relationship
* @throws ElementNotFoundException if the relationship is not found
* @throws IllegalArgumentException if source or target are not entities or relationship name is null
*/
E getRelationship(Discriminator discriminator, E source, E target, String relationshipName) throws ElementNotFoundException;
/**
*
* @param discriminator the discriminator to apply on the query
* @param relationship the relationship in question
* @return the source of the relationship
*/
E getRelationshipSource(Discriminator discriminator, E relationship);
/**
*
* @param discriminator
* @param relationship the relationship in question
* @return the target of the relationship
*/
E getRelationshipTarget(Discriminator discriminator, E relationship);
/**
* @param relationship the relationship in question
* @return the name of the relationship
*/
String extractRelationshipName(E relationship);
/**
* The element type is opaque from the point of the caller. This method provides the caller with the ability to
* extract the ID of the entity represented by the object.
*
* @param entityRepresentation the object representing an element
* @return the ID
*/
String extractId(E entityRepresentation);
/**
* Similar to {@link #extractId(Object)} but extracts the type of element from the representation.
*
* @param entityRepresentation the representation object.
* @return the type of the object represented
*/
Class<?> extractType(E entityRepresentation);
/**
* Each element (including relationships) stores the canonical path to it. This will extract that value from the
* entity representation.
*
* @param entityRepresentation the representation object
* @return the extracted canonical path
*/
CanonicalPath extractCanonicalPath(E entityRepresentation);
/**
* Extracts the identity hash from the provided entity.
*
*
* @param discriminator the discriminator to apply on the query
* @param entityRepresentation the representation object
* @return the identity hash of the entity or null if not supported for that type of entity
*/
String extractIdentityHash(Discriminator discriminator, E entityRepresentation);
String extractContentHash(Discriminator discriminator, E entityRepresentation);
String extractSyncHash(Discriminator discriminator, E entityRepresentation);
/**
* Converts the provided representation object to an inventory element of provided type.
*
* <p>This must support all the concrete subclasses of {@link AbstractElement}, {@link StructuredData} <b>and</b>
* {@link ShallowStructuredData}.
*
* @param <T> the desired type of the element
* @param discriminator
*@param entityRepresentation the object representing the element
* @param entityType the desired type of the element @return the converted inventory element
* @throws ClassCastException if the representation object doesn't correspond to the provided type
*/
<T> T convert(Discriminator discriminator, E entityRepresentation, Class<T> entityType);
/**
* Given the representation of the data entity, this will return the representation of a structured data element
* on the given path "inside" the data entity.
*
*
* @param discriminator
* @param dataEntityRepresentation the representation of the {@link org.hawkular.inventory.api.model.DataEntity}
* instance
* @param dataPath the path in the data to descend to.
* @see org.hawkular.inventory.api.Data.Single#data(RelativePath)
*/
E descendToData(Discriminator discriminator, E dataEntityRepresentation, RelativePath dataPath);
/**
* Creates a new relationship from source to target with given name and properties.
*
* @param discriminator the discriminator to apply on the query
* @param sourceEntity the source of the relationship
* @param targetEntity the target of the relationship
* @param name the name of the relationship
* @param properties the properties of the relationship, may be null
* @return the representation of the newly created relationship
* @throws IllegalArgumentException if source or target are relationships themselves or if name is null
*/
E relate(Discriminator discriminator, E sourceEntity, E targetEntity, String name, Map<String, Object> properties);
/**
* Persists a new entity with the provided assigned path.
*
*
* @param discriminator
* @param path the canonical path to the entity
* @param blueprint the blueprint of the entity
* @return the representation object of the newly created entity
*/
E persist(Discriminator discriminator, CanonicalPath path, Blueprint blueprint);
/**
* Persists the structured data and returns a reference to it. It is the responsibility of the caller to wire it up
* to some other entity by some relationship.
*
* @param structuredData the structured data to persist
* @return the representation of the newly persisted structured data
*/
E persist(StructuredData structuredData);
/**
* Updates given entity with the data provided in the update object.
*
* @param discriminator the discriminator to apply on the query
* @param entity the entity to update
* @param update the update object
* @throws IllegalArgumentException if the entity is of different type than the update
*/
void update(Discriminator discriminator, E entity, AbstractElement.Update update);
/**
* Updates the various hashes on the entity
*
* @param discriminator the discriminator to apply on the query
* @param entity the entity to update the hashes of
* @param hashes the hashes to update
*/
void updateHashes(Discriminator discriminator, E entity, Hashes hashes);
/**
* Simply marks the entity as deleted.
*
* <p>Must fail if there was any update to the entity after the time specified by the discriminator.
*
* @param discriminator the discriminator to apply on the query
* @param entity the entity to delete
* @throws IllegalArgumentException if there was an update after the designated time
*/
void markDeleted(Discriminator discriminator, E entity);
/**
* Conmpletely removes the entity and all its contained entities from storage.
*
* @param entity the entity to remove
*/
void eradicate(E entity);
/**
* Deletes the structured data represented by the provided object.
*
* @param dataRepresentation the backend-specific object representing the structured data to delete
*/
void deleteStructuredData(E dataRepresentation);
/**
* Commits the transaction.
*/
void commit() throws CommitFailureException;
/**
* Rolls back the transaction.
*/
void rollback();
/**
* The query results might sometimes return elements that are not representable in the inventory API because they
* are an implementation detail of the backend. This will tell the API.
*
* @param element the element to check
*/
boolean isBackendInternal(E element);
/**
* See the javadoc in {@link org.hawkular.inventory.api.Inventory#getGraphSON(String)}
*/
InputStream getGraphSON(Discriminator discriminator, String tenantId);
<T extends Entity<?, ?>> Iterator<T> getTransitiveClosureOver(Discriminator discriminator,
CanonicalPath startingPoint,
Relationships.Direction direction, Class<T> clazz,
String... relationshipNames);
/**
* Checks the exception thrown during the commit and returns true if the backend requires explicit rollback after
* such failure occured or false if the failure caused the transaction to close itself automatically.
*
* @param t the exception thrown during commit
* @return true to explictly roll back or false if the exception already caused the transaction to close
*/
default boolean requiresRollbackAfterFailure(Throwable t) {
return true;
}
/**
* Lists the changes of the entity. Note that this is not supported for edges...
*
* @param from from when to return the changes
* @param to to when to return the changes
* @return a history object containing the changes made to the entity in the given time-frame.
*/
<T extends Entity<?, U>, U extends Entity.Update>
EntityHistory<T> getHistory(E entity, Class<T> entityType, Instant from, Instant to);
}