Skip to content
This repository has been archived by the owner on Oct 12, 2021. It is now read-only.

On-the-fly multiple inheritance to match RDF types #120

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

berezovskyi
Copy link
Contributor

I had many conversations with Jim and Jad about the impedance mismatch between Java programming model and the RDF model as a whole. OSLC itself tried to fix it with shapes. We then tried fixing it in Lyo with Lyo Store. After Jim shared with me papers about the ActiveRDF design, I thought that Groovy would allow us to implement similar functionality in the most direct way possible.

Recently, I had chat with Jad that it would be awesome to unmarshal the following piece of RDF:

:req1 a oslc_rm:Requirement, rdfs:Resource;
    dcterms:identifier "REQ-1";
    dcterms:title "Hello";
    dcterms:description "Dummy requirement" .

into an instance of the most relevant shape but also make it implement all the interface matching each RDFS class. This would be the most usable way to reduce the impedance mismatch without forcing people to use bits of Groovy code. We agreed that it would be way over our heads to generate such bytecode on the fly.

Today I was leaning about Eclipse Microprofile and ended up reading a bit of code of the Apache Tamaya code that made use of the java.lang.reflect.Proxy class and I immediately knew I hit the jackpot.

In this PR you can see the implementation of the core idea for this feature. Extra work would have to be done on reflection in order to remove the need to specify all the Java classes during unmarshalling.

Right now the code is invoked like this:

val aRequirement: IRequirementShape = OslcShapeGenerator.newShape(resource,
        RequirementShape::class.java, IRequirementShape::class.java,
        listOf(Requirement::class.java, Resource::class.java))

All of the assertions pass:

assertTrue(aRequirement.identifier == "REQ-1")
assertTrue(aRequirement is IRequirementShape)
assertTrue(aRequirement is Requirement)
assertTrue(aRequirement is Resource)

This means both that the interfaces have been assigned properly and that the RequirementShape getters are called correctly (which is instantiated by the JMH itself).

The PR is not ready for merging but the idea is ready for

Signed-off-by: Andrew Berezovskyi <[email protected]>
@berezovskyi berezovskyi self-assigned this Aug 25, 2019
@SuppressWarnings("ALL")
public class OslcShapeGenerator {
public static <T extends IExtendedResource> T newShape(Resource r, Class<? extends T> shapeClass, Class<T> shapeInterface,
List<Class> rdfInterfaces)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use/prefer varargs here, they are more flexible for caller.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix


import org.junit.Assert.*

class OslcShapeGeneratorTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know kotlin may be easier/quicker to use, but do we really want mixing languages in lyo? Isn't this forcing potential contributors to learn another language in order to contribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. But we need to evolve. I thought that starting from unit tests is fairly low-risk (tests can still be written in Java).

final T shape = (T) Proxy.newProxyInstance(shapeInterface.getClassLoader(),
proxyClasses.toArray(new Class[0]), (proxy, method, args) -> {
System.out.printf("%s called\n", method.toString());
return method.invoke(shapeBean, args);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If, at the end, we are going to rely on shapeClass to being implementing all rdfInterfaces methods, I still cannot see the worth of this change. In other words, if shapeClass does not implement any of the methods declared on any rdfInterfaces classes, this line will fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line will never fail for the reasons you listed (though never say never!). The reason is that all interfaces are supposed to have no methods at all! These interfaces are 1-1 mapping to the RDF types of a resource and are not designed to be used in an OO way (eg for polymorphism). The main advantage is a support of pattern matching of rdf:type values via reflection. The concrete class is still mapped to a shape. Think of this as bringing a tiny bit of open-world thinking to Java. But you are right, the change is fairly minimal technically and you'd need Kotlin (read about when() {} here: https://kotlinlang.org/docs/reference/control-flow.html#when-expression) or Scala to enjoy this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there is an idea to map those on the fly to the entries in the extendedProperies! Maybe will be a good addition.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants