Skip to content
Alejandro Medrano edited this page May 12, 2016 · 1 revision

<< prev | 1 2 3 [4] | next >>




After development

Table of Contents

Testing your service in universAAL Karaf-based distribution

Quick start guide in Karaf features

Karaf provides a simple way to provision applications by means of features. Such a mechanism is mainly provided by a set of commands.

The provisioning system uses xml repositories that define a list of features elements, each one representing an application that can be installed. The feature is identified by its name which must be unique amongst all the repositories used and consists of a set of bundles that need to be installed along with some optional dependencies on other features and some optional configurations.

An example of a feature would be as follow:

    <?xml version="1.0" encoding="UTF-8"?> 
    <features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="universAAL-Ontologies">
        <feature name="uAAL-Ont.X73" resolver="(obr)" version="2.0.3-SNAPSHOT">
          <bundle start-level="61" start="true">mvn:org.universAAL.ontology/ont.X73/2.0.3-SNAPSHOT</bundle>
       </feature>
    </features>

A repository of features follows the same structure but contains more than one feature:

    <?xml version="1.0" encoding="UTF-8"?>
    <features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="universAAL-Ontologies">
       <feature name="uAAL-Ont.PhWorld" resolver="(obr)" version="${pom.version}" description="The Physical World Ontolgy.">
          <feature>uAAL-MW</feature>
          <bundle start-level="50">wrap:mvn:jp.go.ipa/jgcl/${jgcl.version}</bundle>
          <bundle start-level="60" start="true">mvn:org.universAAL.ontology/ont.phWorld/${ont.phWorld.version}</bundle>
       </feature>
       <feature name="uAAL-Ont.X73" resolver="(obr)" version="2.0.3-SNAPSHOT">
          <feature>uAAL-Ont.PhWorld</feature>
          <bundle start-level="61" start="true">mvn:org.universAAL.ontology/ont.X73/2.0.3-SNAPSHOT</bundle>
     </feature>
    </features>

