# Packaging a Java model for Seldon Core using s2i
In this guide, we illustrate the steps needed to wrap your own Java model in a docker image ready for deployment with Seldon Core using [source-to-image app s2i](https://github.com/openshift/source-to-image).
If you are not familiar with s2i you can read [general instructions on using s2i](../wrappers/s2i.md) and then follow the steps below.
## Step 1 - Install s2i
[Download and install s2i](https://github.com/openshift/source-to-image#installation)
* Prerequisites for using s2i are:
* Docker
* Git (if building from a remote git repo)
To check everything is working you can run
```bash
s2i usage seldonio/seldon-core-s2i-java-build:0.1
```
## Step 2 - Create your source code
To use our s2i builder image to package your Java model you will need:
* A Maven project that depends on `io.seldon.wrapper` library
* A Spring Boot configuration class
* A class that implements `io.seldon.wrapper.api.SeldonPredictionService` for the type of component you are creating
* .s2i/environment - model definitions used by the s2i builder to correctly wrap your model
We will go into detail for each of these steps:
### Maven Project
Create a Spring Boot Maven project and include the dependency:
```XML
io.seldon.wrapper
seldon-core-wrapper
0.2.0
```
A full example can be found at `incubating/wrappers/s2i/java/test/model-template-app/pom.xml`.
### Spring Boot Intialization
Create a main App class:
* Add @EnableAsync annotation (to allow the embedded gRPC server to start at Spring Boot startup)
* include the `io.seldon.wrapper` in the scan base packages list along with your App's package, in the example below the Apps's package is `io.seldon.example`.
* Import the config class at `io.seldon.wrapper.config.AppConfig.class`
For example:
```java
@EnableAsync
@SpringBootApplication(scanBasePackages = {"io.seldon.wrapper","io.seldon.example"})
@Import({ io.seldon.wrapper.config.AppConfig.class })
public class App {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}
```
### Prediction Class
To handle requests to your model or other component you need to implement one or more of the methods in `io.seldon.wrapper.api.SeldonPredictionService`, in particular:
```java
default public SeldonMessage predict(SeldonMessage request);
default public SeldonMessage route(SeldonMessage request);
default public SeldonMessage sendFeedback(Feedback request);
default public SeldonMessage transformInput(SeldonMessage request);
default public SeldonMessage transformOutput(SeldonMessage request);
default public SeldonMessage aggregate(SeldonMessageList request);
```
Your implementing class should be created as a Spring Component so it will be managed by Spring. There is a full H2O example in `examples/models/h2o_mojo/src/main/java/io/seldon/example/h2o/model`, whose implementation is shown below:
```java
@Component
public class H2OModelHandler implements SeldonPredictionService {
private static Logger logger = LoggerFactory.getLogger(H2OModelHandler.class.getName());
EasyPredictModelWrapper model;
public H2OModelHandler() throws IOException {
MojoReaderBackend reader =
MojoReaderBackendFactory.createReaderBackend(
getClass().getClassLoader().getResourceAsStream(
"model.zip"),
MojoReaderBackendFactory.CachingStrategy.MEMORY);
MojoModel modelMojo = ModelMojoReader.readFrom(reader);
model = new EasyPredictModelWrapper(modelMojo);
logger.info("Loaded model");
}
@Override
public SeldonMessage predict(SeldonMessage payload) {
List rows = H2OUtils.convertSeldonMessage(payload.getData());
List predictions = new ArrayList<>();
for(RowData row : rows)
{
try
{
BinomialModelPrediction p = model.predictBinomial(row);
predictions.add(p);
} catch (PredictException e) {
logger.info("Error in prediction ",e);
}
}
DefaultData res = H2OUtils.convertH2OPrediction(predictions, payload.getData());
return SeldonMessage.newBuilder().setData(res).build();
}
}
```
The above code:
* loads a model from the local resources folder on startup
* Converts the proto buffer message into H2O RowData using provided utility classes.
* Runs a BionomialModel prediction and converts the result back into a `SeldonMessage` for return
#### H2O Helper Classes
We provide H2O utility class `io.seldon.wrapper.utils.H2OUtils` in seldon-core-wrapper to convert to and from the seldon-core proto buffer message types.
#### DL4J Helper Classes
We provide a DL4J utility class `io.seldon.wrapper.utils.DL4JUtils` in seldon-core-wrapper to convert to and from the seldon-core proto buffer message types.
### .s2i/environment
Define the core parameters needed by our R builder image to wrap your model. An example is:
```bash
API_TYPE=REST
SERVICE_TYPE=MODEL
```
These values can also be provided or overridden on the command line when building the image.
## Step 3 - Build your image
Use `s2i build` to create your Docker image from source code. You will need Docker installed on the machine and optionally git if your source code is in a public git repo.
Using s2i you can build directly from a git repo or from a local source folder. See the [s2i docs](https://github.com/openshift/source-to-image/blob/master/docs/cli.md#s2i-build) for further details. The general format is:
```bash
s2i build seldonio/seldon-core-s2i-java-build:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime:0.1
s2i build seldonio/seldon-core-s2i-java-build:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime:0.1
```
An example invocation using the test template model inside seldon-core:
```bash
s2i build https://github.com/seldonio/seldon-core.git --context-dir=incubating/wrappers/s2i/java/test/model-template-app seldonio/seldon-core-s2i-java-build:0.1 h2o-test:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime:0.1
```
The above s2i build invocation:
* uses the GitHub repo: https://github.com/seldonio/seldon-core.git and the directory `incubating/wrappers/s2i/java/test/model-template-app` inside that repo.
* uses the builder image `seldonio/seldon-core-s2i-java-build`
* uses the runtime image `seldonio/seldon-core-s2i-java-runtime`
* creates a docker image `seldon-core-template-model`
For building from a local source folder, an example where we clone the seldon-core repo:
```bash
git clone https://github.com/seldonio/seldon-core.git
cd seldon-core
s2i build incubating/wrappers/s2i/java/test/model-template-app seldonio/seldon-core-s2i-java-build:0.1 h2o-test:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime:0.1
```
For more help see:
```bash
s2i usage seldonio/seldon-core-s2i-java-build:0.1
s2i build --help
```
## Reference
## Environment Variables
The required environment variables understood by the builder image are explained below. You can provide them in the `.s2i/environment` file or on the `s2i build` command line.
### API_TYPE
API type to create. Can be REST or GRPC.
### SERVICE_TYPE
The service type being created. Available options are:
* MODEL
* ROUTER
* TRANSFORMER
* COMBINER
## Creating different service types
### MODEL
* [A minimal skeleton for model source code](https://github.com/SeldonIO/seldon-core/tree/master/incubating/wrappers/s2i/java/test/model-template-app)
* [Example H2O MOJO](../examples/h2o_mojo.html)