protegeproject / swrlapi

Java API for working with the SWRL rule and SQWRL query languages
Other
99 stars 40 forks source link

Custom Built-in fails to load due to IllegalAccessException #51

Closed jannvck closed 5 years ago

jannvck commented 5 years ago

Hello,

I'm trying to implement a custom Built-in following these instructions. The version of the SWRLAPI I'm using is 2.0.6 from maven.

Please find the implementation of the SWRLBuiltInLibraryImpl class below:

package org.swrlapi.builtins.custom;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import org.swrlapi.builtins.AbstractSWRLBuiltInLibrary;
import org.swrlapi.builtins.SWRLBuiltInLibrary;
import org.swrlapi.builtins.arguments.SWRLBuiltInArgument;
import org.swrlapi.exceptions.InvalidSWRLBuiltInArgumentException;
import org.swrlapi.exceptions.SWRLBuiltInException;
import org.swrlapi.exceptions.SWRLBuiltInLibraryException;

public class SWRLBuiltInLibraryImpl extends AbstractSWRLBuiltInLibrary implements SWRLBuiltInLibrary {

    private static final String PREFIX = "custom";

    private static final String NAMESPACE = "http://localhost/builtin/testJava.owl#";

    private static final String[] BUILT_IN_NAMES = { "addOne" };

    public SWRLBuiltInLibraryImpl() {
        super(PREFIX, NAMESPACE, new HashSet<String>(Arrays.asList(BUILT_IN_NAMES)));
    }

    @Override
    public void reset() throws SWRLBuiltInLibraryException {
        // no internal state
    }

    public boolean addOne(List<SWRLBuiltInArgument> arguments) throws SWRLBuiltInException {
        // do some checks
        checkNumberOfArgumentsEqualTo(2, arguments.size());
        if (!isArgumentAFloat(1, arguments))
            throw new InvalidSWRLBuiltInArgumentException("argument 1 is not a float");
        if (isBoundArgument(1, arguments))
            throw new InvalidSWRLBuiltInArgumentException("argument 1 is bound");
        // calculate
        float arg0 = getArgumentAsAFloat(arguments.get(0));
        arguments.get(0).asVariable().setBuiltInResult(createLiteralBuiltInArgument(arg0 + 1.0f));
        return true;
    }

}

I have created a JAR file from it and put it in swrl-builtins/custom/ which seems to work (but is not documented anywhere!). Then I passed the path to the root directory containing the swrl-builtins folder as an argument to SWRLRuleEngine.loadExternalBuiltInLibraries():

...
SWRLRuleEngine ruleEngine = SWRLAPIFactory.createSWRLRuleEngine(ontology);
ruleEngine.loadExternalSWRLBuiltInLibraries(new File("path-to-project/swrl-builtins"));
...

The Built-in JAR seems to be found but the loading process fails with the following exception:

Exception in thread "main" org.swrlapi.exceptions.IncompatibleSWRLBuiltInClassException: incompatible Java built-in class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl: Class org.swrlapi.builtins.SWRLBuiltInLibraryManager can not access a member of class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl with modifiers "protected"
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.instantiateSWRLBuiltInLibraryImplementation(SWRLBuiltInLibraryManager.java:371)
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.loadExternalSWRLBuiltInLibraries(SWRLBuiltInLibraryManager.java:84)
    at org.swrlapi.factory.DefaultSWRLRuleAndQueryEngine.loadExternalSWRLBuiltInLibraries(DefaultSWRLRuleAndQueryEngine.java:344)
    at de.fzi.autoswift.swrl.SWRLTest$.delayedEndpoint$de$fzi$autoswift$swrl$SWRLTest$1(SWRLTest.scala:41)
    at de.fzi.autoswift.swrl.SWRLTest$delayedInit$body.apply(SWRLTest.scala:13)
    at scala.Function0.apply$mcV$sp(Function0.scala:34)
    at scala.Function0.apply$mcV$sp$(Function0.scala:34)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App.$anonfun$main$1$adapted(App.scala:76)
    at scala.App$$Lambda$5/2108649164.apply(Unknown Source)
    at scala.collection.immutable.List.foreach(List.scala:389)
    at scala.App.main(App.scala:76)
    at scala.App.main$(App.scala:74)
    at de.fzi.autoswift.swrl.SWRLTest$.main(SWRLTest.scala:13)
    at de.fzi.autoswift.swrl.SWRLTest.main(SWRLTest.scala)
Caused by: java.lang.IllegalAccessException: Class org.swrlapi.builtins.SWRLBuiltInLibraryManager can not access a member of class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl with modifiers "protected"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.Class.newInstance(Class.java:436)
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.instantiateSWRLBuiltInLibraryImplementation(SWRLBuiltInLibraryManager.java:368)
    ... 14 more