Provision schema for bundles can be as follow, being optional options those in {}:

    <bundle {start-level="70”} start='true/false'>{wrap:}mvn:GROUP_ID/ARTIFACT_ID/VERSION</bundle>

Dependent features are useful when a given feature depends on another feature to be installed. Such a dependency can be expressed easily in the feature definition.

   <feature name="uAAL-Ont.PhWorld”>
      <bundle start-level="60" start="true">mvn:org.universAAL.ontology/ont.phWorld/2.0.3-SNAPSHOT</bundle>
   </feature>
   <feature name="uAAL-Ont.Health.Disease">
      <feature>uAAL-Ont.PhWorld</feature>
      <bundle start-level="61" start="true">mvn:org.universAAL.ontology/ont.health.disease/2.0.3-SNAPSHOT</bundle>
   </feature>

The effect of such a dependency is to automatically install the required uAAL-Ont.PhWorld feature when the uAAL-Ont.Health.Disease feature is installed.

References to features define in other repositories are allow and can be achieved by adding a list of repository.

   <?xml version="1.0" encoding="UTF-8"?>
   <features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="universAAL-Security">
          <repository>mvn:org.universAAL.middleware/mw.karaf.feature/2.0.3-SNAPSHOT/xml/features</repository>
          <repository>mvn:org.universAAL.ontology/ont.karaf.feature/2.0.3-SNAPSHOT/xml/features</repository>
          <feature name="uAAL-Security.Authorizator">
             <feature>uAAL-Ont.Profile</feature> <!—Comes from ont.karaf.feature repository-->
             <bundle start="true" start-level="70">mvn:org.universAAL.security/security.authorizator/2.0.3-SNAPSHOT</bundle>
          </feature>
   </features>

By default, all the features defined in a repository are not installed at the launch of Apache Karaf, only those that are referenced at the startup time.

Notice that if we have feature A that is dependent of feature B, and a feature C depends on feature A and B. It isn’t necessary to reference both, only feature B.

Furthermore, this dependencies are only taking into account in runtime.

For further information click [here].

Be careful when you define them as there is a risk of 'cycling' dependencies.

Karaf features in universAAL

In order to avoid ‘cycling’ dependencies universAAL provides a feature hierarchy, where third-parties and universAAL bundles are listed in the correct order to be launched. This hierarchy also boosts to not have incompatibilities between third-party libraries when developing. In the case you need a third-party library you would need to check if such library is already provided by some of the feature repository and (if possible) use the same bundle/version. Otherwise you need to check they are compatible and will not provoke conflicts at runtime.

Currently universAAL has defined a set of repositories thought for different purposes where a hierarchy is established between them:

Figure. Hierarchy of universAAL feature repositories

Here are described those repositories that may be needed by applications:

  • mw.karaf.feature: It is the universAAL runtime provided from 2.0.3-SNAPSHOT version where only ‘uAAL-MW’ feature exists.
  • ont.karaf.feature: It contains the set of ontologies available in universAAL. There is one feature defined per each ontology.
    • uAAL-Ont.PhWorld - The Physical World Ontology
    • uAAL-Ont.Impairment - Description of user interaction impairments
    • uAAL-Ont.Recommendation - UI Recommendation options
    • uAAL-Ont.Unit - The Unit System
    • uAAL-Ont.Languages - List of All Human Languages
    • uAAL-Ont.Situation.Reasoner
    • uAAL-Ont.Profile - The Profile Ontology
    • uAAL-Ont.gesturePointing
    • uAAL-Ont.Profile.ui.preferences
    • uAAL-Ont.Profile.contact
    • uAAL-Ont.Profile.ui.mainmenu
    • uAAL-Ont.Security
    • uAAL-Ont.questionnaire
    • uAAL-Ont.Device
    • uAAL-Ont.Activityhub
    • uAAL-Ont.Device.Extra
    • uAAL-Ont.Measurement
    • uAAL-Ont.personalhealthdevice
    • uAAL-Ont.Health.Measurement
    • uAAL-Ont.Health.Disease
    • uAAL-Ont.Profile.Health
    • uAAL-Ont.AV
    • uAAL-Ont.Multimedia
    • uAAL-Ont.ContinuaHealth
    • uAAL-Ont.CHe
    • uAAL-Ont.Dependability
    • uAAL-Ont.Handgestures
    • uAAL-Ont.Furniture
    • uAAL-Ont.Lightning
    • uAAL-Ont.X73
  • ontS.karaf.feature: It is a repository containing a list of basic ontologies thought for being used by AAL applications.
    • uAAL-Ont.Aalfficiencyscores – The AALfficiency Scores ontology
    • uAAL-Ont.Activity – Activity of the user ontology
    • uAAL-Ont.Agenda – Agenda ontology
    • uAAL-Ont.BioMedicalSensors – Biomedical sensors ontology
    • uAAL-Ont.Drools – Drools engine ontology
    • uAAL-Ont.AgendaEventSelection – Agenda events ontology
    • uAAL-Ont.FitBitData – Activity and sleep data ontology
    • uAAL-Ont.LTBA – Long Term Behaviour Analyzer ontology
    • uAAL-Ont.Medication – Medication ontology
    • uAAL-Ont.Message – Message ontology
    • uAAL-Ont.Nutrition – Nutrition ontology
    • uAAL-Ont.QuestionnaireStrategy – Questionnaire Strategy ontology
    • uAAL-Ont.ReadEnergy – Read energy ontology
    • uAAL-Ont.Safety – Security and safety ontology
    • uAAL-Ont.Shopping – Food and shopping list ontology
  • remote.karaf.feature: It contains ‘uAAL-RI.Service.Gateway’.
The rest of repositories that haven’t been described in this section are available in Karaf features of low-level managers.

Figure. remote.karaf.feature



Figure. ont.karaf.feature

Figure. ontS.karaf.feature

universAAL coordinator feature

As you can see in the previous section universAAL provides a lot of components and corresponding features. If you feel lose at this point and don’t know how to start on it don’t worry because there is a solution for everything in this life ;-).

If you need to create a feature, it is because you need to run your application in a universAAL distribution. There are several sort of distributions, depending on the AAL Space configuration is intended for. In this case we are going to introduce the basic scenario where the AAL Space is composed by one node, so all bundles are running in that one.

These distributions are based on features. When you start the classic distribution it is installing a feature called universAAL-coordinator that references to runtime bundles, ontologies and managers to make the Runtime Support Platform run with the minimum set of components needed. Also some optional managers could be included such a uAAL-UI.handler.gui.swing.BluesteelLAF for launching Graphical User Interface modality.

