Sirius provides a set of components used to build modular and scalable Java applications.

Sirius was created by scireum GmbH as foundation of our web based applications. Being advocates of open source software, we decided that a stable and reliable framework, used in production environments might be useful to others. The library is split into several modules to make it useful for different audiences.

Why Sirius?

Discovery Based Programming
Sirius not only supports Dependency Injection but it takes it to the next level. It embraces a programming style called "Discovery Based Programming" which permit to provide services that discover their users at runtime. See the list of Examples within Sirius.
KISS
Keep it simple and stupid: No classloader ticks. No magic implicit variables coming from god knows where. No bytecode rewriting. No layers of indirection if not needed.
Maintainability
By providing loose coupling via IoC-Patterns (inversion of control) like discovery and dependency injection, Sirius permits to write modular, maintainable and testable software with clear responsibilities.
Use your IDE

Sirius can be started in any IDE/Debugger just like any normal Java appplication. As no classloader or bytecode magic is used, class reloading in the VM works a treat.

To further save precious developer time, the framework starts ultra fast. Having a server up and running in less than two seconds drives productivity above level 9000!

Focus on Software Products
There is quite a difference between software which is built and engineered for a single customer and software products which are used by many customers. The latter need more flexibility when it comes to configuration. Also a mechanism to manage customer extensions is required. Both is provided by the Sirius Kernel (Docs).

Also, the discovery based style of Sirius permits to reuse common services across multiple independent products in an easy way.

Made for Production

Running critical servers in production environments forces you to instantly know, what's going on in the system. The exception handling system provided by Sirius provides excellent insight, while neither getting jammed by reoccurring errors nor missing any important message. Leveraging the dependency injection framework, every application creator can decide where and how to collect those errors.

Using the built-in profiler, central activities can be inspected in running production systems with almost no overhead.

A quick Example

The Task

We at scireum build several products which all share quite a large part of their functionality. So we needed a flexible and simple mechanism to build reusable modules. One example would be the TimerService which calls functions in a regular interval.

Depending on the actual product, these functions of course differ. So the first thing you naturally do is to extract an interface which models a function to call:


    public interface TimedTask {
        void runTimer();
    }
                    

The Problem

Now if we try to implement the actual timer service, we run into the problem that we don't know which implementations of our TimedTask exist (each product has different tasks to do).

