Coder Social home page Coder Social logo

kbss-cvut / jopa Goto Github PK

View Code? Open in Web Editor NEW
34.0 11.0 14.0 116.68 MB

Java OWL Persistence API

Home Page: https://github.com/kbss-cvut/jopa/wiki

License: GNU Lesser General Public License v3.0

Java 99.93% Shell 0.01% ANTLR 0.06%
integrity-constraints owl oom java inheritance rdf

jopa's People

Contributors

dependabot[bot] avatar jenspiegsa avatar krupama3 avatar ledsoft avatar marcel-zec avatar psiotwo avatar sidonivs avatar syrcheddar avatar the-alchemist avatar yandoroshenko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jopa's Issues

Support for EntityManager.getReference

The EntityManager.getReference method should be supported by JOPA.

This would provide a performance boost in operations like removal or setting a reference to an entity, as the whole instance wouldn't have to be loaded. See the JPA specification for more details.

Should JOPA support automatic conversion to string?

@psiotwo
When an attribute is mapped to OWLDataProperty or OWLAnnotationProperty and the field type is String (or a collection of strings), should JOPA perform automatic conversion of values loaded from ontology to it, regardless of their actual type? For example:

class A {
@OWLAnnotationProperty(iri="dcterms:created")
private String created;
}

and the following triple:

ex:a dcterms:created "2018-10-28T23:59:59"^^xsd:dateTime .

Should it be loaded and automatically converted to String, or should it be skipped because its value type does not correspond to the field range? Or, should this be configurable?
Note that on update/insertion to repository, the triple value would be based on the attribute mapping, i.e., always string. So the dateTime-typed value would be replaced with a string in the above example on update.

It seems like a reasonable feature to support.

Named graph handling

It seems that the current implementation of named graph support is counter intuitive or at least incomplete.

Currently, when a context is set for an attribute in a descriptor, it means that the assertion is expected to be in the specified context. While this may make sense for literal values, it seems incorrect for entity references. In this case, one would expect the assertion to be in the subject's context and the target of the assertion to be in the referenced context.

Current status in example:

EntityDescriptor d = new EntityDescriptor(context1);
d.addAttributeContext(att1, context2);
d.addAttributeDescriptor(att2, new EntityDescriptor(context2));

Interpreted as:

:subject a :type :context1
:subject :att1 "object1" :context2
:subject :att2 :object2 :context2
:object2 a :type :context2

Expected, for att2 would be:

:subject a :type :context1
:subject :att2 :object2 :context1
:object2 a :type :context2

I.e., shouldn't the property assertions be always in the subject's context and the attribute context would be used only to specify where the rest of the attributes of the referenced entity are? Or should this be configurable.

Support for multilingual attributes

As a user, I need to be able to retrieve string property values in multiple languages in one persistence unit, which JOPA currently does not allow.

Collections AnnotationProperty attributes are not supported

The use-case I have is a set of dc:source annotations for a resource. dc:source property should be modeled as an owl:AnnotationProperty, not owl:DatatypeProperty. However JOPA throws a NotYetImplemented exception for the field

@OWLAnnotationProperty ...
Set sources

throws an exception.

Criteria API support

Implement support for the Criteria API.

This would especially improve possibilities of generic filtering in queries.

Allow to reload data in OntoDriver when using file-based storage

When a file-based storage is used (e.g. a TTL file for RDF4J/Jena driver or OWL file in OWL API driver), it would be nice to be able to reload the data from the file through some API.

This feature would be useful in cases when the file storage is used mostly for reading and an external agent may change the file contents. Being able to reload the driver would allow the application to access the updated data.

Streams in Query API

As a user, I want to be able to use Java 8 streams when working with queries.

Context is treated incorrectly when entity is in one context and attribute is in default

When an entity is stored (or to be stored) in a specific context, but one of its attributes is (or will be) stored in the default context, persist and retrieval do not reflect this fact and attempt to keep the attribute in the entity context.

This causes issues for example when working with GraphDB, which stores inferred statements in a separate context, so the user may set default context for attributes containing such data.

Allow to set in-memory repository on the RDF4J and Jena drivers

When using an in-memory storage in the Jena and RDF4J drivers, I want to be able to set the MemoryStore (for RDF4J/Sesame) or Dataset (for Jena).

Consider a case where data are generated directly using RDF4J or Sesame into a memory store/dataset. Then, the user would like to be able to access this data via JOPA.

