= Example Containerized JaxRS Service :toc: left :source-highlighter: pygments :icons: font // Github specifics ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning: endif::[] Elizabeth Paige Harper epharper@upenn.edu v1.0.0
[NOTE] This repository is a thin demonstrative wrapper around the https://github.com/VEuPathDB/lib-jaxrs-container-core[Core container library], for more in depth documentation of available features and config options see the core library's readme.
== Running the Example
=== Locally
==== Prerequisites
==== Setup & Run
Environment/dependency setup for local development can be done via the command
make install-dev-env
. This will install the dependencies required to build the
project.
After the environment has been set up, the project can be built, tested, and
packaged using make jar
.
Starting the server can be done by running java -jar build/libs/service.jar
.
=== In Docker
==== Prerequisites
==== Setup & Run
The project container can be built using make docker
. The first build
of the container will take several minutes.
.env
file.//------------------------------------------------------------------------------
== Development
=== Quick Start for New Projects
. Create a new project from this template by clicking the "Use this template"
button above.
. Clone your new project and cd into the clone directory
. Run make new-project-initialization
+
This will:
.. Link your new project's git history with the git history of this template
repo which will enable the ability to pull fixes and updates from this
template into your new project.
.. Install the dev tools needed to build and test your project.
. Edit build.gradle.kts
with your service's details
. Edit api.raml
with your API design
. Create any necessary types in docs/schema
. Run make gen-jaxrs
to generate skeleton code
. Further rename and edit the Java packages and source code under src/
as desired
. Implement the generated service interfaces
. Register your services in the Resources
class
. Run make test
to run any unit tests and verify that things compile
=== Quick Start for Contributing
. Clone this project and cd into the clone directory
. Run make link-template-repo
+
This will link your currently checked out project back up to the template repo
which will enable the ability to pull in fixes and updates published to that
template.
. Run make install-dev-env
+
This will install the dev tools needed to build and test the project.
. Edit build.gradle.kts
with your service's details
. Edit api.raml
with your API design
. Create any necessary types in docs/schema
. Run make gen-jaxrs
to generate skeleton code
. Further rename and edit the Java packages and source code under src/
as desired
. Implement the generated service interfaces
. Register your services in the Resources
class
. Run make test
to run any unit tests and verify that things compile
=== Detailed Overview
==== Environment Prerequisites
NPM:: Required to run https://github.com/raml2html/raml2html[raml2html] Maven:: Required to build https://github.com/mulesoft-labs/raml-for-jax-rs[raml-to-jaxrs]
==== Configuration
Configuration for the build is primarily done through the build.gradle.kts
file. Here you can configure the project's name, version, container name,
Java package structure, etc. as well as define your project's dependencies.
For information about adding gradle dependency declarations see the https://docs.gradle.org/current/userguide/declaring_dependencies.html[Gradle docs].
==== API definition
The service api is defined in 2 places initially:
. The api.raml
file which contains the server endpoints.
. The schema/url
folder which contains type schema definitions. +
These files must be valid Raml 1.0 Libraries.footnote:[https://medium.com/raml-api/raml-101-libraries-and-datatypes-fragments-1889b2e82c27[Modular Raml Guide 1]]footnote:[https://www.baeldung.com/modular-raml-includes-overlays-libraries-extensions[Modular Raml Guide 2]]
===== api.raml
====== Including Type Definitions
In the api.raml
file, there is a statement near the top of the file that
declares the Raml file "uses" schema/library.raml
. This is a generated
file based on the contents of the schema library under schema
. The
library.raml
file should not be edited directly.
api.raml
: uses
declaration
[source, yaml, linenums, start=5]The uses
keyword maps an import alias to the imported library. This import
alias is used to access the types defined in that library. In the case of the
above example, library types would be available using lib.\{MyTypeName}
api.raml
: Library type usage.
[source, yaml, linenums, start=36] body:
application/json:
type: lib.HelloResponse
===== Type Schema
Each raml library file under schema
should define a root types
object
defining the types used by the API. The name of the types defined under the
types
object will be the name of the generated Java classes based on those
types.
{nbsp}
package org.veupathdb.service.demo.generated.model;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize( as = HealthResponseImpl.class ) public interface MyType { @JsonProperty("foo") String getFoo();
package org.veupathdb.service.demo.generated.model;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "foo", }) public class HealthResponseImpl implements HealthResponse { @JsonProperty("foo") private String foo;
@JsonProperty("foo") public String getFoo() { return this.foo; }
====
==== Generating a Service
Once your API spec is complete, you can begin development of Java code by
running make gen-jaxrs
. This will create a skeleton of the API in the
generated
source package located under the root package defined using the
projectPackage
value in build.gradle.kts
.
The generated interfaces and types have the basic necessary annotations for use by both Jackson and Jersey.
Once you have implemented the interfaces defined under
\{source-package}.generated.resources
they must be registered in the
\{source-package}.Resources
class.
==== Run Your Service
Running your service locally can be done by following the same steps as defined
above in the <
. Run make jar
. Run java -jar build/libs/service.jar
Running in Docker can be done by:
. Run make build-docker
. Run docker run <your-image-name>
== Other Information
=== Authentication
The base service contains an authentication layer that will be enabled on any
service class or method annotated with @Authenticated
. This authentication
will validate a VEuPathDB user session against the configured account- (for
registered users) and user- (for guest users) databases and append user
profile information to the incoming request object.
=== Make Targets
==== Meta Targets
help
::
. Lists the available make targets. +
This is the default target that will be executed if you just run make
.
==== Project & Environment Setup Targets
new-project-initialization
::
. Runs the make target link-template-repo
. Merges the git history of a new template-based project with the git history of
template that the project was based on. +
. Runs the make target install-dev-env
link-template-repo
::
. Links the currently cloned repository back up to the template repository from
which the project was created. +
This is done to allow pulling in fixes and updates from the template into the
template based project.
merge-template-repo
::
. Pulls in changes from the template repo from which this project was created.
install-dev-env
::
. Checks for system prerequisites.
. Downloads and builds raml-to-jaxrs.
. Installs the Oracle JDBC components into the vendor
directory.
. Downloads, builds, and installs the FgpUtil project into the vendor
directory.
. Installs the required NPM packages
==== Build & Test Targets
compile
::
. Generates code & docs if the API def has changed.
. Compiles Java code if anything has changed.
test
::
. Generates code & docs if the API def has changed.
. Compiles Java code if necessary.
. Runs unit tests.
jar
::
. Generates code & docs if the API def has changed.
. Compiles Java code if necessary.
. Runs unit tests if necessary.
. Packages a self-contained runnable jar.
docker
::
. Runs docker build
for the project.
==== Code & Doc Generation
raml-gen-code
::
. Generates JAX-RS Java code from the RAML spec.
raml-gen-docs
::
. Generates API docs from the RAML spec.
=== Build Process
Presently the build process is operated through a makefile which calls and sets up the necessary prerequisites. This is a temporary solution to be used until all the utilities in https://github.com/VEuPathDB/lib-jaxrs-container-build-utils[the build utils project] are migrated to https://github.com/VEuPathDB/lib-gradle-container-utils[the gradle plugin project].
==== Generating Jax-RS Code
The make gen-jaxrs
command is backed by the https://github.com/mulesoft-labs/raml-for-jax-rs[Mulesoft Raml-for-JaxRs library].
===== Hiccups and limitations
The RAML to JaxRS conversion library has the following known issues that are likely to impact use of the tool:
String
One alternative/workaround for this is defining the enum type in Java and typing the RAML as string with the possible values defined as examples.
Inheritance::
Extending types can work out for trees with at most 1 parent depth, however going beyond that may cause things to generate in a way that has compile errors.
additionalProperties
aka //:
::
Using a catchall block for maps with anything other than a simple value type
will cause the java type to be Map<String, Object>
. Additionally even a
simple value type may have this result.
=== Tunnels and Proxies
When developing or testing a service, you may need to provide that service access to resources that cannot be reached without a tunnel or proxy. This is complicated by the fact that Docker containers, by default, will not see those resources.
To allow Dockerized services access to these external resources, the running
container(s) must be given access to the host machine's network. This can be
done via docker run
using the
https://docs.docker.com/engine/reference/run/#network-settings[network mode "host"]
or via docker-compose
by setting the
https://docs.docker.com/compose/compose-file/compose-file-v3/#network_mode[`network_mode`]
config option to "host".
Binding the containers to your host machine's network will give the containers access to your running tunnels/proxies, but will also mean binding all exposed container ports to those ports on your host machine which may cause conflicts.
=== The project root
The root directory of this project contains multiple standard files used by Gradle, Docker, Make, and GitHub, in addition to custom files created for developer convenience.
.Root Directory Files [cols=".^2,.^1,5", options="header] |=== | File | For | Description
m| .dockerignore
| Docker
| Similar to .gitignore
files, tells Docker to ignore specified file patterns
when performing glob copies from the source directory into the container being
built.
m| .gitignore | Git | Tells git to ignore files matching the specified patterns.
m| api.raml | RAML | API endpoint definition.
m| build.gradle.kts | Gradle | Standard Gradle build script definition.footnote:[https://docs.gradle.org/current/userguide/tutorial_using_tasks.html[Gradle Basics]] This particular build script uses the https://kotlinlang.org/[Kotlin] DSL for gradle. The project configuration is set here and passed to the https://github.com/VEuPathDB/lib-gradle-container-utils[gradle plugin], and all other project dependencies are also set here.
m| Dockerfile | Docker | Docker container definition file.footnote:[https://docs.docker.com/engine/reference/builder/[Dockerfile Reference]]
m| gradlew | Gradle | Standard, autogenerated Gradle script that allows the use of Gradle without requiring the host machine itself to have Gradle installed.
m| makefile
| Make
| Convenience wrapper for the build utils until the
<<Build Process, required tasks>> are performed which will allow simplifying
the build. See <
m| readme.adoc | GitHub | https://asciidoctor.org/[Asciidoc] readme file (the source backing this page).
m| settings.gradle.kts
| Gradle
| File read by gradle before build.gradle.kts; provides the repository and credentials to download the https://github.com/VEuPathDB/lib-gradle-container-utils[VEuPathDB gradle plugin]. This configuration cannot be contained in build.gradle.kts
itself.
|===
.Root Directory Dirs [cols=".^2,.^1,5", options="header] |=== | Directory | For | Description
m| docker/ | Docker/Docker Compose | Contains docker-compose configuration files.
m| gradle/
| Gradle
| Contains the backing source for the gradlew
script in the root directory. +
Keeping this directory in your repository is standard practice for Gradle.
m| schema/
| RAML
| Contains RAML type definitions imported by the api.raml
file in the root
directory. The schema/library.raml
file is autogenerated based on the other
files present in the schema directory.
m | src/ |
---|---|
Java source root |
|===