ClassCastException with Lyo 4.1

Hi,

After my upgrade to Lyo 4.1.0 I receive the following ClassCastException when iterating over a query result:

Exception in thread "main" java.lang.ClassCastException: org.eclipse.lyo.oslc.domains.rm.RequirementCollection cannot be cast to org.eclipse.lyo.oslc.domains.rm.Requirement

The query itself wasn’t changed:

        OslcQueryParameters parameters = new OslcQueryParameters();
        parameters.setSelect("*");

        OslcQuery query = new OslcQuery(client, queryCapability, parameters);
        OslcQueryResult result = query.submit();

        Response response = result.getRawResponse();
        if (response.getStatus() != HttpStatus.SC_OK) {
            throw new RuntimeException(response.getStatusInfo().getReasonPhrase());
        }

        for (Requirement bean: result.getMembers(Requirement.class)) {
              ...
        }

What must I do to avoid that exception?

Looking at the code it’s apparent that the old class org.eclipse.lyo.client.oslc.resources.RequirementCollection extends Requirement, hence can be cast to it.

But the new class org.eclipse.lyo.oslc.domains.rm.RequirementCollection just extends AbstractResource, duplicating all the code of Requirement, and, hence, can not be cast to Requirement.

What’s the ratioale of this design change and how am I supposed to accomodate it?

I am not sure why RequirementCollection used to extend Requirement, maybe @jamsden knows. I don’t think oslc_rm:RequirementCollection was ever a rdfs:subClassOf oslc_rm:Requirement (I asked on the standardization group’s mailing list just in case). It was not the case for the old OSLC RM 2.0 spec either. I guess one could consider a need for a superclass to be able to link to either RequirementCollections or Requirements, but that change should be made to the OSLC RM spec model.

The new classes are generated automatically from the RDF definitions of the standard. rdfs:subClassOf relationship translates into an extends of a corresponding class. For example, see org.eclipse.lyo.oslc.domains.cm.ChangeNotice. There is a slight problem with subclassing of classes vs interfaces, which I reported on Lyo Designer.

If you have improvement ideas, we are open. I logged the superclass issue in the standardization group tracker. Historically, the OSLC standard was shying away from RDFS due to the concerns of reasoning burden, but that still should not have resulted in too lax modelling (which it did and by the time I became a co-chair, the concerns of backwards compatibility were too great to be put aside). For example, oslc_rm:decomposedBy property should have had the shape range of Requirement|RequirementCollection but instead has just Resource (which is Java’s Object in RDF) OSLC Requirements Management Version 2.1. Part 3: Constraints

Thanks for the fast reply.

Somewhere I read that one of the differences between RDF and the OOP world is that in RDF a set of applicable statements induces an instanceof relationship with a concept. Given that RequirementCollection duplicates all the property definitions of Requirement, it should really be modeled as a subtype of Requirement.

On the other hand I imagine that years can pass by until both the specification and the (reference?) implementation have accomodated this insight. While I have a deadline next week; and nobody has recommended, yet, how to fix my broken product.

I anticipate that there are only two approaches to deal with the API incompatibility:

  1. Execute two distinct queries, one for the Requirements and one for the RequirementCollections. That’s slightly slower and causes duplicate effort (code in our product) to collect the sets of (logically) identical properties from the two concepts.

  2. Copy the entire Requirement/RequirementCollection classes (who needs the interfaces?!) together with all their referenced types such as Link, fix the inheritance problem, and finally get rid of the offending oslc-domains.jar. Of course, copying code from other frameworks is always nasty and decouples us from their evolution.

What I don’t fully understand, yet, is the relation of the java.lang.Class literal (“beanClass”) that I have to pass into the getMembers() method and the java.lang.reflect.Constructor that is called by Lyo. I see that Lyo performs a classpath/annotation scan to find bean class candidates for the Jena Resource that’s currently processed. In ResourcePackages.getClassOf() my “beanClass” gets renamed to “preferredType” and the result of that method is then renamed to “mostConcreteResourceClass” and replaces my original beanClass. Nothing in that code checks whether this mostConcreteResourceClass is compatible with my original beanClass. From this perspective it was probably just a fortune that in old Lyo a RequirementCollection accidentally was a Requirement and that my queries did not return objects of a third type.

Above you say that “Resource is Java’s Object in RDF”. I have the feeling that a public and concrete class (with public constructors) for this most-generic concept is missing in Lyo. In the end, and apparently, a single query can result in multiple objects that are not assignment-compatible to each other.

Hi,

Does it make sense for a RequirementCollection to be a subclass of Requirement? Semantically, they mean different things (to me at least). Given that, it would be confusing to get a RC Resource that also claims to be an actual Requirement.

As for the “most-generic concept”, have you considered using AnyResource ? This will give you a java object with the properties listed under an ExtendedProperties list.

Another way of dealing with the “offending” oslc-domains.jar would be to get it as source code, do the necessary changes and build it locally.

Hi Jad,

Literally a RequirementCollection is not a Requirement, otherwise, strictly, it would have to be called RequirementCollectionRequirement :wink:

But, as I mentioned before, in RDF an instance represents a concept if it has all the properties of that concept. So the question should really be “Why does the OSLC specification define all Requirement properties also for RequirementCollection?”.

More practically, I followed your recommendation to use AnyResource as the bean class. Our application operates purely reflectively on the beans, anyways. And AnyResource works well for us when used on a query’s result members.

BUT: I could not find out how to load a resource/uri into an AnyResource directly. The naive approach via OslcClient.getResource() fails at the point where JenaModelHelper.fromJenaModel(Model, Class<?>) is called because AnyResource has no ResourceShape annotation (or any other annotation, for that matter).

Am I missing something? Or am I supposed to replicate all the Jena model handling that happens in/under OslcQueryResult.getMembers(Class)?

While waiting for hints I came up with this approach, which is working well:

    private AnyResource loadBean(String url) {
        Response response = null;

        try {
            OslcClient oslcClient = getOslcClient();
            response = oslcClient.getResource(url, OSLCConstants.CT_RDF);

            Model rdfModel = ModelFactory.createDefaultModel();
            rdfModel.read(response.readEntity(InputStream.class), url);

            Resource resource = rdfModel.getResource(url);
            if (resource != null) {
                return JenaModelHelper.unmarshal(resource, AnyResource.class);
            }

            throw new RepositoryAdapterException("Resource could not be loaded: " + url);
        } catch (Exception ex) {
            throw RepositoryAdapterException.wrap(ex);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

Is there really no API in Lyo that would do this for me?