Java SDK
On this page
This document will guide through the creation of an service using the Java SDK and will provide details about the framework.
Crate your new service project
To simplify the creation of a new service, we provide a skeleton service on GitHub. You can fork this repository, which contains the common features required for most services. Visit the repository here.
You can also explore our sample services to understand the features available in the Slingr integrations framework:
Additionally, take a look at the HTTP Service since most services interact with HTTP REST APIs, and this service covers the basic features. You can also see how services like PDF utilize these features.
While you can start from scratch, we recommend using the skeleton service to get up and running quickly.
Customize the skeleton template
If you’ve used the skeleton service, there are a few adjustments you might want to make:
POM file
The pom.xml
contains several elements you might want to adjust:
groupId
: This specifies your service’s group, which could be your company’s group.artifactId
: This typically represents your service’s name for Maven.version
: This is your service’s version. You can leave it as1.0-SNAPSHOT
since Slingr uses repository tags for versioning.name
: This is a human-readable name for your service.properties > build.main-class
: If you modify the package of the main classio.sliongr.service.SkeletonService
(which you probably will), update it to match the package (except for the classRunner
). For example, if your service’s main class isyourcompany.service.<nameService>
, update it toyourcompany.service.Runner
.
Service descriptor
The appService.json
file has at least two fields to update:
label
: This is a user-friendly service name.name
: This is the internal service name used for registration in Slingr.
For other settings, refer to the Service Features documentation.
Main class name
You’ll likely want to rename and repackage the io.slingr.service.SkeletonService
class, which is your service’s main class.
Additionally, update the @SlingrService
annotation:
@SlingrService(name = "name")
public class SkeletonService extends HttpService {
}
Ensure that this name matches the corresponding entry in appService.json
.
Reading configuration
There are two ways to retrieve configuration settings from the service:
- Define individual fields
- Access the entire configuration using a
Json
object
To define individual variables, follow this syntax within the class annotated with @SlingrService
:
@SlingrService(name = "name")
public class SkeletonService extends HttpService {
@ServiceProperty
private String token;
}
To store the value of the configuration property token
, assign it to the variable token
. Conventionally, the variable name matches the field name. However, you can customize it using the name
attribute within the @ServiceProperty
annotation.
Additionally, you have the option to utilize the @ServiceConfiguration
annotation in the following manner:
@SlingrService(name = "name")
public class SkeletonService extends HttpService {
@ServiceConfiguration
private Json config;
@Override
public void serviceStarted() {
logger.info(String.format("Domain [%s]", config.string("domain")));
}
}
Hooks
Within services, several hooks are available for executing initialization or cleanup tasks.
@SlingrService(name = "name")
public class SkeletonService extends HttpService {
@Override
public void serviceConfigured() {
// the definitions and properties are ready to be used at this point
}
@Override
public void servicesSvcConfigured() {
// the communication with the app is ready at this point
}
@Override
public void webServicesConfigured() {
// the web services server is ready at this point
}
@Override
public void serviceStarted() {
// the service has been started completely
}
@Override
public void serviceStopped(String cause) {
// service has been stopped
}
}
Functions
To implement a function defined in the appService.json
file, employ the @SlingrFunction
annotation:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ServiceFunction(name = "randomNumber")
public Json generateRandomNumber(Json data){
Json res = Json.map();
if (data != null && data.contains("max")) {
res.set("number", random.nextInt(data.integer("max")));
} else {
res.set("number", random.nextInt(10000));
}
return res;
}
}
Upon usage, your function will receive a Json
argument containing the parameters sent to the function. Your function is expected to return a Json
object as the response.
Events
Utilize the Events
interface to dispatch events to the app, which can be accessed through the events()
method:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ServiceWebService(methods = {RestMethod.POST})
private WebServiceResponse inboundEvent(WebServiceRequest request) {
events().send("inboundEvent", Json.map().set("data", request.getJsonBody()));
return new WebServiceResponse();
}
}
Note that the event inboundEvent
must be defined in your appService.json
descriptor.
When dispatching events using send()
, it’s important to note that the process is asynchronous. If you require a response from the app and wish to wait for it, employ sendSync()
instead:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ServiceWebService(methods = {RestMethod.POST})
private WebServiceResponse inboundEvent(WebServiceRequest request) {
Object res = events().sendSync("inboundEvent", Json.map().set("data", request.getJsonBody()));
// do something with the res
return new WebServiceResponse();
}
}
Remember that, in this scenario, to obtain a response, the listener in the app should utilize the keyword return
to send back the desired response.
Data stores
For services requiring data persistence, data stores are accessible. These stores should be defined in the descriptor file, after which they can be utilized within the service:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ServiceDataStore(name = "test_data_store")
private DataStore dataStore;
@ServiceWebService(methods = {RestMethod.POST})
private WebServiceResponse processItems(WebServiceRequest request) {
Json body = request.getJsonBody();
Json query = Json.map().set("code", body.string("code"));
Json item = dataStore.findOne(query);
if (item == null) {
item = body;
dataStore.save(item);
}
events().send("items", item);
return new WebServiceResponse();
}
}
Webhooks
To enable your service to receive HTTP calls, you can achieve this effortlessly by using the @ServiceWebService
annotation:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ServiceWebService(methods = {RestMethod.POST})
private WebServiceResponse genericCommand(WebServiceRequest request) {
// do something with the res
return new WebServiceResponse();
}
@ServiceWebService(path = "/orders", methods = {RestMethod.POST})
private WebServiceResponse processOrder(WebServiceRequest request) {
// do something here
return new WebServiceResponse();
}
}
Given the above sample, the following URLs will be available:
POST https://<yourAppName>.slingrs.io/<env>/services/<serviceName>
POST https://<yourAppName>.slingrs.io/<env>/services/<serviceName>/orders
When those URLs are called, those methods will be invoked.
Handling files
It is possible to upload and download files to/from the app using the utilities in ``
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ApplicationLogger
private AppLogs appLogger;
@ServiceFunction
public Json test1(Json data){
DownloadedFile file = files().download(data.string("fileId"));
// do something with the file
}
@ServiceFunction
public Json test2(Json data) throws IOException {
Json uploadedFileInfo = files().upload("test", IOUtils.toInputStream(data.toString(), "UTF-8"), "application/json");
return uploadedFileInfo;
}
}
Logging
It is possible to send logs to the app from your service using the AppLogs
:
@SlingrService(name = "sample")
public class SampleService extends HttpService {
@ApplicationLogger
private AppLogs appLogger;
@ServiceWebService(methods = {RestMethod.POST})
private WebServiceResponse genericCommand(WebServiceRequest request) {
appLogger.info("Generic command arrived", request.getJsonBody());
// do something with the res
return new WebServiceResponse();
}
@ServiceWebService(path = "/orders", methods = {RestMethod.POST})
private WebServiceResponse processOrder(WebServiceRequest request) {
appLogger.info("Order processing arrived", request.getJsonBody());
// do something here
return new WebServiceResponse();
}
}
In addition to sending a message, you have the option to include supplementary information that will be viewable by clicking on More Info
within the app monitor log.
Setting up a proxy service
Before proceeding to run your service locally, it’s essential to configure a proxy service in the app you intend to use for testing your service’s development. Further details about this can be found in the guide: Create your own services.
Upon adding a new Proxy service
to your app, you will be prompted to provide the Service URI
in the configuration. We recommend employing ngrok as an alternative to opening a port on your router. With ngrok
, you can establish a URI as follows:
./ngrok http 10000
This will provide you with both an HTTP and HTTPS URL. We highly recommend utilizing the HTTPS URL; therefore, copy it and paste it into your service’s configuration.
As for the token, we suggest retaining the auto-generated token, unless you have a specific reason not to.
After creating the service, you will encounter a configuration section similar to the following:
_service_name=proxy
_app_name=yourtestapp
_environment=dev
_pod_id=id
_profile=default
_custom_domain=
_debug=true
_local_deployment=true
_base_domain=slingrs.io
_webservices_port=10000
_extension_broker_api=https://yourtestapp.slingrs.io/dev/services/proxy/api
_token=91833a8b-929f-4eab-b7b4-2383c10cd629
_service_config={}
Copy and paste this configuration into the service.properties
file. Please note that the final property, _service_config
, should contain valid JSON configuration for your service. You may want to avoid overwriting this property. If you’ve utilized the skeleton service, your configuration might resemble the following:
_service_name=proxy
_app_name=yourtestapp
_environment=dev
_pod_id=id
_profile=default
_custom_domain=
_debug=true
_local_deployment=true
_base_domain=slingrs.io
_webservices_port=10000
_extension_broker_api=https://yourtestapp.slingrs.io/dev/services/proxy/api
_token=91833a8b-929f-4eab-b7b4-2383c10cd629
_service_config={"token":"123456"}
If you prefer, an alternative approach is to utilize a separate file if you wish to avoid committing sensitive credentials to your repository. We typically employ service_proxy.properties
for this purpose, which is already included in the .gitignore
of the skeleton service. You can provide this file as an argument when executing your service. Consequently, this configuration will be utilized when your service runs with the proxy.
Please bear in mind that service.properties
is exclusively used when you locally run the service. It’s valuable as a foundation for creating service_proxy.properties
. However, it doesn’t impact the service’s behavior in the cloud since service configuration is managed differently.
Once the proxy service has been established in your app, remember to commit and push changes to ensure proper initialization.
Running your service
Before you proceed with running your service, ensure that you have built it:
cd ENDPOINT_FOLDER
mvn package
You can proceed to run your service either through the command line or using your Integrated Development Environment (IDE). Ensure that you provide the following details when launching:
- Main Class: Set this to
package.Runner
, wherepackage
corresponds to the configuration you established when creating the service. You can find the class path within thepom.xml
file. - Configuration File: Specify the properties file to use. Typically, this will be
service.properties
,service_proxy.properties
, or any other designated properties file. - VM Options: Optionally, you can define the heap size using
-Xmx256M
. This helps restrict the memory usage of your service, minimizing potential issues upon deployment to the cloud. Keep in mind that this memory setting doesn’t precisely mirror the memory available when the service is deployed in the cloud. Total memory consumption includes native memory used by the entire process. Nevertheless, limiting heap memory can be beneficial. - Working Directory: Confirm that the working directory aligns with the location of your service’s code.
Using Maven, you can execute the command below in the root folder of your service:
mvn exec:java -Dexec.mainClass="io.slingr.service.skeleton.Runner" -Dexec.args="configurationFile=serice_proxy.properties" -DROOT_LOGGER=stdout
Testing that your service is working
With your service operational and the proxy service established, let’s perform a brief test to confirm that everything is functioning correctly. To carry out this test, execute the provided code within your builder or monitor console:
var res = svc.proxy.randomNumber({});
log('res: '+JSON.stringify(res));
You should see an output like this:
res: {"number":5560}
We are making the assumption that you are utilizing the skeleton service template, which includes the availability of this method. Alternatively, if you're not using the skeleton template, ensure that you call a method that exists within your own service.
Exploring additional samples
Numerous services have already been developed for the Slingr platform. Feel free to browse through them to gain insights into the various features offered by the services framework.