Support for lexical form of literal values

Support for lexical form of literal values will have to following properties:

  • Annotation @LexicalForm will be added. It can be used together with @OWLAnnotationProperty and @OWLDataProperty.
  • Map to Java fields of type String.
  • Retrieval of the value will take the lexical form of the literal value and load it into the field.
  • Any updates to the field value will result in creation of a simple literal, i.e., literal with the specified lexical form and datatype http://www.w3.org/2001/XMLSchema#string.

This should make the feature compatible with RDF 1.1 and OWL (2), which still uses rdf:plainLiteral, but it has been superseded in RDF 1.1 by xsd:string.

Support for result set mapping

Similarly to JPA, JOPA should support mapping of SPARQL query results to objects based on the SparqlResultSetMapping (SqlResultSetMapping in JPA).

This would help for instance with performance, since it can be used to bulk load data using a single query.

Use rdfs:comment to generate Javadoc

When an OWL entity (class, property) is annotated with an rdfs:comment, OWL2Java should use it to generate javadoc for the resulting artifact (class, attribute, vocabulary constant).

Recursive serialization?

I have two entities - FunctionsDTO (one <=> many) FunctionDTO, mapped like this:

@OWLClass(iri = s_c_functions_dto)
public class FunctionsDTO extends AbstractEntity {
    @Sequence
    @OWLDataProperty(iri = s_p_has_function_dto, fetch = FetchType.EAGER)
    private List<FunctionDTO> functions;
...
}
@OWLClass(iri = s_c_function_dto)
public class FunctionDTO extends AbstractEntity {
...
}

The problem is that serialization only works on the top level of the hierarchy:

{
      "http://onto.fel.cvut.cz/ontologies/s-pipes/has-function-dto" : {
         "@list" : [
            "cz.cvut.kbss.spipes.model.dto.FunctionDTO@316840ee",
            "cz.cvut.kbss.spipes.model.dto.FunctionDTO@2a176721",
            "cz.cvut.kbss.spipes.model.dto.FunctionDTO@63226973"
         ]
      },
      "@type" : [
         "http://onto.fel.cvut.cz/ontologies/s-pipes/functions-dto"
      ]
   }

Am I doing something wrong?

Support for RDF simple literals

JOPA should support simple literals as datatype/annotation property values, i.e. having datatype http://www.w3.org/2001/XMLSchema#string, not requiring language tag.

Both loading and saving.

datatype property with type URI is not loaded into object model

In triplestore I have the following triple:
<http://example.org/question-1> <http://onto.fel.cvut.cz/ontologies/form/has-question-origin> <http://example.org/question-origin-1> .

Within JOPA I used the following mapping to object model:

@OWLDataProperty(iri = "http://onto.fel.cvut.cz/ontologies/form/has-question-origin")
 private URI origin;

Unfortunately, it did not load any data (i.e. class field origin was loaded with null value). As a workaround I can use :

@OWLObjectProperty(iri = "http://onto.fel.cvut.cz/ontologies/form/has-question-origin")
 private URI origin;

The workaround works well so it is not an issue to me, but I was wondering if it is correct behavior.

Reimplement EntityManager.refresh

Reimplement the EntityManager.refresh method, as its current implementation (UoW.revertObject) does not correspond to JPA behavior.

Support for separate Read/Write properties for a field

Currently, each field is accessed using the same property for reading as well as writing.

The goal is to separate these tasks:

  • for each JOPA field allow specifying a multiple read properties and multiple write properties
  • when writing a field, one write property is chosen to serialize the value of the field (TODO - how to specify which property - using descriptors?),
  • when reading a field, each read property is chosen to read from

Use-case: Providing support for specifying a subproperty of a given property upon data authoring.

Polymorphic instance loading strategies

When polymorphic instance load is requested, there are multiple strategies possible.

Consider the following example:

@OWLClass(iri="http://example.org/Person")
class Person {
...
}

@OWLClass(iri="http://example.org/Student")
class Student extends Person {
...
}

and a request to load an instance:

entityManager.find(Person.class, id);

We see two main strategies for this case:

1. Two-step Strategy

This strategy has the following execution plan:

  1. If EntityType<Person> represents entity without subtypes, load entity and exit.
  2. Otherwise, load types of individual id.
  3. If types contain class assertion for class http://example.org/Person, load instance as class Person.
  4. Otherwise, find the unique, most specific subclass of Person which corresponds to an ontological class contained in types of id.
  5. For example, if ontological types of id contain http://example.org/Student, load id as instance of class Student.
  6. In case there are multiple types representing subclasses of Person, throw an exception, because the load would be ambiguous.