Figure. universAAL coordinator feature

Creating a new feature for your application

To create a new feature for your application is not difficult. You need to go to Eclipse and:

  • Click on File->New->Project… to create a new Maven project with Maven Wizard.

Figure. New maven project

  • The Maven project wizard is simple, just select where you want to locate the new project (by default it will be located in a new file in the workspace). Before clicking next make sure Create a simple project is NOT selected.

Figure. Select project name and location

  • To add an Archetype to this list click on Add archetype button. The next step is straight forward, fill in the information you are given about the archetype (Maven will look up this Archetype as it does for any other artifact, by looking up the remote repositories listed in your settings XML. repository URL parameter is not mandatory, as long as you have the universAAL Nexus repository listed in your settings (which you will if you followed the maven configuration procedure)). This step is done only once because the archetype is installed in the local repository, and therefore is listed in the archetype catalog.
    • groupID: org.universAAL.samples
    • artifactID: karaf.feature-archetype
    • version: 2.0.0

Figure. Add feature archetype

  • Then look for feature archetype. Once the archetype is selected click next, and fill in the last required information. This is the groupID, artifactID, version that your new project should have.
    • groupId: org.universAAL.AALapplication
    • ArtifactId: It is APP_NAME.karaf.feature, where you must replace APP_NAME with the corresponding name of your application
    • Version: It is up to you
  • Click on finish and you are ready to go (after a while because it could take some time).

Figure. New feature project

  • Modify the feature according to your application. The previous step generates an empty xml. Now, it is an example of how it should looks like as a feature. Text in red are conventions in universAAL and musn’t be changed. Text in capitals and bold are texts that must be modified according to your application but also some considerations must be taken into account:
    • Applications may be dependent of platform managers not included in the coordinator feature, then you need to add the corresponding dependencies
    • Add those third-party features/bundles that your application needs and are not provided by the universAAL core. VERY IMPORTANT!!!
    • Add ontology features needed from universAAL and/or your own
    • Finally, add your bundles
           <repository>mvn:GROUP_ID/ARTIFACT_ID/VERSION/xml/features</repository>
           …
           <feature name="uAAL-Service-SERVICE_NAME" description="SERVICE_DESCRIPTION" version="SERVICE-VERSION" resolver='(obr)'>
               
               <feature>third-party</feature> 
                   <bundle>third-party</bundle> 
                   …
                   
                   <feature>uAAL-Ont.ONTOLOGY_NAME</feature>
                   <feature>ontology</feature> 
                    … 
                   
                   <bundle start-level='70' start='true'>mvn:<b>GROUP_ID/ARTIFACT_ID/VERSION</b></bundle>
            </feature>
    </features>
  • Following universAAL conventions to implement applications you need to modify the pom file in order to add as parent pom

Figure. Relationship between application features and developments

      <parent>
         <groupId>org.universAAL.AALapplication</groupid>
         <artifactId>Service.pom</artifactid>
         <version>2.0.3-SNAPSHOT</version>
         <relativePath>../service.pom/pom.xml</relativepath>
      </parent> 
  • Then, the feature must be added to the dependency management and module sections at service.pom
         <dependencyManagement>
            <dependencies>
                <dependency>
                     <groupId>org.universAAL.AALapplications</groupid>
                     <artifactId>application.karaf.feature</artifactid>
                     <version>2.0.3-SNAPSHOT</version>
                </dependency>
            </dependencies>
          </dependencymanagement> 
          <modules>
            <module>../application.karaf.feature</module>
          </modules>

In the case of ontologies, it is a bit different. Your feature ontology must be added to the ontS repository described above with the following structure:

                 <feature>ontology</feature>
                    …
                 
                 <bundle start-level='70' start='true'>mvn:<b>GROUP_ID</b>/<b>ARTIFACT_ID</b>/<b>${artifactId.version}</b></bundle>
         </feature>
           ...
      </features>

And add your artefact as dependency to the pom of the ontS feature.

Using a karaf feature in universAAL distributions

Features are installed as normal maven projects from Eclipse or command prompt. Following the example of a basic distribution you need to go to ./universAALdistribution/etc/ folder and edit “org.apache.karaf.features.cfg” file by adding at featuresRepositories section the provision of your application feature:

       mvn:org.universAAL.AALapplication/application.karaf.feature/2.0.3-SNAPSHOT/xml/features

