Overview

Backendless Custom Business Logic is a system designed to allow extensibility and customization of an app backend with custom server-side code. Using Business Logic developers can override and extend the built-in Backendless services or deploy custom API services. The system supports the following types of custom business logic:

API Services

Hosted Services
These services are created based on the deployed Java and JavaScript/Node.js code. For each service Backendless automatically generates REST routes and native SDKs.
Overview
Quick Start Guide

 

Imported Services
These services are created from an external definition such as a WSDL or RAML document or AWS Lambda Functions.

API Event Handlers

This is the code overriding or extending behavior of the built-in Backendless services (User, Data, Geo, etc services).
Overview
Quick Start Guide

Timers

Java or Javascript/Node.js code which runs according to a schedule.
Overview

 

IMPORTANT: Backendless Custom Business logic must be compiled with the Java 8 compiler.

The diagram below illustrates all business logic components within Backendless. The green blocks represent custom server-side code:

server-code-diagram

API Services

Overview

Backendless supports two types of API services: hosted and imported. The hosted API services are created from the code deployed to Backendless. The imported API services are created from a service description such as WSDL and RAML or they can be constructed to proxy to the Amazon Lambda functions.

 

What is a Service?

An API Service is a logical unit with one or more API endpoints. These endpoints are available in two formats:

via HTTP REST endpoint URI
via native SDK

 

Backendless automatically generates both types of endpoints for the deployed code. Access to an API Service is guarded by Backendless security, where individual service operations can be allowed or denied for application's users and roles. Additionally, Backendless gathers analytics for the service' API usage. The diagram below visually demonstrates these concepts:

What-is-API-Service

 

The workflow for developing and deploying a hosted service is very straight-forward:

1. Developer writes the service code;
2. Developer debugs and tests the service code using Backedless CodeRunner;
3. Developer uses Backendless CodeRunner or Backendless Console to deploy the code;
4. Backendless processes the deployed code and creates an API Service;
5. Backendless automatically generates native client-side SDKs as well as REST API routes for the service operations.

 

Code deployed to Backendless automatically becomes an API service when it follows a set of simple rules documented below. For the code to be an API Service, it means it  is accessible through the standard networking mechanisms, such as generated REST API as well as native SDKs created by Backendless.

 

Developer must observe the following rules when creating service code:

The service class must be declared public.
The service class must implement the com.backendless.servercode.IBackendlessService interface. It is a marker interface - it does not declare any methods.
The service class must have the default, public, no-argument constructor.
The service class must not be declared abstract.
There must be at least one public method in the service class (API Engine generates API routes based on the available public methods).
Any classes referenced by the service must be public, cannot be inner or anonymous and must have public, default no-argument constructor.

 

Consider the following sample service:

package com.mbaas;

import com.backendless.Backendless;
import com.backendless.servercode.IBackendlessService;

public class SampleService implements IBackendlessService
{
  public WeatherInfo getWeather( String zipCode )
  {
    return new WeatherInfo( zipCode );
  }

  public long getServerTime()
  {
    return System.currentTimeMillis();
  }

  public void sendEmail( String to, String subject, String body )
  {
    Backendless.Messaging.sendHTMLEmail( subject, body, to );
  }
}

Limitations

There are several considerations and limitations for the runtime behavior of the invoked methods:

The code cannot use native File IO, instead, it must be the Backendless File Service API to work with the application's file storage.
The execution time of a service method is limited for each Backendless Cloud pricing plan. Check the pricing table on the website for details. This limitation does not exist in the Backendless Pro and Managed Backendless versions of the service.
 

Basic Quick Start Guide

This Quick Start guide will walk you through the process of creating a basic Backendless API service. By the time you finish the guide, you will have a working API service hosted in Backendless. You will be able to experience REST-based method invocations and download generated client SDKs.

Requirements

Before you start with the guide, you will need the following:

1. Java Development Kit (JDK) version 8 or above.
2. Java development environment (IntelliJ IDEA, Eclipse, etc), we recommend IntelliJ IDEA.
3. a Backendless account. Register for your free account at https://develop.backendless.com/registration

 

Instructions

1. Login to your Backendless account
2. Click the Download Project Template button at the top of the screen.
3. Select Java and click NEXT:
java-category
4. Click API SERVICE, then click DOWNLOAD:
api-service
5. The browser will download a zip file. Extract the file into a directory which will be referred to as <PROJECT-DIR> throughout this guide.
6. Open the project from <PROJECT-DIR> in a Java IDE (we recommend IntelliJ IDEA). You may need to select an SDK for the project, make sure to choose Java 8.
7. The project includes the CodeRunner SDK and code for a sample Java service. The service code is in the DemoService class located in the com.sample.api package:
package com.sample.api;

import com.backendless.Backendless;
import com.backendless.servercode.IBackendlessService;

public class DemoService implements IBackendlessService
{
  public String getGreeting( String guestName )
  {
    System.out.println( "in get greeing" );
    return "Hello, " + guestName;
  }

  public String saveComment( DemoComment comment )
  {
    DemoComment savedComment = Backendless.Data.of( DemoComment.class ).save( comment );
    return Backendless.FootprintsManager.getObjectId( savedComment );
  }
}

 
Notice the class implements the com.backendless.servercode.IBackendlessService interface. This is a special interface which tells Backendless that the Java class must be treated as an API service. The public methods in the class: getGreeting and saveComment will be service methods accessible via REST and native APIs.
8. Compile the project and make sure there are no compilation errors. The project is configured to place all the compiled code (the .class files) into the <PROJECT-DIR>/classes directory (subdirectories will be created per the package structure).
9. To run the service in the debug mode, which is when the class runs locally but is accessible from the Cloud, run the following command from the <PROJECT-DIR> directory:
on Linux:
./bin/CodeRunner.sh
 
on Windows:
./bin/CodeRunner.bat
 
When CodeRunner starts, it inspects classes placed in the /classes directory, identifies any which are marked as API services and registers them with the Backendless Cloud. You should see the following output after running the command:

[INFO] JDK from "JAVA_HOME" - /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 2017-05-05T13:48:19Z
[INFO] CodeRunner session is running for 2 hours and will be terminated on 01:03:52.701[UTC]
[INFO] Registering runner on: 'http://api.backendless.com
[INFO] Application ID:  'XXXXXXXX-D06E-4610-FF3E-XXXXXXXXXXXX'
[INFO] Secret key:      'XXXXXXXX-7A24-DF87-FF9E-XXXXXXXXXXXX'
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Run dependency analyzer for app: XXXXXXXXX-D06E-4610-FF3E-XXXXXXXXXXXX
ClassPath: /Users/mark/Downloads/CodeRunner/repo/debug-tmp/service-tmp.jar
[INFO] Analyze service: com.sample.api.DemoService
[INFO] Build successfully: 0 handlers, 0 timers, 1 service
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...