If Person were abstract, step 1 could be skipped.

2. Try-first Strategy

  1. Load axioms for EntityType<Person> and individual id.
  2. If the axioms also contain class assertion axiom for http://example.org/Person, reconstruct a Person instance and return.
  3. Otherwise, determine the unique, most specific subclass of Person which corresponds to an ontological class asserted in the loaded axioms.
  4. In case it is for example http://example.org/Student, determine properties, which are in EntityType<Student> and not in EntityType<Person> and were thus not loaded in step 1.
  5. Load the remaining properties.
  6. Reconstruct an instance of class Student and return it.
  7. In case there were multiple matching ontological classes in step 3, throw an exception, because the load would be ambiguous.

If Person were abstract, step 2 could be skipped.

Discussion

Both strategies are suitable for different use cases.

Try-first strategy is useful in cases where subclasses of the superclass do not add any attributes and are used only as a means of classification of the instances. In this case, there would be only one call to the underlying OntoDriver which would load all the necessary data for reconstructing the entity.

Two-step strategy is suitable for cases where subclasses contain additional attributes, because it requires two calls to the underlying OntoDriver anyway, the first one to get the types and the second to load the actual data. It is also more straightforward than the Try-first strategy, because it does not involve any computation of difference between attribute sets of the superclass and the determined subclass.

Note that both strategies lead to the same result in the end - the correct instance being loaded, or an exception being thrown because of class ambiguity.

For the aforementioned reasons, the Two-step strategy should be the default one and will be implemented first.

problem with Sesame OntoDriver and class mapping

Hello,
I have been trying to map my RDF4J server ontology with some models in java for now 3 days, and I still get a lot of errors. Maybe you can help. I have tried to follow the methodology used in the reporting app, but with no more luck.

For example, I get mostly 2 kinds of error :

  • when calling GET /person, java.lang.IllegalArgumentException: Class com.example.test.model.Person is not a known entity in this persistence unit. At cz.cvut.kbss.jopa.model.MetamodelImpl.entity(MetamodelImpl.java:96) ~[jopa-impl-0.12.1.jar:na]
  • when calling GET /persons, Unable to map the query result to class class com.example.test.model.Person cz.cvut.kbss.jopa.exceptions.OWLPersistenceException: Unable to map the query result to class class com.example.test.model.Person at cz.cvut.kbss.jopa.model.TypedQueryImpl.loadResultValue(TypedQueryImpl.java:105)
    [...]
    SesameDriverException: No suitable constructor for value http://www.semanticweb.org/ontologies/library.owl#person1 found in type class com.example.test.model.Person

I have installed an RDF4J server, and imported my simple ontology composed of 2 classes - Person & Book - which look like that :

- <http://www.semanticweb.org/ontologies/library.owl#Person> rdf:type  rdfs:Class
- <http://www.semanticweb.org/ontologies/library.owl#name> rdfs:domain <http://www.semanticweb.org/ontologies/library.owl#Person>
- <http://www.semanticweb.org/ontologies/library.owl#placeOfBirth> rdfs:domain <http://www.semanticweb.org/ontologies/library.owl#Person>

So a person has a name and a placeOfBirth.