and at featuresBoot section the name of your feature in order to be launched when starting.

uAAL-Service-SERVICE_NAME

Run the distribution and enjoy!  

Karaf features of low-level managers

  • ctxt.karaf.feature: It includes the different context components available in universAAL.
    • uAAL-ctxt.CHe – The Context History Entrepot
    • uAAL-ctxt.Profiling.Server – The Profiling Server
    • uAAL – ctxt.Managers

ctxt.karaf.features repository

  • security.karaf.feature: It contains security features
    • uAAL-Security.Authenticator.UserPassword.Client: For user and pass authentication
    • uAAL-Security.Authenticator.Profile: For the service authentication itself
    • uAAL-Security.Authenticator.Dummy: For simulating the authentication service. It is intended for developers, no real environments.

Figure. security.karaf.features repository

lddi.karaf.feature: It includes universAAL-LDDI-KNX feature for the LDDI framework for KNX based on the uAAL-MW

Figure. lddi.karaf.features repository

  • ui.karaf.feature: It contains UI component features
    • uAAL-UI.handler.gui.swing – The UI Swing handler
    • uAAL-UI.handler.gui.swing.BluesteelLAF – The UI Swing Handler’s LAF plugin for BlueSteel look and feel
    • uAAL-UI.handler.gui.swing.classicLAF – The UI Swing Handler’s LAF plugin for classic look and feel
    • uAAL-UI.resource.server – The UI Resource Server
    • uAAL-UI.handler.web – The UI Web handler (deprecated)
    • uAAL-UI.handler.web.html – The new UI Web handler
    • uAAL-UI.DM – The UI Dialog Manager

Figure. uAAL-UI.handler.gui.swing feature

Figure. uAAL-UI.handler.gui.swing.classicLAF feature

Figure. uAAL-UI.handler.web.html feature

Figure. uAAL-UI.Internationalization feature

Figure. uAAL-UI.DM feature

Testing your service in Eclipse

These are some tips of how to make run the new mw 2.0 in Cclipse. The new version includes very important fixing bugs in the UI, some less visible at data representation and serialization level but also important, and some others in Context and LDDI components. For further details click here: http://forge.universaal.org/mediawiki/index.php?title=support:RD_Release_History.



Apart from the previous listed features there is another big change, which is that it has been developed with Apache Karaf OSGi implemenation. So, some of the features provided by MW2.0 are only available with Apache Karaf, while other ones are independent from the target platform. It means that you really don’t need Karaf features to run your services (unless you want to use it. In that case, just ask [email protected] how to create a feature and start using karaf distro)



So after this introduction we can go in with it....

What you need to include in your launch file

You need to go to the Run Configurations. Then will appear a dialog asking you about resolving composites, just say NO, otherwise it will take long and Eclipse will get crazy (at least in my case):



Figure 1. Composite message



