This is how a shaved wookiee could look like:
Swookiee basically is a JVM Runtime for REST Services. It is lightweight (~17 MB, yeah... "lightweight") and includes libraries like guava, joda-time, metrics, etc. It also supports service implementation written in Groovy and Scala (+7MB each). It uses a JAX-RS 2 (Jersey), Jetty, Jackson stack on top of the Equinox OSGi runtime to serve REST Services. swookiee also provides multiple REST APIs to deploy and control services and components via REST.
Our main goals are:
- simplify exposing REST Services in the JVM
- JVM-Polyglot: Solve problems in best fit language
- reduce mandatory infrastructure and architectural knowledge from developers
- define boundaries and APIs
- enable deployment on artifact level (very simple OSGi RFC-182 implementation)
- increase transparency through direct metrics, graphite and JSON logging support
Build & Start
cd com.swookiee.runtime
mvn clean install
cd com.swookiee.runtime
mvn exec:exec
When swookiee is up and running the Web Console can be accessed via http://localhost:8080/system/console
. The default credentials are admin:admin123
.
To expose a REST service in swookiee you can describe the REST API with a simple interface:
@Path("/hello")
@Produces(APPLICATION_JSON)
public interface HelloWorld {
@GET
String hello();
}
Through using the given @Component
annotation the implementation of the interface will be exposed.
@Component
public class HelloWorldService implements HelloWorld {
@Override
public String hello() {
return "Hola!";
}
}
A REST service will be exposed using the OSGi - JAX-RS Connector. Thus, every JAX-RS component which is an available OSGi service will also be published via Jersey. You will find a deteiles Jersey user guide here.
In addition to provide services you might need to register Filter (e.g. for CORS). This can be done via ContainerResponseFilter
and ContainerRequestFilter
implementations:
@Component
@Provider
public class MyFilter implements ContainerResponseFilter {
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
}
}
@Component
@Provider
public class MyFilter implements ContainerRequestFilter {
@Override
public void filter(final ContainerRequestContext requestContext) {
}
}
In order to register an exception mapper you have to implement the ExceptionMapper
interface:
@Component
@Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
@Override
public Response toResponse(final MyException exception) {
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}
In case you want to use logstash and kibana to analyze your logs you might want to use JSON as an output format.
This can be achieved through setting the property productionLogging
to true
. To change the output folder configure loggingDirectory
with your desired path. The output filename will be osgi-json.log
.
The Swookiee Runtime can be configured programmatically and via the UI of the Web Console.
The programmatic configuration can be done via an API on top of the OSGi Configuration Admin. The full sample can be found here
Define a POJO with the components you want to configure. In this case we want to change the admin user credentials and the graphite reporter configuration:
public class Configuration {
public AdminUserConfiguration adminUserConfiguration;
public GraphiteReporterConfiguration graphiteReporterConfiguration;
}
Define a YAML file which matches the attributes of your POJO. Additionally set the desired parameter values:
adminUserConfiguration:
username: "admin"
password: "admin42"
graphiteReporterConfiguration:
graphiteHost: localhost
graphitePort: 2003
reportingIntervalInSeconds : 42
reportingEnabled: true
reportingPrefix: "configDemo"
In order to set your configuration values to the addressed components you can create a Declarative Service Component which is then using ConfigurationUtils
to set your configuration parameters
@Component
public class ConfigurationProviderSample {
private ConfigurationAdmin configAdmin;
@Activate
public void activate(final BundleContext bundleContext) {
final URL configurationFile = bundleContext.getBundle().getResource("Configuration.yaml");
ConfigurationUtils.applyConfiguration(Configuration.class, configurationFile, configAdmin);
}
@Reference
public void setConfigurationAdmin(final ConfigurationAdmin configurationAdmin) {
this.configAdmin = configurationAdmin;
}
public void unsetConfigurationAdmin(final ConfigurationAdmin configurationAdmin) {
this.configAdmin = null;
}
}
Most configuration parameters can be controlled via the Web Console: http://localhost:8080/system/console/configMgr
Every published REST interface will be monitored via metrics. In addition basic JVM statistics will be collected. In order to configure the graphite reporter you can configure various settings:
graphiteReporterConfiguration:
graphiteHost: localhost
graphitePort: 2003
reportingIntervalInSeconds : 42
reportingEnabled: true
reportingPrefix: "configDemo"
Example Screenshot of some made up traffic:
The above described REST Resource can be enriched using swagger-annotations
. A full sample can be found here.
We could add the @Api
and @ApiOperation
annotations to our sample service:
@Path(Status.PATH)
@Produces(APPLICATION_JSON)
@Api(value = Status.PATH, description = "Returns a status")
public interface Status {
final static String PATH = "/status";
@GET
@ApiOperation(value = "Ping the Status API to receive a StatusObject", response = StatusObject.class)
public StatusObject ping();
}
The following steps are important to let swookiee deliver you swagger api documentation:
- Make sure you have the
maven-swagger-plugin
configured properly and you are able to see generated swagger json files? Also make sure that the<basePath>/services</basePath>
is set according to your configuration. Otherwise you won’t be able to test you API. - Is the generated json part of your bundle/artefact/
.jar
? Is the folder accessable from within the Bundle? Set the theBundle-Classpath
to include the documentation folder. - Add the
X-Swagger-Documentation
header in youMANIFEST.MF
or yourmaven-bundle-plugin
configuration to point to the documentation folder inside thejar
, such as:X-Swagger-Documentation: /swagger
.
After installing the bundle you will find the swagger-ui
under http://localhost:8080/swagger/index.html
. Click on the small swookiee icon to see your documentation and test your API.
- provide rpm, deb and other packages
- more JVM languages? (see clojure branch)
- Running swookiee programatically
- Integration Test Tooling
Many thanks to Intuit Data Engineering & Analytics for the support!
The code is published under the terms of the Eclipse Public License, version 1.0.
* derived from "shaved wookiee"