To still make this functionality reusable, we obvisouly cannot specify a list of classes. The only other alternative would be to have a startup handler which registers each implementation at the timer service. First of all, this would be against DRY (don't repeat yourself), also, a complex product would have quite a lot of such extensions to framework services, so this startup handler would be quite complex.

Another problem arises when we split our product into several modules. In this case we'd have many startup handlers and again would need to known which to call.

    
    public class TimerService  {

        private Timer timer;

        // TODO: Who is responsible for calling this at the right time?
        public TimerService() {
            timer = new Timer(true);
            timer.schedule(this::runTimers, SOME_INTERVAL);
        }

        private void runTimers() {
            // TODO: find all implementations of TimedTask... :-(
        }
    }
                    

The Solution

So what we need is a framework that brings together what belongs together. This is esentially what the micro kernel of Sirius does.

We add a tiny little @Register annotation to our implementation of TimedTask. At startup this is discovered and instantiated. So now our implementation of the timer service is quite simple:

    
    @Register
    public class TimerService implements Lifecycle {

        // Automatically contains all implementations of TimedTask 
        // discovered by the framework
        @Parts(TimedTask.class)
        private PartCollection<TimedTask> timers;

        private Timer timer;

        // Part of "Lifecycle" interface - as our class wears a @Registered
        // annotation, this is detected and called at system startup
        @Override
        public void started() {
            timer = new Timer(true);
            timer.schedule(this::runTimers, SOME_INTERVAL);
        }

        private void runTimers() {
            for(TimedTask task : timers) {
                task.runTimer();
            }
        }
    }
                    

As our implementation implements Lifecycle and also wears a @Register annotation it is also discovered at system start. Once all classes are loaded, the started method of all lifecycles are invoked which will then start our timer.

Once this triggers, we iterate through all available implementations of TimedTask (automagically filled by the framework via the @Parts annotation). Now all a consumer of this service has to to is implementing the interface and placing an annotation to make it visible to the framework.

This inversion of control permits loose coupling for modular software.

Further Examples

Well isn't that just one exotic special case? No, in Sirius itself it is used to...

The Kernel Module

Provides the micro kernel based discovery and dependency framework along with common helper classes and core services.

Discovery and DI Kernel
Scans the classpath and brings services and their consumers together. This runtime linking provides loose coupling and helps to build reusable services. See DI Package for further information.
System Configuration
Uses the Typesafe Config Library to load a set of config files. This encourages to provide readable default config in the component configuration which then can be overridde by the application config, a customization or the instance configuration. See Kernel Package for further information.
Commons
The Java APIs need some extensions here and there. Those are provided as helper classes like Strings or Value. See Commons Package for further information.
NLS
Native language support made easy (or at least bearable). The NLS class loads all .properties files at startup and makes them accessible via NLS.get(key) or NLS.fmtr(key). The latter provides a formatting tool which uses named parameters instead of numbers - your translator will love you for using it!
Health
Provides a uniform approach for exception handling and logging. Also contains tools for runtime profiling and monitoring. See Health Package for further information.
Caching
Caching without crashing! Provides a simple caching API which also takes care of validation and eviction. This helps to scale fast without sacrificing resources forever. See Cache Package for further information.
Async
Provides managed thread pools, which help to monitor and control system load. See Async Package for further information.
Frameworks & Customizations
The functionality provided by Sirius is split into frameworks which can be dis- or enabled. This permits to use a module in a project without having to use all of what's provided. Customizations help to develop and maintain customer extensions in one code base. Placed in specific packages, these can be enabled at startup as required. See Kernel Package for further information.

The Web Module

Uses Netty to setup a HTTP server supporting keepalive, chunked transfers, smart upload handling, connection monitoring, and a firewall. Usign Rythm templates and Controllers it provides an intuitive way of building modern web apps.

HTTP Server
Provides a fully asynchronous web server based on Netty. Smart content compression is automatically enabled as well as the zero copy capabilities of Netty. A built-in firewall helps to block or filter unwanted traffic at early stages (before all HTTP processing).
Flexible Dispatching
Sirius provides a set of basic dispatchers which can be extended or overridden by the application. The framework speaks JSON and XML fluently. Of course next to HTML also JSON and XML can be generated as a response.
REST Services
Need to provide an REST API or working withing a microservices architecture? By implementing StructuredService, a service end point is provided which either replied with XML or JSON (depending on the incoming URL).
eMail Helper
Provides a fluent API around Javamail which utilizes the system configuration for the SMTP setup and the definition of mail templates which can then be sent to receivers. See MailService class for further information.
Security
Provides a framework for user authentication and authorization along with permission profiles and helper classes. Also basic user managers using LDAP or the system configuration to identify users are available. See Security Package for further information.
Content Generator
Provides an extensible engine which renders all kinds of templates into an output destination. The generated content can be either sent as HTTP result, be attached to an eMail or used elsewhere. By default the following generators are available:
  • JavaScript which generates XML
  • Velocity which generates Text (Including XML and HTML)
  • Velocity which generates HTML which is then rendered into a PDF
  • ODF (OpenOffice) Text-Documents which are rendered into a PDF
The content engine has also an extensible resource lookup mechanism which is used by all Sirius components. See Content class for further information.

The Database Module

Provides a super thin layer above JDBC adding idiot-proof connection management, DBCP as connection pool as well as profiling for all queries.

JDBC
Provides pooled access to JDBC databases configured via the system configuration. Using SQLQuery, JDBC queries are executed with automatic connection management and smart query generation. See JDBC Package for further information.
Mixing
Mixing is a next generation ORM. It supports a thin and efficient mapping layer between classes and database tables without compromising performance. Its largest benefit however is the support of "Mixins" which permits to extend existing tables and classes on a "per customization" or "per product" basis. This permits to re-use base classes or to add customer specific fields into standard products.

The Search Module

Provides a ORM like API for Elasticsearch.

Entity Mapping
Provides a framework for loading and saving POJOs into Elasticsearch. Also a fluent query API helps to build flexible "SQL like" queries. See Search Packages for further information.
Schema
The schema is autocreated at system startup based on the entities discovered. See Schema class for further information.

Projects using Sirius

  • Tempotrovi uses Sirius to provide a simple yet effective Tool for providing and booking time slots (e.g. for a trade fair)
  • S3 ninja uses the kernel and web module to provide an emulator for the S3 object store API.
  • The Sofware Distribution System uses the kernel and web module to provide rysnc like software updates via HTTP(S).
  • scireum GmbH uses Sirius for all of their Java based software projects, including cloud based web applications and e-commerce systems.

How to use it

An overview of all versions can be found in the Sontype Repository. All releases are also available via Maven Central.

To use Sirius in your application there are basically two ways to do it:

  1. Start/stop the framework by yourself (Sirius.start() / Sirius.stop())
  2. Use the built in launcher (Setup.main()
  3. A a readily built container environment which sets up a classloader and initializes your Sirius server: sirius-runtime

Contribute

Contributions are welcome - the source code is available on GitHub: https://github.com/scireum

License

The MIT License (MIT)

Copyright (c) scireum GmbH

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
            

Sirius is made with all the love in the world by scireum in Remshalden
F**k me on GitHub