After it the Run Configurations screen should be opened, then go to

  • Tab Pax Runner (not uAAL Runner). In the “Profiles” section, keep checked the CONFIG option. Add the following provisions in different levels and be sure that (1) mw, (2) ontologies, (3) UI, (4) LDDI and (5) your service:
    • Mw composite
      • scan-composite:mvn:org.universAAL.middleware/mw.composite/2.0.0/composite
    • WP2 ontologies composite
      • scan-composite:mvn:org.universAAL.ontology/ont.composite/2.0.0/composite
    • UI artifacts
      • mvn:org.openrdf.sesame/sesame-runtime-osgi/2.6.0
      • mvn:org.ufacekit.osgi/swingx.osgi/0.9.2
      • mvn:org.ops4j.pax.web/pax-web-jetty-bundle/1.0.5
      • mvn:org.ops4j.pax.web/pax-web-spi/1.0.5
      • mvn:org.ops4j.pax.web/pax-web-api/1.0.5
      • wrap:mvn:jp.go.ipa/jgcl/1.0
      • mvn:org.universAAL.context/ctxt.che/2.0.0
      • mvn:org.universAAL.context/ctxt.prof.server/2.0.0
      • mvn:org.universAAL.security/security.authorizator/2.0.0
      • mvn:org.universAAL.ui/ui.dm/2.0.0
      • mvn:org.universAAL.ui/ui.handler.gui.swing/2.0.0
      • wrap:mvn:org.universAAL.ui/ui.handler.gui.swing.bluesteelLAF/2.0.0@nostart
      • mvn:org.universAAL.ui/ui.resource.server/2.0.0
    • LDDI artifacts
      • mvn:org.openrdf.sesame/sesame-runtime-osgi/2.6.0
      • mvn:commons-lang/commons-lang/2.6
      • mvn:org.universAAL.ontology/ont.device/2.0.0
      • mvn:org.apache.felix/org.apache.felix.dependencymanager.lddi/3.0.0
      • mvn:org.apache.felix/org.apache.felix.devicemanager/0.9.0-SNAPSHOT
      • wrap:mvn:org.jdom/jdom/1.1.3
      • wrap:mvn:jaxen/jaxen/1.1.3
      • mvn:org.universAAL.lddi/lddi.knx.library/2.0.0
      • mvn:org.universAAL.lddi/lddi.knx.networkdriver/2.0.0
      • mvn:org.universAAL.lddi/lddi.knx.devicemanager/2.0.0
      • mvn:org.universAAL.lddi/lddi.knx.exporter/2.0.0







  • Tab Bundles, deselect all the bundles and Felix 3.0.2 at “Framework” section.
  • Tab (x)= Arguments
    • add the following arguments at "Program arguments" section.
  -console --
  obrRepositories=http://a1gforge.igd.fraunhofer.de/nexus/content/repositories/snapshots/repository.xml,http://a1gforge.igd.fraunhofer.de
  /nexus/content/repositories/releases/repository.xml,http://bundles.osgi.org/obr/browse?_xml=1&amp;cmd=repository --
  org.ops4j.pax.url.mvn.repositories=+http://a1gforge.igd.fraunhofer.de/nexus/content/groups/public,http://a1gforge.igd.fraunhofer.de/nex
  us/content/repositories/snapshots@snapshots@noreleases --log=DEBUG
    • and the following to "VM Arguments".
  -Dosgi.noShutdown=true -Dfelix.log.level=4 -Dorg.universAAL.middleware.peer.is_coordinator=true -
  Dbundles.configuration.location=${workspace_loc}/rundir/confadmin  -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF8
    • at the “Working Directory” section, in the “Other” option change the rundir folder for the one of your service.

Configuring mw2.0 and needed managers

Download the rundir folder at Support svn project http://forge.universaal.org/svn/support/trunk/rundir. Inside you have all needed to make mw2.0 run just needed some adjustments.

Figure 2. Workspace content

Configuring Konnex

For Konnex network. It requires as input a file with the extension “.knxproj” which represents the real KNX installation available at the end-user home. This file can only be generated with the last version of the ETS software, namely ETS4 professional edition. The cost of this software is 960 € (plus 15 € as an additional service fee). The problem is that uAAL partners mostly have an old version of the ETS software (ETS3 professional) where the “Export to .knxproj file” is not supported. In that case, there are only options in order to be able to test this service:

  • Update our ETS software from version 3 to 4. The cost of this upgrade license is 310 €.
  • Talk with a colleague with the required version of the ETS software to do the expected format conversion. The process should be as follows: export our KNX project in any format available in the version 3, share this file with our colleague who has to load our project in the version 4 of the software, export this project to a file with the “.knxproj” extension and back to us again.


KNX installation available in your Living Lab MUST come with a KNX/IP gateway module (interface between KNX and IP networks). This is mandatory. These hardware devices are quite expensive (600 – 1000 €, more or less) so it is probably that are not available in some laboratories.



After getting .knxproj:

  • Copy that file into
  \rundir\configurations\services\data
  • Go to


and modify the lddi.knx.devicemanager.properties file by changing the knxConfigFile property: knxConfigFile = configurations/services/data/YOUR_KNX_PROJ.knxproj

  • In the same folder


open lddi.knx.networkdriver.properties file and modify:

  • IP of the computer running knxnetworkdriver
  myIP=192.168.233.251
  • IP of the KNX/IP network
  knxGatewayIp=192.168.230.1

Deploying

When a service reaches a stable version and if it is still under development should be called SNAPSHOT, otherwise it is the final version and is a RELEASE.