I have checked the source of SWRLBuiltInLibraryManager for this, you can find the corresponding line in the source.

I have also tried creating public methods for each protected method in AbstractSWRLBuiltInLibrary without any difference in the result...

jannvck commented 5 years ago

Maybe it's helpful to see also the higher level code. I tried an implementation in Scala which failed, so I also tried Java which also fails:

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.swrlapi.core.SWRLRuleEngine;
import org.swrlapi.factory.SWRLAPIFactory;

public class SWRLRunner {

    public static void main(String[] args) throws OWLOntologyCreationException {
        OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
        OWLOntology ontology = manager.loadOntologyFromOntologyDocument(new File("data/dev-example.rdf"));
        Map<String, String> map = new HashMap<String, String>();
        map.put("custom:", "http://localhost/builtin/testJava.owl#");
        manager.getOntologyFormat(ontology).asPrefixOWLOntologyFormat().copyPrefixesFrom(map);
        SWRLRuleEngine ruleEngine = SWRLAPIFactory.createSWRLRuleEngine(ontology);
        ruleEngine.loadExternalSWRLBuiltInLibraries(new File("path-to/swrl-builtins"));
    }

}

Exception and stack trace:

Exception in thread "main" org.swrlapi.exceptions.IncompatibleSWRLBuiltInClassException: incompatible Java built-in class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl: Class org.swrlapi.builtins.SWRLBuiltInLibraryManager can not access a member of class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl with modifiers "protected"
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.instantiateSWRLBuiltInLibraryImplementation(SWRLBuiltInLibraryManager.java:371)
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.loadExternalSWRLBuiltInLibraries(SWRLBuiltInLibraryManager.java:84)
    at org.swrlapi.factory.DefaultSWRLRuleAndQueryEngine.loadExternalSWRLBuiltInLibraries(DefaultSWRLRuleAndQueryEngine.java:344)
    at SWRLRunner.main(SWRLRunner.java:21)
Caused by: java.lang.IllegalAccessException: Class org.swrlapi.builtins.SWRLBuiltInLibraryManager can not access a member of class org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl with modifiers "protected"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.Class.newInstance(Class.java:436)
    at org.swrlapi.builtins.SWRLBuiltInLibraryManager.instantiateSWRLBuiltInLibraryImplementation(SWRLBuiltInLibraryManager.java:368)
    ... 3 more
martinjoconnor commented 5 years ago

Have you tried:

public class SWRLBuiltInLibraryImpl extends AbstractSWRLBuiltInLibrary {

instead of:

public class SWRLBuiltInLibraryImpl extends AbstractSWRLBuiltInLibrary implements SWRLBuiltInLibrary {
jannvck commented 5 years ago

Yes, I tried that in the first place. It was redundant anyway, as AbstractSWRLBuiltInLibraray implements this interface. You are right that this could be left out. Currently, I'm investigating on whether the issue might be related to some Java security restrictions.

The constructor of the custom SWRLBuiltInLibraryImpl class is public. Using reflection I found that the constructor access modifier is set to protected at runtime:

Constructor<?>[] constructors = swrlBuiltInLibraryImplementationClass.getDeclaredConstructors();
System.out.println(constructors[0]);

console output:

constructor: protected org.swrlapi.builtins.custom.SWRLBuiltInLibraryImpl()

I found that the ProtectionDomain of the SWRLBuiltInLibraryImpl class does not have read permission for the directory containing the Built-in jar. You can check the permissions using the following code:

System.out.println("protection domain: "+swrlBuiltInLibraryImplementationClass.getProtectionDomain()); Which prints in my case:

protection domain: ProtectionDomain  (file:/path-to/builtin-runnner/bin/ <no signer certificates>)
 sun.misc.Launcher$AppClassLoader@73d16e93
 <no principals>
 java.security.Permissions@7f5e9949 (
 ("java.lang.RuntimePermission" "exitVM")
 ("java.io.FilePermission" "/path-to/builtin-runner/bin/-" "read")
)

The Built-in jar resides not in this path or subdirectories of it. I also tried putting the jar into a subdirectory of this read-permitted path - without any difference.

jannvck commented 5 years ago

The problem was caused by my IDE (Eclipse). For some reason it did not use new compiled .class files. Removing the bin/ folder containing these, cleaning the project and recompiling solved the issue.

Three minor remarks:

martinjoconnor commented 5 years ago

Glad you got this working.

Thanks for the feedback. I will update the documentation and kill the unused variables.

Martin