Moonshine Container Basics
Overview
Moonshine container configures the services, manages dependencies between them and provides the environment for their execution.
Bootstrapping the Container
Below you can find the basic code needed to bootstrap a Moonshine container.
import org.atteo.moonshine.Moonshine;
import org.atteo.moonshine.MoonshineException;
public class Main {
public static void main(String[] args) throws IOException, MoonshineException {
Moonshine moonshine = Moonshine.Factory.builder()
.arguments(args)
.build();
if (moonshine != null) {
moonshine.start();
}
}
}
Note that the builder build() method can actually return null.
This is correct behavior when according to the provided command line arguments, the application is not supposed to be started.
This is true when, for instance, '--help' switch was specified.
If you don't want to alter any startup options, you can use the default Main class provided with the Moonshine:
org.atteo.moonshine.Main
You will also need Moonshine container library itself to execute the project:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.atteo.moonshine</groupId>
<artifactId>bom</artifactId>
<version>0.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.atteo.moonshine</groupId>
<artifactId>container</artifactId>
</dependency>
</dependencies>
Service Definition Class
Service is a basic component of Moonshine application. Services are instantiated from the XML configuration files. They can register bindings in Google Guice, can be started and stopped repeatedly and are finally closed on container shut down.
Let's see how a basic Service looks like:
import javax.xml.bind.annotation.XmlRootElement;
import org.atteo.moonshine.TopLevelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service with simple start/stop logic.
*/
@XmlRootElement(name = "printer")
public class PrinterService extends TopLevelService {
private static final Logger logger = LoggerFactory.getLogger(PrinterService.class);
@Override
public void start() {
logger.warn("Basic service start() method called");
}
@Override
public void stop() {
logger.warn("Basic service stop() method called");
}
}
A lot of things happen here behind the scenes, so let's go through them one by one.
- First of all our service is marked with @XmlRootElement annotation. Its name attribute declares the XML tag which will trigger this service instantiation when specified in the XML configuration file.
- The service extends TopLevelService which means in can be declared just under the root tag in the XML configuration file.
- First sentence of the javadoc of the service class will be used as a service description.
Service instantiation
Services are instantiated by declaring their associated name in the configuration file. The configuration files are read from:
- classpath resources named '/default-config.xml'
- 'config.xml' file the 'config' directory of the application
- additional configuration files can be specified while configuring Moonshine using method.
Below you can see the XML file which will trigger instantiation of the PrinterService defined above:
<config>
<printer/>
</config>
<config>
<printer id='first'/>
<printer id='second'/>
</config>
You can download the code of PrinterService example from here.
The project from the example also contains POM configuration for exec-maven-plugin which allows to start Moonshine Service directly from the source code project. To do that execute:
mvn clean install exec:java
...
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ 01_simple_service ---
15:43:23.853 [org.atteo.moonshine.Main.main()] INFO Moonshine - Bootstrapping Moonshine
15:43:24.604 [org.atteo.moonshine.Main.main()] INFO Moonshine - Building Guice injector hierarchy
15:43:24.711 [org.atteo.moonshine.Main.main()] INFO Moonshine - Configuring: "first" PrinterService (Service with simple start/stop logic)
15:43:24.712 [org.atteo.moonshine.Main.main()] INFO Moonshine - Configuring: "second" PrinterService (Service with simple start/stop logic)
15:43:24.835 [org.atteo.moonshine.Main.main()] INFO Moonshine - Starting services
15:43:24.835 [org.atteo.moonshine.Main.main()] INFO Moonshine - Starting: "first" PrinterService (Service with simple start/stop logic)
15:43:24.835 [org.atteo.moonshine.Main.main()] WARN o.a.moonshine.example.PrinterService - Basic service start() method called
15:43:24.835 [org.atteo.moonshine.Main.main()] INFO Moonshine - Starting: "second" PrinterService (Service with simple start/stop logic)
15:43:24.835 [org.atteo.moonshine.Main.main()] WARN o.a.moonshine.example.PrinterService - Basic service start() method called
15:43:24.835 [org.atteo.moonshine.Main.main()] INFO Moonshine - All services started
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.078s (Wall Clock)
[INFO] Finished at: Mon Oct 28 15:43:24 CET 2013
[INFO] Final Memory: 23M/217M
[INFO] ------------------------------------------------------------------------
15:43:24.970 [Thread-3] INFO Moonshine - Shutting down moonshine
15:43:24.972 [Thread-3] INFO Moonshine - Stopping: "first" PrinterService (Service with simple start/stop logic)
15:43:24.972 [Thread-3] WARN o.a.moonshine.example.PrinterService - Basic service stop() method called
15:43:24.972 [Thread-3] INFO Moonshine - Stopping: "second" PrinterService (Service with simple start/stop logic)
15:43:24.972 [Thread-3] WARN o.a.moonshine.example.PrinterService - Basic service stop() method called
15:43:24.972 [Thread-3] INFO Moonshine - All services stopped
Service Testing
There is also an easy way to start Moonshine container from JUnit tests. Just extend your test class from MoonshineTest. This will initialize and start Moonshine container once before execution of all test methods and then shutdown it after the tests are done. You can find an example project with simple test here.
If you want to customize Moonshine container execution for the tests, annotate your test class with @MoonshineConfiguration annotation. You can, for instance, skip loading of '/default-config.xml' file and provide your own configuration in-place like that:
@MoonshineConfiguration(skipDefault = true, fromString = ""
+ "<config>"
+ " <printer/>"
+ "</config>")
public class PrinterServiceTest extends MoonshineTest {
@Test
public void shouldStartMoonshine() {
}
}
Services Bundled With Moonshine
Moonshine, out of the box, provides services to start and configure transactions, database access with migrations, persistence using Hibernate and Spring Data, two HTTP engines: Jetty and Tomcat, servlets, websockets and REST services. Additionally Moonshine contains services which allow to profile your application and collect metrics which can be retrieved directly using JMX or through the provided REST wrapper or web console.
For instance let modify our test class to measure execution time of a test method. We will use Perf4J service for that. It provides @Profiled annotation which logs the execution time of the annotated method.
@MoonshineConfiguration(fromString = ""
+ "<config>"
+ " <perf4j/>"
+ "</config>")
public class PrinterServiceTest extends MoonshineTest {
@Test
@Profiled
public void shouldLogExecutionTime() {
}
}
18:59:07.921 [main] INFO org.perf4j.TimingLogger - start[1382983147918] time[2] tag[shouldLogExecutionTime]
Next: Writing Services.