Difference between revisions of "Project Development"

From Developer Documents
Jump to navigation Jump to search
 
(93 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
= Overview =
 
= Overview =
  
In Simantics, a semantic database is conceptually broken into ''projects'' and ''database configuration''. A database can contain any number of projects but there are single parts of the database containing configuration data that is global for all projects.
+
From the Simantics project framework's point of view the content of a Simantics database is broken into ''projects'' and ''database configuration''. A database can contain any number of projects. The configuration data is global and shared by all projects. In essence, '''projects are database entities (resources)''', that consist of all the data produced and consumed when working on a subject with Simantics.
  
The database uses the following structure:
+
 
: <code>Root Library : L0.Library (=http:/)</code>
+
<div id="figure1">
::* A common entry point that always exists (see Builtins.URIs.RootLibrary)
+
[[File:Database_structure.svg|center|thumb|500px|Figure 1: Basic database structure from project framework's point of view. ([[:File:Database_structure.graphml|image source]])]]
 +
</div>
 +
 
 +
 
 +
Ok, structurally projects are database entities, how about behavior? Applications have interactive user interfaces, that exert some kind of behavior to perform actions on the project structure in the database. When a Simantics application is started for the purpose of working on a project, the project must be ''loaded''. Minimally, this only means finding the project resource. However, often it is also necessary to perform other kinds of initialization steps during project loading. This is where ''project features'' come into play. Project features are a mechanism for attaching configuration steps to project loading/unloading. See [[#figure2|Figure 2]] for a conceptual drawing.
 +
 
 +
 
 +
<div id="figure2">
 +
[[File:projects.svg|center|thumb|700px|Figure 2: The conceptual foundation of the project framework. ([[:File:projects.graphml|image source]])]]
 +
</div>
 +
 
 +
== Project Features ==
 +
 
 +
The Simantics project system is based on ''features''. Conceptually, a project feature in this context is a mechanism attaching life-cycle handling actions to the events of opening and closing a project. Technically, a project feature in is simply a code contribution that configures/deconfigures an [[svn:foundation/project/trunk/org.simantics.project/src/org/simantics/project/IProject.java|IProject]] object when it is activated/deactivated.
 +
 
 +
An ''IProject'' is an ''IHintContext''. The hint context aspect of IProject is used for runtime storage of all project-related properties. IHintContext allows other parties to listen to changes in these run-time project properties. For example, your project can contain properties that define project-specific services.
 +
 
 +
Project features can ''depend on'' (''require'') other features. This information is used to construct the order in which features configure/deconfigure a project, which is resolved through [[wikipedia:Topological_sorting|topological sorting]].
 +
 
 +
Project features can be marked as ''published''. This property is intended to be used for ''top-level features'' that configure a certain complete piece of functionality for a project. Top-level features are also intended to be presented to users in the ''Simantics Project Manager'' application (SPM, in development). Features can also be marked with ''installGroup'' definitions that are intended to be used to inform SPM of the Eclipse features that need to be installed into a provisioned workbench application in order for it to work with the particular project feature.
 +
 
 +
== Basic Database Structure ==
 +
 
 +
The most basic structure of each Simantics database is as follows:
 +
: <code>Root Library : L0.Library (<nowiki>&lt;http:/&gt;</nowiki>)</code>
 +
::* A common entry point that always exists (see ''Builtins.URIs.RootLibrary''). All resources and data that can be deemed ''reachable'' within a database must be browsable starting from this resource.
 
:: <code>Consists Of</code>
 
:: <code>Consists Of</code>
::: <code>Projects : L0.Library (=http://Projects)</code>  
+
::: <code>Projects : L0.Library (<nowiki>&lt;http://Projects&gt;</nowiki>)</code>  
 
::::* Consists of all projects contained by the database
 
::::* Consists of all projects contained by the database
::: <code>InstalledGraphBundles : L0.Entity (=http://InstalledGraphBundles)</code>
+
::: <code>InstalledGraphBundles : L0.Entity (<nowiki>&lt;http://InstalledGraphBundles&gt;</nowiki>)</code>
 
::::* Consists of the ontology bundles (.tg files) imported into this database. This is part of database configuration. See [[svn:foundation/ontologies/trunk/org.simantics.layer0/graph/DatabaseManagement.pgraph|DatabaseManagement.pgraph]] for the related ontology.
 
::::* Consists of the ontology bundles (.tg files) imported into this database. This is part of database configuration. See [[svn:foundation/ontologies/trunk/org.simantics.layer0/graph/DatabaseManagement.pgraph|DatabaseManagement.pgraph]] for the related ontology.
  
[[File:projects.png|center|frame|480px|The conceptual foundation of the project framework. ([[File:projects.graphml|image source]])]]
+
What is said here does not dictate what can be in a Simantics database. The database can contain arbitrary structures based on the Layer0-ontology. This text only focuses on the parts of the database that are relevant to the project framework.
 +
 
 +
== Terminology ==
 +
; Eclipse Feature: A ''Feature Project'' created in the Eclipse IDE to gather a specific set of functionality encapsulated in ''Eclipse Plug-ins'' into a group of plug-ins, i.e. a ''feature''. Eclipse features should encapsulate logical wholes, such as reusable generic utilities or UI or product-specific functionality.
 +
; Installable Unit (IU): An Eclipse P2 provisioning platform concept, see [http://wiki.eclipse.org/Equinox/p2/Concepts] and [http://wiki.eclipse.org/Installable_Units].
 +
; Project Feature: A mechanism for configuring a Simantics project framework -based project. Project features may depend on each other, but dependencies must be acyclic. Otherwise a working feature configuration order may not be resolvable.
 +
; Project loading: the process of loading a project, i.e. configuring all its project features in dependency order.
 +
 
 +
= Project Loading =
 +
 
 +
This section explains the process of loading a project, step-by-step.
 +
 
 +
As described in the previous section, every project in the database is attached to the the '''<nowiki>http://Projects</nowiki>''' library by convention. Shortly, the project loading consists of:
 +
# Resolving an ordered set of ''project features'' that need to be configured for the project runtime.
 +
# Activating the project, i.e. configuring the resolved project features for an '''org.simantics.project.IProject''' instance.
 +
 
 +
== Step 1: Configured Project Feature Resolution ==
 +
 
 +
Let's look at it through an example. Here is a synthetic example of a top-level project feature.
 +
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="xml">
 +
  <extension
 +
        point="org.simantics.project.feature">
 +
      <feature
 +
            class="org.example.project.ProjectFeature"
 +
            description="Example top-level project feature."
 +
            id="org.example.project"
 +
            label="Example Project"
 +
            published="true">
 +
        <requires id="org.simantics.simulation.experimentManager"/>
 +
        <installGroup id="org.example.feature.group" version="[1.0.0,2.0.0)"/>
 +
      </feature>
 +
  </extension>
 +
</syntaxhighlight>
 +
</div>
 +
There are two important lines to notice here. One is the '''<installGroup>''' definition and the other is the '''published="true"''' attribute. We'll come to the ''why'' shortly.
 +
 
 +
A project must contain definitions of what '''Eclipse Features''' are used with it. ''Eclipse Features'' are '''Feature Projects''' created for the Eclipse environment. Do not mistake them for ''project features'' that have been discussed so far. In our project feature example above, we can see the ''<installGroup>'' element stating '''id="org.example.feature.group"'''. Generally Eclipse feature projects are named '''<functionality>.feature''' and the ID given to them just leaves the '''.feature''' suffix out, leaving '''<functionality>''' as the feature ID. In our example, this gives us an Eclipse feature project named '''org.example.feature''' with ID '''org.example'''.
 +
 
 +
Why exactly does the ''<installGroup>'' say ''org.example.feature.group'' then? The reason for this is the [http://wiki.eclipse.org/Equinox/p2 Eclipse P2 provisioning platform]. When an Eclipse feature, such as '''org.example''' is exported into a P2 repository, the feature project will become an ''installable unit'' with ID '''org.example.feature.group'''. Since Simantics project management is planned on top of P2, we use these P2 concepts in the project framework also.
  
The project system of Simantics is based on ''features''. A feature in this context is simply an abstract means for configuring an [[svn:foundation/project/trunk/org.simantics.project/src/org/simantics/project/IProject.java|IProject]] object. An ''IProject'' is an ''IHintContext''. The hint context aspect of IProject is used for runtime storage of all project-related attributes, such as the currently selected model or services that can be used to perform project-type specific actions on the project in an abstract fashion. Implementing IHintContext allows for other parties to listen to changes in project attributes.
+
Due to all explained above, the project framework expects to find this ''used Eclipse features'' information attached to the project as follows (expressed in [[Graph File Format]]):
 +
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="python">
 +
... : PROJ.Project
 +
    PROJ.HasFeature
 +
        _ : PROJ.FeatureSpec
 +
            PROJ.HasGroupId "org.example.feature.group" : L0.String
 +
</syntaxhighlight>
 +
</div>
 +
Now we are ready to summarize the process of resolving the project features to configure:
 +
# Gather all ''HasGroupId'' property values from ''PROJ.FeatureSpec'' instances attached to the loaded project. Let's call this set  of group ID's '''GID'''.
 +
# Find all such project feature extensions that are marked with both ''published="true" '' and an installGroup that is contained within '''GID'''. Let's call this set of root project features '''ROOTS'''.
 +
# Resolve the final set of project features to load by taking a transitive closure of all project features reachable through ''<requires>'' definitions within project feature extensions starting from '''ROOTS'''. Notice that the requirement definitions specify the order in which the project features must be configured through [[wikipedia:Topological sorting|topological sorting]].
  
Project features can ''depend on'' (''require'') other features. This information is used to construct the order in which features configure/deconfigure a project, which is resolved through [[wikipedia:Topological_sorting|topological sorting]].
+
== Step 2: Activate a project ==
 +
 
 +
After the resolution has been completed, the actual activation, i.e. project feature configuration is trivial. It is performed by <code>IProject.activate()</code>. See [[#Load project from database and activate it|this scenario]].
 +
 
 +
== Simantics Project Management Application (SPM) ==
 +
 
 +
Currently we have planned a ''Simantics Project Management'' (SPM) application that would in the end be responsible for setting up and managing workspaces, their databases and the projects within those databases. The project management side includes managing the ''PROJ.FeatureSpec'' definitions attached to projects.
 +
 
 +
Since SPM is not yet usable, the Simantics Workbench platform code has a feature that works around this. When a new workspace and its database is initialized the platform code will add ''PROJ.FeatureSpec'' definitions to the project with the ''<installGroup>''s defined in all available project features marked with ''published="true"''. This makes the workbench work nicely even without SPM.
  
Project features can be marked as ''published''. This property is intended to be used for ''top-level features'' that configure a certain complete piece of functionality for a project. Top-level features are also intended to be presented to users in the ''Simantics Project Manager'' application (SPM, in development). Features can also be marked with ''installGroup'' definitions that are intended to be used to inform SPM of the Eclipse features that need to be installed into a provisioned workbench application in order for it to work with the particular project feature.
+
* [https://www.simantics.org/wiki/index.php/Project_Management SPM Plans]
  
= Adding configurability to a project =
+
= Project Features Step-by-step =
  
 
== Step 1: define extensions for your project features ==
 
== Step 1: define extensions for your project features ==
Line 30: Line 114:
 
The following example demonstrates the use of all current features of the extension point, namely project features, dependencies between project features (''requires'') and bindings between project features and Eclipse platform features (''installGroup''). This example assumes that you have a plug-in <code>myplugin</code> and a feature project <code>myplugin.feature (feature id=myplugin)</code> for it.
 
The following example demonstrates the use of all current features of the extension point, namely project features, dependencies between project features (''requires'') and bindings between project features and Eclipse platform features (''installGroup''). This example assumes that you have a plug-in <code>myplugin</code> and a feature project <code>myplugin.feature (feature id=myplugin)</code> for it.
  
<blockquote>
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
<source lang="xml">
+
<syntaxhighlight lang="xml">
 
   <extension
 
   <extension
 
         point="org.simantics.project.feature">
 
         point="org.simantics.project.feature">
Line 47: Line 131:
 
             id="myplugin.dummy"
 
             id="myplugin.dummy"
 
             label="My dummy feature">
 
             label="My dummy feature">
 +
      </feature>
 +
      <feature
 +
            class="myplugin.AnotherFeature"
 +
            id="myplugin.another"
 +
            label="My dummy feature">
 +
        <injectDependency targetId="myplugin.project"/>
 
       </feature>
 
       </feature>
 
   </extension>
 
   </extension>
</source>
+
</syntaxhighlight>
</blockquote>
+
</div>
  
The above contains two project features, the first one is published and requires the second one. This means that when loading a project that uses the '''myplugin.project''' feature, '''myplugin.dummy''' feature will be configured before '''myplugin.project'''.
+
The above contains three project features, the first one is published and requires the second one. The third one is injected as a dependency of the first one. This means that when loading a project that uses the '''myplugin.project''' feature, '''myplugin.dummy''' and '''myplug.another''' features will be configured before '''myplugin.project'''.
 +
 
 +
Note that here both '''requires''' and '''injectDependency''' describe the two mechanisms available for defining dependencies. Using '''injectDependency''' is recommended since it promotes proper decoupling and dependency injection.
  
 
== Step 2: define the code for the project features ==
 
== Step 2: define the code for the project features ==
  
* Copy the following classes into corresponding source files:
+
Copy the following classes into corresponding source files:
<blockquote>
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
<source lang="java">
+
<syntaxhighlight lang="java">
 
package myplugin;
 
package myplugin;
 
class MyProjectFeature extends AbstractProjectFeature {
 
class MyProjectFeature extends AbstractProjectFeature {
Line 66: Line 158:
 
     @Override
 
     @Override
 
     public void configure() throws ProjectException {
 
     public void configure() throws ProjectException {
        Collection<String> perspectives = new ArrayList<String>();
 
        perspectives.add(DEFAULT_PERSPECTIVE);
 
 
 
         getProject().setHint(ProjectKeys.DEFAULT_PERSPECTIVE, DEFAULT_PERSPECTIVE);
 
         getProject().setHint(ProjectKeys.DEFAULT_PERSPECTIVE, DEFAULT_PERSPECTIVE);
        getProject().setHint(ProjectKeys.PERSPECTIVES, perspectives);
 
 
     }
 
     }
  
Line 79: Line 167:
 
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
 +
</div>
  
 
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
<source lang="java">
+
<syntaxhighlight lang="java">
 
package myplugin;
 
package myplugin;
 
class MyDummyFeature extends AbstractProjectFeature {
 
class MyDummyFeature extends AbstractProjectFeature {
Line 89: Line 178:
 
     public void configure() throws ProjectException {
 
     public void configure() throws ProjectException {
 
         // Setup some hints if you need to
 
         // Setup some hints if you need to
         // Do something with the database
+
 
 +
         try {
 +
            // Do something with the database
 +
            setupDatabase();
 +
        } catch (DatabaseException e) {
 +
            throw new ProjectException(e);
 +
        }
 +
    }
 +
 
 +
    private void setupDatabase() throws DatabaseException {
 
         getSession().syncRequest(new WriteRequest() {
 
         getSession().syncRequest(new WriteRequest() {
 
             @Override
 
             @Override
Line 104: Line 202:
 
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
</blockquote>
+
</div>
 +
 
 +
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="java">
 +
package myplugin;
 +
class AnotherFeature extends AbstractProjectFeature {
 +
    @Override
 +
    public void configure() throws ProjectException {
 +
    }
 +
 
 +
    @Override
 +
    public void deconfigure() throws ProjectException {
 +
    }
 +
}
 +
</syntaxhighlight>
 +
</div>
  
 
== Optional Step: Using ready-made project features ==
 
== Optional Step: Using ready-made project features ==
  
 
This step is not necessary but is here both for information and educational value. This example shows how to add a feature to your project that advertises ontology dependencies for your project which is necessary for example if (full-text) indexing of ontology dependencies is desired.
 
This step is not necessary but is here both for information and educational value. This example shows how to add a feature to your project that advertises ontology dependencies for your project which is necessary for example if (full-text) indexing of ontology dependencies is desired.
 
Following the instructions at [[org.simantics.graph]] create a project to contain Simantics ontologies and add a new <code>.pgraph</code> file into it. You can fill the .pgraph with something like the following:
 
  
 
A new project feature, here '''myplugin.dependencies''', must also be added to the previous example:
 
A new project feature, here '''myplugin.dependencies''', must also be added to the previous example:
  
<blockquote>
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
<source lang="xml">
+
<syntaxhighlight lang="xml">
 
   <extension
 
   <extension
 
         point="org.simantics.project.feature">
 
         point="org.simantics.project.feature">
Line 126: Line 237:
 
             published="true">
 
             published="true">
 
         <requires id="myplugin.dummy"/>
 
         <requires id="myplugin.dummy"/>
        <!-- Added project feature dependency--->
 
        <requires id="myplugin.dependencies"/>
 
 
         <installGroup id="myplugin.feature.group"/>
 
         <installGroup id="myplugin.feature.group"/>
 
       </feature>
 
       </feature>
Line 134: Line 243:
 
             id="myplugin.dummy"
 
             id="myplugin.dummy"
 
             label="My dummy feature">
 
             label="My dummy feature">
 +
      </feature>
 +
      <feature
 +
            class="myplugin.AnotherFeature"
 +
            id="myplugin.another"
 +
            label="My dummy feature">
 +
        <injectDependency targetId="myplugin.project"/>
 
       </feature>
 
       </feature>
 
       <!-- Added project feature -->
 
       <!-- Added project feature -->
Line 140: Line 255:
 
             id="myplugin.dependencies"
 
             id="myplugin.dependencies"
 
             label="My plugin dependencies">
 
             label="My plugin dependencies">
 +
        <injectDependency targetId="myplugin.project"/>
 
         <!-- DependencyValidationFeature is the feature implementation. -->
 
         <!-- DependencyValidationFeature is the feature implementation. -->
 
         <!-- What is specified after the ':' character is given as -->
 
         <!-- What is specified after the ':' character is given as -->
Line 146: Line 262:
 
       </feature>
 
       </feature>
 
   </extension>
 
   </extension>
</source>
+
</syntaxhighlight>
</blockquote>
+
</div>
  
 +
The '''myplugin.dependencies''' feature definition gives <code>http://www.example.org/MyOntology-1.0/ImportedOntologies</code> as an argument to DependencyValidationFeature which will mark the dependency on the specified namespace requirement in the project.
  
<blockquote>
+
We still need to define the ontology containing the '''NamespaceRequirement''' we referred to in the feature. Following the instructions at [[org.simantics.graph]] create a project to contain Simantics ontologies and add a new <code>.pgraph</code> file into it. You can fill the .pgraph with something like the following:
<source lang="python">
+
 
 +
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="python">
 
L0 = <http://www.simantics.org/Layer0-1.0>
 
L0 = <http://www.simantics.org/Layer0-1.0>
 
PROJ = <http://www.simantics.org/Project-1.0>
 
PROJ = <http://www.simantics.org/Project-1.0>
Line 158: Line 277:
 
     @L0.new
 
     @L0.new
  
 +
//# The project feature refers to this instance
 
MY_ONTOLOGY.ImportedOntologies : PROJ.NamespaceRequirement
 
MY_ONTOLOGY.ImportedOntologies : PROJ.NamespaceRequirement
     L0.HasDescription "Specifies the ontologies required by a this project. Used e.g. for indexing the database."
+
     L0.HasDescription """Specifies the ontologies required by a this project.
 +
Used e.g. for indexing the database."""
 
     PROJ.RequiresNamespace
 
     PROJ.RequiresNamespace
 +
        "http://www.simantics.org/Layer0-1.0" : L0.URI
 
         "http://www.simantics.org/G2D-1.0" : L0.URI
 
         "http://www.simantics.org/G2D-1.0" : L0.URI
 
         "http://www.simantics.org/Diagram-2.0" : L0.URI
 
         "http://www.simantics.org/Diagram-2.0" : L0.URI
</source>
+
</syntaxhighlight>
</blockquote>
+
</div>
  
 
= Development =
 
= Development =
  
== Interfaces ==
+
== Framework Entry Points ==
  
[[File:project-api.png|center|frame|480px|Project framework API and internals. ([[File:project-api.graphml|image source]])]]
+
As shown in [[#figure2|figure 2]] there are two main entry-points in the project framework:
 +
; class Projects: A [[wikipedia:Facade_Pattern|facade]] class containing utilities for loading and managing projects in a Simantics database.
 +
; class ProjectFeatures: A facade class containing utilities for retrieving and filtering project feature extensions with different criteria. The implementation is backed by the ''project feature extension registry''.
 +
 
 +
 
 +
<div id="figure2">
 +
[[File:project-api.png|center|thumb|720px|Figure 2: Project framework API and implementations. ([[:File:project-api.graphml|image source]])]]
 +
</div>
  
 
== Scenarios ==
 
== Scenarios ==
 +
 +
=== Omnipresent Project Features ===
 +
 +
Consider the following scenario: A developer wishes to provide a plug-in that can be inserted into any Simantics workbench application in order to give the plug-in the chance to attach itself to the project worked on with that particular workbench instance.
 +
 +
Project features are a natural instrument for this purpose. The only problem in getting your project feature to load. It may be that your workspace's project does not employ any ''installGroup'' used by your project feature. Also your new project feature is probably not required by any of the project features that are active in your project.
 +
 +
For this purpose, we define a single installGroup ID that the project framework considers to always be included in the set of installGroups for any project. So:
 +
<blockquote>Use installGroup ID '''omnipresent''' for features that are meant to be used with any project. Also remember to set '''published=true'''.</blockquote>
 +
To define an omnipresent project feature, use the following template:
 +
 +
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="xml">
 +
  <extension
 +
        point="org.simantics.project.feature">
 +
      <feature
 +
            description="My omnipresent project feature."
 +
            class="myplugin.MyOmnipresentProjectFeature"
 +
            id="myplugin.omnipresent.feature"
 +
            label="Omnipresent feature"
 +
            published="true">
 +
        <installGroup id="omnipresent"/>
 +
      </feature>
 +
  </extension>
 +
</syntaxhighlight>
 +
</div>
  
 
=== Load project from database and activate it ===
 
=== Load project from database and activate it ===
  
<source lang="java">
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
     public IProject loadAndActivateProject(Session session, Resource project) throws DatabaseException, ProjectException {
+
<syntaxhighlight lang="java">
 +
     public IProject loadAndActivateProject(Session session, Resource project)
 +
            throws DatabaseException, ProjectException
 +
    {
 
         IProject project = Projects.loadProject(session, project);
 
         IProject project = Projects.loadProject(session, project);
 
         project.activate();
 
         project.activate();
 
         return project;
 
         return project;
 
     }
 
     }
</source>
+
</syntaxhighlight>
 +
</div>
  
=== Retrieving the active project ===
+
=== Retrieving the active project in a Simantics workbench application ===
  
 
The Simantics Workbench UI is built so that always when the user is working on a project, the UI will have both '''an active database session''' and '''an active project'''. The active project can be used to store any project-related services and data using the IHintContext interface it implements.
 
The Simantics Workbench UI is built so that always when the user is working on a project, the UI will have both '''an active database session''' and '''an active project'''. The active project can be used to store any project-related services and data using the IHintContext interface it implements.
Line 190: Line 349:
 
<code>SimanticsUI</code> ([[svn:foundation/trunk/org.simantics.ui/src/org/simantics/ui/SimanticsUI.java|SVN]]) is the class for retrieving both ''the active database session'' and ''the active project'' using these:
 
<code>SimanticsUI</code> ([[svn:foundation/trunk/org.simantics.ui/src/org/simantics/ui/SimanticsUI.java|SVN]]) is the class for retrieving both ''the active database session'' and ''the active project'' using these:
  
<source lang="java">
+
<div style="background-color:#f8f8f8; border: 1px dashed #cccccc; padding: 1ex; margin-left:2em; margin-top: 1em; margin-bottom:1em;">
 +
<syntaxhighlight lang="java">
 
public class SimanticsUI {
 
public class SimanticsUI {
 
     ...
 
     ...
Line 227: Line 387:
 
     ...
 
     ...
 
}
 
}
</source>
+
</syntaxhighlight>
 +
</div>
 +
 
 +
[[Category: Project Development]]

Latest revision as of 10:05, 1 August 2012

Overview

From the Simantics project framework's point of view the content of a Simantics database is broken into projects and database configuration. A database can contain any number of projects. The configuration data is global and shared by all projects. In essence, projects are database entities (resources), that consist of all the data produced and consumed when working on a subject with Simantics.


Figure 1: Basic database structure from project framework's point of view. (image source)


Ok, structurally projects are database entities, how about behavior? Applications have interactive user interfaces, that exert some kind of behavior to perform actions on the project structure in the database. When a Simantics application is started for the purpose of working on a project, the project must be loaded. Minimally, this only means finding the project resource. However, often it is also necessary to perform other kinds of initialization steps during project loading. This is where project features come into play. Project features are a mechanism for attaching configuration steps to project loading/unloading. See Figure 2 for a conceptual drawing.


Figure 2: The conceptual foundation of the project framework. (image source)

Project Features

The Simantics project system is based on features. Conceptually, a project feature in this context is a mechanism attaching life-cycle handling actions to the events of opening and closing a project. Technically, a project feature in is simply a code contribution that configures/deconfigures an IProject object when it is activated/deactivated.

An IProject is an IHintContext. The hint context aspect of IProject is used for runtime storage of all project-related properties. IHintContext allows other parties to listen to changes in these run-time project properties. For example, your project can contain properties that define project-specific services.

Project features can depend on (require) other features. This information is used to construct the order in which features configure/deconfigure a project, which is resolved through topological sorting.

Project features can be marked as published. This property is intended to be used for top-level features that configure a certain complete piece of functionality for a project. Top-level features are also intended to be presented to users in the Simantics Project Manager application (SPM, in development). Features can also be marked with installGroup definitions that are intended to be used to inform SPM of the Eclipse features that need to be installed into a provisioned workbench application in order for it to work with the particular project feature.

Basic Database Structure

The most basic structure of each Simantics database is as follows:

Root Library : L0.Library (<http:/>)
  • A common entry point that always exists (see Builtins.URIs.RootLibrary). All resources and data that can be deemed reachable within a database must be browsable starting from this resource.
Consists Of
Projects : L0.Library (<http://Projects>)
  • Consists of all projects contained by the database
InstalledGraphBundles : L0.Entity (<http://InstalledGraphBundles>)
  • Consists of the ontology bundles (.tg files) imported into this database. This is part of database configuration. See DatabaseManagement.pgraph for the related ontology.

What is said here does not dictate what can be in a Simantics database. The database can contain arbitrary structures based on the Layer0-ontology. This text only focuses on the parts of the database that are relevant to the project framework.

Terminology

Eclipse Feature
A Feature Project created in the Eclipse IDE to gather a specific set of functionality encapsulated in Eclipse Plug-ins into a group of plug-ins, i.e. a feature. Eclipse features should encapsulate logical wholes, such as reusable generic utilities or UI or product-specific functionality.
Installable Unit (IU)
An Eclipse P2 provisioning platform concept, see [1] and [2].
Project Feature
A mechanism for configuring a Simantics project framework -based project. Project features may depend on each other, but dependencies must be acyclic. Otherwise a working feature configuration order may not be resolvable.
Project loading
the process of loading a project, i.e. configuring all its project features in dependency order.

Project Loading

This section explains the process of loading a project, step-by-step.

As described in the previous section, every project in the database is attached to the the http://Projects library by convention. Shortly, the project loading consists of:

  1. Resolving an ordered set of project features that need to be configured for the project runtime.
  2. Activating the project, i.e. configuring the resolved project features for an org.simantics.project.IProject instance.

Step 1: Configured Project Feature Resolution

Let's look at it through an example. Here is a synthetic example of a top-level project feature.

<syntaxhighlight lang="xml">

  <extension
        point="org.simantics.project.feature">
     <feature
           class="org.example.project.ProjectFeature"
           description="Example top-level project feature."
           id="org.example.project"
           label="Example Project"
           published="true">
        <requires id="org.simantics.simulation.experimentManager"/>
        <installGroup id="org.example.feature.group" version="[1.0.0,2.0.0)"/>
     </feature>
  </extension>

</syntaxhighlight>

There are two important lines to notice here. One is the <installGroup> definition and the other is the published="true" attribute. We'll come to the why shortly.

A project must contain definitions of what Eclipse Features are used with it. Eclipse Features are Feature Projects created for the Eclipse environment. Do not mistake them for project features that have been discussed so far. In our project feature example above, we can see the <installGroup> element stating id="org.example.feature.group". Generally Eclipse feature projects are named <functionality>.feature and the ID given to them just leaves the .feature suffix out, leaving <functionality> as the feature ID. In our example, this gives us an Eclipse feature project named org.example.feature with ID org.example.

Why exactly does the <installGroup> say org.example.feature.group then? The reason for this is the Eclipse P2 provisioning platform. When an Eclipse feature, such as org.example is exported into a P2 repository, the feature project will become an installable unit with ID org.example.feature.group. Since Simantics project management is planned on top of P2, we use these P2 concepts in the project framework also.

Due to all explained above, the project framework expects to find this used Eclipse features information attached to the project as follows (expressed in Graph File Format):

<syntaxhighlight lang="python"> ... : PROJ.Project

   PROJ.HasFeature
       _ : PROJ.FeatureSpec
           PROJ.HasGroupId "org.example.feature.group" : L0.String

</syntaxhighlight>

Now we are ready to summarize the process of resolving the project features to configure:

  1. Gather all HasGroupId property values from PROJ.FeatureSpec instances attached to the loaded project. Let's call this set of group ID's GID.
  2. Find all such project feature extensions that are marked with both published="true" and an installGroup that is contained within GID. Let's call this set of root project features ROOTS.
  3. Resolve the final set of project features to load by taking a transitive closure of all project features reachable through <requires> definitions within project feature extensions starting from ROOTS. Notice that the requirement definitions specify the order in which the project features must be configured through topological sorting.

Step 2: Activate a project

After the resolution has been completed, the actual activation, i.e. project feature configuration is trivial. It is performed by IProject.activate(). See this scenario.

Simantics Project Management Application (SPM)

Currently we have planned a Simantics Project Management (SPM) application that would in the end be responsible for setting up and managing workspaces, their databases and the projects within those databases. The project management side includes managing the PROJ.FeatureSpec definitions attached to projects.

Since SPM is not yet usable, the Simantics Workbench platform code has a feature that works around this. When a new workspace and its database is initialized the platform code will add PROJ.FeatureSpec definitions to the project with the <installGroup>s defined in all available project features marked with published="true". This makes the workbench work nicely even without SPM.

Project Features Step-by-step

Step 1: define extensions for your project features

First add a plug-in dependency on org.simantics.project.

Extensions are defined using the org.simantics.project.feature extension point. See the extensions point's own reference document for detailed information (currently only viewable by opening the EP in eclipse).

The following example demonstrates the use of all current features of the extension point, namely project features, dependencies between project features (requires) and bindings between project features and Eclipse platform features (installGroup). This example assumes that you have a plug-in myplugin and a feature project myplugin.feature (feature id=myplugin) for it.

<syntaxhighlight lang="xml">

  <extension
        point="org.simantics.project.feature">
     <feature
           description="My project type's main project feature."
           class="myplugin.MyProjectFeature"
           id="myplugin.project"
           label="My project"
           published="true">
        <requires id="myplugin.dummy"/>
        <installGroup id="myplugin.feature.group"/>
     </feature>
     <feature
           class="myplugin.MyDummyFeature"
           id="myplugin.dummy"
           label="My dummy feature">
     </feature>
     <feature
           class="myplugin.AnotherFeature"
           id="myplugin.another"
           label="My dummy feature">
        <injectDependency targetId="myplugin.project"/>
     </feature>
  </extension>

</syntaxhighlight>

The above contains three project features, the first one is published and requires the second one. The third one is injected as a dependency of the first one. This means that when loading a project that uses the myplugin.project feature, myplugin.dummy and myplug.another features will be configured before myplugin.project.

Note that here both requires and injectDependency describe the two mechanisms available for defining dependencies. Using injectDependency is recommended since it promotes proper decoupling and dependency injection.

Step 2: define the code for the project features

Copy the following classes into corresponding source files:

<syntaxhighlight lang="java"> package myplugin; class MyProjectFeature extends AbstractProjectFeature {

   private static final String DEFAULT_PERSPECTIVE = "myplugin.myperspective";
   
   @Override
   public void configure() throws ProjectException {
       getProject().setHint(ProjectKeys.DEFAULT_PERSPECTIVE, DEFAULT_PERSPECTIVE);
   }
   @Override
   public void deconfigure() throws ProjectException {
       // Dispose of anything that needs to be disposed of within the project's
       // hint context which can be accessed through getProjectElement().
   }

} </syntaxhighlight>

<syntaxhighlight lang="java"> package myplugin; class MyDummyFeature extends AbstractProjectFeature {

   @Override
   public void configure() throws ProjectException {
       // Setup some hints if you need to
       try {
           // Do something with the database
           setupDatabase();
       } catch (DatabaseException e) {
           throw new ProjectException(e);
       }
   }
   private void setupDatabase() throws DatabaseException {
       getSession().syncRequest(new WriteRequest() {
           @Override
           public void perform(WriteGraph graph) throws DatabaseException {
               //...
           }
       });
   }
   @Override
   public void deconfigure() throws ProjectException {
       // Dispose of anything that needs to be disposed of within the project's
       // hint context which can be accessed through getProjectElement().
   }

} </syntaxhighlight>

<syntaxhighlight lang="java"> package myplugin; class AnotherFeature extends AbstractProjectFeature {

   @Override
   public void configure() throws ProjectException {
   }
   @Override
   public void deconfigure() throws ProjectException {
   }

} </syntaxhighlight>

Optional Step: Using ready-made project features

This step is not necessary but is here both for information and educational value. This example shows how to add a feature to your project that advertises ontology dependencies for your project which is necessary for example if (full-text) indexing of ontology dependencies is desired.

A new project feature, here myplugin.dependencies, must also be added to the previous example:

<syntaxhighlight lang="xml">

  <extension
        point="org.simantics.project.feature">
     <feature
           description="My project type's main project feature."
           class="myplugin.MyProjectFeature"
           id="myplugin.project"
           label="My project"
           published="true">
        <requires id="myplugin.dummy"/>
        <installGroup id="myplugin.feature.group"/>
     </feature>
     <feature
           class="myplugin.MyDummyFeature"
           id="myplugin.dummy"
           label="My dummy feature">
     </feature>
     <feature
           class="myplugin.AnotherFeature"
           id="myplugin.another"
           label="My dummy feature">
        <injectDependency targetId="myplugin.project"/>
     </feature>
     <feature
           class="org.simantics.project.features.DependencyValidationFeature:http://www.example.org/MyOntology-1.0/ImportedOntologies"
           id="myplugin.dependencies"
           label="My plugin dependencies">
        <injectDependency targetId="myplugin.project"/>
     </feature>
  </extension>

</syntaxhighlight>

The myplugin.dependencies feature definition gives http://www.example.org/MyOntology-1.0/ImportedOntologies as an argument to DependencyValidationFeature which will mark the dependency on the specified namespace requirement in the project.

We still need to define the ontology containing the NamespaceRequirement we referred to in the feature. Following the instructions at org.simantics.graph create a project to contain Simantics ontologies and add a new .pgraph file into it. You can fill the .pgraph with something like the following:

<syntaxhighlight lang="python"> L0 = <http://www.simantics.org/Layer0-1.0> PROJ = <http://www.simantics.org/Project-1.0>

MY_ONTOLOGY = <http://www.example.org/MyOntology-1.0> : L0.Ontology

   @L0.new

//# The project feature refers to this instance MY_ONTOLOGY.ImportedOntologies : PROJ.NamespaceRequirement

   L0.HasDescription """Specifies the ontologies required by a this project.

Used e.g. for indexing the database."""

   PROJ.RequiresNamespace
       "http://www.simantics.org/Layer0-1.0" : L0.URI
       "http://www.simantics.org/G2D-1.0" : L0.URI
       "http://www.simantics.org/Diagram-2.0" : L0.URI

</syntaxhighlight>

Development

Framework Entry Points

As shown in figure 2 there are two main entry-points in the project framework:

class Projects
A facade class containing utilities for loading and managing projects in a Simantics database.
class ProjectFeatures
A facade class containing utilities for retrieving and filtering project feature extensions with different criteria. The implementation is backed by the project feature extension registry.


Figure 2: Project framework API and implementations. (image source)

Scenarios

Omnipresent Project Features

Consider the following scenario: A developer wishes to provide a plug-in that can be inserted into any Simantics workbench application in order to give the plug-in the chance to attach itself to the project worked on with that particular workbench instance.

Project features are a natural instrument for this purpose. The only problem in getting your project feature to load. It may be that your workspace's project does not employ any installGroup used by your project feature. Also your new project feature is probably not required by any of the project features that are active in your project.

For this purpose, we define a single installGroup ID that the project framework considers to always be included in the set of installGroups for any project. So:

Use installGroup ID omnipresent for features that are meant to be used with any project. Also remember to set published=true.

To define an omnipresent project feature, use the following template:

<syntaxhighlight lang="xml">

  <extension
        point="org.simantics.project.feature">
     <feature
           description="My omnipresent project feature."
           class="myplugin.MyOmnipresentProjectFeature"
           id="myplugin.omnipresent.feature"
           label="Omnipresent feature"
           published="true">
        <installGroup id="omnipresent"/>
     </feature>
  </extension>

</syntaxhighlight>

Load project from database and activate it

<syntaxhighlight lang="java">

   public IProject loadAndActivateProject(Session session, Resource project)
           throws DatabaseException, ProjectException
   {
       IProject project = Projects.loadProject(session, project);
       project.activate();
       return project;
   }

</syntaxhighlight>

Retrieving the active project in a Simantics workbench application

The Simantics Workbench UI is built so that always when the user is working on a project, the UI will have both an active database session and an active project. The active project can be used to store any project-related services and data using the IHintContext interface it implements.

SimanticsUI (SVN) is the class for retrieving both the active database session and the active project using these:

<syntaxhighlight lang="java"> public class SimanticsUI {

   ...
   /**
    * Returns the database session context associated with the currently active
    * workbench window. This method should be used to retrieve session contexts
    * only when the client is sure that the correct workbench window has focus.
    * 
*

* If the client knows the workbench window it is working with, but it isn't * sure that the correct workbench window has focus, use * {@link #getSessionContext(Object)} instead. *

    * 
    * @return the session context associated with the currently active
    *         workbench window or null if the active window has no
    *         session context
    */
   public static ISessionContext getSessionContext() { ... }
   /**
    * @return the currently open and active project as an IProject or
    *         null if there is no active session or project
    */
   public static IProject peekProject() { ... }
   /**
    * @return the currently open and active project as an IProject
    * @throws IllegalStateException if there is no currently active database
    *         session, which also means there is no active project at the
    *         moment
    */
   public static IProject getProject() { ... }
   ...

} </syntaxhighlight>