2 Application environment
This section dives into the basic framework of Oskari and further into the technical perspectives of Oskari's frontend and backend.
2.1 Framework
An Oskari-based web application consists of frontend code for browser-based user interface and backend functionalities that are run on the server. The user interface is implemented in JavaScript and the server functionality in Java. Both, the frontend and the backend, are built with extensibility in mind.
An example for building your own service is provided as a template application that can be run as is and it enables an easy starting point for customizing the service.
The picture below shows the generic idea of Oskari.
2.2 Frontend
The user interface is a Javascript-based single-page app. The UI is built by selecting a series of bundles that provide functionalities/capabilities for an application. You can mix and match the bundles or create new ones to customize the application for your needs.
Bundles are used as uniform containers to ship and share new functionality to the application setups. Additions to an existing functionality are implemented as plugins shipped within the bundles.
A bundle can work "as is" for providing its functionality with its own user interface and/or it can provide a documented API that can be used to interact with the functionality programmatically. One example of a bundle that doesn't have an UI itself would be a bundle called drawtools
that only provides an API that is used by measurement tools, my places functionality and others that has the user "draw" something on the map. The API also helps implementing a customized drop-in replacements for functionalities when required.
2.2.1 Frontend architecture
An Oskari-based frontend application includes the Oskari framework code and a selection of bundles that implement functionalities for the application. Bundles have a lifecycle and are started in sequence. Bundles can communicate with each other using events, requests and services. The framework code of Oskari provides the messaging system for events, requests and service registry but also an API which bundles need to implement so they can be included in an Oskari based application like having lifecycle functions/a starting point that can be called when the functionality is started.
The sequence diagram below explains what happens in the frontend initialisation process (try reloading the page if it's not rendered properly).
sequenceDiagram Frontend->>Frontend: Oskari.app.startApplication() Frontend->>Server: GetAppSetup Server-->>Frontend: list of bundles with config loop For each bundle Frontend->>Bundle: start Bundle->Bundle: init functionality Bundle->>Frontend: bundle.started end Frontend->>Frontend: app.started
The frontend code calls the Oskari.app.startApplication()
which triggers a call for the server action route called GetAppSetup
. The GetAppSetup
response lists all the bundles that should be started for that specific application and the configuration and state of those bundles (like which layers are on the map and what are the coordinates for the center of the map etc). The frontend then proceeds with starting the requested bundles with the included configuration in sequence until the whole application has been started. An event is triggered after each started bundle and another once the whole application has been started that enables programmatically react to such lifecycle events.
The bundle called mapfull
is usually a starting point for the bundle sequence as it creates the map implementation that most functionalities expect to be present to be useful.
Bundle functionality
- Bundles can provide an API for other bundles to request some operation through a request handler.
- A bundle can provide a request class and register a handler for the request in the Oskari framework. This is refered to as the request API for the bundle and should be fairly stable.
- Another bundle can then send the request which will be processed by the other bundle.
- Another way to communicate with other bundles is to send out an event through Oskari core.
- Any bundle registered as an eventlistener for the given event is then notified about the event.
2.2.2 Libraries and technologies
Oskari frontend uses the following libraries and technologies (for details see package.json on the oskari-frontend repository):
- OpenLayers (map implementation)
- jQuery (older UI implementations, migrating towards React)
- React (current UI implementations)
- Ant Design (UI components and icons)
- CesiumJS
- Lo-Dash
- geostats.js
- D3.js
You can get a list of licenses for all the libraries with npm, for example by running:
npx license-checker
Just to get summary of licenses you can add --summary after the command:
npx license-checker --summary
2.2.3 Source code and folder structure
You can find an Oskari-based sample application source code here.
The sample application frontend source code has the following folder structure: /applications - Definitions for application setups combining bundles into a specific application /bundles - Implementation for application specific bundles /packages - The main import point for application specific bundles
We are likely to remove the packages folder in the future and just have the bundle.js or similar in the bundles folder as this is mostly an older relic for the framework part. The path of the bundle.js nor the filename doesn't really matter any more. What matters is what is referenced on the main.js file under applications and that import points to the actual place where the main starting point (bundle.js currently) is located.
You can find Oskari frontend source code here.
Oskari frontend source code has the following folder structure: /api - The documentation of bundles and APIs they provide with a change log of changes to the API /bundles - Implementation files for extension bundles /packages - Definition files for extension bundles (mainly linking to files under bundles-folder) /resources - Common CSS styles/images /src - Code for Oskari framework /tools - Random templates and scripts for generating CSV-files based on localization (most likely deprecated) /webpack - Helpers and configurations for current build tools /libraries - Older jQuery plugins and other dependencies/libraries that are not reasonably available through npm
The main folders are the bundles (for debugging functionality implementations), packages (for linking functionalities to be used in applications), src (for framework code) and webpack (for build scripts). The packages folder is likely to be removed in the future and when it is, the imports on the applications need to be updated (but this is a light change and will be documented on the migration guide when done).
The folder structure follows a pattern where the first folder under the base folder is a namespace folder. Oskari uses framework
and mapping
for most of the bundles and admin
for admin tools, but this is optional and you can separate your bundles to own namespace. The next folder in the structure is named bundle. This is just a convention and is not a functional requirement. The next folder is named after the {bundle-identifier}
.
2.3 Backend
Backend functionality of the platform is implemented as a Java servlet, which can also be extended to handle new functionality.
2.3.1 Backend architecture
Oskari backend architecture depicted:
Backend architecture can be divided into three layers: service layer, control layer and interface layer. Webapp for Oskari server is packaged as oskari-map.war in sample-server-extension.
It handles most of the server side functionality alone. The webapp is extensible and can be compiled from oskari-server components and built on them to create your own geoportal/web mapping server.
The backend is layered as services, controls, interfaces (though only webapp-map uses this extensively at the moment).
Service layer
Service modules should be common libraries usable in any application. The actual business logic for Oskari operations should be in these modules.
- service-base has some common helpers and domain classes which are used throughout Oskari backend
- service-permission is a generic authorization lib for deciding who gets to see/do what
- service-search is a generic search lib that can be extended by adding and registering search channels.
- service-map has most (maybe a bit too much) of the business logic for servicing the Oskari functionalities
- service-control defines the control/routing structures/interfaces for control-layer to build upon
- shared-test-resources has some common helpers/templates to help testing
Control layer
Control modules build on top of the service layer.
- control-base is the basis for all control-modules and has most of the basic AJAX handlers needed by the Oskari frontend.
- NOTE! control-base contains some very specific functionalities that should be separated into separate control-extensions (for example thematic maps support)
- control-myplaces has AJAX funtionality related to myplaces functionality.
- control-example has example implementations for AJAX functionalities required by Oskari but usually overridden by platform specific functionalities such as user and content management.
- content-resources has tools, templates and scripts for populating and upgrading the database
Functions:
- Handles AJAX requests made by Oskari frontend
- Parses request parameters for input values to be used on service invocations (ActionParameters is not )
- calls one or more services to do business logic
- format a response based on service response
Interface layer
The interface-modules build on top of the control-modules. Basically an HTTP interface with reference implementations for:
- HTTP Servlet: oskari-server/servlet-map
- Webapp: sample-server-extension/webapp-map
Responsible for:
- Handling user sessions
- Generating an ActionParameters object based on incoming request abstracting/normalizing the request for control layer
- Forwarding the request to control layer
2.3.2 Libraries and technologies
Oskari backend uses the following libraries and technologies:
- HAProxy (proxy, load balancer)
- Apache (proxy, load balancer)
- Nginx (proxy, load balancer)
- F5 load balancer
- Jetty
- Tomcat
- PostgreSQL (database)
- PostGIS (spatial database)
2.3.3 Source code and folder structure
You can find Oskari backend source code in here.
Oskari backend source code has the following folder structure:
The folder structure follows a pattern where the first folder under the base folder is a namespace folder. Oskari uses framework for the main bundles, but this is optional and you can separate your bundles to own namespace. The next folder in the structure is named bundle. This is just a convention and is not a functional requirement. The next folder is named after the <bundle-identifier>
.
2.4 Directory structure for Oskari
The main folders for Oskari are:
/applications
- Definitions for application setups combining bundles into a specific application
/bundles
- Implementation files for extension bundles
/packages
- Definition files for extension bundles
/resources
- CSS styles/images for extension bundles
/sources
- Oskari core
/libraries
- jQuery plugins and other dependencies/libraries
The folder structure follows a pattern where the first folder under the base folder is a namespace folder. Oskari uses framework for the main bundles, but this is optional and you can separate your bundles to own namespace. The next folder in the structure is named bundle
. This is just a convention and is not a functional requirement. The next folder is named after the <bundle-identifier>
.
|--bundles
| |--
| |--bundle
| |--
| |--instance.js
|--packages
| |--
| |--bundle
| |--
| |--bundle.js
|--resources
|--
|--bundle
|--
|--css
| |--style.css
|--images
|--image.png
2.4.1 Bundle implementation convention
Any events, requests (and request handlers) and services a bundle implements should be separated into subfolders under the bundles implementation. In addition if you have divided the code into views or components that are shown on the flyout, you can create subfolders for them as well.
|--bundles
|--
|--bundle
|--
|--event
| |--MyEvent.js
|--request
| |--MyRequest.js
| |--MyRequestHandler.js
|--service
| |--MyService.js
|--component
| |--MyComponent.js
|--view
| |--MyLoggedInView.js
| |--MyGuestView.js
|--instance.js
|--Tile.js
|--Flyout.js