Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cz.cvut.kbss.jopa.id;

import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.ontodriver.Connection;

/**
* Generates identifiers for entities.
*/
public interface IdentifierGenerator {

/**
* Generate identifier for the specified entity.
*
* @param entity Entity for which to generate the identifier
* @param entityClass Entity class
* @param connection Storage connection
* @return Generated identifier
*/
<T> Object generate(Object entity, EntityType<T> entityClass, Connection connection);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cz.cvut.kbss.jopa.model.annotations;

import cz.cvut.kbss.jopa.id.IdentifierGenerator;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Specifies the identifier generator to use for the entity.
*/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IdGenerator {

/**
* The identifier generator class.
*/
Class<? extends IdentifierGenerator> value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cz.cvut.kbss.jopa.id;

import cz.cvut.kbss.ontodriver.Connection;
import cz.cvut.kbss.ontodriver.exception.IdentifierGenerationException;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
import cz.cvut.kbss.ontodriver.model.Assertion;
import cz.cvut.kbss.ontodriver.model.AxiomImpl;
import cz.cvut.kbss.ontodriver.model.NamedResource;
import cz.cvut.kbss.ontodriver.model.Value;

import java.net.URI;
import java.util.Set;

/**
* Common superclass with utilities for identifier generation.
*/
public abstract class AbstractIdentifierGenerator implements IdentifierGenerator {

/**
* Checks if a class assertion for the specified identifier exists in the repository.
* <p>
* This check is done against the default context.
*
* @param identifier Instance identifier
* @param classIri Class identifier
* @param connection Repository connection
* @return {@code true} if the class assertion exists, {@code false} otherwise
* @throws IdentifierGenerationException If unable to check if the identifier exists
*/
protected boolean exists(URI identifier, URI classIri, Connection connection) {
try {
return connection.contains(new AxiomImpl<>(NamedResource.create(identifier),
Assertion.createClassAssertion(false),
new Value<>(classIri)), Set.of());
} catch (OntoDriverException e) {
throw new IdentifierGenerationException("Unable to check if identifier '" + identifier + "' exists.", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cz.cvut.kbss.jopa.id;

import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.ontodriver.Connection;
import cz.cvut.kbss.ontodriver.exception.IdentifierGenerationException;

import java.net.URI;
import java.util.Objects;
import java.util.Random;

public class RandomNumberIdentifierGenerator extends AbstractIdentifierGenerator {

private static final int GENERATION_THRESHOLD = 64;

private static final Random RANDOM = new Random();

@Override
public <T> URI generate(Object entity, EntityType<T> entityClass, Connection connection) {
Objects.requireNonNull(entity);
Objects.requireNonNull(entityClass);
Objects.requireNonNull(connection);

final URI classUri = entityClass.getIRI().toURI();
int counter = 0;
while (counter++ < GENERATION_THRESHOLD) {
final URI id = generate(classUri);
if (!exists(id, classUri, connection)) {
return id;
}
}
throw new IdentifierGenerationException("Unable to generate a unique identifier.");
}

private URI generate(URI classUri) {
if (classUri.getFragment() != null) {
return URI.create(classUri + "_instance" + RANDOM.nextInt());
} else {
String base = classUri.toString();
if (base.endsWith("/")) {
return URI.create(base + "instance" + RANDOM.nextInt());
} else {
return URI.create(base + "/instance" + RANDOM.nextInt());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package cz.cvut.kbss.jopa.model;

import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
import cz.cvut.kbss.jopa.id.IdentifierGenerator;
import cz.cvut.kbss.jopa.id.RandomNumberIdentifierGenerator;
import cz.cvut.kbss.jopa.loaders.PersistenceUnitClassFinder;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
Expand Down Expand Up @@ -61,6 +63,8 @@ public class MetamodelImpl implements Metamodel, MetamodelProvider, MetamodelCla
// Proxy classes for results of EntityManager.getReference
private final Map<Class<?>, Class<?>> referenceProxyClasses = new ConcurrentHashMap<>();

private Map<EntityType<?>, IdentifierGenerator> idGenerators;

private NamedQueryManager namedQueryManager;
private ResultSetMappingManager resultSetMappingManager;
private NamedEntityGraphManager namedEntityGraphManager;
Expand Down Expand Up @@ -108,6 +112,7 @@ private void initFromMetamodelBuilder(MetamodelBuilder metamodelBuilder) {
this.namespaceResolver = metamodelBuilder.getNamespaceResolver();
this.typeReferenceMap = metamodelBuilder.getTypeReferenceMap();
this.lazyLoadingProxyClasses.putAll(metamodelBuilder.getLazyLoadingEntityProxyClasses());
this.idGenerators = metamodelBuilder.getIdGenerators();
}

/**
Expand Down Expand Up @@ -176,6 +181,10 @@ public NamedEntityGraphManager getNamedEntityGraphManager() {
return namedEntityGraphManager;
}

public IdentifierGenerator getIdentifierGenerator(EntityType<?> entityType) {
return idGenerators.getOrDefault(entityType, new RandomNumberIdentifierGenerator());
}

@Override
public Set<URI> getModuleExtractionExtraSignature() {
return Collections.unmodifiableSet(getSignatureInternal());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
package cz.cvut.kbss.jopa.model.metamodel;

import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
import cz.cvut.kbss.jopa.id.IdentifierGenerator;
import cz.cvut.kbss.jopa.loaders.PersistenceUnitClassFinder;
import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
import cz.cvut.kbss.jopa.model.NamedEntityGraphManager;
import cz.cvut.kbss.jopa.model.NamedEntityGraphProcessor;
import cz.cvut.kbss.jopa.model.TypeReferenceMap;
import cz.cvut.kbss.jopa.model.annotations.IdGenerator;
import cz.cvut.kbss.jopa.model.annotations.Inheritance;
import cz.cvut.kbss.jopa.model.annotations.InheritanceType;
import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty;
Expand All @@ -38,7 +40,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -333,6 +337,32 @@ public Map<Class<?>, Class<?>> getLazyLoadingEntityProxyClasses() {
return Collections.unmodifiableMap(lazyLoadingProxyClasses);
}

public Map<EntityType<?>, IdentifierGenerator> getIdGenerators() {
final Map<Class<? extends IdentifierGenerator>, IdentifierGenerator> generators = new HashMap<>();
final Map<EntityType<?>, IdentifierGenerator> result = new HashMap<>();
for (EntityType<?> et : getEntities().values()) {
if (et.getJavaType().isInterface() || !et.getIdentifier().isGenerated()) {
continue;
}
final IdGenerator idGeneratorAnn = et.getIdentifier().getJavaField().getAnnotation(IdGenerator.class);
if (idGeneratorAnn == null) {
continue;
}
final IdentifierGenerator gen = generators.computeIfAbsent(idGeneratorAnn.value(), cls -> {
try {
final Constructor<? extends IdentifierGenerator> constructor = cls.getDeclaredConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException | SecurityException e) {
throw new MetamodelInitializationException("Custom identifier generator class " + cls + " is missing public no-arg constructor.");
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new MetamodelInitializationException("Failed to create instance of custom identifier generator " + cls, e);
}
});
result.put(et, gen);
}
return result;
}

public Set<AnnotatedAccessor> getAnnotatedAccessorsForClass(IdentifiableType<?> k) {
return annotatedAccessors.getOrDefault(k, Collections.emptySet());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface EntityMappingHelper extends ConfigurationHolder {

<T> EntityType<T> getEntityType(Class<T> cls);

URI generateIdentifier(EntityType<?> et);
Object generateIdentifier(Object entity, EntityType<?> et);

<T> T getOriginalInstance(T clone);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ Object extractFieldValueFromInstance(Object instance) {
}

<E> URI resolveValueIdentifier(E instance, EntityType<E> valEt) {
URI id = EntityPropertiesUtils.getIdentifier(instance, valEt);
Object id = EntityPropertiesUtils.getIdentifier(instance, valEt);
if (id == null) {
id = mapper.generateIdentifier(valEt);
id = mapper.generateIdentifier(instance, valEt);
EntityPropertiesUtils.setIdentifier(id, instance, valEt);
}
return id;
return id instanceof URI uri ? uri : URI.create(id.toString());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package cz.cvut.kbss.jopa.oom;

import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.oom.exception.UnpersistedChangeException;
import cz.cvut.kbss.jopa.sessions.util.AxiomBasedLoadingParameters;
Expand Down Expand Up @@ -81,14 +80,6 @@ public interface ObjectOntologyMapper {
*/
<T> void loadFieldValue(T entity, FieldSpecification<? super T, ?> fieldSpec, Descriptor descriptor);

/**
* Generates a fresh identifier for an instance of the specified entity type.
*
* @param et Entity type used as base for the identifier
* @return New entity identifier
*/
URI generateIdentifier(EntityType<?> et);

/**
* Persists the specified entity into the underlying ontology.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,8 @@ public <T> void persistEntity(URI identifier, T entity, Descriptor descriptor) {
}

@Override
public URI generateIdentifier(EntityType<?> et) {
try {
return storageConnection.generateIdentifier(et.getIRI().toURI());
} catch (OntoDriverException e) {
throw new StorageAccessException(e);
}
public Object generateIdentifier(Object entity, EntityType<?> et) {
return uow.getMetamodel().getIdentifierGenerator(et).generate(entity, et, storageConnection);
}

private <T> void persistPendingReferences(T instance, NamedResource identifier) {
Expand Down Expand Up @@ -293,23 +289,25 @@ public <T> T getEntityFromCacheOrOntology(Class<T> cls, URI identifier, ObjectGr
if (orig != null) {
return orig;
}
return defaultInstanceLoader.loadCached(getEntityType(cls), identifier, objectGraphInfo.descriptor()).orElseGet(() -> {
if (instanceRegistry.containsKey(identifier)) {
final Object existing = instanceRegistry.get(identifier);
if (!cls.isAssignableFrom(existing.getClass())) {
throw individualAlreadyManaged(identifier);
}
// This prevents endless cycles in bidirectional relationships
return cls.cast(existing);
} else {
// setup loading params
LoadingParameters<T> params = initEntityLoadingParameters(cls, identifier, objectGraphInfo);
return axiomsPerEntity.containsKey(identifier) ? loadEntityFromAxioms(params) : loadEntityInternal(params);
}
});
}

private <T> LoadingParameters<T> initEntityLoadingParameters(Class<T> cls, URI identifier, ObjectGraphInfo objectGraphInfo) {
return defaultInstanceLoader.loadCached(getEntityType(cls), identifier, objectGraphInfo.descriptor())
.orElseGet(() -> {
if (instanceRegistry.containsKey(identifier)) {
final Object existing = instanceRegistry.get(identifier);
if (!cls.isAssignableFrom(existing.getClass())) {
throw individualAlreadyManaged(identifier);
}
// This prevents endless cycles in bidirectional relationships
return cls.cast(existing);
} else {
// setup loading params
LoadingParameters<T> params = initEntityLoadingParameters(cls, identifier, objectGraphInfo);
return axiomsPerEntity.containsKey(identifier) ? loadEntityFromAxioms(params) : loadEntityInternal(params);
}
});
}

private <T> LoadingParameters<T> initEntityLoadingParameters(Class<T> cls, URI identifier,
ObjectGraphInfo objectGraphInfo) {
return new LoadingParameters<>(cls, identifier, objectGraphInfo.descriptor(), objectGraphInfo.fetchGraph(), false, uow.isReadOnly());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ private Object initEntityIdentifier(Object entity, EntityType<Object> et) {
Object id = getIdentifier(entity);
if (id == null) {
EntityPropertiesUtils.verifyIdentifierIsGenerated(entity, et);
id = storage.generateIdentifier(et);
id = getMetamodel().getIdentifierGenerator(et).generate(entity, et, storage.getConnection());
EntityPropertiesUtils.setIdentifier(id, entity, et);
}
return id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.oom.ObjectOntologyMapper;
import cz.cvut.kbss.jopa.oom.ObjectOntologyMapperImpl;
Expand Down Expand Up @@ -76,10 +75,6 @@ public <T> void merge(T entity, FieldSpecification<? super T, ?> fieldSpec, Desc
mapper.updateFieldValue(entity, fieldSpec, descriptor);
}

public URI generateIdentifier(EntityType<?> et) {
return mapper.generateIdentifier(et);
}

public <T> void persist(Object identifier, T entity, Descriptor descriptor) {
final URI idUri = getIdentifierAsUri(identifier);
mapper.persistEntity(idUri, entity, descriptor);
Expand Down Expand Up @@ -165,6 +160,10 @@ public RepositoryMetadata getRepositoryMetadata() {
return connection.getRepositoryMetadata();
}

public Connection getConnection() {
return connection;
}

@Override
public <T> T unwrap(Class<T> cls) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,6 @@ public Collection<Axiom<?>> find(AxiomDescriptor descriptor) {
@Override
public void persist(AxiomValueDescriptor descriptor) {}

@Override
public URI generateIdentifier(URI classUri) {
return null;
}

@Override
public void update(AxiomValueDescriptor descriptor) {}

Expand Down
Loading
Loading