How to prepare an AAL Service to be released - SVN Strategy

Each effective POM (it is a POM with all inherited information and resolved variables) must contain a maven-release-plugin configuration compliant with WP4 tagging policy.

  • The variable service.name.svn is used to generate WP4 tag policy that look like:
 tags/
     A.0/
     B.0/
        Service1/
           artifact1/
           artifact2/
           …
           sevice.pom/
        Service2/
           artifact1/
           …
           service.pom/

It is accomplished when in the step 3, serviceX.pom inherits from new_services.pom.

Deploy Strategy

The deployment strategy in WP4 has been configured from version C.0 onwards.

Configuration of private repositories

Before deployment you’ll have to configure the access to the private repositories. There are 3 repositories configured for WP4, all of them are protected with a user password:

To make sure your maven is able to download/upload artifacts from/to each repo you have to edit your settings.xml file (usually inside <user_folder>/.m2/settings.xml, if it doesn’t exist use the template provided by uAAL: http://forge.universaal.org/svn/support/trunk/resources/maven-repo-settings/settings.xml). Add the following servers to your servers section:&lt;/p&gt;</user_folder> <server> &lt;id&gt;uaal&#45;wp4&#45;private&lt;/id&gt; &lt;username&gt;wp4&#45;development&lt;/username&gt; &lt;password&gt;$pass$&lt;/password&gt; </server> <server> &lt;id&gt;uaal&#45;wp4&#45;release&lt;/id&gt; &lt;username&gt;wp4&#45;development&lt;/username&gt; &lt;password&gt;$pass$&lt;/password&gt; </server> <server> &lt;id&gt;uaal&#45;wp4&#45;thridparty&lt;/id&gt; &lt;username&gt;wp4&#45;development&lt;/username&gt; &lt;password&gt;$pass$&lt;/password&gt; </server> Where pass is the password distributed by email (if you haven’t received the password please contact [email protected]), it is the same for the 3 servers.

Artifact deployment

Once your settings.xml is configured you’ll be able to download artifacts from it, but also upload artifacts to it. To upload a compiled artifact just execute the following command in a terminal under the directory of the project you want to upload:

mvn deploy –Prelease

Note: the –Prelease option is important. It will activate the release profile, and this means that along with the .jar compiled artifact, javadoc and sources will be included too. This is useful when other developers depend on your artifact, eclipse will automatically load the javadoc and sources to develop and debug easily.

If you execute this in a parent pom (for example in myService.pom) all modules of this project will be deployed.

You can use eclipse to deploy:

  1. Right click on the proyect to be deployed (a partent pom will also work as above explained).
  2. Run as -> maven build
  3. On goal: “deploy”; on profile:”release” (without quotation marks).
  4. Press run.

Manual artifact deployment

When there is a library that is not in the maven central repo (please double check it is not available on any of the uAAL repos either) you can upload it as a maven artifact:

  1. Go to http://nexus.lst.tfo.upm.es
  2. Log in (top right corner), using wp4-development as user and the password you received.
  3. Open repositories (left panel)
  4. Click on third party (or whatever reposioty you wish to upload manually)
  5. Open the Artifact Upload tab
  6. If the artifact hasn’t got a pom file select GAV by parameters.
  7. Add the artifact files (remember to press “add artifact” before next step).
  8. Click on upload artifact to complete.

How to release an AAL Service

Execute the following in myService.pom directory:

 mvn release:clean release:prepare release:perform –P release

Changing Version

If you changed myService.pom’s version then, under the same directory, execute the following:

 mvn versions:update-child-modules versions:commit

This command helps keep all the submodules’ parent tag in sync with the actual myService version.

How to create External Runners

To execute universAAL platform without using Eclipse you can follow instructions here

Expert Groups information

  Link : http://forge.universaal.org/gf/project/middleware/
  Link :http://forge.universaal.org/gf/project/lddi
  Link :http://forge.universaal.org/gf/project/uaalsecurity/
  Link :http://forge.universaal.org/gf/project/uaal_context/
  Link :http://forge.universaal.org/gf/project/service
  Link :http://forge.universaal.org/gf/project/rinterop
  Link :http://forge.universaal.org/gf/project/uaal_ui/

Chapters