Backendless API For Android
Backendless API For Android
Contents
Table of Contents
User Service
1 Overview
................................................................................................................................... 6
2 Setup ................................................................................................................................... 6
3 Core Classes
................................................................................................................................... 9
4 Error ...................................................................................................................................
Handling
11
5 User Properties
................................................................................................................................... 11
6 Retrieve
...................................................................................................................................
User Entity Properties
13
7 User Registration
................................................................................................................................... 14
8 Login................................................................................................................................... 17
9 Facebook
...................................................................................................................................
Login
21
10 Update
...................................................................................................................................
User Properties
25
11 Get Current
...................................................................................................................................
User
28
12 Logout
................................................................................................................................... 28
13 Password
...................................................................................................................................
Recovery
29
14 Security
................................................................................................................................... 31
15 Role To
...................................................................................................................................
User Mapping
35
Data Service
37
1 Overview
................................................................................................................................... 37
2 Setup................................................................................................................................... 38
3 Sync ...................................................................................................................................
and Async Calls
41
4 Error ...................................................................................................................................
Handling
42
5 Native...................................................................................................................................
vs External Databases
42
6 Using...................................................................................................................................
External Databases
43
7 Data Object
................................................................................................................................... 47
8 Saving
...................................................................................................................................
Data Objects
48
9 Updating
...................................................................................................................................
Data Objects
52
10 Deleting
...................................................................................................................................
Data Objects
59
11 Retrieving
...................................................................................................................................
Schema Definition
65
12 Basic...................................................................................................................................
Search
67
13 Advanced
...................................................................................................................................
Search
73
14 Using...................................................................................................................................
Dates in Search
80
15 Relations
...................................................................................................................................
Overview
81
16 Relations
...................................................................................................................................
(Save/Update)
87
17 Relations
...................................................................................................................................
(Delete)
97
2015 Backendless Corp.
Messaging Service
131
1 Overview
................................................................................................................................... 131
2 Setup
................................................................................................................................... 131
3 Core...................................................................................................................................
Classes
135
4 Sync...................................................................................................................................
and Async Calls
138
5 Error...................................................................................................................................
Handling
139
6 Push...................................................................................................................................
Notification Setup (Android)
139
7 Push...................................................................................................................................
Notification Setup (iOS)
143
8 Device
...................................................................................................................................
Registration
153
9 Retrieve
...................................................................................................................................
Device Registration
154
10 Managing
...................................................................................................................................
Registrations
155
11 Cancel
...................................................................................................................................
Device Registration
156
12 Message
...................................................................................................................................
Publishing
157
13 Publish
...................................................................................................................................
Push Notifications
164
14 Cancel
...................................................................................................................................
Scheduled Message
165
15 Message
...................................................................................................................................
Subscription
166
16 Cancel
...................................................................................................................................
Subscription
172
17 Sending
...................................................................................................................................
Email
173
File Service
175
1 Overview
................................................................................................................................... 175
2 Setup
................................................................................................................................... 175
3 Sync...................................................................................................................................
and Async Calls
179
4 Error...................................................................................................................................
Handling
180
5 Handling
...................................................................................................................................
Files via Console
180
6 File ...................................................................................................................................
Upload
184
7 Save...................................................................................................................................
Files From Byte Arrays
189
8 File ...................................................................................................................................
Download
191
9 File ...................................................................................................................................
Deletion
192
10 Directory
...................................................................................................................................
Deletion
193
11 Git Integration
................................................................................................................................... 193
12 Web...................................................................................................................................
Hosting
195
13 Custom
...................................................................................................................................
Domain Name
196
14 Custom
...................................................................................................................................
Web Template Hosting
197
15 Files...................................................................................................................................
Security
200
2015 Backendless Corp.
Contents
Geo Service
5
201
1 Overview
................................................................................................................................... 201
2 Setup
................................................................................................................................... 203
3 Sync...................................................................................................................................
and Async Calls
206
4 Error...................................................................................................................................
Handling
207
5 Adding
...................................................................................................................................
a Geo Category
207
6 Deleting
...................................................................................................................................
a Geo Category
211
7 Retrieving
...................................................................................................................................
Geo Categories
213
8 Adding
...................................................................................................................................
a GeoPoint
215
9 Updating
...................................................................................................................................
a GeoPoint
217
10 Deleting
...................................................................................................................................
a GeoPoint
217
11 Importing
...................................................................................................................................
Geo Data
219
12 Search
...................................................................................................................................
in Category
221
13 Search
...................................................................................................................................
in Radius
224
14 Search
...................................................................................................................................
in Rectangular Area
232
15 Geo...................................................................................................................................
Point Clustering
239
16 Relations
...................................................................................................................................
with Data Objects
248
17 Geofence
...................................................................................................................................
Designer
252
18 Geofence
...................................................................................................................................
API
263
Index
269
User Service
1.1
Overview
The Backendless User Service empowers applications with the functionality related to the user accounts
such as user registrations, logins, password recovery and logouts. The core concept which the User
Service relies on is the User entity. The structure of the entity is configurable, that is a developer can
decide which properties "describe" a user in the context of a given version of the application. Typically,
properties describing a user are the ones collected during the user registration process. The User
Service provides the API enabling the following functionality for the applications built on top of
Backendless:
User Registration - Applications use the Backendless' registration API to let the users register
and create accounts for subsequent logins. Application developers can restrict access to the
server-side resources for specific user accounts or based on roles.
User Login - the API lets the registered users login to establish their identity within the
application.
Password Recovery - Backendless supports a complete workflow allowing users to recover lost or
forgotten passwords.
User Logout - the API lets the logged in users terminate their session and disassociate their
identity from the application.
Updating User Registration - the API supports the operation of updating user information.
1.2
Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.
Download SDK
The SDK can be downloaded from the Backendless website.
Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );
Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
2015 Backendless Corp.
User Service
-dontwarn
-keep class weborb.** {*;}
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.
User Service
3. The "App Settings" section is selected by default. The interface contains the text fields for
"Application ID" and secret keys for each supported client-side environment.
4. Use the "Copy" button to copy the value into the system clipboard.
Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".
1.3
Core Classes
The Backendless User Service uses the following core classes:
Backendless.UserService - provides access to the user service API. All user-related operations,
such as user registration, user update, login, logout and password recovery are available through this
class.
BackendlessUser - represents a user registered with the application. The class is used in:
User Registration - an instance of the class contains a list of user properties
User Registration/Properties Update - an instance of the class contains a list of properties to be
updated
The Login method returns an instance of the class upon successful login.
BackendlessUser Class Definition
The BackendlessUser class is defined as:
10
package com.backendless;
import java.util.HashMap;
import java.util.Map;
public class BackendlessUser
{
// Returns all properties of the user object
public Map<String, Object> getProperties()
// sets the properties of the user to the values from the
specified map
public void setProperties( Map<String, Object> properties )
// adds properties from the specified map to the existing
properties collection
public void putProperties( Map<String, Object> properties )
// retrieves the value of the property
public Object getProperty( String key )
// sets the value of the property
public void setProperty( String key, Object value )
// returns internal id of the user object assigned by
Backendless.
// The value is available only for registered user accounts.
public String getUserId()
// sets the password for the account
public void setPassword( String password )
// retrieves the password for the user account
public String getPassword()
}
AsyncCallBack<T> - an interface used in the asynchronous API calls. An implementation of the
interface must provide two methods. One accepting a result from the server (with the generic type T as
the argument) and the other for handling errors reported by the server.
AsyncCallback Class Definition
The AsyncCallback<T> interface is defined as:
package com.backendless.async;
import com.backendless.exceptions.BackendlessFault;
public interface AsyncCallback<T>
{
public void handleResponse( T response );
public void handleFault( BackendlessFault fault );
}
User Service
11
1.4
Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. Currently all the error codes are numbers, however the method
returning the error code returns the String type. This is done for future expansion of the error code
system which may include characters. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional
information about the error
public String getMessage();
//
public String getDetail();
public String toString();
}
The asynchronous calls receive the fault through the following method in the callback object:
public void handleFault( BackendlessFault fault )
For the synchronous calls the fault object can be obtained through the BackendlessException
exception which the synchronous method may throw. The exception class provides access to a
BackendlessFault object through the following method:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}
1.5
User Properties
When a user registers with an application, he provides information which establishes the person's
identity. For one application these properties may include name, phone number and email address, for
another it could be user id, age and gender. Backendless User Service allows each application to have a
custom set of properties associated with the user entity. There are two ways to define what properties
the User entity should have in a Backendless application:
12
screen:
1. Login to the console
2. Select the desired application and version
3. Click the Users icon on the left side of the interface. The "User Properties" panel is selected by
default.
The interface consists of two lists: Available Properties and Selected Properties. The Selected
Properties list contains the properties assigned to the User entity - these are the effective properties for
the selected version of the application. The Available Properties list is simply a storage for the noneffective properties which can be moved to the Selected list if needed. A property can be moved between
the lists by clicking its name.
Identity Property
Among the Selected Properties, one must be marked as identity. This is the property Backendless
uses for the Login and Restore Password operations.. As users register, Backendless ensures the
provided value for the property selected as identity is unique in the context of a specific version for
an application.
Password Property
"password" is a special property. Backendless automatically adds the property when an application
is created. The following rules apply to the password property:
Password cannot be moved out of the Available Properties list.
Password cannot be marked as Identity.
Password is always a required property
To add a property, click the "Add Custom Property" button. New properties automatically added to the
Selected Properties list. To move a property to the other list, simply click its name.
User Service
13
object with the user registration information Backendless creates a user property. This behavior can be
turned off/on using the Dynamic User Definition configuration setting in the console (select the Users >>
User Properties). The setting is turned on by default:
1.6
14
{
for( UserProperty userProp : properties )
{
System.out.println( "Property name - " + userProp.getName();
System.out.println( "\trequired - " + userProp.isRequired();
System.out.println( "\tidentity - " + userProp.isIdentity();
System.out.println( "\tdata type - " + userProp.getType();
}
}
public void handleFault( BackendlessFault fault )
{
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
List<UserProperty> properties = Backendless.UserService.
describeUserClass();
for( UserProperty userProp : properties )
{
System.out.println( "Property name - " + userProp.getName();
System.out.println( "\trequired - " + userProp.isRequired();
System.out.println( "\tidentity - " + userProp.isIdentity();
System.out.println( "\tdata type - " + userProp.getType();
}
1.7
User Registration
The user registration API can be used to create user accounts in the application. The registration
request must provide a user object as a collection of key/value properties. The collection must contain
all the required properties which must include a property marked as identity as well as the "password"
property. Unless the properties are modified in the console, the default property marked as identity is
"email" . Additionally, the "email" property is required if the application is configured to confirm email
addresses for the registered users.
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
Backendless.UserService.register( user, new
AsyncCallback<BackendlessUser>() );
where:
user
callback
- an instance of the BackendlessUser class which contains property values for the
account registration.
- an object which receives either a return value or an error from the server. The return
value from the server is an instance of the BackendlessUser class with the ID assigned
by the server-side. The class of the callback object must implement the
AsyncCallback<BackendlessUser> interface .
User Service
15
Description
2002
3009
3010
3011
3012
3013
3014
3021
General user registration error. Details included with the error message.
3033
3038
3039
3040
3041
3043
8000
16
Email Confirmations
Backendless can send out an email requesting new registered users to confirm their email address. This
feature can be configured in the Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Registration".
User Service
17
When email confirmations are required (the feature is enabled by default), the "email" user property is
required and must contain a value formatted as an email address. To configure the text of the email
message, select "Communication & Email Templates" from the Users menu in the console and select
the "User registers" event.
External Registration
User registrations can be duplicated in an external system through the External Registration Callback.
Developer can specify a URL where Backendless sends a POST request to with the user registration
data as a JSON object. The external registration callback is synchronous and takes place in the same
transaction as the Backendless registration call. As a result, the external system must return result as
fast as possible. The format of the request and response for the external registration is the same as the
request/response body of the Backendless registration API.
To configure the callback:
1. Login to the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Registration".
4. Turn on the "Execute registration callback" toggle.
5. Enter the URL of the script into the "Callback URL" text field.
1.8
Login
Registered users can login to establish their identity with the application using the API below. The login
operation requires two properties: one marked as user identity and the second is password .
Backendless automatically assigns the "AuthenticatedUser" role to all successfully logged in users.
The role can be used to differentiate access to various resources (persistent objects, messaging
channels, media streams) between authenticated users and guests.
Asynchronous Methods:
The method call does not block - it returns immediately. The AsyncCallback argument receives
either the response or the fault returned by the Backendless servers.
public void Backendless.UserService.login( String login,
String password,
AsyncCallback<BackendlessUser>
callback );
public void Backendless.UserService.login( String login,
String password,
boolean stayLoggedIn,
AsyncCallback<BackendlessUser>
callback );
where:
2015 Backendless Corp.
18
login
password
stayLoggedIn
callback
- an object which receives either a return value or an error from the server.
The class must implement the AsyncCallback<BackendlessUser>
interface .
Synchronous Method:
public BackendlessUser Backendless.UserService.login( String login, String
password, boolean stayLoggedIn ) throws BackendlessException ;
where:
login
password
stayLoggedIn
Error Codes:
The following errors may occur during the Login API call. See the Error Handling section for details
on how to retrieve the error code when the server returns an error.
Error Description
Code
2002
3000
3001
3002
3003
User Service
3006
3034
3036
3038
3044
Multiple login limit for the same user account has been reached.
8000
19
External Authentication
Similar to external registration, Backendless supports external authentication. When configured,
Backendless delegates the authentication process to an external system by sending the provided
user credentials to a URL. The URL of the external authentication system can be configured in
Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Login".
4. The "External authentication" section contains the configuration settings.
20
When the external authentication is enabled and user attempts to login, Backendless sends the
following request to the specified URL:
POST http://external-authentication-url
Authorization: Basic base64-encoded-login:password
Where base64-encoded-login:password is constructed as follows:
1. login and password are combined into a string "login:password"
2. The resulting string literal is then encoded using Base64
Multiple Logins
The Multiple Logins feature enables login using the same account from different computers or
devices. Multiple logins can be configured in the Backendless Console:
1. Log into the console and select the application.
2. Click the "Users" icon in the vertical icon menu on the left.
3. Click "Login".
4. The "Multiple Logins" section contains the configuration settings.
When the feature is turned on (multiple logins allowed), the configuration setting may include the
maximum number of simultaneous logins for the selected version of the application. When the
feature is disabled (multiple logins are not allowed), the configuration must indicate whether which
login should be invalidated (first or second for the account):
Session Timeout
Backendless supports session expiration which can be configured in console. Along with the
session timeout interval, the configuration can also include a forwarding URL which is used to
redirect requests to for the expired sessions.
User Service
21
Account Lockout
An application powered by Backendless can be configured to lock out accounts with failed logins.
The console has two configuration options: number of failed logins before the account is locked and
a time interval to wait before the account is available for logins again.
1.9
Facebook Login
Backendless integrates with Facebook to support user authentication and login into a Backendless
application with a Facebook account. Using the integration application developer can provide a way to
skip the application registration step and allow users to use their Facebook identity to enter and
experience the application. Backendless provides two ways to handle Facebook logins:
Easy Facebook Login - The Facebook SDK is not required on the client. The client application
makes a Backendless API call to initiate the login and the user authenticates in a Facebook
popup window.
Login with Facebook SDK - The client application uses the Facebook SDK to authenticate the
user and then delegates to a Backendless API call to link the Facebook identity to a
BackendlessUser instance.
Both approaches - Easy Login and Login with SDK, rely on the concepts and a configuration change
described below:
Property Mapping
Since a Backendless application has its own set of user properties, they need to be mapped to the
corresponding Facebook Graph API user fields. A mapping references a Facebook user field and the
name of a Backendless property. The mapping is a required argument in the Backendless API call to
login. Once the user is authenticated, Backendless obtains the field values from the Facebook user
graph object and populates the mapped properties in the BackendlessUser object. For the very first
login, Backendless also registers the user. In this case the returned BackendlessUser object will have
2015 Backendless Corp.
22
Permissions
The client application can request the permissions from the user to access additional user information or
perform various actions. Requested permissions is a collection of string objects passed as an argument
in the Backendless API call to login a Facebook user.
Backendless Configuration
Backendless backend must be configured with Facebook's application App ID and secret key. See the "
Social Settings" chapter of the user guide (the Manage section) for detailed instructions for configuring
Backendless with Facebook.
API
Description:
Perform Facebook user login with the default permission. Backendless will obtain the Facebook
fields which are available without access token - see the complete list of available fields.
2015 Backendless Corp.
User Service
23
responder
Description:
Perform Facebook user login and request the specified permissions. Backendless SDK
constructs a UI dialog to show the Facebook login screen.
public void Backendless.UserService.loginWithFacebook(
android.app.Activity
context,
Map<String, String>
facebookFieldsMappings,
List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
context
- must be the current activity context.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user
permissions
responder
Description:
Perform Facebook user login and request the specified permissions. Backendless SDK uses
the provided WebView to host the Facebook login screen.
public void Backendless.UserService.loginWithFacebook(
android.app.Activity
context,
android.webkit.WebView
webView,
Map<String, String>
facebookFieldsMappings,
24
List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
- must be the current activity context.
- a view component where the Facebook login dialog will be
rendered.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user
properties. Keys must be the names of the Facebook fields,
values - the names of the Backendless properties. See the
Property Mapping section for additional details.
permissions
- a collection of the Facebook permissions/scopes which the
application requests access to.
responder
- an object which receives either a return value or an error from
the server. The class must implement the
AsyncCallback<BackendlessUser> interface .
context
webView
If onActivityResult() method is already defined, add the following line to the activity class
(Android Activity) of your application:
Session.getActiveSession().onActivityResult( this, requestCode, resultCode,
data );
Description
Logs in a Facebook user into a Backendless application. Uses the SDK to display the login
dialog with the specified permissions.
public void Backendless.UserService.loginWithFacebookSdk(
android.app.Activity
User Service
25
context,
Map<String, String>
facebookFieldsMappings,
List<String> permissions,
AsyncCallback<BackendlessU
ser> responder )
where:
context
- must be the current activity context.
facebookFieldMappings - a mapping between the Facebook fields and Backendless user
permissions
responder
Description:
Associates a Facebook user with BackendlessUser. Can be used when the Facebook login
must be handled independently.
public void Backendless.UserService.loginWithFacebookSession(
com.facebook.Session
facebookSession,
com.facebook.model.
GraphUser facebookUser,
Map<String, String>
facebookFieldsMappings,
AsyncCallback<BackendlessU
ser> responder )
where:
facebookSession
responder
1.10
26
Error Codes:
The following errors may occur during the Update User Properties API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error.
Error
Code
Description
2002
3018
3024
3028
3029
3030
3031
3045
User Service
27
28
1.11
1.12
Logout
The Logout operation terminates user session and disassociates the AuthenticatedUser role from the
subsequent requests made by the client application.
Asynchronous Call Method Signature:
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.logout( AsyncCallback<Void>
callback )
where:
- an object which is notified when the server completes the logout operation or returns
an error. The class must implement the AsyncCallback<Void> interface .
callback
Description
2002
3007
3023
User Service
29
{
public void handleResponse( Void response )
{
// user has been logged out.
}
public void handleFault( BackendlessFault fault )
{
// something went wrong and logout failed, to get the error
code call fault.getCode()
}
});
}
public void handleFault( BackendlessFault fault )
{
// login failed, to get the error code call fault.getCode()
}
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
BackendlessUser user;
try
{
user = Backendless.UserService.login( username, password );
}
catch( BackendlessException exception )
{
// login failed, to get the error code, call exception.getFault
().getCode()
}
try
{
// now log out:
Backendless.UserService.logout();
}
catch( BackendlessException exception )
{
// logout failed, to get the error code, call exception.getFault
().getCode()
}
1.13
Password Recovery
Password recovery sends an email to the user's email address with a link where the user can change
the password.
Asynchronous Call Method Signature:
30
The method call does not block - it returns immediately. The AsyncCallback argument receives either
the response or the fault returned by the Backendless servers.
public void Backendless.UserService.restorePassword( String
identityValue, AsyncCallback<Void> callback )
where:
identityValue
callback
- a value for the property marked as identity which uniquely identifies the user
within the application.
- an object which is notified when the server completes the logout operation or
returns an error. The class must implement the AsyncCallback<Void>
interface .
Error Codes:
The following errors may occur during the Password Recovery API call. See the Error Handling section
for details on how to retrieve the error code when the server returns an error.
Error
Code
Description
2002
3020
Unable to find user with the specified login (invalid user identity).
3025
3038
User Service
31
});
Synchronous Call Example:
Backendless.initApp( appId, secretKet, version ); // where to get
the argument values for this call
try
{
Backendless.UserService.restorePassword( "james.bond" );
}
catch( BackendlessException exception )
{
// password recovery failed, to get the error code, call
exception.getFault().getCode()
}
1.14
Security
All Backendless API operations can be restricted either for specific user accounts or for roles. A user
account may be associated with one or more roles. Backendless supports several built-in system roles
as well as developer-defined roles. The system roles include:
NotAuthenticatedUser - any user who has not authenticated to a Backendless application.
AuthenticatedUser - any user who has successfully logged in.
SocialUser - any user who has logged in through a social network.
FacebookUser - any user who has logged in with a Facebook account.
TwitterUser - any user who has logged in with a Twitter account.
Developer-defined roles can be added using the Backendless Console. Roles are defined at the
application version level, that is a particular version of an application may have its own set of developerdefined roles.
Backendless manages permissions as tuples consisting of:
Operation - Users interact with Backendless either via console or the API. Any request to
Backendless is an operation which may have a permission associated with it.
Resource - Some operations target specific resources. These can be data tables, messaging
channels or media tubes.
Principal - Either a specific user identity or a role associated with the user initiating the operation.
There are two levels of permissions for the API operations - global and resource-specific. The resourcespecific permissions guard access to the specific resources (data tables, messaging channels, etc).
They have higher priority and are checked first. When Backendless receives an API call from a client, it
determines the user associated with the request and obtains a list of roles associated with the user
account (for the users who have not authenticated, the NotAuthenticatedUser role is used). The
resource-specific permissions can be set to either inherit the permission from the global matrix or
explicitly grant or deny access to the resource for the given user or role. The diagram below illustrates
the process when the resource-specific permission is set to inherit:
32
When the resource-specific permission is explicitly set to either grant or deny access, the global
permissions are bypassed:
Global Permissions
The global service permissions apply to all resources managed by a particular service. For example,
global Data Service permissions for a particular role apply to all data tables. These permissions can be
viewed and modified by clicking a role on the Users > Security and Restrictions screen in Backendless
Console:
User Service
33
34
Resource-Specific Permissions
To view, assign or modify resource-specific permissions, use a corresponding screen in the
Backendless Console. For example, to restrict access to a data table, switch to the Data view, select a
table and click the "Schema and Permissions" button. The user interface has two views - one is for
managing permissions for user accounts and the other for roles. To modify permissions for a user
account:
1. Click the "User Permissions" list item.
2. Enter the user name in the search field.
3. Select the user and click the "Add" button.
4. The table displays the permissions for various operations for the selected user and the resource.
5. Click an icon representing the permission state to modify the permission.
User Service
35
Similarly permissions can be assigned or modified for specific roles - use the "Role Permissions" list
item.
To modify the permissions for Messaging channels, click the "Messaging" icon, select a channel and
switch to the "Permissions" view.
1.15
36
{
//here we get all user roles - "userRoles".
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
// get user roles, to get the error code call backendlessFault.
getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
// login failed, to get the error code call backendlessFault.getCode()
}
} );
where:
identity
roleName
callback
where:
identity
roleName
String roleName,
AsyncCallback<Voi
User Service
37
d> callback );
where:
identity
roleName
callback
where:
identity
roleName
Error Codes:
The following errors may occur during the Login API call. See the Error Handling section for
details on how to retrieve the error code when the server returns an error.
Error
Code
Description
2002
2005
3038
3057
3058
3059
Data Service
2.1
Overview
The Backendless Data Service is a highly scalable, object storage system. It is available via intuitive API
which supports all basic data persistence operations - Create, Retrieve, Update and Delete (CRUD). The
Data Service operates with persistent data at the object level, that means applications use the APIs to
save, update, delete or search for objects, rather than traditional database records. Developers using the
Data Service API do not need to know or understand the databases, schema creation rules, stored
procedures or SQL syntax.
The Data Service follows the following rules when working with user objects:
1. Objects persisted by the Data Service must specify the "type" or be an instance of a class (for
client applications written in strongly-typed languages).
2. Backendless automatically creates tables for each persisted type it has not seen before and
saves objects in the corresponding tables. Each table has columns corresponding to the
properties of the persisted objects.
3. Backendless creates three additional system-level columns for each new table:
38
2.2
Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.
Download SDK
The SDK can be downloaded from the Backendless website.
Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );
Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}
Data Service
39
(AVD) files:
OS X and Linux: ~/.android/
Windows Vista and Windows 7: C:\Users\your_user_name\.android\
2. If you are using Eclipse with ADT, and you are not sure where your debug keystore is located, you
can select Windows > Prefs > Android >Build to check the full path, which you can then paste
into a file explorer to locate the directory containing the keystore.
3. List the SHA-1 fingerprint.
For Linux or OS X, open a terminal window and enter the following:
keytool -list -v -keystore ~/.android/debug.keystore -alias
androiddebugkey -storepass android -keypass android
For Windows Vista and Windows 7, run:
keytool -list -v -keystore "%USERPROFILE%\.android\debug.
keystore" -alias androiddebugkey -storepass android -keypass
android
4. You should see output similar to this:
Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01
18:04:04 PST 2033
Certificate fingerprints:
MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:
A1:66:6E:44:5D:75
Signature algorithm name: SHA1withRSA
Version: 3
5. The line that begins SHA1 contains the certificate's SHA-1 fingerprint. The fingerprint is the
sequence of 20 two-digit hexadecimal numbers separated by colons.
2.
Create an API project in the Google APIs Console
1. In a browser, navigate to the Google APIs Console.
If you haven't used the Google APIs Console before, you're prompted to create a project that
you use to track your usage of the Google Maps Android API. Click Create Project; the
Console creates a new project called API Project. On the next page, this name appears in
the upper left hand corner. To rename or otherwise manage the project, click on its name.
If you're already using the Google APIs Console, you will immediately see a list of your
existing projects and the available services. It's still a good idea to use a new project for
Google Maps Android API, so select the project name in the upper left hand corner and then
click Create.
2. You should see a list of APIs and services in the main window. If you don't, select Services from
the left navigation bar.
3. In the list of services displayed in the center of the page, scroll down until you see Google Maps
Android API v2. To the right of the entry, click the switch indicator so that it is on.
40
4. This displays the Google Maps Android API Terms of Service. If you agree to the terms of service,
click the checkbox below the terms of service, then click Accept. This returns you to the list of
APIs and services.
3.
Obtain a Google Maps API key
If your application is registered with the Google Maps Android API v2 service, then you can request
an API key. It's possible to register more than one key per project.
1. Navigate to your project in the Google APIs Console.
2. In the Services page, verify that the "Google Maps Android API v2" is enabled.
3. In the left navigation bar, click API Access.
4. In the resulting page, click Create New Android Key....
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.
Data Service
41
Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".
2.3
The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;
42
import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}
When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.
2.4
Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}
The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )
The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}
2.5
Data Service
43
developer working with a Backendless backend does not need to do anything special to configure it. All
the data persisted through the Data Service API and Backendless console is stored internally, in the
database maintained by the Backendless service.
The external storage can be any database located elsewhere which the Backendless service connects
to with the connection parameters provided by the developer. Currently MySQL/MariaDB databases is
the only type of the supported external storage.
An application must be configured to use either the native or an external storage mechanism, they
cannot be combined into the hybrid mode. However, regardless of which mechanism is enabled, the
Data Service API can be used to work with the data. Additionally, if the application is configured to use
an external database, Backendless console can be used the manage the external data the same way it
does so with native. The only exception is the management of an external schema - Backendless
Console displays it in the view only mode.
To learn more about external data storage, see the Using External Databases chapter.
2.6
44
3. In the Database Connection pop-up window, specify the connection details. To test the
connection, click the Test link. To proceed with the connection, click the Next button.
4. Once a connection is established (it may take several minutes), console displays a list of the
databases in the Database drop-down menu. Select a database and click the Ok button to
confirm your choice.
Data Service
45
5. Backendless will inspect the selected database and an email is delivered to the application
developer email address with the status of the inspection.
6. Upon successful database inspection the Data Management screen displays a list of the
tables from the external database.
46
Re-connect (or edit the connection details) to current external database by clicking the
connect button:
Data Service
47
retrieving relations
varchar( x )
int
Integer
long
Long integer
Boolean
datetime
Datetime
text
Text
other types
String
Limitations
There are several limitations for working with external databases:
2.7
, objectId .
You can not use the native and external databases simultaneously; once you establish a
connection with an external database, a native one will not be available any longer and vice
versa.
Only one external database can be used in an application (cannot connect to two different
external databases).
The schema of an external database cannot be edited in Backendless Console.
You can not import data to or export data from an external database using Backendless
Console.
If the database schema changes, it is important to re-inspect the database to avoid errors.
Data Object
Backendless supports persistence of arbitrary Java objects. Object's class does not need to implement
any special Backendless interfaces or extend classes. There are however a few requirements imposed
on the classes of the persisted objects:
Class must contain the default, public no-argument constructor.
Class must contain either at least one public field or a pair of getter and setter methods
conforming to the Java Bean naming convention.
Optional requirement. Backendless automatically assigns a unique ID to every persisted
48
object. If the application needs to have access to the assigned ID, the class must implement
the following methods:
private String objectId;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
Optional requirement. In addition to objectId, Backendless maintains two additional properties
for every persisted object - created and updated . The former contains the timestamp when
the object was initially created in the Backendless data store. The latter is updated every time
the object is updated. To get access to these properties, the class must implement the
following methods:
private Date created;
private Date updated;
public Date getCreated()
{
return created;
}
public void setCreated( Date created )
{
this.created = created;
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
2.8
Data Service
49
Synchronous Method:
public <E> E Backendless.Persistence.of( E ).save( E entity ) throws
BackendlessException
Asynchronous Method:
public <E> void Backendless.Persistence.of( E ).save( E entity,
AsyncCallback<E> responder )
where:
E
entity
responder
Return Value:
The synchronous method returns the saved object. The asynchronous call receives the return
value through a callback executed on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge( int age ) {
this.age = age;
}
public String getPhone() {
return phone;
50
}
public void setPhone( String phone ) {
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle( String title ) {
this.title = title;
}
}
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
Data Service
51
You can identify a customer object by unique combination of name and surname and save this object to
an external database as follows:
private void saveAlreadyExistingObject()
{
// get a customer entity from DB, modify its properties and update it. Get
it again to make sure update is correct
// Notice that values of the primary keys cannot be changed.
Customer melinda = new Customer();
melinda.setFirstName( "Melinda" );
melinda.setLastName( "Grey" );
melinda = Backendless.Persistence.of( Customer.class ).findById( melinda );
System.out.println("Melinda's rate is "+melinda.getDiscountRatePercent()+"
percent");
melinda.setDiscountRatePercent( melinda.getDiscountRatePercent() + 2 );
Backendless.Persistence.of(Customer.class).save(melinda);
Customer updatedMelinda = new Customer();
updatedMelinda.setFirstName( "Melinda" );
updatedMelinda.setLastName( "Grey" );
52
2.9
URL:
https://api.backendless.com/<version>/data/<table-name>/<object-id>
where:
<version>
<table-name>
<object-id>
Request Headers:
application-id: app-id-value
secret-key: secret-key-value
Content-Type:application/json
application-type: REST
where:
application-id
Data Service
secret-key
Content-Type
application-type
53
Example:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-H Content-Type:application/json
-X PUT
-d "{\"name\":\"Bob\", \"age\":20 }"
54
-v https://api.backendless.com/v1/data/Person/6C77C11B-E9B3-EB14FFA2-69F38CF48800
Notice the objectId value is put directly into the URL. The same value is optional in the JSON
body of the request.
URL:
https://api.backendless.com/<app version>/data/bulk/<table-name>?
where=<where clause>
where:
<app version>
<table-name>
<where clause>
Request Headers:
application-id: app-id-value
secret-key: secret-key-value
Content-Type:application/json
application-type: REST
where:
application-id
secret-key
Content-Type
application-type
Sample Request:
Bulk update allows changing several data objects selected by the specified criteria. For
instance, if you have the list of employees and need to update their salary info depending on
their time of employment, you can do it by using the bulk update feature.
2015 Backendless Corp.
Data Service
55
Asynchronous Method:
public <E> void Backendless.Persistence.save( E entity, AsyncCallback<E>
responder )
where:
entity
responder
Return Value:
The synchronous method returns the updated object. The asynchronous call receives the return
value through a callback executed on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
56
private
private
private
private
private
String objectId;
String name;
int age;
String phone;
String title;
The following code saves a new instance of the Contact class and subsequently updates it:
Synchronous API:
public void updateContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationID, secretKey, version);
Contact contact = new Contact();
Data Service
57
Asynchronous API:
public void updateContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationID, secretKey, version);
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Backendless.Persistence.save( contact, new AsyncCallback<Contact>() {
public void handleResponse( Contact savedContact )
{
savedContact.setTitle( "Most favorite" );
savedContact.setPhone( "666-666-666" );
Backendless.Persistence.save( savedContact, new
AsyncCallback<Contact>() {
@Override
public void handleResponse( Contact response )
{
// Contact instance has been updated
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with fault.
getCode()
}
});
}
58
When updating a data object in an external database, you need to take into consideration that such
object does not have some properties characteristic for the native data objects. Thus, these properties
will be omitted in the queries when saving a data object to an external database.
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have the following Customer class
public class Customer
{
private String lastName;
private Integer discountRatePercent;
private String firstName;
public String getLastName()
{
return lastName;
}
public void setLastName( String lastName )
{
this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}
You can identify a customer object by unique combination of name and surname and update this object
in an external database as follows:
private void saveAlreadyExistingObject()
{
// get a customer entity from DB, modify its properties and update it. Get
it again to make sure update is correct
// Notice that values of the primary keys cannot be changed.
Customer melinda = new Customer();
melinda.setFirstName( "Melinda" );
melinda.setLastName( "Grey" );
melinda = Backendless.Persistence.of( Customer.class ).findById( melinda );
System.out.println("Melinda's rate is "+melinda.getDiscountRatePercent()+"
Data Service
59
percent");
melinda.setDiscountRatePercent( melinda.getDiscountRatePercent() + 2 );
Backendless.Persistence.of(Customer.class).save(melinda);
Customer updatedMelinda = new Customer();
updatedMelinda.setFirstName( "Melinda" );
updatedMelinda.setLastName( "Grey" );
updatedMelinda = Backendless.Persistence.of( Customer.class ).findById
( updatedMelinda );
System.out.println("Now Melinda's discount rate is "+updatedMelinda.
getDiscountRatePercent() +" percent.");
// The asynchronous call will look as follows:
Backendless.Persistence.of(Customer.class).save( melinda, new
AsyncCallback<Customer>(){
public void handleResponse(Customer c) { }
public void handleFault (BackendlessFault f) { }
});
}
2.10
URL:
https://api.backendless.com/<version>/data/<table-name>/<object-id>
where:
<version>
<table-name>
<object-id>
Request Headers:
application-id: app-id-value
60
secret-key: secret-key-value
application-type: REST
where:
application-id
secret-key
application-type
Request Body:
None
Sample Response Body:
{
"deletionTime" : timestamp in milliseconds
}
Example:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-X DELETE
-v https://api.backendless.com/v1/data/Orders/6C77C11B-E9B3-EB14FFA2-69F38CF48800
URL:
https://api.backendless.com/<app version>/data/bulk/<table-name>?
where=<where clause>
where:
<app version>
<table-name>
<where clause>
Data Service
61
secret-key
application-type
mandatory.
Sample Request:
Bulk delete allows removing several data objects selected by the specified criteria. For instance,
if you have the list of employees and need to delete some of them depending on their time of
employment, you can do it by using the bulk delete feature.
To test the feature:
1. Create three persons/employees:
employed for 15 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Tom\", \"age\":35,
\"salary\":0, \"workDays\":\"15\"}" -v https://api.backendless.
com/v1/data/Person
employed for 0 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Bob\", \"age\":20,
\"salary\":0, \"workDays\":\"0\"}" -v https://api.backendless.
com/v1/data/Person
employed for 0 days curl -H application-id:application-id-value-fromconsole-H secret-key:secret-key-value-from-console -H ContentType:application/json -X POST -d "{\"name\":\"Brad\", \"age\":22,
\"salary\":10, \"workDays\":\"0\"}" -v https://api.backendless.
com/v1/data/Person
2. To delete all staff members that are employed for 0 days, set the value of <where clause>
argument in URL to workDays=0 , that is: where=workDays=0 . The URL encoded query will
look as follows:
curl
-H application-id:application-id-value-from-console
-H secret-key:secret-key-value-from-console
-X DELETE
-v https://api.backendless.com/v1/data/bulk/Person?where=workDays%
62
3D0
As a result you will receive the response displaying the number of data object deleted (2
objects). The second and third employees (Bob and Brad) will be deleted from the database.
Sample Response:
Server will return the number of the deleted objects.
Synchronous Method:
Long result = Backendless.Persistence.of( E ).remove( E entity );
Asynchronous Method:
Backendless.Persistence.of( E ).remove( E entity, AsyncCallback<Long>
responder );
where:
entity
responder
Return Value:
The synchronous method returns the time stamp when the server-side removed the object from
the data store. The asynchronous call receives the return value through a callback executed on
the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
Data Service
63
Asynchronous API:
public void deleteContact()
{
String applicationID = "";
String secretKey = "";
String version = "";
64
Data Service
65
this.lastName = lastName;
}
public Integer getDiscountRatePercent()
{
return discountRatePercent;
}
public void setDiscountRatePercent( Integer discountRatePercent )
{
this.discountRatePercent = discountRatePercent;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName( String firstName )
{
this.firstName = firstName;
}
}
You can identify a customer object by unique combination of name and surname and delete this object
from an external database as follows:
private void deleteExistingObject()
2.11
where:
tableName
callback
Synchronous Method:
public List<ObjectProperty> Backendless.Persistence.describe( String
tableName )
where:
2015 Backendless Corp.
66
tableName
Return value:
Method returns a collection of the ObjectProperty instances. The ObjectProperty class includes
the following Java bean properties:
autoLoad:boolean
- applies only to relations. If true, the property is set to autoload related data for the data retrieval queries.
customRegex:String
- a regular expression assigned to the column as a validator.
The validator applies when a new object is saved in the table
or an existing one is updated.
defaultValue:Object
- a default value assigned to any object saved/updated in the
table where the column does not have a value.
isPrimaryKey:boolean
- true if the column is or is a part of a primary key.
name:String
- contains the name of a property
relatedTable:String
- contains the name of the related table(s)
required:boolean
- defines whether a property is optional or required for the
requests which save the initial object or update an existing
one.
type:String
- defines the property type
Example:
The example below describes how to retrieve the properties of the user object. Consider the
following class:
public class Person
{
private String name;
private int age;
public Person ()
{
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}
}
Save an instance of the Person class in the persistent storage (Backendless create the Person
table if it does not exist):
Data Service
67
2.12
Basic Search
Backendless supports multiple basic search operations. These include finding an object by ID, finding
first or last object in the collection or retrieving the entire persisted collection. Each method is available
in both synchronous and asynchronous versions:
Please note: searching for an object in a native database differs from searching for an object in an
external database.
68
Find first data object of class E. The first data object is the first one saved in the data store:
public void Backendless.Persistence.of( E ).findFirst() throws
BackendlessException;
Find last data object of type E. The last data object is the last one saved in the data store:
public <E> E Backendless.Persistence.of( E ).findLast() throws
BackendlessException;
Asynchronous Methods:
Retrieve all data objects of type E:
public void Backendless.Persistence.of( E ).find
( AsyncCallback<BackendlessCollection<E>> responder );
Find first data object of type E. The first data object is the first one saved in the data store:
public void Backendless.Persistence.of( E ).findFirst( AsyncCallback<E>
responder )
Find last data object of type E. The last data object is the last one saved in the data store:
public void Backendless.Persistence.of( E ).findLast( AsyncCallback<E>
responder )
where:
E
responder
- class object for the data type to search data objects for. For
example, if the class is "Person", the argument should Person.
class.
- a responder object which will receive a callback when the
method successfully returns the result or if an error occurs.
Applies to the asynchronous methods only.
Example:
Consider the following class:
package com.sample;
public class Contact
{
Data Service
private
private
private
private
private
69
String objectId;
String name;
int age;
String phone;
String title;
70
Contact.class ).find();
Asynchronous call:
Backendless.Persistence.of( Contact.class).find( new
AsyncCallback<BackendlessCollection<Contact>>(){
@Override
public void handleResponse( BackendlessCollection<Contact>
foundContacts )
{
// all Contact instances have been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find first contact:
Synchronous call:
Contact firstContact = Backendless.Persistence.of( Contact.class ).
findFirst();
Asynchronous call:
Backendless.Persistence.of( Contact.class).findFirst( new
AsyncCallback<Contact>(){
@Override
public void handleResponse( Contact contact )
{
// first contact instance has been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find last contact:
Synchronous call:
Contact lastContact = Backendless.Persistence.of( Contact.class ).
findLast();
Asynchronous call:
Backendless.Persistence.of( Contact.class).findLast( new
AsyncCallback<Contact>(){
@Override
public void handleResponse( Contact contact )
Data Service
71
{
// last contact instance has been found
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
});
Find contact by ID:
Synchronous call:
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
// save object synchronously
Contact savedContact = Backendless.Persistence.save( contact );
Contact lastContact = Backendless.Persistence.of( Contact.class ).
findById( savedContact.getObjectId() );
Asynchronous call:
Contact contact = new Contact();
contact.setName( "Jack Daniels" );
contact.setAge( 147 );
contact.setPhone( "777-777-777" );
contact.setTitle( "Favorites" );
Backendless.Persistence.save( contact, new AsyncCallback<Contact>()
{
@Override
public void handleResponse( Contact savedContact )
{
Backendless.Persistence.of( Contact.class ).findById(
savedContact.getObjectId(), new AsyncCallback<Contact>() {
@Override
public void handleResponse( Contact response )
{
// a Contact instance has been found by ObjectId
}
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved
with fault.getCode()
}
} );
}
72
@Override
public void handleFault( BackendlessFault fault )
{
// an error has occurred, the error code can be retrieved with
fault.getCode()
}
} );
You can identify a customer object by unique combination of name and surname and search for this
object in an external database as follows:
2015 Backendless Corp.
Data Service
73
2.13
Advanced Search
Advanced search use-cases supported by Backendless include:
Search with query - a query is an SQL-92 expression (the where clause) referencing data object
properties.
Paged search - sets the page size and index (offset) to search from.
Sorted search - lists the data object properties to sort the result collection by.
Additionally, search requests may include special modifiers:
Request for specific properties of the data objects returned in the result collection.
Request to return related data objects attached to the 'parent' objects in the result collection.
74
public
public
public
public
int getPageSize();
void setPageSize( int pageSize );
int getOffset();
void setOffset( int offset );
}
The QueryOptions class is used by BackendlessDataQuery and is defined as:
package com.backendless.persistence;
public class QueryOptions
{
public QueryOptions()
public QueryOptions( int pageSize, int offset )
public QueryOptions( int pageSize, int offset, String sortBy )
public QueryOptions( String sortBy )
public
public
public
public
Data Service
75
Asynchronous Method:
Backendless.Persistence.of( E ).find( BackendlessDataQuery query,
AsyncCallback<BackendlessCollection<E>>
);
where:
query
responder
Return Value:
The synchronous method returns a collection of strongly typed objects found as a result of the
query execution. The asynchronous call receives the return value through a callback executed
on the AsyncCallback object.
Example:
Consider the following class:
package com.sample;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
public String getObjectId() {
return objectId;
}
public void setObjectId( String objectId ) {
this.objectId = objectId;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
76
Data Service
77
With the ability to link data objects with geo points, you can search for data
objects by distance. This type of search returns all data objects located within
the specified distance. Distance-based search uses a special function in
whereClause of the search request. The syntax of the function is:
distance(
center point latitude,
center point longitude,
columnname which contains geo point.latitude,
columnname which contains geo point.longitude ) <operator>
units-function(value)
where:
<operator>
units-function
For example, the following expression searches for data objects located within 200 miles from
2015 Backendless Corp.
78
the point at 30.26715, -97.74306. Each data object must have the "coordinates" property
of type GeoPoint.
distance( 30.26715, -97.74306, coordinates.latitude, coordinates.longitude )
< mi(200)
Run the following query/code to store a data object representing Bob with a link to his home in
Austin, TX:
Friend bob = new Friend();
Data Service
79
bob.setName( "Bob" );
bob.setPhoneNumber( "512-555-1212" );
bob.setCoordinates( new GeoPoint( 29.76328, -95.36327 ) );
bob.getCoordinates().addCategory( "Home" );
bob.getCoordinates().addMetadata( "description", "Bob's home" );
Backendless.Data.of( Friend.class ).save( bob );
Run the following query/code to store a data object representing Jane with a link to his home in
Houston, TX:
Friend jane = new Friend();
jane.setName( "Jane" );
jane.setPhoneNumber( "281-555-1212" );
jane.setCoordinates( new GeoPoint( 29.76328, -95.36327 ) );
jane.getCoordinates().addCategory( "Home" );
jane.getCoordinates().addMetadata( "description", "Jane's home" );
Backendless.Data.of( Friend.class ).save( jane );
Run the following query/code to store a data object representing Fred with a link to his home in
San Antonio, TX:
Friend fred = new Friend();
fred.setName( "Fred" );
fred.setPhoneNumber( "210-555-1212" );
fred.setCoordinates( new GeoPoint( 29.42412, -98.49363 ) );
fred.getCoordinates().addCategory( "Home" );
fred.getCoordinates().addMetadata( "description", "Fred's home" );
Backendless.Data.of( Friend.class ).save( fred );
Once the data is in the persistent object and geo location storage, run the following code/query
to perform a distance-based search:
String whereClause = "distance( 30.26715, -97.74306, coordinates.latitude,
coordinates.longitude ) < mi(200)";
BackendlessDataQuery dataQuery = new BackendlessDataQuery( whereClause );
QueryOptions queryOptions = new QueryOptions();
queryOptions.setRelationsDepth( 1 );
dataQuery.setQueryOptions( queryOptions );
BackendlessCollection<Friend> friends = Backendless.Data.of( Friend.class ).
find( dataQuery );
for( Friend friend : friends.getData() )
{
System.out.println( String.format( "%s lives at %f, %f tagged as '%s'",
friend.getName(), friend.getCoordinates().getLatitude(), friend.
getCoordinates().getLongitude(),
(String) friend.getCoordinates().
getMetadata( "description" ) ) );
The search returns all data objects within the specified distance. Each data object has the
coordinates property containing the coordinates of a geo point associated with this data
object.
80
2.14
Comparison Operators
Backendless supports the following date comparison operators:
Column's value is after the specified date/time: use either > or the after keyword:
birthDate > '22-Dec-1980'
birthDate after 1427068800000
Column's value is before the specified date/time: use either < or the before keyword:
birthDate < '22-Dec-1980'
birthDate before 1427068800000
Column's value is either at or after the specified date/time: use either => or the at or after
keyword:
birthDate >= '28-10-1988'
birthDate at or after '10/28/1988 00:00:00 GMT-0200'
Column's value is either at or before the specified date/time: use either <= or the at or
before keyword:
birthDate >= '28-10-1988'
birthDate at or after '10/28/1988 00:00:00 GMT-0200'
Note: the whereClause-based queries can be tested in the Backendless Console with the SQL Search
turned on.
Data Service
81
Example
2.15
Relations Overview
A data object stored in a Backendless backend may reference other objects. These references are
called relations. There are two types of relations: one-to-one and one-to-many. Relations may be
declared manually in a table schema using the Backendless Console or derived (and added to schema)
from the objects which are being saved. Additionally, Backendless supports bidirectional relations
between the objects stored in the Data Service and other entities in a Backendless backend. For
example, a data object may have a relation with a User object and/or Geo Point objects.
Creating Relations
Relations can be declared manually by using the Backendless Console. Creating a relation between
objects involves two main steps:
1. Declaring a relationship between the tables where the objects are stored.
2. Establishing relations between the objects from the related tables.
82
6. The pop-up window will display new menu options. Select a related table and the cardinality
of the relations from the corresponding drop-down menus. The one-to-one relation means that
a table's object can be linked with only one object from the related table. While the one-tomany relation means that a table's object can be linked with several objects from the related
Data Service
83
table.
84
2. Click the plus icon next to the object, for which you want to create a relation. The Set
Related Object pop-up window will display the list of objects from the related table.
3. Each object in the displayed popup has either a radio button or a checkbox next to the
object's data. Radio buttons are used for one-to-one relations, while checkboxes apply for the
one-to-many relations. Select the object(s) which will be linked to the parent object.
4. Click the Set Related Object button to save the changes.
The Data Service API can be used to link objects from different tables to form a relation. For more
information see the Relations (Save/Update) section of the guide.
Editing Relations
2015 Backendless Corp.
Data Service
85
You can edit the relations between the data objects via the Backendless Console:
1. Click the name of the table containing the object with relations. Click the plus icon next to
the relation you want to edit:
2. The Set Related Object pop-up window will display. If you want to link a data object with
different object(s), click the radio-button or check-box(s) next to the necessary object(s).
3. Click the Set Related Object button to save the changes.
Refer to the Relations (Save/Update) section to learn how to update relations by using the API.
Deleting Relations
You can delete both relations between the objects and between the tables if necessary. Relations
between the tables can be deleted only by using the Backendless Console. While relations between the
objects can be deleted either via the Backendless Console or API.
To delete relations between the tables by using the Backendless Console:
1. Click the name of the table that you want to unlink from another table.
2. Click the Table Schema and Permissions button at the top right. The Table Schema and
Permissions page will display.
3. Click the check-box(s) next to the column associated with the relation(s) between the
tables.
86
2. The Set Related Object pop-up window will display. Click the Unlink Related Object
button to delete the relation.
Additionally, a relation between two objects (not tables) can be deleted by using the API. For more
information see the Relations (Delete) section.
Data Service
2.16
87
Relations (Save/Update)
Backendless Data Service supports a complete set of CRUD (create, retrieve, update, delete) operations
for related objects.
Please note: handling relations in a native database differs from handling relations in an external
database.
Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public Contact getOwner()
{
return owner;
}
public void setOwner( Contact owner )
88
{
this.owner = owner;
}
public List<Contact> getContacts()
{
return contacts;
}
public void setContacts( List<Contact> contacts )
{
this.contacts = contacts;
}
}
Contact Class
package com.sample.entity;
import java.util.Date;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
private Address address;
private Date updated;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public String getName()
{
return name;
}
public void setName( String name )
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge( int age )
{
this.age = age;
}
Data Service
Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;
89
90
}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}
}
Data Service
91
92
contacts.add( contact2 );
PhoneBook phoneBook = new PhoneBook();
phoneBook.setOwner( owner );
phoneBook.setContacts( contacts );
PhoneBook savedPhoneBook = Backendless.Persistence.save( phoneBook );
Add Contact to an existing PhoneBook (uses the "savedPhoneBook" object created at the
end of the example above):
Data Service
93
Update a property in the aggregated object (update the phone number in the "owner" of
PhoneBook) and save PhoneBook:
// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
savedPhoneBook.getOwner().setPhone( "555-555-555" );
PhoneBook updatedPhoneBook = Backendless.Persistence.save( savedPhoneBook );
Removing one contact from PhoneBook, adding another and re-saving PhoneBook:
94
// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
Address contact2Address = new Address();
contact2Address.setStreet( "Cavern-X" );
contact2Address.setCity( "Sedona" );
contact2Address.setState( "Arizona" );
Contact contact2 = new Contact();
contact2.setName( "Wade Winston Wilson" );
contact2.setAge( 22 );
contact2.setPhone( "222-222-222" );
contact2.setTitle( "Super heroes" );
contact2.setAddress( contact2Address );
savedPhoneBook.getContacts().remove( 0 );
savedPhoneBook.getContacts().add( contact2 );
PhoneBook updatedPhoneBook = Backendless.Persistence.save( savedPhoneBook );
Data Service
95
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have OrderItem class and Order class
public class OrderItem
{
private
private
private
private
private
private
Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;
96
Data Service
97
{
this.totalCost = totalCost;
}
public Integer getId()
{
return id;
}
public void setId( Integer id )
{
this.id = id;
}
}
To find an order item with relation(s) and update it, run the following query:
private void findAnOrderItemWithRelationsAndUpdateARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of(OrderItem.class).findFirst
( 2 );
Order order = foundItem.getOrder_id();
order.setTotalCost( order.getTotalCost()+100 );
Backendless.Persistence.of( Order.class ).save( order );
}
2.17
Relations (Delete)
Backendless Data Service supports a complete set of CRUD (create, retrieve, update, delete) operations
for related objects.
Please note: handling relations in a native database differs from handling relations in an external
database.
Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
2015 Backendless Corp.
98
Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;
public String getObjectId()
{
return objectId;
}
public void setObjectId( String objectId )
{
this.objectId = objectId;
}
public Contact getOwner()
{
return owner;
}
public void setOwner( Contact owner )
{
this.owner = owner;
}
public List<Contact> getContacts()
{
return contacts;
}
public void setContacts( List<Contact> contacts )
{
this.contacts = contacts;
}
}
Contact Class
package com.sample.entity;
import java.util.Date;
public class Contact
{
private String objectId;
private String name;
private int age;
private String phone;
private String title;
private Address address;
private Date updated;
Data Service
99
100
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
}
Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;
}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}
}
Data Service
101
// use any of the samples above to the point when PhoneBook is saved,
// then use the savedPhoneBook instance:
Long result = Backendless.Persistence.of( PhoneBook.class ).remove
( savedPhoneBook );
102
Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;
Data Service
}
public void setPricePerItem( String pricePerItem )
{
this.pricePerItem = pricePerItem;
}
public Order getOrder_id()
{
return Order_id;
}
public void setOrder_id( Order Order_id )
{
this.Order_id = Order_id;
}
public Manufacturer getManufacturer_id()
{
return Manufacturer_id;
}
public void setManufacturer_id( Manufacturer Manufacturer_id )
{
this.Manufacturer_id = Manufacturer_id;
}
}
103
104
To delete relation for an order item object, run the following query:
private void deleteARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of( OrderItem.class ).
findFirst( 2 );
Order order = foundItem.getOrder_id();
foundItem.setOrder_id( null );
Backendless.Persistence.of( OrderItem.class ).save( foundItem );
}
2.18
Relations (Retrieve)
When a data object or a collection of objects is retrieved using the client API, Backendless does not
automatically include related objects into the returned object hierarchy. The reason for this is it would
make it very easy to load a lot of unnecessary data which could impact application's performance. There
are multiple ways to retrieve related data:
Relations auto-load - a mechanism built-into Backendless Console for configuring specific
relations to be included into responses with the parent objects.
Single-step relations retrieval - initializing and including related objects into the response
when running a find query for parent objects.
Two-step relations retrieval - a process of retrieving relations where the first step is to load the
parent object and the second step is to load specific relations for the given parent.
Loading with relations depth - retrieving related objects by specifying the depth of the object
hierarchy
Loading related child objects by condition applied to parent - load related objects using a
search query where the condition applies to the parent object properties.
Please note: retrieving relations in a native database differs from retrieving relations in an external
database.
Data Service
105
For any two tables A and B where A has a relationship column linking it to B, the console includes the
"auto load" checkbox for the relationship column. Selecting the checkbox instructs Backendless to
return all related B objects when the parent instance of A is retrieved through an API call. For example,
in the image above, the Order table has the one-to-many "items" relationship with the OrderItem table.
When the "auto load" checkbox in the "items" column is selected, all related OrderItem objects will be
included into the response for a find query for the Order table.
T.class
106
relationProps.add( "items" );
relationProps.add( "items.manufacturer" );
Backendless.Data.of( Order.class ).loadRelations( firstOrder,
relationProps );
The result of the second loadRelations call is the firstOrder object is populated with the related
OrderItem objects.
The diagram shows a hierarchy for class structure - the Order class has two relations: with OrderItem
and Customer classes. Each in turn has a relation to the Manufacturer and Address classes. When an
instance or a collection of Order objects is retrieved from Backendless, the API may include a parameter
specifying the depth of relations to include into the response. If the relation depth is 1, then all related
instances of OrderItem and Customer will be included into each Order object. If the relation depth is 2,
then not only OrderItem and Customer instances will be included, but the corresponding Manufacturer
and Address objects as well.
Data Service
107
Asynchronous methods:
public void Backendless.Data.of( T.class ).findFirst( int
relationsDepth, AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).findLast( int
relationsDepth, AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).findById( int
relationsDepth,
AsyncCallback<E> responder );
public void Backendless.Data.of( T.class ).find(
BackendlessDataQuery dataQuery,
AsyncCallback<BackendlessCollection<E>> responder );
Example:
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions queryOptions = new QueryOptions();
queryOptions.relationsDepth = 2;
query.setQueryOptions( queryOptions );
BackendlessCollection<Foo> collection = Backendless.Data.of( Foo.
class ).find( query );
Notice the PhoneBook entity references Contact through two properties - "owner" (the one to one
relationship) and "contacts" (the one to many relationship). Each contact entity also references
Address. These entities will be used to demonstrate how Backendless handles related entities in all
basic data persistence operations. Consider the following class definitions for these entities:
PhoneBook Class
package com.sample.entity;
import java.util.List;
public class PhoneBook
{
private String objectId;
private Contact owner;
private List<Contact> contacts;
108
Data Service
109
110
this.address = address;
}
public Date getUpdated()
{
return updated;
}
public void setUpdated( Date updated )
{
this.updated = updated;
}
}
Address Class
package com.sample.entity;
public class Address
{
private String street;
private String city;
private String state;
public String getStreet()
{
return street;
}
public void setStreet( String street )
{
this.street = street;
}
public String getCity()
{
return city;
}
public void setCity( String city )
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState( String state )
{
this.state = state;
}
Data Service
111
}
The general structure of the query to load a collection of child objects for a specific parent object is:
ParentTableName[ relatedPropertyForChildrenCollection ].
parentColumnName COLUMN-VALUE-CONDITION
When a query in this format is used to fetch a collection of child object, the table addresses in the
request must be one which the "relatedPropertyForChildrenCollection" points to. The
examples below demonstrate the usage of this syntax:
Find all contacts in a city for a specific phone book:
// use a sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "address.city = 'Smallville'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();
Find all contacts for the specific phone book where the city name contains letter 'a':
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "address.city like '%a%'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();
Find all contacts where age is greater than 20 for a specific phone book:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age > 20" );
112
Find all contacts for a specific phone book where age is within the specified range:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age >= 21 and age <= 30" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();
Find all contacts for a specific phone book where age is greater than 20 and the city is
Tokyo:
// use an sample above where PhoneBook is created with multiple contacts,
// then use the savedPhoneBook instance:
StringBuilder whereClause = new StringBuilder();
whereClause.append( "PhoneBook[contacts]" );
whereClause.append( ".objectId='" ).append( savedPhoneBook.getObjectId() ).
append( "'" );
whereClause.append( " and " );
whereClause.append( "age > 20 and address.city = 'Tokyo'" );
BackendlessDataQuery dataQuery = new BackendlessDataQuery();
dataQuery.setWhereClause( whereClause.toString() );
List<Contact> result = Backendless.Persistence.of( Contact.class ).find(
dataQuery ).getCurrentPage();
The objects in an external database does not have objectId parameter, the composite primary key is
used instead. Thus, you will need to use another method signatures for CRUD operations. You can
either provide the key-value mapping or an instance of a typed object with all primary keys properly
initialized. Assuming you have OrderItem class and Order class
public class OrderItem
{
private
private
private
private
private
private
Integer itemId;
String itemName;
Integer quantitySold;
String pricePerItem;
Order Order_id;
Manufacturer Manufacturer_id;
Data Service
{
return itemId;
}
public void setItemId( Integer id )
{
this.itemId = itemId;
}
public String getItemName()
{
return itemName;
}
public void setItemName( String itemName )
{
this.itemName = itemName;
}
public Integer getQuantitySold()
{
return quantitySold;
}
public void setQuantitySold( Integer quantitySold )
{
this.quantitySold = quantitySold;
}
public String getPricePerItem()
{
return pricePerItem;
}
public void setPricePerItem( String pricePerItem )
{
this.pricePerItem = pricePerItem;
}
public Order getOrder_id()
{
return Order_id;
}
public void setOrder_id( Order Order_id )
{
this.Order_id = Order_id;
}
public Manufacturer getManufacturer_id()
{
return Manufacturer_id;
}
public void setManufacturer_id( Manufacturer Manufacturer_id )
{
this.Manufacturer_id = Manufacturer_id;
}
}
113
114
To find an order item with relation(s) and update it, run the following query:
private void findAnOrderItemWithRelationsAndUpdateARelation()
{
OrderItem item = new OrderItem();
item.setItemId( 1 );
OrderItem foundItem = Backendless.Persistence.of(OrderItem.class).findFirst
( 2 );
Order order = foundItem.getOrder_id();
order.setTotalCost( order.getTotalCost()+100 );
Backendless.Persistence.of( Order.class ).save( order );
}
2.19
Data Service
115
between the two entity types must exist in an application. For instance, in a taxi
ordering application a data object may represent a taxi car, while a geo point
represents its location on the map. Link the two together provides great benefits such
as retrieving both objects at once and managing as a consistent, cohesive object
hierarchy.
The Data-to-Geo integration is implemented through object relations. A data table
schema may declare a table column with a special data type - "GeoPoint Relationship".
As a result, the data objects in the table may contain a reference to one or more
GeoPoints. When a data object with a related GeoPoint is saved, Backendless persists
information about both the data object and the geo point in the corresponding
persistent systems and sets up the relationship. Likewise, when a data object is
retrieved by using the API, any related geo points can be retrieved using the same
principle for loading data relations. The data-to-geo relation is bidirectional, it means a
geo point may reference a data object in its metadata. You can learn more about it in
the Relations with Data Objects section of the Geolocation documentation.
The relationship between a data object and a geo point (or a collection of) can be established by using
either the "code first" or the "schema first" approaches. With the former, the relationship is determined
by the data structure persisted with the API. If a data object references a GeoPoint (or a collection of) in
one of its properties, Backendless interprets it as a relation and, as a result, will create a relation
column in the data table schema. With the latter ("schema first") approach, application developer can
declare a relationship in the data table schema first. In either one of these approaches, once a
relationship is declared, data objects and geo points may be linked together by using the Backendless
console as well.
116
4. Enter the name of the column in the Name field. This column will represent a data-to-geo
relationship.
5. Select the Geopoint Relationship option from the Type drop-down list.
6. New menu options will become available. Select the cardinality of the relation from the
corresponding drop-down menu. The one-to-one relation means that a table's object can be
linked with only one geo point, while the one-to-many relation means that a table's object can
be linked with multiple geo points.
Data Service
117
3. Click the plus icon next to the object, for which you want to create a data-to-geo relation.
The Set Related GeoPoint pop-up window will display the list of the geo points.
118
4. Use the Geo Category drop-down list to select a geo category from which the points should
be displayed.
5. If you declared a one-to-one relation for a table the object belongs to, you will be able to link
this object with only one geo point (by the means of a radio button). If it is a one-to-many
relationship, the interface uses check boxes, which allow for multiple selection. Click a radiobutton or select check-boxes next to the geo points which you want to link with the data
object.
6. Click the Set Related GeoPoint button to save the changes.
Once a relation is established, it is shown in the data browser as a hyperlink. The hyperlink for the oneto-one relations displays the coordinates of the related geo point. For the one-to-many relations the link
says "multiple Geopoints". In both cases, the link opens the Geolocation screen of the console which
displays the related geo point(s).
Updating Relations
You can update a data-to-geo relation by following the steps shown below:
1. Click the name of the table containing an object that has a data-to-geo relation you want to
edit . Click the plus icon next to the relation:
Data Service
119
2. The Set Related GeoPoint pop-up window will display. Use the radio-buttons (one-to-one
relations) or check-boxes (one-to-many relations) next to select/deselect the geo points.
3. Click the Set Related GeoPoint button to save the changes.
120
2. The Set Related GeoPoint pop-up window will display. Click the Unlink Relation button to
delete the relation.
Data Service
121
Creating a relationship between a data object and a geo point (or a collection of) uses the same API as
saving a data object with a related entity. In the case of data-to-geo relations, the related entity is a geo
point or a collection of geo points. Consider the example that below saves a data object with a related
geo point. The geo point is also persisted in the Geo Service
The example below demonstrates how to link a taxi (a data object) with its location (geo point). First,
create TaxiCab class:
public class TaxiCab
{
public
public
public
public
String carmake;
String carmodel;
GeoPoint location;
List<GeoPoint> previousDropOffs;
122
point.setLongitude( -74.0059 );
point.addCategory( "taxi" );
point.addMetadata( "service_area", "NYC" );
// one-to-many relation between a data object and geo points
List<GeoPoint> previousDropOffs = new ArrayList<GeoPoint>();
GeoPoint droppOff1 = new GeoPoint( 40.757977, -73.98557 );
droppOff1.addMetadata( "name", "Times Square" );
droppOff1.addCategory( "DropOffs" );
previousDropOffs.add( droppOff1 );
GeoPoint droppOff2 = new GeoPoint( 40.748379, -73.985565 );
droppOff2.addMetadata( "name", "Empire State Building" );
droppOff2.addCategory( "DropOffs" );
previousDropOffs.add( droppOff2 );
TaxiCab taxi = new TaxiCab();
taxi.carmake = "Toyota";
taxi.carmodel = "Prius";
// link one point to data object
taxi.setLocation( point );
// link several points to data object
taxi.setPreviousDropOffs( previousDropOffs );
Backendless.Persistence.save( taxi );
2.20
Security
Data Service supports a very flexible security mechanism for restricting access to objects stored in
Backendless. Security permissions apply to users and roles. A permission can either grant or reject an
operation for a particular asset. In the context of Data Service, the asset is an object which your app can
retrieve, update or delete. Permissions can be granted or rejected globally, where they apply to all tables
and all objects in the data store. Additionally, every table may have its own permission matrix and owner
policy a special instruction whether object owners can or cannot retrieve/update/delete the objects
they own. Finally, every object has its own Access Control List (ACL) which is a matrix of permissions
for the operations applicable specifically to the object:
Data Service
123
The security system is multi-layered. For an API call to retrieve, update or delete object(s), the system
goes through several where each can trim the scope of the operations. The layered order of the decision
making is important and consists of the following:
1.
2.
3.
4.
5.
6.
7.
8.
9.
Where:
User-defined roles roles created by the application developer
System roles roles built into Backendless (Authenticated User, NonAuthenticated User,
SocialUser, etc)
Consider the following guide which illustrates the decision making process:
1. Backend receives an API request to load data from a table (the Find operation). All objects
become candidates for the retrieval. Backendless goes through the security permissions chain
to determine which ones must be included.
124
2. ObjectACL for the user who makes the call. Backendless checks if there are any
restrictions for the user account at the object level. Any object in the collection with ACL which
rejects access to the user is excluded from the result. To see or modify the permissions for a
particular object, click the key icon in the ACL column in the data browser in management
console.
Data Service
125
3. ObjectACL for user-defined roles assigned to the user who makes the call. This is the
same check as the one above, except Backendless looks into the permissions for the roles
defined by the application developer. If the user belongs to any of the custom roles,
Backendless checks if these roles are allowed to perform the current operation. In the
screenshot below, only the MyRole role will be checked in this step, since this is the only
custom role in the application:
4. Table permissions for the User account. Every table in Backendless may have its own set of
permissions for users and roles. At this point Backendless checks if the currently logged in
user is allowed to run the current operation. For example, if the Find operation is denied for the
user, no objects would be returned.
5. Table permissions for the user-defined roles. This step is identical to the one described
above with the exception that is checks custom roles for the table. Since this guide reviews the
decision making process for the Find operation, Backendless checks the column for Find. If any
of the custom roles deny access, the operation is rejected and no data is returned.
126
6. Owner Policy. When a new object is created in Backendless, the system automatically links
it with the account of the currently logged in user. You can see that information in the ownerId
column in any of your tables in the data browser. With the association between objects and
users, Backendless provides a way to control whether users can get access to the data they
created. This is done through a concept we call Owner Policy. The policy is available on the
Schema and Permissions screen. Select a table in the data browser and click the Table
Schema and Permissions button in the upper right corner. Select the Owner Policy menu
item. Owner policy can be global (select All Tables from the drop down in the upper right
corner) or it could apply to a specific table.
Granting a permission for an operation in Owner Policy, guarantees that the objects owned by
the current user will be included in the resulting collection. Denying a permission, takes out the
owned objects from the collection of candidate objects to return. Consider the following:
Granting Find permission in Owner Policy:
Results in the following. The objects with bold border are guaranteed to be returned. All other
objects will be subject to the subsequent permission checks.
Data Service
127
The objects owned by the current user will be excluded from the resulting collection. All
remaining objects will be decided by the subsequent permission checks.
7. Object ACL for system roles. This check is identical to step 3 (Object ACL for custom roles).
The difference is the system roles cover larger groups of users. For example, this step would
make possible to restrict access to specific objects for all authenticated (or not authenticated)
2015 Backendless Corp.
128
users, yet the object would be returned with a query made by the objects owner if the Owner
Policy (previous step) grants access.
8. Table permissions for system roles. Identical to step 5, this checks if any of the system
roles reject the operation at the table level.
9. Global custom roles. Global policy applies to all tables and objects. By default all table level
permissions inherit from the global policy. You can configure in the console at: Users >
Security and Permissions. Create a new role and click it to configure the permission matrix:
2.21
Permissions API
Every data object in Backendless has its own access control list (ACL) - a matrix of operations and
principals (application's users or roles). An intersection of an operation and a principal contains a
permission which determines whether the principal has the right to execute the operation. These
permission could be either grant or deny. Backendless console provides an easy to understand way to
see and manage these permissions. For example, the screenshot below demonstrates an ACL matrix
for an object. Notice the intersection of a column for the Update operation and the
NotAuthenticatedUser role. The cell contains a red X icon representing that the permission is denied:
Data Service
129
In addition to managing the ACL permissions with Backendless Console there is also Permissions API:
To grant access for a user
Backendless.Data.Permissions.FIND.grantForUser( userid, dataObject,
responder );
Backendless.Data.Permissions.REMOVE.grantForUser( userid,
dataObject, responder );
Backendless.Data.Permissions.UPDATE.grantForUser( userid,
dataObject, responder );
where:
userid
dataObject
responder
dataObject
permission.
- a data object for which you want to grant the permission.
responder
130
responder );
where:
dataObject
responder
dataObject
responder
where:
rolename
dataObject
responder
Data Service
131
Messaging Service
3.1
Overview
Data Messaging is an essential function of mobile and desktop applications. It can be used for a
multitude of functions including chat or private messaging, system update broadcast, maintaining game
scores, etc. The Backendless Messaging Service provides API and software infrastructure enabling
publish-subscribe message exchange pattern and mobile push notifications. The service consists of the
following core concepts: channels, publishers, subscribers and registered devices:
channel
- a logical medium "carrying" the messages.
publisher
- a program using the Publishing API to send messages to a channel.
subscriber
- a program using the Subscription API to receive messages from a channel.
registered device
- a mobile device registered with a Backendless channel to receive
push notifications.
Publish-Subscribe Messaging
With the publish-subscribe pattern, one part of the code (or an entire application) can subscribe to
receive messages and another publishes messages. A message can be any data - Backendless
supports messages of primitive or complex data types. To enable publish-subscribe messaging,
Backendless supports the concept of a channel. Subscriptions are "attached" to a channel (or multiple
channels) and messages are published into a channel. By default Backendless sends all messages
published into a channel to all the channel's subscribers. However, a subscription can include message
filters, in this case Backendless delivers only the messages matching the filter.
Push Notifications
A message published to a channel can be tagged as a push notification, thus triggering the logic for
distributing it to the registered devices. Backendless supports push notifications for iOS, Android and
Windows Phone devices. Messages published as push notifications can target either a specific
subscriber (as a device) or a group of subscribers. Subscribers can be grouped by operating system (for
example, a message sent to all registered iOS devices) or as a collection of individual registrations. The
Backendless messaging API supports different types of push notifications - badge updates, alerts, etc.
3.2
Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.
Download SDK
The SDK can be downloaded from the Backendless website.
Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
132
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );
Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}
Messaging Service
133
134
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
2. Substitute your API key for API_KEY in the value attribute. This element sets the key com.google.
android.maps.v2.API_KEY to the value of your API key, and makes the API key visible to any
MapFragment in your application.
3. Save AndroidManifest.xml and re-build your application.
Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".
Messaging Service
135
Push Notifications
Android applications must include special configuration into the application manifest file to enable
support for Backendless Push Notifications:
1. Add the following permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE"/>
<permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE" android:
protectionLevel="signature"/>
where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
2. Add receiver declaration into the <application> element in the manifest file
<receiver android:name="com.backendless.push.BackendlessBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="[APP PACKAGE NAME]"/>
</intent-filter>
</receiver>
where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
Receiver is responsible for processing incoming push notifications. Notice the value for the
android:name attribute in the <receiver> element - com.backendless.push.
BackendlessBroadcastReceiver . This is the default receiver, it contains the logic for processing
push notifications and displaying them in the device's Notification Center. The implementation
can be extended in order to get access to the published messages. See the Message
Subscription section for additional details.
3.3
Core Classes
The Backendless Messaging Service uses the following core classes:
Backendless.Messaging - is the central point for all Backendless Messaging APIs. Provides
access to the device registration, subscription management and messaging publishing functionality
SubscriptionOptions - may be used in the subscription call to establish subscriber identity and set messaging filte
package com.backendless.messaging;
public class SubscriptionOptions
{
public SubscriptionOptions()
public SubscriptionOptions( String subscriberId )
public SubscriptionOptions( String subscriberId, String subtopic )
public SubscriptionOptions( String subscriberId, String subtopic, String
selector )
// id uniquely identifying the subscriber in the application
public String getSubscriberId()
public void setSubscriberId( String subscriberId )
136
DeliveryOptions - used in the publishing API for targeted message delivery. Supported options
include: tagging a message as a push notification, scheduling message delivery in the future,
scheduling repeated message delivery and message expiration.
package com.backendless.messaging;
public class DeliveryOptions
{
// accepts a mask value used by Backendless to route the message
// to the registered devices with the specified operating system.
// The mask value may consist of the following values:
// PushBroadcast.IOS, PushBroadcast.ANDROID, PushBroadcast.WP and
PushBroadcast.ALL
public void setPushBroadcast( int pushBroadcast )
Messaging Service
137
138
// returns the name of the channel the subscription gets messages from
public String getChannelName();
// cancels the subscription
public boolean cancelSubscription();
// suspends the subscription (the client stops receiving new messages)
public void pauseSubscription();
// resumes the subscription
public void resumeSubscription();
3.4
The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;
import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}
When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.
Messaging Service
3.5
139
Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}
The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )
The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}
The only "special case" from these rules is the Device Registration operation. Any error which may
occur during device registration is delivered to the receiver class configured in the application's manifest.
3.6
140
Messaging Service
141
8. Click Save. At this point the backend is configured and is ready to publish push notifications to
Android devices.
In your project you should register the device in order to receive or send push notifications. To
accomplish this, do the following:
1. Login to Google Developers Console and select your previously created project.
2. Copy the project number located at the top of the screen:
142
Android applications must also include special configuration into the application manifest file to enable
support for Backendless Push Notifications:
1. Add the following permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE"/>
<permission android:name="[APP PACKAGE NAME].permission.C2D_MESSAGE" android:
protectionLevel="signature"/>
where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
2. Add receiver declaration into the <application> element in the manifest file
<receiver android:name="com.backendless.push.BackendlessBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
Messaging Service
143
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="[APP PACKAGE NAME]"/>
</intent-filter>
</receiver>
where [APP PACKAGE NAME] is the top level package name, it must be the same value as the
package attribute in the manifest root element.
Receiver is responsible for processing incoming push notifications. Notice the value for the
android:name attribute in the <receiver> element - com.backendless.push.
BackendlessBroadcastReceiver . This is the default receiver, it contains the logic for processing
push notifications and displaying them in the device's Notification Center. The implementation
can be extended in order to get access to the published messages. See the Message
Subscription section for additional details.
3.7
Creating App ID
Creating Certificate Request
Generating an SSL Certificate
Configuring Backendless App/Backend with the Certificate
Creating App ID
1. First we are going to create an App ID for the mobile application which will receive Push
Notifications. Login to Apple Developer Member Center. Click on App IDs in the Identifiers
section. Use the plus sign + button to create a new ID:
144
2. When prompted enter App ID Prefix. Make sure it is descriptive enough so you recognize it
later when you return to the Member Center.
3. Select Explicit App ID in the App ID Suffix section and enter the same bundle ID which you
will be using in the application:
Messaging Service
145
4. In App Services select the services which the application will use and click continue:
5. Make sure that Push Notifications are enabled and click submit. This will conclude the App ID
creation for the app:
2015 Backendless Corp.
146
Messaging Service
147
2. Enter your email address and Common Name (leave the CA Email Address field empty),
select Saved to disk and click Continue:
148
2. Select certificate type there are two options Development and Production. For now select
Apple Push Notification service SSL (Sandbox):
Messaging Service
149
4. Next you will see the instructions for generating a CSR which you have already created by
now. Click Continue to proceed to the next step.
5. Select the CSR file created and saved to the disk earlier and click Generate:
150
Messaging Service
151
11. Enter a password for the certificate. Make sure to make a record of the password you will
need to use it later in the instructions when you submit the certificate to Backendless:
152
12. Enter your Mac OS X account password to confirm the action. At this point you have a
certificate for Push Notifications.
2. Click Manage > App Settings. Locate the Mobile Settings section and upload the .p12
certificate created earlier. Make sure to enter the same password you used when created the
certificate:
Messaging Service
153
3.8
Device Registration
Device registration is required in order for the mobile application running on the device to receive push
notifications from Backendless. Device registration request may contain a list of messaging channels
the device is registering with and an expiration date/time when the registration should be canceled.
Backendless delivers messages marked as push notifications published to the channels to all devices
registered with the channels. If no channels specified, Backendless registers the device with the "default"
channel. If the call references a non-existing channel, Backendless creates the channel and registers the
device with it. Registration expiration is a point in time (expressed as a timestamp) when the device
registration should expire. Backendless removes the device registration at the specified time and device no
longer receives published notifications.
In order to receive push notifications on a mobile device, application running on the device must register
154
where:
context
gcmSenderID
channel(s)
expiration
Errors:
The following errors may occur during the message publishing API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error:
Error Description
Code
5004
Invalid expiration date. The expiration date must be after the current
time.
8000
Example:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
Backendless.Messaging.registerDevice( MainActivity.this, gcmSenderID );
3.9
Messaging Service
155
Errors:
The following errors may occur during the device registration API callThe following errors may
occur during the device registration API call. See the Error Handling section for details on how to
retrieve the error code when the server returns an error:
Error
Code
Description
5000
Examples:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeviceRegistration devReg = Backendless.Messaging.getDeviceRegistration();
3.10
Managing Registrations
Application developers can manage device registrations using the Backendless Console. To see the
device registrations:
1. Login to console at https://backendless.com/develop
2. Click the application for which you would like to see the device registrations.
3. Click the "Messaging" icon.
4. Select the "Devices" tab.
156
The table displays all the current device registrations. Using the interface, you can:
Search for device registrations. The search string applies to all columns
Remove device registrations. To remove, use the check boxes and then click the "Delete
Selected" button
Deliver push notifications to the selected devices.
3.11
Messaging Service
Error
Code
Description
5001
157
Example:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
try
{
Backendless.Messaging.unregisterDevice();
}
catch( BackendlessException exception )
{
// either device is not registered or an error occurred during
de-registration
}
3.12
Message Publishing
Application can publish messages to Backendless for subsequent distribution to subscribers.
Backendless delivers published messages to subscribers as message objects and/or to devices as
push notifications. A message must be published to a channel (or a group of channels). Backendless
supports unlimited number of channels. Applications can use them as a filtering mechanism - channel
subscribers see messages published only to that channel. Similarly, devices can specify a channel (or a
group of them) when registering for push notifications. Message publishing supports the following
scenarios:
Publishing with message headers - headers is a collection of name = value pairs of arbitrary
data. Subscribers can set additional filters expressed as SQL queries which Backendless applies
to the headers. When the query matches the published data in headers, message is delivered to
the corresponding subscriber. See example.
Publishing to a subtopic - Subtopics provide an additional level of message filtering.
Multiple subtopics can be defined within a channel. Both publishers and subscribers can
specify a subtopic within a channel. Subtopic names can be defined using a multi-tiered
format:
maintoken[.secondaryToken][.additionalToken]
To receive messages from more than one subtopic, subscribers can use the wildcard
character (*) in place of any tokens in the subtopic name. For instance, a subscriber
could subscribe to the following subtopic: "news..business.* ", and the publisher sends
messages to "news.business.newyork " and "news.business.tokyo ". In this case the
messages published to either subtopic will be delivered to the consumer.
158
The wildcard character in the last position will match any token in that position as well as
tokens after it. For instance, subtopic com.foo.* will match all of the following: com.foo.
bar, com.foo.abc.def , etc. However, the wildcard character in any position other than
the last will match only one token. For example, subtopic com.*.foo will match com.
abc.foo and com.123.foo , but will not match com.foo .
See example.
Publishing a message only/also as a push notification - By default Backendless delivers
published messages only to the "pub/sub subscribers", that is programs subscribed to receive
messages using the Subscription API. However, published messages can also be delivered as
push notifications to the registered devices. The publishing API provides a way to configure the
delivery mode for the following three modes:
API Subscribers (see example)
Only as Push Notifications (see example)
API Subscribers and Push Notifications (same example as above, see the comment in the
example's code)
Publishing a push notification to a group of devices - Backendless can deliver messages
published as a push notifications to devices grouped
by operating system. That is messages can be delivered only to Android devices, iOS or Windows
Phone or a any combination of these. See example.
Publishing a push notification and targeting specific devices - By default Backendless
delivers published messages to all matched subscribers. (Subscribers may be matched by the
topic name or a query). Alternatively, publishers can direct messages to specific subscribers by
specifying the subscriber or device ID in message meta-data. See example.
Delayed publishing - Backendless immediately processes any published messages and
delivers them to subscribers without any delay. However, publishers can specify the time when
the message should be processed. This is applicable to all the publishing options listed above.
Message processing can be canceled at any time using the message cancellation API. See
example.
Scheduled (repeated) publishing - Backendless supports repeated message processing - a
message is published once, but delivered to subscribers with the specified frequency. Repeated
delivery can stop either at the specified time or they can be canceled using the message
cancellation API. For instance, this could be used for reminders or scheduled tasks. See example
.
Method Signatures
Synchronous Methods:
Publishes message to "Default" channel. The message is not a push notification, it does not
have any headers and does not go into any subtopics.
public MessageStatus Backendless.Messaging.publish( Object message
) throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message
) throws BackendlessException
Messaging Service
159
Publishes message to "Default" channel. The message is not a push notification, it may have
headers and/or subtopic defined in the publishOptions argument.
public MessageStatus Backendless.Messaging.publish( Object message,
PublishOptions
publishOptions ) throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message,
PublishOptions publishOptions )
throws BackendlessException
Publishes message to "Default" channel. The message may be configured as a push
notification. It may have headers and/or subtopic defined in the publishOptions argument.
public MessageStatus Backendless.Messaging.publish( Object message,
PublishOptions publishOptions,
DeliveryOptions deliveryOptions )
throws BackendlessException
Same as above, but published into the specified channel.
public MessageStatus Backendless.Messaging.publish( String
channelName,
Object message,
PublishOptions publishOptions,
DeliveryOptions deliveryOptions )
throws BackendlessException
Asynchronous Methods:
The same set of methods is available for the asynchronous invocation. The difference in the
method signatures is the AsyncCallback<MessageStatus> argument:
public void Backendless.Messaging.publish( Object message,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( String channelName,
Object message,
AsyncCallback<
MessageStatus> responder )
public void Backendless.Messaging.publish( Object message,
PublishOptions
publishOptions,
AsyncCallback<
MessageStatus> responder )
160
publishOptions
deliveryOptions
Return value:
MessageStatus
package com.backendless.messaging;
public class MessageStatus implements Comparable<MessageStatus>
{
public PublishStatusEnum getStatus();
public String getMessageId();
@Override
public String toString();
Messaging Service
161
@Override
public int compareTo( MessageStatus messageStatus );
}
Supported message statuses are:
package com.backendless.messaging;
public enum PublishStatusEnum
{
FAILED, PUBLISHED, SCHEDULED, CANCELLED
}
Errors:
The following errors may occur during the message publishing API call. See the Error Handling
section for details on how to retrieve the error code when the server returns an error:
Error
Code
Description
5003
5007
5030
Examples:
Basic message publishing
Publishing with message headers
Publishing to a subtopic
Publishing a message only as a push notification
Publishing a message as a push notification and targeting specific group of devices (grouped by
OS)
Publishing a push notification and targeting specific devices
Delayed publishing
Repeated publishing
162
weather.setHumidity( 70 );
weather.setTempurature( 80 );
MessageStatus status = Backendless.Messaging.publish( weather, publishOptions
);
Publishing to a subtopic
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version);
PublishOptions publishOptions = new PublishOptions();
publishOptions.setSubtopic( "news.business.newyork" );
MessageStatus status = Backendless.Messaging.publish( "get free coffee at
Moonbucks today", publishOptions );
Messaging Service
163
Delayed publishing
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeliveryOptions deliveryOptions = new DeliveryOptions();
Date publishDate = new Date( System.currentTimeMillis() + 20000 ); // add 20
seconds
deliveryOptions.setPublishAt( publishDate );
MessageStatus status = Backendless.Messaging.publish( "This message was
scheduled 20 sec ago", null, deliveryOptions );
Repeated publishing
String applicationID = "";
String secretKey = "";
String version = "";
Backendless.initApp( applicationId, secretKey, version );
DeliveryOptions deliveryOptions = new DeliveryOptions();
deliveryOptions.setRepeatEvery( 20 ); // the message will be delivered every
20 seconds
deliveryOptions.setRepeatExpiresAt( new Date( System.currentTimeMillis() +
60000 ) );
MessageStatus status = Backendless.Messaging.publish( "This message was
scheduled 20 sec ago", null, deliveryOptions );
164
3.13
Description
iOS
"ios-alert":value
"ios-badge":value
"android-ticker-text":value
"android-content-title":value
Messaging Service
165
as it is visible in
the Android
Notification
Center
"android-content-text":value
Sets the
message of the
notification
which appears
under androidcontent-title
in the Android
Notification
Center.
Windows
Phone
"wp-title":value,
"wp-content":value
"wp-type":"TILE":
"wp-title" : value,
"wp-backgroundImage" : URL string,
"wp-badge" : number value,
"wp-backTitle" : value,
"wp-backImage" : URL string,
"wp-backContent" : value
Sets the
properties for a
tile notification.
"wp-type":"RAW",
"wp-raw":XMLString
Sets the
properties for a
raw notification
Push notifications can be published directly from Backendless Console or using the API (see the
examples in the Message Publishing section).
3.14
responder
166
PublishStatusEnum.CANCELED ).
Return value:
boolean
Errors:
The following errors may occur during the message cancellation API call. See the Error
Handling section for details on how to retrieve the error code when the server returns an error:
Error
Code
Description
5040
Examples:
String applicationID = "";
String secretKey = "";
String version = "";
String gcmSenderID = "";
Backendless.initApp( applicationId, secretKey, version);
DeliveryOptions deliveryOptions = new DeliveryOptions();
Date publishDate = new Date( System.currentTimeMillis() + 20000 ); // add 20
seconds
deliveryOptions.setPublishAt( PushPolicy.ONLY );
MessageStatus status = Backendless.Messaging.publish( "Test Message", null,
deliveryOptions );
boolean result = Backendless.Messaging.cancel( status.getMessageId() );
3.15
Message Subscription
In order to receive published messages, application must subscribe to a channel using the API below.
Using the API, an application becomes an "API subscriber". Another form of subscription can be
accomplished by using the Device Registration API which provides a way to receive push notifications.
Note that the same mobile application can use both device registration and message subscription APIs.
Synchronous Methods
Subscribing to "Default" channel:
public Subscription Backendless.Messaging.subscribe(
AsyncCallback<List<Message>> subscriptionResponder ) throws
BackendlessException
Messaging Service
167
pollingInterval
subscriber.
- contains settings for filtered message delivery. Available options
include subtopic and selector. See the Message Filtering section
below.
- the interval in milliseconds between subsequent polling
requests. Each polling request checks for messages for the
subscription.
Return value:
Subscription
Asynchronous Methods:
168
All asynchronous methods have exactly the same signatures as the synchronous counterparts
except for the AsyncCallback argument which receives a notification about the successful or
failed completion of the method:
Subscribing to "Default" channel:
public void Backendless.Messaging.subscribe(
AsyncCallback<List<Message>> subscriptionResponder,
AsyncCallback<Subscription> methodCallback )
throws BackendlessException
Messaging Service
169
AsyncCallback<Subscription> methodCallback )
throws BackendlessException
where:
channelName
- name of the channel to subscribe to.
subscriptionResponder - responder object where Backendless delivers messages for the
subscriptionOptions
pollingInterval
methodCallback
subscriber.
- contains settings for filtered message delivery. Available options
include subtopic and selector. See the Message Filtering section
below.
- the interval in milliseconds between subsequent polling
requests. Each polling request checks for messages for the
subscription
- a responder object notified of the successful or failed
completion of the subscription operation. An invocation the
handleResponse callback method represents a successful
message subscription. The argument of the callback is an
instance of the Subscription class - an object representing the
established subscription. A Subscription object provides access
to a unique identifier which can be used for subscription
cancellation.
Errors:
The following errors may occur during the message cancellation API call. See the Error
Handling section for details on how to retrieve the error code when the server returns an error:
Error
Code
Description
5008
5009
5010
Messages
The Java/Android client retrieves messages through an implementation of the AsyncCallback interface.
All subscribe methods described above receive the following argument:
AsyncCallback<List<Message>> subscriptionResponder . Backendless delivers new messages
published to the subscribed channel through the receivedResponse method of the
subscriptionResponder object:
new AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
for( Message message : response )
{
String publisherId = message.getPublisherId();
Object data = message.getData();
}
}
public void handleFault( BackendlessFault fault )
{
// handle error here
}
170
}
The response argument in the handleResponse method is a collection of the com.backendless.
messaging.Message objects:
package com.backendless.messaging;
public class Message
{
public String getMessageId();
public Map<String, String> getHeaders();
public Object getData();
public String getPublisherId();
public long getTimestamp();
public String toString();
}
where:
messageId
headers
data
publisherID
timestamp
Examples:
Backendless.initApp( activityContext, appId, secretKey, versionName ); //
where to get then argument values
Backendless.Messaging.subscribe( channelName,
new AsyncCallback<List<Message>>()
{
public void handleResponse( List<Message> response )
{
for( Message message : response )
{
String publisherId = message.getPublisherId();
Object data = message.getData();
}
}
public void handleFault( BackendlessFault fault )
{
Toast.makeText( ChatActivity.this, fault.getMessage(), Toast.
LENGTH_SHORT ).show();
}
}
},
new AsyncCallback<Subscription>()
{
Messaging Service
171
Message Filtering
Backendless message filtering is a powerful mechanism enabling conditional message delivery, interestbased subscriptions and private messaging. A subscription request may include filters in the form of
subtopics and selectors. Backendless applies subscriber's filters to every message published into the
channel and they match, the message is delivered to the subscriber.
Subtopics
Multiple subtopics can be defined within a channel. Both publishers and subscribers can specify
a subtopic within a channel. Subtopic names can be defined using a multi-tiered format:
maintoken[.secondaryToken][.additionalToken]
To receive messages from more than one subtopic, subscribers can use the wildcard character
(*) in place of any tokens in the subtopic name. For instance, a subscriber could subscribe to
the following subtopic: "news..business.* ", and the publisher sends messages to "news.
business.newyork " and " news.business.tokyo ". In this case the messages published to
either subtopic will be delivered to the consumer.
The wildcard character in the last position will match any token in that position as well as
tokens after it. For instance, subtopic com.foo.* will match all of the following: com.foo.bar,
com.foo.abc.def , etc. However, the wildcard character in any position other than the last will
match only one token. For example, subtopic com.*.foo will match com.abc.foo and
com.123.foo , but will not match com.foo .
Selectors
A selector is a query expressed using the SQL-92 syntax and formatted as the condition part of
the SQL's WHERE clause. A query condition must reference the headers of the published
messages. When a message is published and a subscriber has a selector query, Backendless
executes the query on the headers of the published message. If the result of the query is true,
the message is delivered to the subscriber. Consider the following example where the subscriber
will receive only messages containing the "city " header with the value of "Tokyo ":
Publisher:
String applicationID = "set the app id here";
String secretKey = "set your secret key kere";
String version = "set your version name here";
String gcmSenderID = "set your gcmSenderID from Google API portal";
Backendless.initApp( applicationId, secretKey, version);
PublishOptions publishOptions = new PublishOptions();
172
3.16
Cancel Subscription
In order to stop a client from polling for messages, it must issue subscription cancellation request using
the API method described below:
Method Signatures:
subscriptionObject.cancelSubscription() throws
BackendlessException;
where:
subscriptionObject
Return value:
boolean
Examples:
Messaging Service
173
3.17
Sending Email
Backendless provides API for email delivery on behalf of your application. Before the API can be used,
the Backendless backend must be configured with your own SMTP server information. This is an
important requirement as the API will not work if the Manage > App Settings > Email Settings section
in Backendless Console contains default values.
Configuration
To configure a backend:
1.
2.
3.
4.
174
where:
SMTP Server
Port
From
User ID
Password
Security
Make sure to click Test before saving any configuration changes. The Discard button discards any
unsaved changes.
where:
subject
messageBody
bodyParts
Messaging Service
recipient
recipients
attachments
responder
175
Example:
File Service
4.1
Overview
Every Backendless backend/app is allocated a dedicated file storage space. The file storage is located
remotely on the Backendless servers. The file storage can be used to store application's files and ondemand video streams. Backendless File Service provides the API to work with the file storage. The API
supports the following operations:
File Upload - upload files to the applications's file storage. The operation creates directories up
the hierarchy if necessary. Returns file URL which can be used to download or share the file
with others.
File Download - download file using file's URL. The download operation is subject to the
permissions from the File access control list (ACL).
File Deletion - delete a file from the file storage. The delete operation is subject to the
permissions from the File access control list (ACL).
Directory Deletion - same as file deletion, but applies to the directories.
File/Directory Security (File ACL) - assign/unassign user and roles permissions to upload,
download and delete files and directories. This API is used to modify file or directory ACL.
In addition to the API implementation, the File Service enables the following capabilities:
Git Integration - application developers can interact with the file storage as with a git repository.
Web Hosting - file storage can be used to host static web content.
Custom Domain Name - a custom domain name can be mapped to the file storage in a
Backendless backend. This feature in combination with the Web Hosting provides a way to host
websites on Backendless.
Custom Web Templates Hosting - includes HTML files and JS scripts for special pages used in
various workflows such as user email confirmation, password change and session expiration.
4.2
Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.
176
Download SDK
The SDK can be downloaded from the Backendless website.
Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );
Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}
File Service
177
178
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.
File Service
179
Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".
4.3
The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;
180
import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}
When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.
4.4
Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}
The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )
The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}
4.5
File Service
181
Editing a file
Getting public URL for a file
Creating file archives
2. Select a directory where a new file should created. Click the New File button at the top of
the file listing table.
3. Enter the name in the File name field and select a file extension from the Syntax
highlighter drop-down menu:
182
4. Enter the contents for the file as necessary. Click the Save button.
Editing a File
Backendless supports in-browser editing of the files with the following extensions:
.conf, ,css, .csv, .htm, .html, .ini, .java, .js, .log, .php, .
properties, .py, .rb, .sh, .txt, .xml, .xsd
To edit a file:
1. Select a directory containing the file on the Files screen of the console.
2. Click the Edit file icon next to the file to open it for editing:
3. Once the changes in the file are made click the Save button.
File Service
183
Archiving Files
Backendless Console includes a feature enabling to compress directories into a single ZIP file. The
feature applies specifically to directories, meaning an individual file cannot be compressed - it must be
placed into a directory first.
Notice: archiving of directories with total content size greater than 100 Mb may take longer time;
Backendless sends an email to the application developer upon successful completion of the operation.
To archive a directory:
1. Log in to Backendless Console. Select an application and click the Files icon.
2. Navigate to a directory which should be compressed.
3. Click the ZIP Directory button:
184
4. Once the directory is compressed into an archive, it will appear in the parent directory:
4.6
File Upload
The file upload operation delivers and saves a local file in the remote Backendless file storage. The return
value of the operation is the file URL which has the following structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>/<file name>
where:
<application id>
<version name>
File Service
<path>
<file name>
185
The URL assigned to a file and returned as a result of the upload operation accounts for any security
permissions assigned to the file (or the folder it is located in).
Backendless Console includes a file browser with the management functions to upload files, create or
delete directories and files. The file browser is available in the Files section of the console:
File browser also provides a way to see the contents of the files. Every file is a link which opens the file.
The URL of files in file browser is not the same as the URL returned by the file upload operation. The
reason file browser uses a different URL is to let the application developer see the file contents without
any application security constraints. The only constraint applied to the URLs available in file browser is
the application developer must be logged to the console.
Methods:
Java and Android
Synchronous Methods:
Upload a file to a remote path in the Backendless storage.
public BackendlessFile Backendless.Files.upload( File file, String remotePath
) throws Exception
Upload a file to a remote path and receive updates for the status of the upload. Updates are
represented as percentages and are delivered via the uploadCallback argument.
public BackendlessFile Backendless.Files.upload( File file,
String remotePath,
UploadCallback
uploadCallback ) throws Exception
Create a file in the remote Backendless file storage from the bytes provided by OutputStream .
public BackendlessFile Backendless.Files.uploadFromStream(
IOutputStreamRouter outputStreamRouter,
String remoteName,
String remotePath
) throws Exception
Asynchronous Methods:
2015 Backendless Corp.
186
Upload a file to a remote path and receive updates for the status of the upload. Updates are
represented as percentages and are delivered via the uploadCallback argument.
public void Backendless.Files.upload( File file,
String remotePath,
UploadCallback uploadCallback,
AsyncCallback<BackendlessFile>
responder )
where:
file
remotePath
uploadCallback
outputStreamRouter
remoteName
responder
Android Only
Synchronous Method:
Upload a bitmap image and save as a file in the remote Backendless storage.
public BackendlessFile Backendless.Files.upload( android.graphics.Bitmap
bitmap,
android.graphics.Bitmap.
CompressFormat compressFormat,
int quality,
String remoteName,
String remotePath ) throws
Exception
Asynchronous Method:
Same as the method above, but the upload is handled asynchronously.
void
public void Backendless.Files.upload( android.graphics.Bitmap bitmap,
android.graphics.Bitmap.CompressFormat
compressFormat,
int quality,
String remoteName,
String remotePath,
AsyncCallback<BackendlessFile>
responder ) throws Exception
where:
bitmap
File Service
compressFormat
quality
remoteName
remotePath
responder
187
188
package com.backendless.files.router;
import java.io.IOException;
import java.io.OutputStream;
public abstract class IOutputStreamRouter
{
private OutputStream outputStream;
// returns the output stream, to which the bytes must be written to
public OutputStream getOutputStream()
{
return outputStream;
}
// Sets the output stream, to which the bytes must be written to in
// order to upload contents to the remote Backendless storage.
// This method is called internally by Backendless client library.
public void setOutputStream( OutputStream outputStream )
{
this.outputStream = outputStream;
}
// An implementation of this method must write data to the stream
returned
// by the getOutputStream() method. The method must observe the size of
the buffer
// passed as the argument when writing bytes to the output stream
public abstract void writeStream( int bufferSize ) throws IOException;
public void flush() throws IOException
{
outputStream.flush();
}
public void close() throws IOException
{
outputStream.close();
}
}
Example:
Bitmap photo = (Bitmap) getIntent().getExtras().get( "data" );
Backendless.Files.Android.upload( photo, Bitmap.CompressFormat.PNG, 100,
"myphoto.png", "mypics", new AsyncCallback<BackendlessFile>()
{
@Override
public void handleResponse( final BackendlessFile backendlessFile )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
File Service
189
4.7
Save a file in a remote path in the Backendless file storage. The overwrite argument
determines if the file should be overwritten if it already exists.
public void saveFile( String path, String fileName, byte[]
fileContent, boolean overwrite )
Asynchronous Methods:
Save file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. If the file with the same name already exists, it
is not overwritten and error is returned.
public void saveFile( String filePathName, byte[] fileContent,
AsyncCallback<Void> responder )
Save a file in a remote path in the Backendless file storage. The path includes both the directory
where the file should be saved and the file name. The overwrite argument determines if the file
should be overwritten if it already exists.
public void saveFile( String filePathName, byte[] fileContent,
boolean overwrite, AsyncCallback<Void> responder )
Save a file in a remote path in the Backendless file storage. If the file with the same name
already exists, it is not overwritten and error is returned.
190
path
fileName
fileContent
overwrite
responder
- identifies both the directory where the file should be saved and
the file name. Must start with "/" which represents the root
directory of the remote file storage.
- path of the directory where the file should be stored. Must start
with "/" which represents the root directory of the remote file
storage.
- name of the file where the byte content should be written to.
- an array of bytes to save.
- the file is overwritten if the argument value is true and the file
already exists. Otherwise, if the value is false and another file
with the same name already exists, an error is returned.
- a responder object which receives a callback when the method
successfully completes or if an error occurs. Applies to the
asynchronous methods only.
Example:
The example below describes how to save a file entitled "fox.txt" from the string "The quick
brown fox jumps over the lazy dog." You will need to specify:
content of a new file ("The quick brown fox jumps over the lazy dog")
where to save a new file ("testfolder")
a name of the newly created file ("fox.txt")
whether a new file should overwrite the existing file, if any (true)
public static void saveFile()
{
byte[] bytes = "The quick brown fox jumps over the lazy dog".getBytes();
String savedFileURL = Backendless.Files.saveFile( "tempfolder", "fox.txt",
bytes, true );
System.out.println( "File saved. File URL - " + savedFileURL );
}
The server will return notification and link to the newly added file ("File saved. File URL - " )
or an error.
Errors:
Error codes returned on attempt to save a file from the byte array.
Error Description
Code
6016
When saving a new file from the byte array, the payload exceeds
2,800,000 bytes.
6003
A file you are trying to save already exists in the system and cannot
overwrite since overwrite argument is ether set to false or
File Service
191
omitted.
4.8
File Download
Files fetched with the URL scheme defined above are subject to the security constraints and
permissions established by the application developer. See the Files Security section for additional
details on how to secure file storage. Fetching a file secured by an access control list (ACL) policy
requires an additional HTTP header in the request:
2015 Backendless Corp.
192
user-token:<value>
where:
<value>
4.9
- Value of the user token established for the current user session
as a result of the user login operation. The token uniquely
identifies the user session. It is used by Backendless to
establish user identity for all operations where the token is
present. This is necessary in order to determine permissions
applicable to the user and the roles associated with the account.
File Deletion
To delete a file from the Backendless file storage, it must be identified by the file path/name. Files in the
Backendless storage have the following URL structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>/<file name>
The API to delete a file uses the <path>/<filename> part to identify the file which must be deleted.
Methods:
Synchronous Method:
public void Backendless.Files.remove( String filePath ) throws Exception
Asynchronous Method:
public void Backendless.Files.remove( String filePath, AsyncCallback<Void>
responder )
where:
filePath
responder
- Path of the file to delete. The path must consist of the file path
and file name.
- A responder object which receives a callback when the method
successfully deleted the file or if an error occurs. Applies to the
asynchronous methods only.
Example:
Backendless.Files.remove( "pictures/myphoto.png", new AsyncCallback<Void>()
{
@Override
public void handleResponse()
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}
File Service
4.10
193
Directory Deletion
To delete a directory from the Backendless file storage, it must be identified by the its path. Directories
in the Backendless storage have the following URL structure:
https://api.backendless.com/<application id>/<version name>/files/
<path>
The API to delete a directory uses the <path> element from the URL above.
Methods:
Synchronous Method:
public void Backendless.Files.removeDirectory( String path ) throws Exception
Asynchronous Method:
public void Backendless.Files.removeDirectory( String path,
AsyncCallback<Void> responder )
where:
path
responder
Example:
Backendless.Files.remove( "pictures", new AsyncCallback<Void>()
{
@Override
public void handleResponse()
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}
4.11
Git Integration
Backendless file storage can also function as a git repository. This could be very convenient for
deploying multiple files from the developer's computer with a single command. Git integration is disabled
by default. To enable git for the file storage:
1. Open Backendless Console
2. Select your app/backend
3. Click Manage and scroll to the Enable .git support section
4. Use the toggle to turn git integration on or off:
194
When the git integration is turned on, all files present in or uploaded to the file storage are immediately
committed to the repository. This integration is bi-directional. It means that any files committed into the
git repository by the means of git, will also be copied into the file storage. When git integration is being
turned off, the git repository is deleted with all the associated history (the files remain in the file storage).
With the git integration enabled, a new folder (.git) appears in the File Browser on the Files screen. The
folder contains the files from the git repository. When a file is uploaded to file storage either via the
Upload API or using the File Browser, it is automatically committed to the repository. Likewise, when a
file is pushed into the repository, it becomes available and visible in the file storage. The same applies to
editing and deleting files either in the Backendless Console or in git repository.
When git is enabled, the repository is available at the following address:
https://git.backendless.com/<application id>/.git
where:
<application id>
When the Backendless backend is configured with a custom domain name, the repository URL is:
http://<custom domain name>/.git
The repository uses the same authentication as Backendless Console. That means all git commands
must use the same developer email address and password as for logging in to Backendless Console.
It is important to note that any system level files created by git are also placed into the file storage (the .
git directory). These files are accounted for when calculating the file space used by the app/backend.
File Service
195
and need to "integrate" your local git project with the git repository in Backendless.
> cd /path/to/my/repo
> git remote add origin http://git.backendless.com/<your application
id>/.git
# pushes the repo and its refs for the first time to Backendless git
> git push -u origin --all
# pushes any tags to Backendless git
> git push -u origin --tags
4.12
Web Hosting
Backendless file storage includes a special directory which facilitates web hosting for the app/backend.
The directory name is /web :
196
The /web folder serves as the web server root. The web server is available at the following URLs:
With custom domain name enabled for the account:
http://custom domain name
Without custom domain name:
https://api.backendless.com/<application id>/<version name>/files/
web
where:
<application id>
<version name>
4.13
File Service
197
The Custom Domain mapping is a feature included into Backendless Plus package. The package
enables multiple features for a flat monthly subscription fee. Backendless Plus can be enabled in
console at Manage > Billing .
4.14
198
File Service
199
The look and feel as well as the logic in the pages can be customized by modifying the HTML/CSS /JS
files provided for each template. For example, the contents of the change_password folder is:
200
4.15
Files Security
Access to files and directories can be restricted using permissions. Backendless supports the following
permissions for files and directories:
Read - permission to download a file. This permission can be applied to a directory, in that case it
applies recursively to all files contained therein.
Write - permission to upload a file or modify a directory by uploading files into it.
Remove - permission to delete a file or a directory.
To modify the permission matrix for a file or a directory, click the "Edit Permissions" link in file browser
in console. The permission assignment screen contains allows to work with permissions for specific
user accounts or for application roles.
To assign permissions to a user account, type in userid in the "enter user name" field, select the user(s)
and click "Add":
File Service
201
To modify a permission for an operation for a user, click the icon in the corresponding column. The icon
has 3 states:
- inherit GRANT permission from the global permission matrix. This is the default permission.
- explicit GRANT of the permission for the operation. Allows the user to perform the operation.
- DENY permission for the operation. Restricts the user from performing the operation.
Managing permissions for roles is identical to users, except all roles are automatically listed in the table:
Geo Service
5.1
Overview
Backendless Geolocation Service is a system supporting management and search of geo points. A geo
point in the most primitive format consists of a pair of coordinates: latitude and longitude. Optionally a
geo point may contain metadata, which is a collection of arbitrary key/value pairs. A geo point belongs
to a category, which is a logical grouping of geo points. The diagram bellow illustrates these concepts:
Backendless allows infinite number of geo points managed for an application. Geo points can be added
via an API call or the import functionality in Backendless console. Once the backend is populated with
geo points, the search API can be used to run the following types of geo queries:
Radius-based search - Searches for geo points in a circular map area defined by the coordinates
of the central point and a radius. Backendless returns all geo points within the area.
202
Search in a rectangular map area - Searches for geo points in a rectangular map area identified by
the coordinates of two corners defining the area (North West and South East):
Additionally, the geo search API supports the following search options available in the APIs:
Filtering by categories - Both types of search (radius-based and rectangular) can specify the
categories in which the backend should search for the geo points.
Query-based search - The metadata associated with the geo points can be used in queries
which should be formatted using the SQL-92 syntax. For example, the geo point shown in the
image above can be discovered with the following queries:
cuisine = 'French'
cuisine LIKE 'Fr%' and Atmosphere = 'Casual'
cuisine = 'French' and (Price = '$$$$' or Price = '$$$')
Relative search - Runs a search for a subset of metadata key/value pairs to match up to the
specified threshold value. The threshold must be expressed as a percentage of matches.
Geo Service
5.2
203
Setup
Pure Java and Android clients can consume the Backendless services using the class library (JAR)
provided in the Backendless SDK for Java. Make sure to reference backendless.jar located in the /lib
folder of the SDK in the project dependencies and the runtime classpath to get access to the API.
Download SDK
The SDK can be downloaded from the Backendless website.
Maven integration
The backendless client library for Android and Java is available through the Maven repository. To add a
dependency for the library, add the following to pom.xml:
<dependency>
<groupId>com.backendless</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
Before the Java/Android client uses any of the APIs, the code must initialize the Backendless
Application using the following call:
Backendless.initApp( application-id, secret-key, version );
Proguard Configuration
If your Android application uses Proguard, it is necessary to add the following configuration parameters
to proguard.cfg:
-dontwarn
-keep class weborb.** {*;}
204
Geo Service
205
5. In the resulting dialog, enter the SHA-1 fingerprint, then a semicolon, then your application's
package name. For example:
BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75;com.
example.android.mapexample
6. The Google APIs Console responds by displaying Key for Android apps (with certificates) followed
by a forty-character API key, for example:
AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
4.
206
Make sure to use the "Android Secret Key" for the secret-key argument.
The version argument must contain the name of the targeted version. When a new application is
created, the default version name is "v1" . To manage versions, login to the console, select the
"Manage" icon and click "Versioning".
5.3
The handleResponse( T response ) method is called when a response for the asynchronous
operation is available. If the operation resulted in error, it is delivered to the handleFault method. See
the Error Handling section for additional information on how to process errors.
Backendless SDK for Android/Java provides an abstract implementation of the AsyncCallback interface:
package com.backendless.async.callback;
Geo Service
207
import com.backendless.exceptions.BackendlessFault;
public abstract class BackendlessCallback<T> implements AsyncCallback<T>
{
@Override
public void handleFault( BackendlessFault fault )
{
throw new RuntimeException( fault.getMessage() );
}
}
When using BackendlessCallback developer need to implement only the handleResponse method. For
Android applications, the exception will terminate the application and the error will appear in the log.
5.4
Error Handling
When the server reports an error, it is delivered to the client through an instance of the
BackendlessFault class. A BackendlessFault object provides access to an error code which uniquely
identifies the root cause of the error. In addition to the error code, the fault object may include an error
message which provides additional information about the error:
package com.backendless.exceptions;
public class BackendlessFault
{
// returns the error code
public String getCode();
// returns the error message which provides additional information about
the error
public String getMessage();
public String getDetail();
public String toString();
}
The asynchronous API calls accept the AsyncCallback argument which receives the fault object
through the following method:
public void handleFault( BackendlessFault fault )
The synchronous API calls declare a checked exception - BackendlessException. The fault object can
be obtained from the exception:
package com.backendless.exceptions;
public class BackendlessException extends Throwable
{
public BackendlessFault getFault();
}
5.5
208
must start with a literal. Category names can be inspected using Backendless Console (see the image
below) or using the API call retrieving a list of categories.
Geo Service
209
Asynchronous Method:
public void Backedless.Geo.addCategory( String categoryName,
AsyncCallback<GeoCategory> responder )
where:
categoryName
responder
Return Value:
An instance of GeoCategory representing the new category with object properties for category's
objectId, name and number of geo points in it:
GetCategory.java
package com.backendless.geo;
public class GeoCategory implements Comparable<GeoCategory>
210
{
private String objectId;
private String name;
private int size;
// returns objectId assigned to the category
public String getId()
{
return objectId;
}
// returns category name
public String getName()
{
return name;
}
// returns number of geo points in the category
public int getSize()
{
return size;
}
public void setName( String name )
{
this.name = name;
}
@Override
public boolean equals( Object o )
{
if( this == o )
return true;
if( o == null || getClass() != o.getClass() )
return false;
GeoCategory that = (GeoCategory) o;
if( !name.equals( that.name ) )
return false;
if( objectId != null ? !objectId.equals( that.objectId ) : that.objectId
!= null )
return false;
return true;
}
@Override
public int hashCode()
{
int result = objectId != null ? objectId.hashCode() : 0;
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString()
Geo Service
211
{
final StringBuffer sb = new StringBuffer();
sb.append( "GeoCategory" );
sb.append( "{name='" ).append( name ).append( '\'' );
sb.append( ", size=" ).append( size );
sb.append( '}' );
return sb.toString();
}
@Override
public int compareTo( GeoCategory arg )
{
if( this == arg )
return 0;
int nameDiff = this.name == null ? (arg.getName() == null ? 0 : -1) :
this.name.compareTo( arg.getName() );
if( nameDiff != 0 )
return nameDiff;
return size - arg.getSize();
}
}
Example:
Backendless.Geo.addCategory( "mycategory", new AsyncCallback<GeoCategory>()
{
@Override
public void handleResponse( GeoCategory category )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}
5.6
212
Asynchronous Method:
public void Backedless.Geo.deleteCategory( String categoryName,
AsyncCallback<Boolean> responder )
where:
Geo Service
categoryName
responder
213
Return Value:
true if the category is deleted, false otherwise.
Example:
Backendless.Geo.deleteCategory( "mycategory", new AsyncCallback<Boolean>()
{
@Override
public void handleResponse( Boolean result )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}
5.7
Asynchronous Method:
public void Backedless.Geo.addCategory( AsyncCallback<List<GeoCategory>>
responder )
where:
responder
Return Value:
A list of the GeoCategory objects. Each GeoCategory instance represents a category with the
properties of objectId, name and number of geo points in it:
GetCategory.java
package com.backendless.geo;
public class GeoCategory implements Comparable<GeoCategory>
{
private String objectId;
private String name;
private int size;
// returns objectId assigned to the category
public String getId()
214
{
return objectId;
}
// returns category name
public String getName()
{
return name;
}
// returns number of geo points in the category
public int getSize()
{
return size;
}
public void setName( String name )
{
this.name = name;
}
@Override
public boolean equals( Object o )
{
if( this == o )
return true;
if( o == null || getClass() != o.getClass() )
return false;
GeoCategory that = (GeoCategory) o;
if( !name.equals( that.name ) )
return false;
if( objectId != null ? !objectId.equals( that.objectId ) : that.objectId
!= null )
return false;
return true;
}
@Override
public int hashCode()
{
int result = objectId != null ? objectId.hashCode() : 0;
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer();
sb.append( "GeoCategory" );
sb.append( "{name='" ).append( name ).append( '\'' );
sb.append( ", size=" ).append( size );
sb.append( '}' );
return sb.toString();
Geo Service
215
}
@Override
public int compareTo( GeoCategory arg )
{
if( this == arg )
return 0;
int nameDiff = this.name == null ? (arg.getName() == null ? 0 : -1) :
this.name.compareTo( arg.getName() );
if( nameDiff != 0 )
return nameDiff;
return size - arg.getSize();
}
}
Example:
Backendless.Geo.getCategories(new AsyncCallback<List<GeoCategory>>()
{
@Override
public void handleResponse( List<GeoCategory> category )
{
}
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
}
5.8
Adding a GeoPoint
This API adds a geo point to the backend geo location storage. Once a geo point is added, it becomes
searchable through all search mechanisms supported by Backendless. At the present moment there are
two ways to add geo points: (1) using this API or (2) using the Backendless console's import function.
Methods:
Synchronous Methods:
public GeoPoint savePoint( GeoPoint geoPoint ) throws BackendlessException
public GeoPoint savePoint( double latitude,
double longitude,
Map<String, Object> metadata ) throws
BackendlessException
public GeoPoint savePoint( double latitude,
double longitude,
List<String> categories,
Map<String, Object> metadata ) throws
BackendlessException
Asynchronous Method:
public GeoPoint savePoint( GeoPoint geoPoint, AsyncCallback<GeoPoint>
216
where:
geoPoint
latitude
longitude
categories
metadata
responder
Return Value:
An instance of the GeoPoint class representing the new geo point added to the application's
geo location storage. Below is a brief version of the class. The complete source code listing is
available in the Backendless github repository.
Example:
List<String> categories = new ArrayList<String>()
categories.add( "restaurants" );
categories.add( "cool_places" );
Map<String, Object> meta = new HashMap<String, Object>();
meta.put( "name", "Eatzi's" );
Backendless.Geo.savePoint( 32.81, -96.80, categories, meta, new
AsyncCallback<GeoPoint>()
{
@Override
public void handleResponse( GeoPoint geoPoint )
{
System.out.println( geoPoint.getObjectId() );
}
Geo Service
217
@Override
public void handleFault( BackendlessFault backendlessFault )
{
}
});
}
5.9
Updating a GeoPoint
Geo update API relies on the same methods used for Adding a Geo Point. The primary difference is
in order to update a geo point it must have the objectId property assigned by Backendless. The
semantics of the properties in an update request is as follows:
objectId is a required property.
All other properties (latitude , longitude , categories , metadata ) are optional, but at least
one must contain a value.
If latitude or longitude contain values, the new values replace the existing ones.
If categories contains a value, the geo point is moved to the specified categories (with
coordinates and metadata).
If categories is null , the geo point stays in the current category.
If metadata is null , the geo point keeps the current metadata.
If metadata contains any key/value pairs, the new metadata replaces the existing one.
If metadata is an empty object/dictionary, the existing metadata is removed.
5.10
Deleting a GeoPoint
There are two ways to delete a geopoint from the Geolocation storage:
Using the Backendless Console
Using the API
218
6. A confirmation notification will appear in the top right. The selected geopoint(s) are removed.
Asynchronous Method:
public void removePoint( GeoPoint geoPoint, AsyncCallback<Void> responder )
where:
geoPoint
responder
Geo Service
219
method only.
Return Value:
None
Example:
The code below demonstrates how to delete a geopoint. A geopoint is added first, then
subsequently deleted.
Synchronous API sample:
GeoPoint geoPoint = new GeoPoint( -31.96, 115.84 );
GeoPoint savedGeoPoint = Backendless.Geo.savePoint( geoPoint );
Backendless.Geo.removePoint( savedGeoPoint );
5.11
220
Geo Service
5.12
221
Search in Category
This API supports two types of geo searches:
Search in one or more geo categories.
Search in one or more categories based on metadata properties.
Methods:
Synchronous Method:
public BackendlessCollection<GeoPoint> Backedless.Geo.getPoints(
BackendlessGeoQuery geoQuery ) throws BackendlessException
Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )
where:
geoQuery
responder
Return Value:
A collection of GeoPoint objects matching the search query. The class properties are:
objectId
- an ID assigned to geo point by Backendless when it is saved in
the backend geo location storage.
latitude
- latitude of the geo point.
longitude
- longitude of the geo point.
categories
- an array of geo categories the point belong to.
metadata
- metadata associated with the geo point. Accepted values for
this parameter are: String, Number (integer and double), and
Data Service objects. Date values must be represented as
number in the Unix timestamp format (number of milliseconds
since January 1, 1970 at UTC). Learn more about using date in
search queries for category, radius, or rectangular area search.
Geo points returned from the search query are contained inside of BackendlessCollection .
Since the search query may produce a large number of geo points, not all of them are returned
at once. Instead, all found geo points are divided into 'pages'. The size of each page is
determined by the pageSize parameter in the query object. The first response returns the first
page. The collection class includes methods for loading additional pages.
BackendlessCollection also includes the total number of all geo points found by the search
operation (the totalObjects value).
All geo points in the entire search result are indexed. The index of the first geo point is 0. The
offset parameter in the query object and in some method of BackendlessCollection
specifies the index from which to load the next page of geo points. For example, suppose the
entire search result is 200 points (the totalObjects value returned in the collection is 200). If
the initial pageSize is 20, then only 20 geo points are returned in the first response. To get the
second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
222
The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search query:
Search in category by date by using synchronous call:
public void searchByDateInCategorySync() throws ParseException
{
try
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:
mm" );
Date updated = dateFormat.parse( "17.01.2015 at 12:00" );
// create
Geo Service
223
224
5.13
Search in Radius
This API supports multiple types of geo searches:
Search for geo points located within specified distance (radius) from a given point.
Search in radius based on metadata.
Methods:
Synchronous Method:
public BackendlessCollection<GeoPoint> Backedless.Geo.getPoints(
BackendlessGeoQuery geoQuery ) throws BackendlessException
Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )
Geo Service
225
where:
geoQuery
responder
Return Value:
A collection of GeoPoint objects matching the search query. The GeoPoint class is defined as
GeoPoint.java
226
package com.backendless.geo;
import
import
import
import
import
java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;
String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;
public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );
Geo Service
227
228
second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
The geo points in the search results will be sorted by their proximity to the central point (center
of the radius): the geo points that are closest to the central point will be listed first.
Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import
com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;
Geo Service
229
230
stored as a number of milliseconds since January 1st, 1970 at UTC. The example below
demonstrates the use of a date/time timestamp in a search query.
Using dates in where clause when searching in radius
The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search condition:
Search in radius by date by using synchronous call:
public void searchByDateInRadiusSync() throws ParseException
{
try
{
// date
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy 'at' HH:
mm" );
Date updated = dateFormat.parse( "17.01.2015 at 12:00" );
// create
GeoPoint geoPoint = new GeoPoint( 21.306944, -157.858333 );
geoPoint.setCategories( Arrays.asList( "City", "Coffee" ) );
geoPoint.addMetadata( "Name", "Starbucks" );
geoPoint.addMetadata( "City", "Honolulu" );
geoPoint.addMetadata( "Parking", true );
geoPoint.addMetadata( "updated", new Date().getTime() );
geoPoint = Backendless.Geo.savePoint( geoPoint );
System.out.println( String.format( "searchByDateInRadius -> point: %s",
geoPoint ) );
// search
BackendlessGeoQuery query = new BackendlessGeoQuery( 21.30, -157.85, 50,
Units.KILOMETERS );
query.addCategory( "City" );
query.setWhereClause( String.format( "updated > %d", updated.getTime
() ) );
query.setIncludeMeta( true );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( query
);
System.out.println( String.format( "searchByDateInRadius GETPOINTS: %s",
points.getCurrentPage() ) );
}
catch( BackendlessException e )
{
System.err.println( String.format( "searchByDateInRadius FAULT = %s", e )
);
}
}
Geo Service
231
232
5.14
Asynchronous Method:
public void getPoints( BackendlessGeoQuery geoQuery,
AsyncCallback<BackendlessCollection<GeoPoint>>
responder )
where:
geoQuery
responder
Return Value:
A collection of GeoPoint objects matching the search query. The GeoPoint class is defined as:
GeoPoint.java
Geo Service
233
package com.backendless.geo;
import
import
import
import
import
java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;
String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;
public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );
234
Geo Service
235
second page of geo points, they should be loaded from offset 20, third from 40 and so on. The
formula for calculating the offset is:
[value of offset in the current response] + [size of current page ].
Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import
com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;
236
// Loads previous using pageSize and offset from the current query
object.
public BackendlessCollection<E> previousPage() throws
BackendlessException
// Same as above, but is loaded asynchronously
public void previousPage( AsyncCallback<BackendlessCollection<E>>
responder )
}
The search query used to retrieve geo points may reference date values. These
values must be stored as a number of milliseconds since January 1st, 1970 at
UTC. The example below demonstrates the usage of a date/time timestamp in a
search query:
Search in rectangular area by date by using synchronous call:
Geo Service
237
238
Geo Service
5.15
239
Backendless creates clusters by splitting the map into a grid of squares. Geo points
which belong to a square are placed into the same cluster. When a square contains
only one point, it remains non-clustered.
240
4. Console reloads geo points and clusters for the current viewport of the map and displays the
results. A cluster is visualized as a blue marker on the map with a number indicating how
many geo points it represents.
Geo Service
241
Geo clustering is also available with the "Search in Radius" option, which searches for geo points in a
circular area. To enable this functionality, click the Search in radius toggle:
If you want to see the geo points in a cluster, zoom in the map or double-click a cluster's marker.
Zooming the map in too much (when using the clustering along with the search in radius) may result that
the search radius will be much bigger than the visible part of the map on the screen. In this case, the
pop-up window will display offering you to resize (zoom out) the map. Clicking the Yes button zooms the
map out.
242
Clicking a cluster's marker will reveal the coordinates and metadata of a cluster.
Geo Service
243
Once the clustering parameters are set, the geo point search API will return clustered geo points. The
return value is a collection of GeoCluster and/or GeoPoint objects. Instances of the latter may be
returned when a geo point does not have any neighboring points within the grid's square it belongs to.
The GeoCluster class extends from GeoPoint and supports all the inherited properties: latitude,
longitude, categories and metadata. Additionally, geo cluster has its own property representing the
number of points the cluster consists of. Since a GeoCluster object an instance of the GeoPoint class,
the processing of search responses may look like this:
int mapWidth = 500;
double westLongitude = 23.123;
double eastLongitude = -80.238;
BackendlessGeoQuery geoQuery = new BackendlessGeoQuery();
geoQuery.addCategory( "geoservice_sample" );
geoQuery.initClustering( westLongitude, eastLongitude, mapWidth );
BackendlessCollection<GeoPoint> points = Backendless.Geo.getPoints( geoQuery
);
Iterator<GeoPoint> iterator=points.getCurrentPage().iterator();
while( iterator.hasNext() )
{
GeoPoint geoPointOrCluster =iterator.next();
if( geoPointOrCluster instanceof GeoCluster )
{
GeoCluster geoCluster = (GeoCluster) geoPointOrCluster;
System.out.println( "Number of points in the cluster: " + geoCluster.
getTotalPoints() );
}
else
{
System.out.println( "Loaded geo point" );
}
System.out.println( "latitude - " + geoPointOrCluster.getLatitude() + ",
longitude - " + geoPointOrCluster.getLongitude() );
}
244
Methods:
Revealing the geo points from a cluster is made via com.backendless.Geo class.
Synchronous method
public BackendlessCollection<GeoPoint> loadGeoPoints( final
GeoCluster geoCluster )
Asynchronous method
public void loadGeoPoints( final GeoCluster geoCluster, final
AsyncCallback<BackendlessCollection<GeoPoint>> responder )
Return Value:
A collection of GeoPoint objects that are included into a cluster. The GeoPoint class is defined
as:
GeoPoint.java
Geo Service
245
package com.backendless.geo;
import
import
import
import
import
java.io.Serializable;
java.util.ArrayList;
java.util.HashMap;
java.util.List;
java.util.Map;
String objectId;
double latitude;
double longitude;
List<String> categories;
Map<String, String> metadata;
Double distance;
public GeoPoint( int latitudeE6, int longitudeE6, List<String> categories, Map<String, Str
{
this.latitude = (double) latitudeE6 / multiplier;
this.longitude = (double) longitudeE6 / multiplier;
this.categories = categories;
this.metadata = metadata;
}
public String getObjectId();
public double getLatitude();
public void setLatitude( double latitude );
public double getLongitude();
public void setLongitude( double longitude );
246
Geo Service
247
Below is a compressed version of the BackendlessCollection class. The full source code
listing is available in the Backendless github repository:
BackendlessCollection.java
package com.backendless;
import
import
import
import
com.backendless.async.callback.AsyncCallback;
com.backendless.exceptions.BackendlessException;
com.backendless.geo.BackendlessGeoQuery;
com.backendless.persistence.BackendlessDataQuery;
248
5.16
Geo Service
249
5. Type in a metadata property name in the Metadata property name field. The new property
will be associated with the related data object.
6. Select a data table from from the Data table drop-down menu. If you want to establish
relation with a user object, select the Users option from the drop-down menu. A list of the
data objects which belong to the selected table will display.
7. Select the check-boxes for the data object(s) you want to link with the geo point.
8. Click the Add Related Objects button to establish a relation and save the changes.
Once the relation is established, the name of the property and the related data table will display next to
the corresponding geo point.
Updating Relations
2015 Backendless Corp.
250
You can update a geo to data relation (for instance, add or remove objects to/from the relation) by
following the instructions below:
1. Select a geo category and locate the geo point.
2. To update a geo to data relation, click the link with the name of the data table in the geo point
metadata column.
Deleting Relations
To delete a relation between a geo point and a data object:
1. Select a geo category and locate the geo point.
2. Click the link with the name of the data table in the geo point metadata column. The Add
Related Object pop-up window will display.
Geo Service
251
3. Un-check the check-boxes next to the data objects you want to unlink from the geo point.
4. Click the Update Relation button to save the changes.
String carmake;
String carmodel;
GeoPoint location;
List<GeoPoint> previousDropOffs;
252
{
this.carmodel = carmodel;
}
public GeoPoint getLocation()
{
return location;
}
public void setLocation( GeoPoint location )
{
this.location = location;
}
public List<GeoPoint> getPreviousDropOffs()
{
return previousDropOffs;
}
public void setPreviousDropOffs( List<GeoPoint> previousDropOffs )
{
this.previousDropOffs = previousDropOffs;
}
}
5.17
Geofence Designer
About Geofencing
Geofencing on the surface, comprises drawing a stationary geometric boundary around an area on a
Geo Service
253
map. This action creates programmatically a set of simple or complex global coordinates which
represent a shape. A boundary represents a fence, which surrounds the area. For Backendless, the
boundary and area become meaningful when a Geopoint crosses the boundary or stays within the area.
Geofences work with Geopoints. A Geopoint is the most elementary Geolocation concept in
Backendless. It is a point (latitude and longitude coordinate pairs) on the map that is accessible via API
and allowed to move (change coordinates), i.e., a user carrying a mobile device. In addition to the
coordinates, the Geopoint includes metadata in context for the Geopoint.
254
place and Click again. The first line segment appears. Repeat these steps until you have nearly
completed the shape of your boundary. (Its not a Geofence just yet.) Click the cursor on the last control
point (which was the first one set). Backendless detects a closed shape and enables a new Geofence.
NOTE: If you accidentally close the Geofence before completing the drawing, the New Geofence dialog
box appears. If you click Cancel, the Geofence will be removed. To keep the Geofence, click Save,
then re-edit the shape as needed.
Immediately after the shape closes, a popup appears prompting you to name the Geofence. Enter a
name in the Geofence Name text box. Since this example uses the state of Nevada, it makes sense to
name it Nevada. Click Save to enable the Geofence. (We will refer to this example again.)
The result is a new item row in the List of Geofences. The Geofence area is filled with green, and the
item row is highlighted in yellow when a Geofence is selected on the map or on the list. See the image
below.
Geo Service
255
Deleting a Geofence
A Delete button is positioned directly below the interactive map. For each selected checkbox next to the
Geofence hyperlink, the Delete button removes those Geofences. Once a Geofence is deleted, it cannot
be restored.
256
Geo Service
257
Conversely, Backendless could track only employees, excluding visitors, and function as a time clock
action, such as clock-in and clock-out.
258
Three event types are organized in columns in the List of Geofences. The events types are:
On Enter a Geopoint crosses the Geofence boundary into the defined area
On Stay a Geopoint remains in the Geofence area for at least a specified period
On Exit a Geopoint crosses the Geofence boundary out of the defined area
Actions
For each of the above events, a developer can select an action and specify parameters to be executed
from Backendless. There are four action types:
Push notification
Publish-subscribe (pub/sub) message
Send a custom event
Call a URL
The scenarios for choosing an action are wildly different; however, they drive the action and parameter
choices the developer makes. When an action type is selected for an event, a dialog appears where
action parameters can be entered. The fields in the dialog are specific to the action type.
Whenever an action is configured, visual elements indicate whether the parameters are complete. A gear
icon and green checkmark indicate proper configuration, where as a red X in place of the checkmark
indicates improper configuration. The configuration can be edited by clicking the gear to reopen the
currently assigned action dialog.
Push Notification Action
All three Geofence events can trigger this action. Push Notification, in basic form, is a message sent to
a mobile device associated with a Geopoint or to a group of devices registered with a channel. The
Configure Push Notification Action dialog provides flexible parameter options:
Content Configuration configure Push Notification content look and feel for Android, iOS, or
Windows Phone.
Message Headers allows header name and header value.
Delivery to individual Geopoints or those registered to a channel.
Geo Service
259
260
Geo Service
261
262
Geo Service
263
Once server-side monitoring is activated by setting the Is Active toggle to ON, a play button appears
next to the gear icon. This button executes the action on-demand for any Geopoints within the
Geofence. (This function can be useful when debugging.)
5.18
Geofence API
Client-side geofence monitoring is the process of tracking the position of the device in relation to the
geofences defined in Backendless. Functionality in Backendless client libraries loads information about
264
geofences (or a specific geofence) and tracks device positional changes. When the device crosses,
stays in, or exits a geofence boundary, the Backendless client executes a callback. Based on this,
Backendless client-side monitoring supports the following options:
In-app callback for the on enter, on stay and on exit events. A callback is executed on the
device when it's location enters, stays in or exits a geofence. With this approach the client
application decides how to handle a geofence event.
In-app callback interface
Client applications must use a special interface to receive local callbacks. The
Backendless library invokes the interface methods when the current device's location
enters, stays in or exits a geofence:
Consider the following example of the in-app callback interface implementation. The
example creates and saves a geopoint with dummy coordinates (0,0). As the device
changes its location, the in-app callback is executed and the code saves the device's
location on the server. See the example below.
Remote callback for the on enter, on stay and on exit event. The Backendless client
automatically notifies the server when a geofence event occurs. If there is an action associated
with the event, Backendless executes on the server-side.
The API applies the options above either to a specific or all geofences. It is important to note that a
Backendless client can monitor only the geofences which are not activated for the server-side
monitoring. In other words, a geofence with the "Is Active toggle in the Backendless console set to ON
cannot be tracked on the client-side.
Geofence APIs
The Geolocation service provides the following APIs:
Client-side location monitoring:
Start location monitoring for a specific geofence with an in-app callback
Geo Service
265
where:
geofenceName
callback
responder
Example:
Example:
266
responder
Example:
where:
geoPoint
responder
Example:
where:
geofenceName
Example:
Example:
Geo Service
267
where:
- minimum time in milliseconds as an
interval between location updates;
minDistance
- minimum distance in meters as a difference
between locations; if distance is less than
this value, android service will not send new
coordinates to app;
acceptedDistanceAfterReboot
- max distance between two locations after
rebooting the device; if distance is greater
than this value, service resets location status
and starts fence(s) monitoring from the
beginning. Used when user reboots the
device.
minTime
Example:
where:
geofenceName
geoPoint
callback
Example:
where:
geofenceName
geoPoint
callback
268
where:
geofenceName
geoPoint
callback
Example:
where:
geofenceName
query
callback
Example:
The example loads geopoints from a geofence called "Manhattan". The returned geopoints will have
metadata properties and will match the SQL query:
Index
Index
-Bbackendless.jar
-IIdentity
11
-LLogin
17
Logout
28
-PPassword
property
recovery
11
29
-UUser Properties
defining with API 11
defining with console
11
retrieve user entity description
13
update user registration
25
User Registration
API call
14
disable registration
14
email confirmation
14
registration with external system
14
269
270
Back Cover