Some instances of the Person class are (lib: = http://www.semanticweb.org/ontologies/library.owl#)

- lib:person1 rdf:type lib:Person
- lib:person1 lib:name "Clint Eastwood"
- lib:person1 lib:placeOfBirth "San Francisco"
- lib:person2 rdf:type lib:Person
- lib:person2 lib:name "Marty McFly"
- lib:person2 lib:placeOfBirth "New York"

My Person Model looks like that :

@OWLClass(iri = "http://www.semanticweb.org/ontologies/library.owl#Person")
public class Person implements Serializable {

    @Id
    private URI uri;

    @OWLDataProperty(iri = "http://www.semanticweb.org/ontologies/library.owl#name")
    private String name;

    @OWLDataProperty(iri = "http://www.semanticweb.org/ontologies/library.owl#placeOfBirth")
    private String placeOfBirth;

    public Person(){}
    
    ... Getters & setters
}

My DAO looks like that :

@Repository
public class PersonDao extends BaseDao<Person> {

    protected PersonDao(){
        super(Person.class);
    }

    public List<Person> findAll(EntityManager em){
        List<Person> toto = em.createNativeQuery("SELECT ?x WHERE { ?x a ?type . }", Person.class).setParameter("type", typeUri)
                .getResultList();

        for(Person p : toto){
            System.out.println(p.getName());
            System.out.println(p.getPlaceOfBirth());
        }

        return toto;
    }

    public Person find(URI uri){
        Objects.requireNonNull(uri);
        final EntityManager em = entityManager();
        try {
            return findByUri(uri, em);
        } finally {
            em.close();
        }
    }

    protected Person findByUri(URI uri, EntityManager em) {
        return em.find(type, uri);
    }
}

My controller is like that :

@RestController
@RequestMapping("/tests")
public class TestController {

    private final PersonService personService;

    @Autowired
    public TestController(PersonService personService){
        this.personService = personService;
    }
    
   [...]

    @GetMapping(value = "/persons", produces = MediaType.APPLICATION_JSON_VALUE)
    public Collection<Person> getAllPersons(){
        return personService.findAll();
    }

    @GetMapping(value = "/person", produces = MediaType.APPLICATION_JSON_VALUE)
    public Person getPerson(){
        URI uri = 
    URI.create("http://www.semanticweb.org/webatrio/ontologies/library.owl#person1");
        return personService.find(uri);
    }

My PersistenceProvider looks like that :

@Configuration
@PropertySource("classpath:config.properties")
public class PersistenceFactory {

    private static final Map<String, String> DEFAULT_PARAMS = initParams();

    private final Environment environment;

    private EntityManagerFactory emf;

    @Autowired
    public PersistenceFactory(Environment environment) {
        this.environment = environment;
    }

    @Bean
    @Primary
    public EntityManagerFactory getEntityManagerFactory() {
        return emf;
    }

    @PostConstruct
    private void init() {
        // Allow Apache HTTP client used by RDF4J to use a larger connection pool
        // Temporary, should be configurable via JOPA
        System.setProperty("http.maxConnections", "20");
        final Map<String, String> properties = new HashMap<>(DEFAULT_PARAMS);
        properties.put(ONTOLOGY_PHYSICAL_URI_KEY, environment.getProperty(REPOSITORY_URL.toString()));
        properties.put(DATA_SOURCE_CLASS, environment.getProperty(DRIVER.toString()));

        this.emf = Persistence.createEntityManagerFactory("books", properties);
    }


    private static Map<String, String> initParams() {
        final Map<String, String> map = new HashMap<>();
        map.put(OntoDriverProperties.ONTOLOGY_LANGUAGE, "en");
        map.put(SCAN_PACKAGE, "com.example.test.model");
        map.put(JPA_PERSISTENCE_PROVIDER, JOPAPersistenceProvider.class.getName());
        return map;
    }

}

File RestConfig :

@Configuration
@ComponentScan(basePackages = "com.example.test")
public class RestConfig {

    @Bean
    public ObjectMapper objectMapper() {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
}

My service class looks like that :

@Service
public class PersonRepositoryService extends BaseRepositoryService<Person> implements PersonService {

    private final PersonDao personDao;

    @Autowired
    public PersonRepositoryService(PersonDao personDao){
        this.personDao = personDao;
    }


    @Override
    protected GenericDao<Person> getPrimaryDao() {
        return personDao;
    }
}

my config.properties :

repositoryUrl=http://localhost:8080/rdf4j-server/repositories/books2
driver=cz.cvut.kbss.ontodriver.sesame.SesameDataSource

For more information, the querying on the server is happening, the error occur when trying to map the results with the model.

Thank you for the help.

Support mapping owl:oneOf to enums

Currently, enums are mapped to plain data/annotation literals, so the information about the value being enumerated basically gets lost in the repository. At the same time, OWL already provides a mechanism of enumerating values - owl:oneOf. JOPA should support mapping to this.

Distinguishing string forms in the value sets of @Properties

Problem

When @properties are used they do not distinuguish between object IRIs, literals and their datatypes. For example. I have two OWL Classes:
@owlclass(iri="http://example.org#A")
class A {
@properties
private Map<String,Set> props;
@types
private Set types;
}

@owlclass(iri="http://example.org#B")
class B extends A {
@OWLDataProperty(iri="http://example.org#p")
private Integer p;
}

Then persisting a new instance of A, where the property is set through properties and the type B is set through types stores the property as a language-tagged literal. This makes the property unloadable into B through em.find(), because the type is Integer for B.

Proposal 1

Create a new Value class that serves for grouping IRIs and literals with datatypes/language tags

Proposal 2

Use a simple language in the values of the @properties map. Each string would be either:

  • '<'+IRI+'>' which would be interpreted as IRI and the property would be interpreted as an Object Property
  • plainliteral whicch would be interpreted as a plain literal and equipped with the PU default language tag, i.e. plainliteral@en
  • '"'+lexform+'"^^'+datatypeIRI which would be interpreted as a datatyped literal.

Allow using Lombok and JOPA in the same project

Currently, a project cannot use JOPA and Lombok due to problems in the Maven build configuration (AspectJ maven plugin and Lombok).
Find a way to configure the build so that both libraries can be used in a project.

Support specification of descriptors for object property collection elements

Currently, it is possible to specify a descriptor for an object property collection, i.e., a single descriptor is used for all elements of the collection. However, such functionality is not sufficient.

For example, consider the following class:

@OWLClass("skos:Concept")
public class Term {
    @OWLObjectProperty(iri="skos:broader")
    private Set<Term> parents;

    // ... other attributes
}

If the parent terms belong to different vocabularies, which are stored in separate contexts (RDF named graphs), it is currently not possible to reference them.

It should be possible to specify descriptors for individual elements of the collection

OWL2Java generates duplicate properties

When OWL2Java processes an ontology with imports or different contexts and a property/class is encountered multiple times, multiple constants are generated for these occurrences into the vocabulary file.

It should skip properties/classes which were already added.

Support for collection attributes

Currently, only ListAttribute and SetAttribute are supported.

Adding support for CollectionAttribute should be relatively easy. Its semantics would be set-based, as is usual with RDF/OWL.

Properly implement persistence context scopes

Currently, JOPA uses a mix of TRANSACTIONAL and EXTENDED persistence scope strategies.

Both scopes should be separated and implemented properly according to JPA spec, which is vague in certain aspects but the general idea is there.

Merge calculates changes incorrectly

When merging an instance with a reference to another instance, JOPA calculates changes on the reference and considers it a change if any of its attributes have changed, even though it should only check that it is the same instance. An example:

A.hasB = B;
B.name = "Test";
persist(A, B);
B.name = null;
result = merge(A)
=> hasChanges(A) == true, which is wrong, because B is still the same instance.
Correct behavior:
hasChanges(A) == false && result.hasB.name == "Test", because there is no cascading to B, so the value from storage should be used.

Virtuoso OntoDriver

As a user, I want to be able to use Virtuoso server as storage. This will, according to documentation, require a custom OntoDriver implementation using either Virtuoso's RDF4J or Jena provider.

The driver should probably allow the user to choose whether the RDF4J or Jena Virtuoso Provider will be used for connection. The other choice is to support only one of the providers for starters.

Note that Virtuoso code (including the providers) is licensed under GPLv2, so the driver, which will probably have to incorporate the provider(s) - as they are not published in Maven - will have to adhere to this license as well.

Relevant sources:

Non-deterministic Vocabulary generation

When generating a Vocabulary from scripts that have the same prefix defined for different values, there's no way to predict which one will get what Vocabulary entry.

Discovered by @blcham.

NoSuchMethodError while executing jopa-maven-plugin:owl2java-transform

jopa-maven-plugin:0.9.14:owl2java-transform fails within my project with error :
[ERROR] Failed to execute goal cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform (generate-sources) on project semantic-pipes-forms: Execution generate-sources of goal cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform failed: An API incompatibility was encountered while executing cz.cvut.kbss.jopa:jopa-maven-plugin:0.9.14:owl2java-transform: java.lang.NoSuchMethodError: com.google.inject.util.Types.collectionOf(Ljava/lang/reflect/Type;)Ljava/lang/reflect/ParameterizedType

It seems to me that the problem is in conflicting dependencies of the plugin:

~/.m2/repository/org/sonatype/sisu/sisu-guice/3.1.0/sisu-guice-3.1.0-no_aop.jar
~/.m2/repository/com/google/inject/guice/4.1.0/guice-4.1.0.jar

as both of them contain:
./com/google/inject/util/Types.class

more-complete-error.log

JDK9 support

JDK9 class loader is not a java.net.URLClassLoader anymore => cz.cvut.kbss.jopa.loaders.EntityLoader.discoverEntities fails with ClassCastException.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.