10. When deploying a service, it is recommended to review the initial CodeRunner output. Notice the following lines (they will be different with your own service(s):
[INFO] Analyze service: com.sample.api.DemoService
[INFO] Build successfully: 0 handlers, 0 timers, 1 service
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed
These lines confirm that the Java class is recognized as a service and that it is built and properly registered with Backendless
11. Return to Backendless Console and click the Business Logic icon. The screen displays the service in the debug mode:
java-service-in-debug.zoom50
 
Any time you launch the local copy of CodeRunner with your code, you will see your services showing up as "Services in Debug" in Backendless Console. This means the service is registered with Backendless, but it runs locally on your computer (with CodeRunner as the code container). You can generate client SDKs for the service, invoke the service methods from the console and inspect responses. Most importantly, for the invocations sent through Backendless, you can still debug the service code locally in your IDE.
12. The first operation of the service getGreeting is automatically selected in the console. The Java method has the guestName argument, which is displayed in the Parameters section. Enter a value into the field for guestName and click the INVOKE button to invoke the method. Backendless invokes the service and displays the result in the RESPONSE panel:
invoke-result-java.zoom80
13. Backendless also generates a cURL command for any invocation. To see the command for the invocation of the getGreeting method, click the CODE section:
curl-invoke-java
 
Copy the cURL command and paste it into a terminal window to invoke the method with the entered argument value.
14. Backendless also generates native SDKs with libraries to invoke your service. Click the download icon next to the service name to download an SDK:
download-sdk-40
 
For additional information about using the SDKs and invoking services from your code, see the Client SDKs and the Service Invocation chapters of this guide.

 

Advanced Quick Start Guide

This Quick Start guide will walk you through the process of creating a Backendless API service by uploading Java code to API Engine. By the time you finish the guide, you will have a working API service hosted in Backendless API Engine. You will be able to experience REST-based method invocations and download generated client SDKs.

Setup

1. Login to your Backendless account
2. Click the Download Project Template button at the top of the screen.
3. Select Java and click NEXT:
java-category
4. Click API SERVICE, then click DOWNLOAD:
api-service
5. The browser will download a zip file. Extract the file into a directory which will be referred to as <PROJECT-DIR> throughout this guide.
6. Open the project from <PROJECT-DIR> in a Java IDE (we recommend IntelliJ IDEA). You may need to select an SDK for the project, make sure to choose Java 8.
7. The project already contains a sample API service which can be ignored - you will be implementing a new, more advanced service as a part of this guide.

Service Design

The service developed in this guide is a trivialized shopping cart. The APIs generated by Backendless from the code allows adding items to a shopping cart, retrieving and deleting the contents of the cart. Each operation of the service must be stateless, as a result, any intermediary state between the service calls must be stored elsewhere (i.e. Backendless persistent storage, server-side cache, etc). In the design of the code, the state is a JavaScript object representing a shopping cart. The object is stored in Backendless Cache between the service invocations. Every method call retrieves the cart object from cache, updates it as necessary and then places it back into cache. The code identifies every shopping cart by a logical name, thus the same service code will be able to handle multiple cart objects. The "purchase" API call persists the contents of the shopping cart in the Backendless database. The diagram below illustrates the design:

API-Service-Design

Service Development

1. Create a Java class called ShoppingCartService in the com.mbaas.service package. The name of the package is not important, as long as there is one. Make sure the class implements the com.backendless.servercode.IBackendlessService interface. This is a marker interface - it does not declare any methods. When a class implements the interface, Backendless will handle the class as an API Service:
package com.mbaas.service;

import com.backendless.servercode.IBackendlessService;

public class ShoppingCartService implements IBackendlessService
{
}
2. The service will use three additional classes (see the diagram in the Service Design section) - Create the class files with the code shown below in your project:
 
ShoppingItem class:
package com.mbaas.shopping;

public class ShoppingItem
{
    public String objectId;
    public String product;
    public float price;
    public int quantity;
}

ShoppingCart class:
package com.mbaas.shopping;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ShoppingCart
{
  private List<ShoppingItem> items = new ArrayList<>();

  public void addItem( ShoppingItem item )
  {
    items.add( item );
  }

  public List<ShoppingItem> getItems()
  {
    return items;
  }

  public boolean deleteItem( String productName )
  {
    Iterator<ShoppingItem> iterator = items.iterator();

    while( iterator.hasNext() )
    {
      ShoppingItem item = iterator.next();

      if( item.product.equals( productName ) )
      {
        iterator.remove();
        return true;
      }
    }

    return false;
  }
}

Order class:
package com.mbaas.shopping;

import java.util.Iterator;
import java.util.List;

public class Order
{
    private String objectId;
    private List<ShoppingItem> items;
    private float orderPrice;

    public float getOrderPrice()
    {
        return orderPrice;
    }

    public void setOrderPrice( float orderPrice )
    {
        this.orderPrice = orderPrice;
    }

    public String getObjectId()
    {
        return objectId;
    }

    public void setObjectId( String objectId )
    {
        this.objectId = objectId;
    }

    public List<ShoppingItem> getItems()
    {
        return items;
    }

    public void setItems( List<ShoppingItem> items )
    {
        this.items = items;

        Iterator<ShoppingItem> iterator = items.iterator();

        while( iterator.hasNext() )
        {
            ShoppingItem item = iterator.next();
            orderPrice += item.price * item.quantity;
        }
    }
}
3. Return to the ShoppingCartService class and add the method shown below. The method is private and will be used by other methods added in the subsequent steps. The method is responsible for retrieving a ShoppingCart object from Backendless cache.
package com.mbaas.service;

import com.backendless.servercode.IBackendlessService;
import com.backendless.Backendless;

public class ShoppingCartService implements IBackendlessService
{
  private ShoppingCart getCart( String cartName )
  {
    if( cartName == null || cartName.trim().length() == 0 )
      throw new IllegalArgumentException( "Missing cart name" );

    return Backendless.Cache.get( cartName, ShoppingCart.class );
  }
}
4. Add the addItem, deleteItem, getItems and purchase methods which will become API operations once the code is deployed to Backendless. Every method expects the name of the cart. The service implementation maps the ShoppingCart object to the shopping cart name and stores/retrieves it from the server-side cache. If the object is not available in cache (which means it would be the first request for the cart), an object is created and stored in cache. Once a cart is updated in the addItem, deleteItem methods, it is placed back into cache. The purchase method retrieves the shopping cart object from cache and stores its contents (the ShoppingItem objects) in the Order table of the Backendless database
package com.mbaas.service;

import com.backendless.Backendless;
import com.backendless.servercode.IBackendlessService;
import java.util.List;

public class ShoppingCartService implements IBackendlessService
{
  public void addItem( String cartName, ShoppingItem item )
  {
    ShoppingCart shoppingCart = getCart( cartName );

    if( shoppingCart == null )
      shoppingCart = new ShoppingCart();

    shoppingCart.addItem( item );
    item.objectId = null;
    Backendless.Cache.put( cartName, shoppingCart );
  }
  
  public boolean deleteItem( String cartName, String productName )
  {
    ShoppingCart shoppingCart = getCart( cartName );

    if( shoppingCart == null )
      return false;

    boolean result = shoppingCart.deleteItem( productName );
    Backendless.Cache.put( cartName, shoppingCart );
    return result;
  }

  public List<ShoppingItem> getItems( String cartName )
  {
    ShoppingCart shoppingCart = getCart( cartName );

    if( shoppingCart == null )
      shoppingCart = new ShoppingCart();

    return shoppingCart.getItems();
  }

  private ShoppingCart getCart( String cartName )
  {
    if( cartName == null || cartName.trim().length() == 0 )
      throw new IllegalArgumentException( "Missing cart name" );

    return Backendless.Cache.get( cartName, ShoppingCart.class );
  }
}
5. Build the project. Make sure all the compiled classes are in the classes directory as shown in the image below. When using the API Service template, they will be copied into the classes directory automatically.
service-compiled-code

Service Debugging

Backendless supports the "remote-registration-local-debugging" model. This means that the service code can be debugged locally on the developer's computer while the service registration and its API endpoints become available through Backendless. API requests for a service in debug can be sent to Backendless and it routes them to the local debugging process running on the developer's computer. The diagram below illustrates the model:

Local-Debug-Remote-Registration

 

To start the local debugging process:

1. Open a command prompt and change the current directory to <PROJECT-DIR>.
2. Run the following command to start the CodeRunner process:
3. on Linux:
./bin/CodeRunner.sh
 
on Windows:
./bin/CodeRunner.bat
4. When CodeRunner starts, it displays the following message:

Listening for transport dt_socket at address: 5005
[INFO] JDK from "JAVA_HOME" - /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 2017-05-05T13:48:19Z
[INFO] CodeRunner session is running for 2 hours and will be terminated on 00:03:31.562[UTC]
[INFO] Registering runner on: 'http://api.backendless.com'
[INFO] Application ID:  'F3A5A127-XXXX-XXXX-XXXX-4FF9FA181800'
[INFO] Secret key:      '3DF7B59A-XXXX-XXXX-XXXX-455442C71100'
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Run dependency analyzer for app: F3A5A127-XXXX-XXXX-XXXX-4FF9FA181800
[INFO] Analyze service: com.mbaas.shopping.ShoppingCartService
[INFO] Analyze service: com.sample.api.DemoService
[INFO] Build successfully: 0 handlers, 0 timers, 2 services
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...

5. When deploying a service, it is recommended to review the initial CodeRunner output. Notice the following lines:
[INFO] Analyze service: com.sample.api.DemoService
[INFO] Build successfully: 0 handlers, 0 timers, 2 services
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed
These lines confirm that the Java classes in the project are recognized as services and that they are properly registered with Backendless.
6. At this point the service is registered with Backendless, any invocation of the service operations will be routed to your local CodeRunner.

Working with the Service

The instructions below apply to both modes of service execution (debug and deployment).

1. Once the service is in the debug mode, Backendless generates the service's REST APIs and the native SDKs. You can see the service and its operations on the Business Logic > API Services screen. For the service reviewed in this guide you will see the following screen when the service is running in the debug mode:java-api-service-v4.zoom70

 

2. Backendless Console lists all available service operations/methods. To see the details of an operation and to invoke it, click the operation name. For example, click the addItem method. When invoking the method from Backendless Console, the request body must be a JSON object which combines the shopping cart name and the item being added to the cart:
{
  "cartName": "string",
  "item": {
    "objectId": "string",
    "product": "string",
    "price": 0,
    "quantity": 0
  }
}
. However, when the operation is invoked from a native client (Android, iOS, JS), method arguments will be represented by objects from the native programming environment. In other words, you do not need to convert everything to JSON when working with API Services, unless you use service's REST API.

 

3. To invoke the addItem method from Backendless console, you will need to provide argument values. Click the model schema area to copy its contents to the to the argument value area:
click-to-copy-schema

 

4. Modify the argument value as shown below. The objectId element should be removed, it is not needed for this operation.
{
  "cartName": "mycart",
  "item": {
    "product": "Hoverboard",
    "price": 499,
    "quantity": 1
  }
}

 

5. Click INVOKE to make an invocation of the method:
js-invocation-result-v4

 

6. Notice you can get a complete cURL request which executes the same REST API request by switching to the CODE section:
curl-request-api-service

 

7. To see the contents of the shopping cart, click the getItems method. Enter the same name for the shopping cart that was used in the addItem invocation ("mycart") and click INVOKE again:
getitems-invoke-java-v4.zoom65
 
8. The result is a JSON array of ShoppingItem objects (so far there is only one in the collection). This maps closely to the formal response type of the getItems() method in Java:
  public List<ShoppingItem> getItems( String cartName )
  {
    ShoppingCart shoppingCart = getCart( cartName );

    if( shoppingCart == null )
      shoppingCart = new ShoppingCart();

    return shoppingCart.getItems();
  }
9. The final step is finalizing the purchase with an invocation of the purchase method. The method retrieves the shopping cart object from the server-side cache and stores all the ShoppingItem objects in the Backendless database. To invoke the method, click purchase in the list of method, enter the name of the shopping cart in the form and click INVOKE.
invoke-purchase-v4.zoom60
 
You can see the saved objects on the Data screen of Backendless Console. There are two data tables:
 
Order table:
order-table.zoom60
 
ShoppingItem table:
shopping-item.zoom60

Service Deployment

To deploy the service, run the following command in a command prompt/terminal window:

on Linux:
./bin/Deploy.sh
 
on Windows:
./bin/Deploy.bat

 

The utility packages all the code found in the /classes directory along with the dependencies from the /libs directory and uploads to the Backendless servers. A successful deployment produces the following output in the command prompt window:

[INFO] JDK from "JAVA_HOME" - /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 2017-05-05T13:48:19Z
[INFO] CodeRunner session is running for 2 hours and will be terminated on 18:59:22.130[UTC]
[INFO] Parsing event model...
[INFO] Run dependency analyzer for app: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
ClassPath: /Users/mark/Downloads/CodeRunner/repo/debug-tmp/service-tmp.jar
[INFO] Analyze service: com.mbaas.shopping.ShoppingCartService
[INFO] Analyze service: com.sample.api.DemoService
[INFO] Build successfully: 0 handlers, 0 timers, 2 services
[INFO] Publishing 0 event handlers and 0 timers and 2 services to the server…
[INFO] Service published
[INFO] If you connected to coderunner for debug - please stop debugging
[INFO] CodeRunner will shutdown now
[INFO] Debugging Utility disconnected successfully
[INFO] Thank you for using Backendless

 

Once deployed, the service(s) will appear in Backendless Console in the DEPLOYED SERVICES category.

deployed-services

REST Routes

Backendless uses the following rules when creating REST APIs from method signatures:

Method's name prefix

HTTP command

Route

Argument(s)

addXXX( T )

POST

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of T sent
in request body

setXXX( T )

PUT

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of T sent
in request body

setXXX( int indexArg, T valueArg )

PUT

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of indexArg and
valueArg in request body:
{

   "indexArg" : value,

   "valueArg" : value

}

deleteXXX( T )

DELETE

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of XXX sent
in request body

getXXX()

GET

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

n/a

getXXX( int index )

GET

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX?index=value

The index argument must be
sent as a query argument

none of the above - foobar()

POST

/<app ID>/<REST API key>/services/<service name>/<service version>/foobar


 

where:

<app ID>        - Application ID from the Manage > App Settings screen in Backendless Console.

<REST API Key>        - REST API Key for the application from the Manage > App Settings screen in Backendless Console.

<service name>        - Service name as shown in Backendless Console. Service name is the same as the name of the service class.

<service version>        - Version number/name assigned to service at the time when code is uploaded/published to Backendless.

 

Consider the following service:

package com.foo.shopping;

public class ShoppingService implements IBackendlessService
{
  public void addItem( ShoppingItem item ) {
    // skipping implementation for brevity
  }
  
  public ShoppingItem[] getCartItems() {
    // skipping implementation for brevity
  }
  
  public boolean deleteItem( ShoppingItem item ) {
    // skipping implementation for brevity
  }

  public double calculateTotal() {
    // skipping implementation for brevity
  }
}

 

Assuming the name assigned to the service is ShoppingService and version is 1.0, API Engine generates the following REST API routes for the service:

 

addXXX method:

Java signature:

public void addItem( ShoppingItem item )

REST API:

POST http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/Item
Request body: a JSON object representing ShoppingItem

 

getXXX method:

Java signature:

public ShoppingItem[] getCartItems()

REST API:

GET http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/CartItems

 

deleteXXX method:

Java signature:

public boolean deleteItem( ShoppingItem item )

REST API:

DELETE http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/Item

 

all other methods:

Java signature:

public double calculateTotal()

REST API:

POST http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/calculateTotal

Invocation Context

The Backendless container which hosts and runs the services code provides a special object for every method invocation. The object is an instance of the com.backendless.servercode.InvocationContext class and provides access to the following data:

Application ID - id of the application in which context the method is invoked.
String applicationId = InvocationContext.getAppId();
User ID - objectId of the currently logged in user whose identity is used to invoke the method. A value for this parameter is not present if there is no logged in user (or if the user-token header is not passed for the REST invocations).
String userObjectId = InvocationContext.getUserId();
User token - value of the user-token header sent by the client application when invoking the service.
String userToken = InvocationContext.getUserToken();
User roles - an array of the role names of the logged-in user (if any) who made the API call.
InvocationContext.getUserId();
Device type - the type of the device which initiated the method invocation.
DeviceType deviceType = InvocationContext.getUserId();

// get device type corresponding to the API Key. Possible values are:
// "IOS", "ANDROID", "JS", "REST", "BL" (business logic)
String deviceTypeName = deviceType.name();

// get role name corresponding to the API key. Possible values are:
// "IOSUser", "AndroidUser", "JSUser", "RestUser", "ServerCodeUser"
String deviceTypeRoleName = deviceType.getRoleName();
HTTP headers - HTTP headers sent by the client application when invoking the service.
Map<String, String> httpHeaders = InvocationContext.getHttpHeaders();

Custom Errors

Service code may return custom error code and/or error message using the API documented below. The API also supports returning an HTTP status code along with the error code/message. The following sample service demonstrates possible usages of the custom error API:

package com.mbaas;

import com.backendless.exceptions.BackendlessException;
import com.backendless.servercode.IBackendlessService;

public class ErrorService implements IBackendlessService
{
  public void generateCustomError()
  {
    String errorMessage = "I am a specific error message";
    String errorCode = "245";
    throw new BackendlessException( errorMessage, errorCode );
  }
  
  public void generateCustomErrorWithHTTPstatusCode()
  {
    String errorMessage = "I am a specific error message";
    String errorCode = "245";
    int httpStatusCode = 500;
    throw new BackendlessException( errorMessage, errorCode, httpStatusCode );
  }
}

Logging

Service code can use the Logging API available in the Java/Android SDK as well as REST. The API is documented at:

https://backendless.com/documentation/utilities/android/ut_logging.htm

 

Documentation for the management and configuration of logging is available at:

https://backendless.com/documentation/manage/mgmt_logging.htm

 

 

Service Listing

Backendless Console lists both deployed and in-debug services on the API SERVICES section of the Business Logic screen:

services-in-debug-deployed1.zoom30

 

 

The deployed services are deployed to and run on the Backendless servers. The in-debug services are the ones for which a local debugging session with CodeRunner has been started. These services function exactly the same way as the deployed services except the service code runs on the developer's computer. As soon as the debug session is stopped, the service is removed from the list.

 

To refresh a list of services, use the refresh icon:

services-refresh-list.zoom30

Operation Listing

Backendless Console displays a list of service operations when a service is selected. To select a service simply click its method name. Operations are shown as REST routes with the HTTP operation and the path name:

service-and-operations-v4

 

Client SDK and Service Invocation

Backendless instantly generates native client side SDKs when a service is registered. This applies to all API services, regardless of the language used to implement them. The SDKs can be downloaded using the Download Client SDK icon:

download-client-sdk2

 

The generated client-side code is very easy-to-use and is self-explanatory. There is a class representing client-side proxy of the remote service. The class contains the same methods as the service. The implementation of the methods hides the complexity of the remote method invocation. To call an API operation, simply create an instance of the proxy class and invoke its methods.

REST Service Invocation

Backendless Console provides a visual interface for invoking service operations via the generated REST interface. To invoke a service operation, click its name. Console renders operation arguments based on the argument type information from the service code.

 

Backendless Console uses the generated REST API when invoking service methods. As a result, you can also see the details of an invocation using the Network tab in the browser's developer tools.

 

HTTP GET operations

Parameters for the HTTP GET operations must be sent as URL's query params. Consider the following example:

public String getGreeting( String guestName )
{
  // skipped for brevity
}

Since the method name starts with "get", Backendless will generate an HTTP GET route for the operation. When the method is selected in console, the parameter is rendered as shown below:get-method-java-v4.zoom90

HTTP POST|PUT|DELETE operations

Parameters for POST\PUT\DELETE operations must be sent in the payload/body of an HTTP request. Console renders the parameters based on the operation's formal argument list. Consider the following example:

public void postNote( String message, boolean important )
{
  // skipped for brevity
}
Backendless creates a REST route based on the method name. Method name starts with "add" and per the REST Routes rules, Backendless will generate an HTTP POST route for the operation. For methods with more than one argument, Backendless combines them into a JSON object on the request side:

invoke-post-method-v4

 

Clicking the body schema area copies its contents into the body field where it can be edited. To invoke the method click the INVOKE button which sends a service invocation request to Backendless.

Service Security

Access to service APIs can be restricted with the user/role-based security of the application where the service runs. A permission either allows or denies invocation of a method for a specific user or all users who belong to a role. Follow the steps below to configure service security:

1. Login to Backendless console, select the application where the service is deployed and click the Business Logic icon.
2. Select a service from the list in the API Services section.
3. Click the Set Permissions icon in the toolbar:
set-permissions-icon

The permissions management user interface has two sections: User Permissions and Roles Permissions. Both sections use the same principle for applying permissions to users/roles for all or specific service methods. Select a method which should be secured using the method selector list or if the permission assignment for a user/role is going to be the same for all methods, use All Methods:

api-services-permission-method-selector.zoom80

 

To change permission assignment, click the icon at the intersection of a user/role row and the Method column. The icon represents the current permission and has three states which can be changes by clicking the icon.

ico-grant - Represents a permission allowing method invocation for the specified user or role.

ico-reject   - Represents a permission denying method invocation for the specified user or role.

ico-checkmark-gray - Represents "inherited" permission allowing method invocation. Since service permissions do not have the concept of global assignments, the gray check mark essentially is the same as the green one.

 

Runtime Permission Handling

The NotAuthenticatedUser role applies to any API request which does not have a user identity identified by the user-token HTTP header. The value of the user-token header is obtained on the client side from the result of the login API.

 

When Backendless receives a service API call, it uses the value from the user-token header to determine the user's identity and all the roles the user belongs to. Once a list of roles is identified, Backendless checks all assigned permissions for the user and his roles for the invoked method. If any of the permissions deny access, the invocation will be denied and an error is returned back to the client.

 

When testing service invocations with Backendless console, an authenticated request can be performed by logging in a user on the HEADERS page for a method:

login-to-impersonate.zoom80

 

Once a user is logged in, Console uses the assigned user-token for all service method invocations made through console.

Analytics

Backendless automatically tracks service API usage. Every service method invocation is captured by the Backendless Analytics module and can be viewed using Backendless Console:

1. Login to Backendless Console and select the app where the service is deployed.
2. Click the Manage icon, then Analytics.
3. Click the Service Name and Method Name checkboxes.
4. Select the service(s) from the Service Name dropdown list to see the usage of the service APIs:
analytics-codless.zoom70

The API usage is categorized by the type of clients initiated method calls. To see a chart corresponding to a specific client type, service and method simply click the checkbox for that row. The Total Calls column shows the number of API calls made by the specific client type (the All Clients rows show the total calls across all client types).

API Event Handlers

Overview

An event handler is a custom, server-side code which responds to an API event. For every API call Backendless generates two types of events - "before" and "after". The before event is fired before the default logic of the API implementation is executed and the "after" event is triggered right after the default API implementation logic. An event handler can respond to either one of these events. A synchronous (blocking) event handler participates in the API invocation chain and can modify the objects in the chain's flow. For example, the "before" event handlers can modify arguments of the API calls, so the default logic gets the modified objects. Similarly, an "after" handler can modify the return value (or exception) so the client application which made the API request receives the modified value. The diagram below illustrates the flow:

backendless-incocation-chain

All built-in Backendless services emit the "before" and "after" events for every API call. Consider the following example which is an API event handler for the "beforeCreate" API event. The event is triggered when a client application saves an object into the "Contact" data table:

package com.consoledemo.events.persistence_service;

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.Asset;
import com.backendless.servercode.extension.PersistenceExtender;

import com.consoledemo.models.Contact;

@Asset( "Contact" )
public class ContactTableEventHandler extends PersistenceExtender<Contact>
{ 
  @Override
  public void beforeCreate( RunnerContext context, Contact contact) throws Exception
  {
    // add your code here
  } 
}
There are several important design elements in the code snippet:

The @Asset annotation specifies the "asset" or in this case a data table for which the event will be triggered.
The class extends com.backendless.servercode.extension.PersistenceExcender. This is required in order for the custom code to inject itself into the API invocation handling chain. Every API service has its own extender class which must be derived from by the an API event handler.
The beforeCreate method is the event handler method. The name of the method indicates it will be invoked before the default implementation logic of the create method from the Data service API.
The event handler method has the same signature as the corresponding method from the Data service with the exception of the RunnerContext argument.

Quick Start

The goal of this chapter is to enable a developer to quickly become familiar with Backendless custom business logic feature. By the end of the instructions provided below you will have a Backendless application with an API event handler which modifies an object before it is saved with the Backendless API.

 

 

 

Generating Event Handler Code

 

1. Login to Backendless console and create a new application called OrderManagement. You can use an existing application, however this guide assumes that certain data tables are created, which may cause a conflict in an existing app. Use caution if you decide to use an existing app.
2. Click the Data icon to navigate to the Data/Schema browser section of the console:
click-data
 
3. Click the "plus" icon to create a new data table. The icon is located right above the "APP TABLES" section. The reason a table should be created first is because one of the next steps will be registering an event handler for this table. Name the table "Order".
new-order-table
 
4. Click CREATE to create the table. Click YES in the popup to modify the table schema. Enter "customername" in the Name field as shown below:
new-column-customer-name
 
5. Click CREATE to add the column.
6. Click the Business Logic icon to navigate to the custom business logic section:
click-business-logic
 
7. Click EVENT HANDLERS to switch to the event handler management screen.
8. Click the New Event Handler link and make the selections in the popup as shown in the screenshot below.
create-java-event-handler-v4
9. Click SAVE to finalize the creation of the event handler. Backendless creates an event handler and displays it in the list. Notice the event handler is color-coded - the blue color indicates the event handler is in the "Draft" model which means the code has not been deployed to Backendless yet:
event-handler-java-draft
10. Click the Download menu and select JAVA. Backendless generates a zip file with the same name as the name of the application. The zip file contains all the generated source code as well as project files:
download-java-codegen-eventhandler

Customizing and Running Code

1. Expand the downloaded zip file and open the project in a Java IDE (we recommend IntelliJ IDEA).
 
2. The project already includes the CodeRunner™ SDK. The directory where you unzip the project file will be further referenced as [project-home]in this guide. The CodeRunner™ SDK distribution (which is included into the zip) has the following structure:
coderunner-contents
where:
/bin - contains utilities for debugging and deploying custom code.
/classes - directory which СodeRunner™ uses by default when it looks for custom business logic code to debug or deploy.
/libs - may contain additional Java dependencies (.jar files) which will be added to the deployment.
 
3. The downloaded project uses[project-home]/classes as the directory for the compiled output. This is important as the /classes directory is automatically recognized by CodeRunner™ when it looks for custom code.
 
4. Open OrderTableEventHandler.java and modify as shown below (adding the code into the beforeCreate method):
package com.coolapp.events.persistence_service;

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.Asset;
import com.backendless.servercode.extension.PersistenceExtender;

import com.coolapp.models.Order;

        
/**
* OrderTableEventHandler handles events for all entities. This is accomplished
* with the @Asset( "Order" ) annotation. 
* The methods in the class correspond to the events selected in Backendless
* Console.
*/
    
@Asset( "Order" )
public class OrderTableEventHandler extends PersistenceExtender<Order>
{
    
  @Override
  public void beforeCreate( RunnerContext context, Order order) throws Exception
  {
    System.out.println( "About to insert modified Order object into the data store" );
    String customerName = order.getCustomername();
    System.out.println( "customer name received from client - " + customerName );
    order.setCustomername( customerName.toUpperCase() );
    System.out.println( "customer name after the change - " + order.getCustomername() );
  }
}

 
5. Compile the code and make sure [project-home]/classes contains the following directory structure:
coderunner-with-compiled-code

 

6. Open a command prompt window and change the current directory to [project-home]/bin.
7. Run the CodeRunner™ script as shown below:
on Linux:
./bin/CodeRunner.sh
 
on Windows:
./bin/CodeRunner.bat

 

8. Once CodeRunner™ starts, it produces the following output:

[INFO] JDK from "JAVA_HOME" - /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 2017-05-18T11:27:43Z
[INFO] CodeRunner session is running for 2 hours and will be terminated on 21:40:58.239[UTC]
[INFO] Registering runner on: 'https://api.backendless.com'
[INFO] Application ID:  'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
[INFO] Secret key:      'ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ'
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Build successfully: 1 handler, 0 timers
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...

 

9. At this point CodeRunner™ is connected to Backendless and the code is ready for local debugging. Return to Backendless Console, click the Business Logic icon, select EVENT HANDLERS:
event-handler-debug-java-v4

 

10. To test the code you must issue an API request to store an object in the Order table. You could write a client application using any of the Backendless SDKs or with the REST API. Backendless Console includes a REST Console, which is a graphical interface for creating Data Service REST requests. Click the Data icon, select the Order table and then click REST Console.
rest-console-order-table-v4.zoom60

 

11. Enter the following text into the Request Body area and click the POST button. This will send a REST API request to create an object in the Order table:

{
"customername":"Acme Corp"
}

12. You will see the following response in Response Body. As you can see the name of the customername property is no in upper case, which is the change made by the event handler:
event-handler-in-action.zoom70
13. Also notice the following output in the window where CodeRunner™ runs. The output comes from the custom code you developed:

About to insert modified Order object into the data store
customer name received from client - Acme Corp
customer name after the change - ACME CORP

 

14. You can also see the created object in the Data Browser in console:
data-object-in-data-browser.zoom80

Local Debugging

1. Now that you confirmed that the custom code is plugged into the API invocation chain, it is important to establish a connection between the coderunner process and the IDE debugger. CodeRunner is automatically configured to accept remote debugging connections on port 5005. To connect to the process, use the remote debugging facility in the IDE. For example, in IntelliJ IDEA, select "Run" from the main menu and then select "Edit Configurations". Click the "plus" icon to add a new configuration. Select "Remote" from the list and enter "ordermanagement" into the name field as shown below:
idea-remote-debug.zoom70

 

2. Click OK and launch the debugger. Once the debugger is attached to the CodeRunner process, you can establish breakpoints in the code and re-run the client application. Usually debugging takes more time than the duration of the timeout on the client-side. You should expect that the client application (or even the curl request) will receive a timeout, but you should still be able to continue the debugging of your custom code.
idea-debug-backendless.zoom70s

Deploying Code to Production

Deploying code to production can be done using the Deploy.sh utility available in the [project-home]/bin directory.

1. In a command prompt window change the current directory to [project-home]/bin.
2. Run the following command:
on Linux:
./bin/Deploy.sh
 
on Windows:
./bin/Deploy.bat
3. Once the deployment script runs, it should produce the following output:

your-comp-name:bin username$ ./deploy.sh
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2016 Backendless Corp. All rights reserved.
[INFO] Version: 3.0.10 Build date: 20160309-0738
[INFO] CodeRunner session is running for 2 hour and will be terminated on 16:26:25.795[UTC]
 
[INFO] Registering runner on: https://api.backendless.com with secretKey: 3EAC25F1-5C3F-27CF-FFE8-57347B89A100
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Build successfully: 1 handler, 0 timers
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...
[INFO] Publishing 1 event handler and 0 timers and 0 services to the server…
[INFO] Successfully published all event handlers and timers.
[INFO] If you connected to coderunner for debug - please stop debugging
[INFO] CodeRunner will shutdown now
[INFO] Debugging Utility disconnected successfully
[INFO] Thank you for using Backendless

 

4. Once the code is deployed to production, it is immediately plugged into your application. You can verify the production deployment in Backendless Console. Open the Business Logic section and click EVENT HANDLERS. All deployed event handlers appear with the green color code.

 

This concludes the Quick Start. You can try repeating the process for other event handler or timers. If you run into any problems or have any questions, do not hesitate contacting us at http://support.backendless.com.

Event Handler Asset

Event handlers triggered by the APIs for data, messaging, file and geolocation services must declare an "asset". Asset is a general term used to identify one of the following:

a data table for the Data service APIs.
a messaging channel for the Messaging APIs.
a file or a directory for the File service APIs.
a geocategory for the Geolocation APIs.

 

Consider the following example, which is an event handler for the Data Service's beforeUpdate API event:

package com.coolapp.events.persistence_service;

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.Asset;
import com.backendless.servercode.extension.PersistenceExtender;

import com.coolapp.models.Order;

import java.util.HashMap;
import java.util.Map;
import java.util.List;
        
/**
* OrderTableEventHandler handles events for the "Order" table. This is accomplished
* with the @Asset( "Order" ) annotation. 
*/
    
@Asset( "Order" )
public class OrderTableEventHandler extends PersistenceExtender<Order>
{   
  @Override
  public void beforeUpdate( RunnerContext context, Order order ) throws Exception
  {
    // add your code here
  }
    
}

Notice the @Asset annotation. It specified the name of the table for which the event handler is registered.. When event handlers are created in Backendless Console, it automatically selects the appropriate asset (table, messaging channel, file/directory, geo category) and puts it into the generated code.

 

It is possible to create a generic event handler, which would respond to all API events for a given service. For example, the event handler below is triggered when a new object is created in any of the application's tables:

package com.coolapp.events.persistence_service;

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.Asset;
import com.backendless.servercode.annotation.Async;
import com.backendless.servercode.extension.PersistenceExtender;

import java.util.HashMap;
        
/**
* GenericTableEventHandler handles events for all entities. This is accomplished
* with the @Asset( "*" ) annotation.  The asterisk denotes "all entities". 
* The methods in the class correspond to the events selected in Backendless
* Console.
*/
    
@Asset( "*" )
public class GenericTableEventHandler extends PersistenceExtender<HashMap>
{
  @Override
  public void beforeCreate( RunnerContext context, HashMap hashmap) throws Exception
  {
    // add your code here
  }
}
Notice the @Asset("*") annotation. This indicates that the event handler will be triggered for the beforeCreate events for all tables in the application. To determine the name of the actual table which was targeted in the API, use the context.getEventContext() method. For more information see, the Runtime Context section.

Runtime Context

All event handlers receive the context argument which is an instance of com.backendless.servercode.RunnerContext. The argument provides access to additional contextual information for the event:

context.getAppId() - returns Application ID.
context.getUserId() - returns objectId of a user logged in in the client application which made the API call. If there is no logged in user, this value is null.
context.getUserToken() - returns the value of the user-token header representing session of the currently logged in user in the client application which made the API call. If there is no logged in user, this value is null.
context.getUserRoles() - returns an array of security roles of the user whose identity was used in the original API call. If there is no logged in user, the array will contain the NotAuthenticatedUser role and a role corresponding to the API key (for example RestUser if the request is made with the REST API Key).
context.getHttpHeaders() - returns java.util.Map<String, String> which is a collection of the HTTP header names and the corresponding values from the original request.
context.getEventContext() - returns the name of the "asset" for which the event has been triggered. This is useful for "generic" (catch all) event handlers which process events for multiple tables/files/messaging channels, etc. For instance, for the data event handlers, the method returns the name of the table targeted in the API call. For more information, see the Event Handler Asset section of the documentation.

Custom Events

In addition to the built-in events triggered by the API calls, Backendless supports custom, developer-defined events. A custom event is implemented by an event handler which can be defined using Backendless Console. Custom events can be triggered through a specialized API call from a client library or by other custom business logic. A custom event may accept event arguments and return a value which is delivered back to the client-side that has triggered the event.

Consider the following custom event handler:

package com.backendless.servercodedemo.events.custom_events;

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.Asset;
import com.backendless.servercode.annotation.Async;
import com.backendless.servercode.annotation.BackendlessEvent;
import com.backendless.servercode.annotation.BackendlessGrantAccess;
import com.backendless.servercode.annotation.BackendlessRejectAccess;
import com.backendless.servercode.extension.CustomEventHandler;

import java.util.Collections;
import java.util.Map;

/**
* FooEventHandler handles custom event "foo". This is accomplished with the
* BackendlessEvent( "foo" ) annotation. The event can be raised by either
* the client-side or the server-side code (in other event handlers or timers).
* The name of the class is not significant, it can be changed, since the event
* handler is associated with the event only through the annotation.
*/
@BackendlessEvent( "foo" )
public class FooEventHandler extends CustomEventHandler
{
  @Override
  public Map handleEvent( RunnerContext context, Map eventArgs )
  {
    // add your code here
    return Collections.emptyMap();
  }
}

The code above is a custom event handler for the "foo" event. The event name is declared using the @BackendlessEvent annotation. Event arguments are delivered to the code via the eventArgs argument.

 

Custom Event Return Value

The return value of a custom event handler is sent to the client application where the event was dispatched. For example, the following code returns a Java java.util.HashMap object. Each client type adapts the return value to a data structure in the native environment. For instance, Android/Java client would receive the response as java.util.Map.

import com.backendless.servercode.RunnerContext;
import com.backendless.servercode.annotation.BackendlessEvent;

import java.util.Map;
import java.util.HashMap;
    
@BackendlessEvent( "foo" )
public class FooEventHandler extends com.backendless.servercode.extension.CustomEventHandler
{
  @Override
  public Map handleEvent( RunnerContext context, Map eventArgs )
  {
    HashMap result = new HashMap();
    result.put( "status", "Event Processed" );
    return result;
  }
}

 

Create Custom Event Handler in Console

To create a custom event handler using Backendless console:

1. Login to console and select an application. Click the Business Logic icon.
2. Click EVENT HANDLERS, then click the New Event Handler link:
new-event-handler-js.zoom80
3. Make the selection as shown in the screenshot below and enter CoolEvent for the Event field:custom-event-java-v4
4. Click SAVE. Backendless generates event handler and places it into the Draft mode. You can see the code for the event handler by downloading the project (see the Download link in the menu) or by switching to the CODING section and navigating to the Java source file with the event code. Notice, the Java code cannot be edited in console, it is provided only as a reference.
5. Download the code and follow the same steps for developing/debugging/deploying the event handler as in the Quick Start guide.

 

API for Dispatching Custom Events

All Backendless client SDKs include the functionality for dispatching a custom event. The API allows to send event arguments to the server-side and receive the result from the event handler.

 

Android/Java:

HashMap args = new HashMap();
args.put( "weather", "sunny" );

Backendless.Events.dispatch( "foo", args, new AsyncCallback <Map>()
{
  @Override
  public void handleResponse( Map result )
  {
    System.out.println( "received result " + result );
  }

  @Override
  public void handleFault( BackendlessFault backendlessFault )
  {
    System.out.println( "got error " + backendlessFault.toString() );
  }
});


iOS:

All methods are available via the backendless.events accessor. Method signatures:

-(void)dispatch:(NSString *)name args:(NSDictionary *)eventArgs response:(void(^)(NSDictionary *data))responseBlock error:(void(^)(Fault *fault))errorBlock;


JavaScript:

Method signature:

Backendless.Events.dispatch( eventName, eventArgs, Async );

Example:

var eventArg = {weather:"sunny"};
 
var successHandler = function( response ) {
};
 
var errorHandler = function( response ) {
};
 
Backendless.Events.dispatch( "foo", eventArgs )
 .then( successHandler )
 .catch( errorHandler );


.NET:

Method signatures:

// synchronous method
public IDictionary Backendless.Events.Dispatch( String eventName, IDictionary eventArgs );
 
// asynchronous method
public void Backendless.Events.Dispatch( String eventName, IDictionary eventArgs, AsyncCallback<IDictionary> callback )

Example:

Dictionary<String, String> eventArgs = new Dictionary<string, string>();
eventArgs.Add( "weather", "sunny" );
 
AsyncCallback<IDictionary> callback = new AsyncCallback<IDictionary>(
       result =>
       {
         System.Console.WriteLine( "received result - " + result );
       },
       fault =>
       {
         System.Console.WriteLine( "Error - " + fault );
       } );
 
Backendless.Events.Dispatch( "foo", eventArgs, callback );


REST:

Method:

 POST

URL:

https://api.backendless.com/<APP ID>/<REST API Key>/servercode/events/<event name>

where:

<APP ID> - the ID of your application generated upon its creation. You can find this header in the Manage > App Settings section of the Backendless Console.
<REST API Key> - the key of your application generated upon its creation. You can find this header in the Manage > App Settings section of the Backendless Console.
<event name> - name of the event to dispatch

 

Request headers:

Content-Type - the static value, should be set to application/json. This header is mandatory.

 

Request body:

a JSON object with arguments for the event handler

 

Example:

curl \
-H Content-Type:application/json \
-X POST  \
-d '{"weather":"sunny"}' \
-v \
https://api.backendless.com/APP-ID/REST-API-KEY/servercode/events/foo

Timers

About Timers

A timer in the Backendless custom business logic system is a block of code with some execution schedule. A timer itself does not need to perform any scheduling work, Backendless automatically calculates the time for the next execution step of the timer, allocates computing resources and runs the code. Consider the following timer implementation:

package com.backendless.ordermanagement.timers;

import com.backendless.servercode.annotation.BackendlessTimer;
import com.backendless.servercode.extension.TimerExtender;

@BackendlessTimer("{'startDate':1398186600000,'expire':1398878100000,'frequency':{'schedule':'daily','repeat':{'every':1}},'timername':'cool'}")
public class CoolTimer extends TimerExtender
{
  @Override
  public void execute( String appVersionId ) throws Exception
  {
    // add your custom logic here
  }
}

A timer must follow the following guidelines:

A timer implementation must be a public class which extends com.backendless.servercode.extension.TimerExtender.
A time must have the @BackendlessTimer annotation which defines timer's schedule and the name.
A timer must override the execute method from the TimerExtender class. Any custom business logic must be in the method.

 

Timer Schedule

The @BackendlessTimer annotation must be used to define timer's attributes. The annotation's argument is a JSON object represented as a string. The JSON object has the following properties:

Property name

Required

Data Type

Description

startDate

Yes

Number

Contains a timestamp when the timer must be executed for the first time. The timestamp is the number of milliseconds between the time the timer must run and midnight, January 1, 1970 UTC.

expire

No

Number

Contains a timestamp when the timer schedule is complete. No timer events will happen after the specified timestamp. The timestamp is the number of milliseconds between the time the timer schedule expires and midnight, January 1, 1970 UTC. If the parameter is missing, the timer never expires unless it is scheduled to execute only once.

frequency

Yes

JSON Object

Contains the definition of timer's frequency of execution. This is a complex data structure with a detailed explanation below (see the Timer Frequency section).

timername

Yes

String

Timer name for identification purposes. The timer will appear with the specified name in Backendless Console. The name must start with a letter, it cannot contain spaces or dashes.

Timer's frequency is determined by a JSON object expressed as a string and assigned to the frequency  attribute in the BackendlessTimer annotation. For example, the following declaration sets the timer's frequency as 'weekly' which will be repeated every week on Mondays.

@BackendlessTimer("{'startDate':1398189720000,'frequency':{'schedule':'weekly','repeat':{'every':1,'on':[2]}},'timername':'my_timer'}")
public class WeeklyTimer extends TimerExtender

The 'frequency' JSON object has the following properties:

Property name

Required

Data Type

Description

schedule

Yes

String

Defines how frequently the timer should run. Possible values are:

'once' - the timer will run only one time.
'daily' - the timer will run on daily basis, repeated every XX number of days set by the repeat property.
'weekly' - the timer will run on weekly basis, on the specified days, repeated every XX number of weeks.
'monthly' - the timer will run on monthly basis, on the specified months, dates, or days of the week.

repeat

No

JSON Object

Defines how frequently the timer execution should be repeated. The structure of the object varies depending on the value set in the schedule property. See the table below for details.

 

The repeat property is a JSON object. The structure of the object varies depending on the value of the schedule property:

schedule property value

repeat object syntax

Description

'once'

Does not apply.

Repeat object must not be present.

'custom'

{
 'every':NUMBER
}

NUMBER is the number of seconds between each execution of the timer.  For example:

{'every':100} - indicates that the timer should execute every 100 seconds.

{'every':5} - requests the timer to run every 5 seconds.
 
The highest possible value is 86400 seconds. The lower limit of the allowed values depends on the billing plan:
Free plan - 60 seconds
Backendless Plus - 5 seconds
Cloud Enterprise - 1 second

'daily'

{
 'every':NUMBER
}

NUMBER is the number of days between each execution of the timer. For example:

{'every':1} - indicates that the timer should execute every day.

{'every':5} - requests the timer to run every 5 days.

'weekly'

{
 'every':NUMBER,
 'on':[1,2,3,4,5,6,7]
}

NUMBER is the number of weeks between each execution of the timer. Numbers in the 'on' array indicate the days of the week when the timer should run - all are optional, but at least one must be present. 1 indicates Sunday, 2 - Monday and so on. For example:

{'every':2, 'on':[2,5]} - requests the timer to run every 2 weeks (every other week) on Monday (2) and Thursday (5).

'monthly'

{
 'every':[MONTHS ARRAY],
 'on':
 {
    'days':[DAYS ARRAY],

    'weekdays':

    {

      'on':[WEEK ASSIGNMENT],

      'weekdays':[1,2,3,4,5,6,7]

    }
 }
}

MONTHS ARRAY is a numeric array of numbers representing months when the timer should run. 1 stands for January and 12 is for December. For example: 'every':[1,3,11] indicates that the monthly timer should run every January, March and November. Once the months when the timer runs are established, the 'on' property is used to set the detailed schedule. There are two mutually exclusive options: 'days' and 'weekdays'. If one is set, the other one must be null.

 

DAYS ARRAY is a numeric array of days on which the timer should run. 1 represents the first day of the month, 31 stands for the 31st. For example: 'days':[1,5,10] indicates that the timer must run on the 1st, 5th and 10th days of the selected months.

 

WEEK ASSIGNMENT is an array of week indicators with possible values: 'first', 'second', 'third', 'fourth' and 'last'. These values are used in combination with the 'weekdays' property which is an array of numbers. The numbers in the 'weekdays' array indicate the days of the week when the timer should run - all are optional, but at least one must be present. 1 indicates Sunday, 2 - Monday and so on. For example:

'weekdays':

     {

       'on':['first', 'last'],

       'weekdays':[1,3,7]

    }

requests the monthly timer to run on the first and last Sunday, Tuesday and Sunday of the months from the MONTHS ARRAY.

CodeRunner

Custom server code development, debugging and runtime execution in Backendless is made possible with a special utility/container called Backendless CodeRunner™. It is available for download from the Backendless website and is also automatically included into the server-side code/projects generated using Backendless console. The CodeRunner SDK includes several command line tools enabling the process of debugging custom server-side code and subsequently deploying it to Backendless. It is important to note that CodeRunner makes it possible to debug custom code entirely on the developer's computer while the custom code is "plugged into" the API workflow in the cloud. This means any custom code whether it is an API service, API event handler or a timer, can be debugged using the traditional approaches (line-by-line code step through, variable inspection, STDOUT logging) by invoking that code through the centralized Backendless Cloud service. See the Development and Debugging section for details.

 

The process of developing custom business logic with Backendless is slightly different between API services and event handlers/timers. The only difference is the latter may include the step of code-generation which simplifies the workflow.

API Event Handlers and Timers


Backendless supports two approaches for developing custom business logic:

1. Expressive, code-driven approach. With this approach a developer bypasses code generation and builds the custom code for event handlers and timers by using the published APIs.
2. Declarative, console-driven approach. This approach relies on a code generator built into Backendless Console. Using the code generator developer selects the API events and configures schedules for timers for which he intends to add custom logic. The system automatically generates the code which can be downloaded for further development.
 
The diagram below illustrates the process:
backendless-development-process
where:
1. Developer generates code for event handlers and configures timer schedules using Backendless Console. The console includes a code generator which creates the place holder Java code for the API events and timers. The code can be downloaded as a project archive which includes project files for IntelliJ IDEA and Eclipse IDEs. This step is optional, but it significantly simplifies the development process and saves development time.
2. Using an IDE developer writes custom business logic. The compiled code is passed to CodeRunner™, which is a command line utility used for local debugging of the custom business logic code. Developer can optionally attach to the CodeRunner™ process to set breakpoints in the code for the line-level debugging.
3. CodeRunner™ registers with the Backendless Cloud Service. It instructs Backendless to route the API and timer events to the developer computer for the debugged code.
4. Client application sends an API request.  Backendless determines if there is an event handler registered for the received API request and routes the event information to CodeRunner™.
5. CodeRunner™ receives the request and executes the code. Developer can inspect and debug invocations locally before the custom code is deployed to the production environment.

API Services


The workflow of developing API services is illustrated in the diagram below:
service-dev-process
where:

1. Developer downloads and installs the Backendless CodeRunner SDK. Using an IDE developer writes the service code. The compiled code is passed to CodeRunner™, which is a command line utility used for local debugging. Developer can optionally attach to the CodeRunner™ process to set breakpoints in the code for step-by-step debugging.
2. CodeRunner™ registers with the Backendless Cloud Service. It instructs Backendless to route the service invocation requests to the developer computer for the debugged code.
3. Client application sends a service API request.  Backendless determines if there is a service registered for the received API request and routes it to CodeRunner™.
4. CodeRunner™ receives the request and executes the code. Developer can inspect and debug invocations locally before the custom code is deployed to the production environment.

Deployment Models

A “deployment model” combines API event handlers, timers and API services into a single group. The purpose of that grouping is to allow "fragmented" deployment of the business logic into the application. For example, you could separate the API services code from the event handlers and timers in separate projects and deploy them independently of each other.

 

It is important to keep the following in mind when working with business logic:

Deployment models are associated with languages. It means a model with the same name may exist with Java business logic and JS business logic, but it is not the same model – they will contain different business logic.
 
A model cannot contain more than one API event handler attached to the same "asset", which is a table/file/messaging channel. This means, for instance, if you have a "afterUpdate" event handler processing events for the "Person" table and the event handler is in model “X”, the same model “X” cannot have another "afterUpdate" handler for the table "Person".
 
Contrary to what’s stated above, you can have the same kind of an API event handler in different models.

 

When downloading code from the Backendless Console, you must identify the language and then the model for which to download the code.

 

When deploying code using CodeRunner (JS or Java), the deployment model name for the code must be specified either in the CodeRunner's configuration file or from the command line.

 

Deploying code into a model deletes all previously deployed code in the same model.

 

When the code is debugged locally (while being plugged into the Cloud servers), it takes precedence over the same code which may have been previously deployed. In other words, the Debug mode has higher precedence than the Production mode.

Models in Console

When creating an event handler or timer in the Backendless Console you will see a new drop-down list where you can select an existing model. To create a new one, simply type in the desired model and click the menu option to create it. The screenshot below is for creating a new event handler. You will see the identical "Model" drop-down in the "New Timer" popup:

new-java-handler-model

 

The Event Handlers screen in console displays the model name for every handler (see the MODEL column):

event-handlers-with-models.zoom70

 

When you download the generated code for event handlers and timers, you need to choose the model for which to download the code. The downloaded code contains only the event handlers and the timers for the selected model:

download-java-models.zoom70

 

Similarly, if you were to edit the code directly in the Backendless Console on the CODING screen, you need to choose the language and the model. The displayed tree of directories and files will contain only the code for the selected model:

java-model-coding.zoom80

Models with CodeRunner

CodeRunner lets you control what model the code is deployed to. When you download the Cloud Code from the Backendless Console, the generated project is already configured for the deployment model where the code is declared. For example, suppose an event handler is created in the "validator" model, then the downloaded project will reference the model in the /bin/runner.properties file:

application.deployment.model = validator

. The referenced model is used when the code is either debugged locally or deployed to production.

 

When the code is being deployed to the Backendless servers, any other previously code in the same mode will be removed and replaced by the current deployment. CodeRunner informs about it with the following message:

IMPORTANT!
The business logic code will be deployed to model "MODELNAME".
Any business logic which is already deployed on the server in that model
will be removed and replaced with the code from your current project.
If this is an undesired behavior, stop now and set a different deployment model
either by using the -model argument or changing the model name in bin/coderunner.properties.
Would you like to continue? (Y/N)

 

Use the -model argument to change the name of the deployment model without modifying the configuration file:

./bin/Deploy.sh -model YOUR-MODELNAME

 

 

Event Handler Chains

Deployment models enable support for assigning multiple event handlers to an API event. This approach promotes better design for cloud code with a clean division of responsibilities between the event handlers. It is important to note that any data received as arguments for an API call is passed from one event handler to another. If an event handler placed at the beginning of the chain makes a change to an argument (or the return value), then all other event handlers down the chain will get the modified value. The diagram below illustrates event handler chains:

api-flow-multiple-handlers

Backendless Console includes a visual tool enabling the control of the placement order. Suppose an application contains event handlers as shown in the image below:

event-handlers-with-models.zoom70

 

Notice there are three beforeCreate event handler with the generic (*) context, which means the event handlers will be executed when an object is created in any of the application tables. Two of the handlers are written in JavaScript and the third one is in Java. When Backendless detects you have more than one event handler applicable to a particular context, it displays the "handler ordering" icon:

event-handler-ordering-icon.zoom64

 

Clicking the icon opens up a popup where you can control the execution order for all applicable event handlers. Notice that the invocation chain can combine event handlers written in different languages:

Debugging

Backendless custom business logic can be debugged locally on the developer's computer while it is plugged into the Backendless Cloud service. This means your business logic code can be invoked through the API accessible via Backendless Cloud while the custom code runs on your computer. To develop and debug custom business logic developers must use CodeRunner™ SDK.

 

If you develop API services, you should download the CodeRunner SDK from the Backendless website. If you develop API event handlers and/or timers and used the  Business Logic code generation built into Backendless console, CodeRunner™ is already included into the generated ZIP file.

 

The CodeRunner™ distribution has the following directory structure:

coderunner-contents

The /classes folder is the default location where CodeRunner™ searches for any classes with custom business logic (services, event handlers and timers). For convenience, set the /classes directory as the path for the compilation output for the classes with custom business logic.
The /libs folder  must be used for any 3rd party libraries (.jar files) the custom business logic depends on.

 

To run CodeRunner™, open a command prompt window and change the current directory to the bin directory from the CodeRunner™ distribution.

 

Run the following command from the /bin directory to start CodeRunner™ in the local debug mode:

Linux:

./CodeRunner.sh

Windows:

CodeRunner.bat

 

Once coderunner starts, it produces the following output:

your-comp-name:bin username$ ./CodeRunner.sh
Listening for transport dt_socket at address: 5005
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 20160323-0551
[INFO] CodeRunner session is running for 2 hours and will be terminated on 14:32:34.194[UTC]
[INFO] Registering runner on: 'https://api.backendless.com' with version 'v1'
[INFO] Application ID:  'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
[INFO] Secret key:      'ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ'
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Build successfully: 3 handlers, 2 timers
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...

 

In some cases CodeRunner™ may report the following messages:

[INFO] JDK from "JAVA_HOME" - not set.
[INFO] JRE from "JRE_HOME"  - not set.
[WARN] WARNING! It appears you do not have JAVA_HOME and JRE_HOME environment variables set.
This is not necessarily a problem, but it may lead to errors during CodeRunner execution.
If you experience problems related to security policy permissions, the JAR utility or others,
please try setting up the JAVA_HOME environment variable by pointing it to the root directory
of your JDK location (must be version 1.8 or higher).

 

If you see the message and CodeRunner™ subsequently reports runtime errors, it is required to set the JAVA_HOME property which must point to the root of the JDK installation.

Attaching IDE Debugger

CodeRunner™ is automatically configured to accept remote debugging connections on port 5005. To connect an IDE debugger to the CodeRunner™ process, use the remote debugging facility in the IDE. For example, in IntelliJ IDEA, select Run from the main menu and then select Edit Configurations. Click the "plus" icon to add a new configuration. Select Remote from the list and enter "ordermanagement" into the name field as shown below:
idea-remote-debug.zoom70

 

Click OK and launch the debugger. Usually debugging takes more time than the duration of the timeout on the client-side. You should expect that the client application (or even the curl request) will receive a timeout, but you should still be able to continue the debugging of your custom code.

idea-debug-backendless.zoom40s

 

If you need to set a breakpoint in the code, it is important to configure them in a way that does not block the entire process. In IntelliJ IDEA, this is accomplished as described below:

1. Set a breakpoint on a line of code.
2. Right click the breakpoint to open the context dialog.
3. Chose Thread for the Suspend option as shown in the screenshot:
thread-suspend

 

Video overview of the local debugging functionality:

Deployment

Backendless CodeRunner™ includes a command line utility enabling deployment of custom server code to Backendless. Once the code is deployed, it is immediately "activated", which means:

 

API Services - published service code is packaged as API Services. REST endpoints and native SDKs are generated.
API Event handlers - attached to the API invocation chain. The code will be triggered when the corresponding APIs are invoked by the clients.
Timers - timers are scheduled and begin their execution according to the declared schedules.

 

To deploy the code:

1. In a command prompt window change the current directory to [backendless-coderunner-home]/bin.
2. Run the following command:
Linux:

./Deploy.sh

Windows

Deploy.bat

3. Once the deployment script runs, it should produce the following output:

./Deploy.sh
[INFO] JDK from "JAVA_HOME" - /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home
[INFO] CodeRunner(tm) Backendless Debugging Utility
[INFO] Copyright(C) 2017 Backendless Corp. All rights reserved.
[INFO] Version: 4.0.0 Build date: 20170429-0707
[INFO] CodeRunner session is running for 2 hour and will be terminated on 20:11:51.591[UTC]
[INFO] Registering runner on: 'http://api.backendless.com' with version 'v1'
[INFO] Application ID:  'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX'
[INFO] Secret key:      'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX'
[INFO] Runner successfully registered
[INFO] Parsing event model...
[INFO] Build successfully: 2 handlers, 1 timers, 1 service
[INFO] Deploying model to server, and starting debug...
[INFO] Model successfully deployed.
[INFO] Waiting for events...
[INFO] Publishing 2 event handlers and 1 timers and 1 service to the server…
[INFO] Service published
[INFO] If you connected to coderunner for debug - please stop debugging
[INFO] CodeRunner will shutdown now
[INFO] Debugging Utility disconnected successfully
[INFO] Thank you for using Backendless

 

You can verify the production deployment in Backendless Console. API services appear on the Business Logic > API SERVICES screen under the DEPLOYED SERVICES heading:

deployed-services-heading

For event handlers and timers, open the Business Logic section and navigate to either EVENT HANDLERS or TIMERS. Event handlers and timers deployed to production appear with the green color code:

event-handlers-in-prod.zoom80

 

 

The cumulative size of the deployed code (which includes all dependencies) is limited for the Backendless Online deployments to 2 megabytes. It can be expanded by purchasing a Function Pack from the Backendless Marketplace. This limitation does not exist in the Managed Backendless and Backendless Pro versions of the service.

Logging

Custom server-side code can log information using the API provided by Backendless SDK for Java/Android. The API is modeled after the log4j framework and closely follows it . Consider the following example:

import com.backendless.logging.Logger;
import com.backendless.logging.LogBuffer;

@Asset( "*" ) 
public class GenericTableEventHandler extends com.backendless.servercode.extension.PersistenceExtender <HashMap> 
{ 
  @Override 
  public void beforeCreate( RunnerContext context, HashMap hashmap ) throws Exception 
  {
    // the following line is required to avoid buffering in server code
    LogBuffer.getInstance().setLogReportingPolicy( 1, 0 );

    Logger log = Logger.getLogger( GenericTableEventHandler.class ); 
    log.info( "informational log message" ); 
    log.warn( "warning message" ); 
    log.debug( "debug message" ); 
    log.error( "error message" ); 
  } 
}

 

It is important to note that using logging in custom server-side code requires a special configuration of the logging policy using the following API. This is required to avoid creation of a log buffer and an additional thread responsible for submission of the log messages to the Backendless servers:

 LogBuffer.getInstance().setLogReportingPolicy( 1, 0 );

 

 

The sample code above logs four different messages, each for separate logging level. The log levels are organized into a hierarchy:

Log level

Includes messages from log levels

debug

debug, info, warn, error

info

info, warn, error

warn

warn, error

error

error

The log file is saved in the /logging directory of the Backendless File Service storage.

The following logging APIs are available:

Retrieving Logger object:

public static Logger getLogger( Class clazz )

Log a message with the debug level:

public void debug( String message )

Log a message with the info level:

public void info( String message )

Log a message with the warn level:

public void warn( String message )
public void warn( String message, Throwable throwable )

Log a message with the error level:

public void error( String message )
public void error( String message, Throwable throwable )