EulerGUI Architecture
© Jean-Marc Vanel - $Date: 2012-09-01$ - under Creative Commons License
TOC
There is an N3 translation of the bytecode of EulerGUI, made with EulerGUI, that can be used with EulerGUI or other tools to make queries and statistics:
Project is the algorithmic facade. This UML diagram shows its structure:
sources * Project<>------------N3Source <>---------------| n3Query | <>---------------| searches * |
Project has a (partial) publish-subscribe pattern :
void addProjectListener(ProjectListener listener)
It is partial because other events like inference engine launch, add a sub-project, etc, are not published. PENDING Adding a sub-project or a post-processing project raises the issue that the Project object changes and is re-created as a different object and class, which is problematic with observers.
The class Project is too big. We should extract all inference engines bindings, and have an SPI and an API for that.
N3Source is the other central class; it has a sub-class per type of data converted to N3:
eulergui.inputs.N3SourceFromClassesTree eulergui.inputs.N3SourceFromOtherFormat eulergui.inputs.N3SourceFromPlainXML eulergui.inputs.N3SourceFromSPARQLSource eulergui.inputs.N3SourceFromXMI eulergui.inputs.N3SourceFromXML_Gloze eulergui.inputs.N3SourceFromXSD n3_project.N3SourceFromRDF
N3Source currently has no publish-subscribe pattern.
See below in Refactorings section.
ProjectGUI, the main GUI class, does a bit too much, but it mostly does GUI layout and delegates the application behavior to the XXXManagement classes.
DESIGN PROPOSAL
Threads should be used for long operations, mainly inference runs. It also has the advantage to be able to break these operations.
We actually need a singleton for the EulerGUI application
EulerGUI <>---- * ProjectGUI<>----N3Source
TODO explain ?????????????
Implemented in class DroolsTripleHandler, it leverages on the N3 iterator, class RDFIterator . The Drools code that is generated is described here in the Manual: "Translating N3 into Drools language". The N3 triples that are interpretable as Java calls and instantiations are described below in N3 - Java mapping (embedded Java objects) .
DESIGN PROPOSAL
????????????????????
It also leverages on the N3 iterator, class RDFIterator .
TODO explain more ?????????????
It also leverages on the N3 iterator, class RDFIterator .
Implemented in class Instanciator class.
The code that is generated is described here in the Manual: "Generate Java code".
It is used in Generated Swing applications .
This architecture replaced the JavaScript+Java translator used in versions up to 1.8 . It is used in Internal Knowledge Base (the ApplicationKB class), and in the generated Swing applications .
This is made to enable the N3 rule engine to trigger actions on pure Java implementation objects. The main flow of information goes in the direction N3 ==> Java, and consists of creating new Java objects and calling methods or properties on them. However, the reverse direction is possible :
:myJavaObject javap:prop ?VALUE
.
The general idea (compatible with older N3 to
JavaScript+Java translator ) is the following. A triple such as this in the
antecedent of a rule stands for a test on existence of a Java object of class
p.MyClass
bound to name ?OBJ :
?OBJ a java:p-MyClass. |
The same triple in the consequent of a rule, or as fact triple, stands for the instantiation of a Java object of class p.MyClass bound to ?OBJ , a name, blank node, or URI. In any case, the URI or blank node bound to ?OBJ can be involved in ordinary (non Java) RDF triples. So one has the power to annotate an URI as a Java object, or the converse.
Likewise, a triple like this is interpreted as a property call:
?OBJ javap:myProperty ?PROP . |
which is a logical test on the antecedent side, and an actual call on the consequent side or outside of a rule. In the latter case, this actually associates an URI to the return value of the property call.
Here is a complete example outside of a rule (it shows a Swing JFrame with text "Papilio" ) :
_:obj a java:javax-swing-JFrame . _:obj javap:contentPane _:PANE . _:LAB a java:javax-swing-JLabel ; javam:setText "Papilio" . _:PANE javam:add _:LAB . |
which is equivalent to:
_:obj a java:javax-swing-JFrame . _:obj javam:getContentPane _:PANE . _:LAB a java:javax-swing-JLabel ; javam:setText "Papilio" . _:PANE javam:add _:LAB . |
where prefix javam:
indicates a method call. Here only when the
variable in object position is not already bound to a Java instance, and the
method has no argument, the return value is assigned to the N3 term. Otherwise,
in particular with methods having arguments, bound arguments are used for the
method call, e.g. javam:setText
just above.
These property or method call patterns also work on the consequent side of a rule . Examples are here: N3 rule javaMappingAccessFieldAsN3Variable.n3 ; project BloodPressure.n3p . See also this page for a commented example and good practices in mixing Java and N3: N3 rules triggering Java .
A triple in a rule consequent like this is always interpreted as a Java method call :
?INST javam:myMethod ( ?ARG1 ?ARG2 ) . |
But the nice thing is that you don't have to instantiate ?INST in the same rule.
In the course of the inference, the instantiation of ?INST can happen in another rule, before or after the method call. This is what we call deferred Java calls; it allows for greater flexibility in the way you design mixed Java+N3 rules.
We leverage on the capability of Drools engine to host any Java object in
the Knowledge Base (called Object Oriented RETE) . So we insert Java
application objects in the KB along with business objects, which are
implemented by Triple
objects). That is, we insert the Java objects in the Knowledge Base (KB)
(without passing through a terminal stage of instantiation as in previous
architecture N3 to JavaScript+Java translator ).
The Java objects are wrapped in a simple class Assignment, that adds a global name to the Java object. Assignment has two fields: name, a string, and reference, the object itself. The class Assignment is a simple Java bean with this constructor and corresponding properties:
|
The instances of Assignment in the Drools KB are a map of Java objects (the reference field) keyed by N3 terms (the name field).
Assignment objects are introduced into the KB in one of two ways. They can
be inserted from the Java API, by method
TripleStoreDrools.assign()
. Or they can be inserted from an N3
rule, as is detailed below.
The N3 to Drools translation is implemented in class N3JavaMappingBuiltin .
DONE:
In the following example, let MyClass
be any class, in the
package p. Below we give the N3 triple, and the Java translation in Drools DRL
language in the antecedent and in the consequent.
Class membership:
?OBJ a java:p-MyClass. _:OBJ2 a java:p-MyClass. pf:OBJ3 a java:p-MyClass. # 3 <http://ex.com#OBJ> a java:p-MyClass. # 4 |
Translation in the antecedent:
Assignment( name == $OBJ, reference.class == p.MyClass, $OBJ_reference : reference ) Assignment( name == "_:OBJ2", reference.class == p.MyClass, $OBJ2_reference : reference ) Assignment( name == "<http://mySite.org#OBJ3>", reference.class == p.MyClass, $OBJ3_reference : reference ) |
NOTES:
$OBJ : name
will be used instead of name == $OBJ
when the Drools variable
$OBJ has not yet been created.$V_object = Boolean.create( $V )
Translation in the consequent or in plain triples :
$OBJ_reference = new p.MyClass(); insert(new Assignment($OBJ, $OBJ_reference )); insert(new Assignment("_:OBJ2", $OBJ_reference )); insert(new Assignment("<http://mySite.org#OBJ3>", $OBJ_reference )); |
NOTE: we will implement OBJ2 first. We have no use case (yet?) for a Java object being associated to an URI; this object would be potentially visible from the Internet.
Method or property call:
?OBJ java:myProperty ?PROP . |
Translation in the antecedent
Assignment( $OBJ : name, reference.class == p.MyClass, $OBJ_reference : reference ) p.MyClass( $PROP : myProperty, this == $OBJ_reference ) |
Note that method call not recommended in the antecedent, because of potential side effects, but property is OK.
Translation in the consequent or in plain triples :
DeferredPropertyAssignment dpa_OBJ3 = new DeferredPropertyAssignment(); dpa_OBJ3.setSubject( OBJ.toString() ); dpa_OBJ3.setPredicate( "<http://java.sun.com/class#myProperty>" ); dpa_OBJ3.setObject( $PROP ); |
NOTE:
- Casting will also occur on argument, even when it is of primitive type.
- The normal case is when been referred to as a Java object in the antecedent side. In this case the class name, e.g. p.MyClass , will be obtained from the method
DroolsTripleHandler.javaTypeFromN3Variable(String var)
Java object coming from another rule
The issue that this solves is this: in several occasions an RDF Id is assigned Java properties before it is assigned a Java type (see above "Java object coming from another rule" ).
A special case is when ?OBJ has not been referred to as a Java object in the antecedent or consequent side. In this case there are several designs.
The solution is a runtime mechanism. A new Java infrastructure class
DeferredPropertyAssignment
keeps track of the property assignments and method calls, in cases like this .
Then, when later (or before as the rule firing order is unpredictable) the Java
type gets known from an object creation triple, the Java call can actually be
made.
There are a Drools rules that says: "when there is a deferred property assignment ?X ?P ?V , and ?X is a Java object, then apply the property assignment and delete the deferred property assignment". These rules allow to wait until calling object and arguments (if not literal) have all been assigned a Java object.
Notes:
DeferredPropertyAssignment
objects are
inserted in the RETE memory in the consequent side ;Here is the rule in Drools language (infrastructure-rules.drl); this could not be expressed in N3. This is an "under the hood rule", that will be added to every rule base.
rule "DeferredPropertyAssignment literal argument" when $DPA : DeferredPropertyAssignment( $S : subject, $O : object ) $A : Assignment( name == $S ) eval( eulergui.util.N3TermHelper.isLiteral($O) ) ) then $DPA.assign( $A ); retract( $DPA ); end rule "DeferredPropertyAssignment object argument" when $DPA : DeferredPropertyAssignment( $S : subject, $O : object ) $A : Assignment( name == $S ) $ARG : Assignment( name == $O ) then $DPA.assign( $A, $ARG ); retract( $DPA ); end |
Status
The feature is implemented, and it is activated in the user Drools runs ( the Swing rules for form generation worked with very few modifications ).
In the API, the feature is activable with this method in class Project :
setJavaObjectsInDrools( boolean jod ) |
It is activated in the internal rule engine.
In know exactly what is implemented and works, look at the test class BasicRuntimeTest .
Related changes:
:x a java:javax-swing-JFrame . :x javam:setVisible ( true ) .
What Java objects are inserted in Drools/N3 engine :
( you know that anything can be inserted in a Drools KB ).
Also, there are several singleton objects prefilled by the framework:
gui:GUIKBAdaptor
of type GUIKBAdaptereg:STORAGE
of type ITripleJavaStoreDuring the user session, these N3 events are inserted in the Internal Knowledge Base, con,foming to the concept of "Event Condition Action (ECA)".
eg:n3SourceChanged eg:uri <n3SourceUri> . _:d eg:hasOpenedProject <projectUri> .
Familiarly referred to as the "conscience of the application", it is work in progress, to be released after release 1.9 .
It is implemented in singleton class ApplicationKB , which contains a Drools RETE Knowledge Base, and subscribes to application and model (Project) events. It is populated by :
$HOME/.eulergui/preferences.n3
The details of the distribution of work between Java and rules are not carved in stone, but the general principle is "the software makes decisions based on formal rules, not on conventional procedural code".
During the current session, the Internal Knowledge Base will receive and accumulate events; it is how we are going to implement the Déductions project's vision of the "good servant".
DESIGN PROPOSAL - WIP
We need a lightweight Extension points architecture, allowing to minimize the number of places to modify when adding features.
Inspired by Spring and the eclipse Extension points, we thought of this declarative framework, where the extensions (a.k.a. plug-ins) are specified in N3 syntax, following the naming conventions for Java object instantiations, property and method call explained here: see Generate Java code , N3 - Java mapping .
Here is a first simple example of an extension specification in N3, where we add to EulerGUI an N3 Data Source:
# create an instance of N3SourceFromJavaByteCode _:ds a java:eulergui-inputs-N3SourceFromJavaByteCode. # add this instance to EulerGUI _:EulerGUI javam:addDataSource ( _:ds ) . |
The added instance of N3SourceFromJavaByteCode must implement the interface N3SourceFactory .
public interface N3SourceFactory { boolean canCreateFromURI(URI uri, URI baseUri); N3Source createFromURI(URI uri, URI baseUri) throws N3SourceException; } |
Then the facade method addDataSource() will add the instance of N3SourceFromJavaByteCode to a list of N3SourceFactory in the global factory SourceFactory .
Conceptually, the extension specification is similar to a Spring configuration file.
This N3 file is added to the Internal Knowledge Base. Prior to inference
launch, the _:EulerGUI
N3 blank node is associated to the EulerGUI
Java singleton instance :
tripleStore.assign( "_:EulerGUI", eulerGUI ); |
For an infrastructure KB, this call must happen before any N3 source is added.
For Java extensions in N3, the chronological sequence is:
due to the N3==>Java translations (see preceding paragraph "N3 - Java mapping"), the Java instantiations and calls happen at once
We will implement this for the Internal Knowledge Base first; see below "Include rules to extend EulerGUI behavior". Then we will refactor the generated Swing application to use this.
It is possible to write an N3 rule base that will check the type correctness of the Java calls made in an extension specification in N3.
Extension points need to be equiped with interfaces, extension points, and extensions. We need to find a lightweight architecture for that. With the current implementation in ProjectChangeSupport, adding a new extension point needs an intervention at 3 places:
Extension point need to be represented by a class :
class ExtensionPoint { void add( Extension extension ); /** fire Event to all registered extensions */ void fireEvent( Event e ); } interface Extension { /** receive notification of Event */ public void notify( Event e ); } |
So the previous configuration example would be extended to this :
# create an instance of N3SourceFromJavaByteCode _:ds a java:eulergui-inputs-N3SourceFromJavaByteCode. # add this instance to EulerGUI _:EulerGUI javam:addExtension ( _:ds java:eulergui-inputs-N3SourceFactoryExtensionPoint ) . |
The method addExtension()
in the application singleton
EulerGUI
will add the Extension in a Map of Sets keyed by the
Extension points classes.
To most Extension points, an event type is associated, and most event types will extend this:
class ProjectEvent extends Event { private Project project; }
Or this for N3 source events:
class N3SourceEvent extends ProjectEvent { private N3Source n3; }
(getters and setters are implicit).
The name of the class will indicate the event type, e.g. :
class UserSaveEvent extends N3SourceEvent { }
Here is a list of the "event" extension points, related to the main EulerGUI window :
There are also 2 events types, related to the editor window: Save and SaveAs.
There are also non event extension points, like the
N3SourceFactoryExtensionPoint
mentioned above.
New extensions to develop (many more in next paragraph):
Among the extension points, some are particularly interesting because they can be implemented partially or totally by a rule, in the Internal Knowledge Base .
Here is an example of a silly little rule that fires when an N3 Source From Java Class Tree is added, and changes its File Name to "tutu" and inactivates the N3 Source :
{ ?x a java:eulergui-inputs-N3SourceFromClassesTree . } => { ?x javam:setFileName "tutu". ?x javam:setActivated false . }. |
In this simple example, there is no event, or one could say the object appearance is the event. In general, we need to have consequences of events that fire just once, like e.g. most most extension for "Save in editor". There are at least two designs for this:
Design 2. is the one enabling processing of the user past events. During the current session, the Internal Knowledge Base will receive and accumulate events; it is how we are going to implement the Déductions project's vision of the "good servant".
Anyway, the Java wrapper will add the event as e.g.
@prefix eg: <http://deductions.sf.net/gui_generic.owl#> . _:ev a eg:UserSaveEvent; eg:about <mySource.n3> ; eq:timestamp "2010-..." . |
And after the event processing by "rule1" this will be asserted:
_:ev eg:status ( "consumed" "rule1").
The Java wrapper will look like this :
class RuleExtensionSupport implements Extension { public void notify( Event e ) { insertEventInKB( e.getClass().getSimpleName(), e.getN3Source(), timestamp() ); } } |
The currently implemented rules are in application-rules.n3.
Here is some work for the new embedded (a.k.a. internal) rule engine ( this engine is only aware of the user actions, the user configuration, and the projects ) :
A KB is maintained which contains all the triples in the Project; this is
the one that is already used for inferencing, and it is already resident in
memory. Let's call it "user data KB". It can be accessed with
Project.getWorkingMemory()
.
We need some application related queries to run on this "user data KB". But we are reluctant to alter this KB with non user rules. Of course, we can add a rule and remove it afterwards. To be thread safe, the operation of adding the temporary rule, and running the non user rule, must be synchronized. That is not needed if the rule is run through Euler.
Some features for the "user data KB" rule engine :
this implies launching a special rule base that analyses user rules and generates a skeleton for the properties and classes declarations not declared anywhere;
a rule base already exists, which warns of undeclared properties: see deductions/n3_new/model-rules-coherence.n3p
for recursion it needs the builtin log:parsedAsN3
(see CwmBuiltins;
this built-in lacks today in the Drools N3 engine )
( could also be done with a Java callback )
we need the association between row numbers and columns on the Statements;
to navigate from a resource to its definition, we assume it is declared in the project, and since the "user data KB" contains all triples of the project, a simple rule and query can do the job
For plain N3 URI
In an N3 query, one can use substring-after ( tested in test/concatenationQ.n3 ) to get the list of possible completions . When substring-after returns an empty string, it is not a possible completion.
One would also like to have the details about the completed suggestions. To do that, one can combine this with implementation for ordinary tooltips.
For this a rule base can do all the job, returning e.g. triples like:
eg:completion <http:my.com/foo#bar> "Foo Bar: the property FooBar means ..." .
The object could be formatted in HTML.
For prefixed N3 URI
It is more complicated, but one can get a map of prefix associations from
ParserLink.getKnownURIPrefixes()
Then, if the string to complete does not begin with "<" , nor is a number, nor a blank node, and does not include a ":", the Known URI Prefixes can be used for completion.
If the string to complete does include a ":", then expand the prefix abbreviation, and use plain N3 URI algorithm.
Inactivated sources
Inactivated sources should also be searched by the N3 editor to provide labels and comments about the predicates and classes. To achieve that, one possible design is to have an extra KB containing all the inactivated sources. This extra KB will be serached the same way as the main user KB for labels and comments.
When we will integrate the rules to flag undeclared terms, the generated declarations will be added to Inactivated sources, as well as a list of classic ontologies like OWL, FOAF, etc , if they are effectively used.
http://classycle.sourceforge.net/works.html
JDepend
TODO explain more <<<<<<<
DESIGN PROPOSAL
EulerGUI as a Protégé view.
EulerGUI will mostly stay as is, except for removals in the File menu of things (Open Project) that are already in Protégé. It seems that the largest task is to make an adaptor from the OWL API to the Project class.
Maybe we need first to have the N3 project file, see new N3 project file.
Official doc. on Protégé plugins :
http://protegewiki.stanford.edu/index.php/PluginAnatomy
Another question is : will Protégé Felix plugins work with Maven : see
http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html ?
TODO explain more <<<<<<<
DESIGN PROPOSAL
EulerGUI as an eclipse plugin.
This looks much more complex than the Protégé plugin. First there is the Swing-SWT affair. Then eclipse architecture is overly complex. Hopefully we can find another RDF or OWL plugin as starting point.
People will expect to find sub-projects and sources in a tree view, actions for N3 sources in a pull-down menu, etc.
TODO explain more <<<<<<<
DESIGN PROPOSAL
We could integrate D2R into EuleGUI.
TODO explain more <<<<<<<
DESIGN PROPOSAL
We need connectors (i.e. servers and clients) for the most standard protocols: Soap, Rest with XML, ... what more ?
This will reuse the XML Schema translators that we already have .
This is an oportunity to advance in the vision of the "intelligent modularity".
TODO explain more <<<<<<<
DESIGN PROPOSAL
We should design the intelligent extraction of data compatible with a query ; ask Martin what he thinks ... One idea is to limit a priori the depth of the extraction , for example in the FaceBook example it is 2 .
Note that RDF stores like Virtuoso that handle rdfs:subClassOf and rdfs:subPropertyOf could potentially with the same algorithms handle other transitive properties.
??????????????????????????
Being started as an exploration tool, EulerGUI has several good practices not implemented:
Internet cache ==> unified API for data input.
We defined an N3 based architecture for extension points, similar to Spring configuration files, but in N3.
Should we use another architecture like OSGi declarative services ?
We now have added in the ApplicationKB:
WORK IN PROGRESS : some architecture points to think about, like:
In EulerGUI we need to add other data sources, like Java source code (and other programming languages), Javadoc, various databases (JDBC, XML-DB, no-SQL stores), AI sources (KIF, CLIPS, TPTP, ...). Right now we plan Javadoc, and recently did Java source code .
For that we have this interface, yet unused, to model a source factory for a specific file format :
eulergui.inputs.N3SourceFactory
Then we need to to track all places where , e.g. , N3SourceFromRDF is created, and replace the call to constructor with a loop on all the N3SourceFactories . Plus a piece of code to setup all the N3SourceFactories; use extension point mechanism .
SimplifiedURI is a mess, it has so many implicit states. Maybe it should be re-implemented in Prolog.
It has improved, with many test cases. We implemented Simplified URI's like:
./dir/file.n3
but we also want that SimplifiedURI are re-evaluated when saving the project in a different place. TODO.
Instanciator class is also a mess, TODO:
@prefix jclass: <http://java.sun.com/class#> . @prefix java: <http://java.sun.com/property#> .
This class is too big. Work has begun to extract special features in separate plugins (see implementors of interface N3TranslationPlugin).
But we also have a more interesting project to make an N3 to Drools translator implemented as an N3 rule base.