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...
- ...process log data
Interface: LogTap.java,
Example: MemoryBasedHealthMonitor.java,
Famework: Log.java,
- ...provide metrics for the health monitor
Interface: MetricProvider.java,
Example: SystemMetricProvider.java,
Famework: Metrics.java,
- ...handle incoming HTTP requests
Interface: WebDispatcher.java,
Example: HelpDispatcher.java,
Famework: WebServerHandler.java,
- ...create REST services speaking JSON and XML
Interface: StructuredService.java,
Example: NodeInfoService.java,
Famework: ServiceDispatcher.java,
- ...provide controllers to the REST based web framework
Interface: Controller.java,
Example: SystemController.java,
Famework: ControllerDispatcher.java,
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:
- Start/stop the framework by yourself (Sirius.start() / Sirius.stop())
- Use the built in launcher (Setup.main()
- A a readily built container environment which sets up a classloader and initializes your Sirius server: sirius-runtime
Contribute
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