indoona SDK for Java

The indoona SDK for Java will allow you connect your users to indoona and make requests on behalf of them.

Installation

The Java SDK is available as a Maven project or as a precompiled jar file: you can download either of the two from here. If you are using Maven, go to the root folder of the indoona SDK, then run:

mvn install

then include the following XML snippet in your own pom.xml file:

<dependencies>
	.
	.
	.
	<dependency>
		<groupId>com.indoona.openplatform</groupId>
		<artifactId>indoona-java-sdk</artifactId>
		<version>0.9.5</version>
	</dependency>
	.
	.
	.
</dependencies>

and you’re done.

Setting up your app

First you have to create an App, if you didn’t it yet, and configure it. At the end of this process, you will obtain OAuth2 credentials to initialize the SDK.

Getting started

In this section you will learn the basic steps to connect your application with the indoona Open Platform using the Java SDK. For a comprehensive explanation of the SDK, have a look at the SDK reference.

Initializing

The Java SDK must be initialized at startup with the OAuth2 credentials obtained at app creation stage.

import com.indoona.openplatform.sdk.provider.ProviderLocator;
// import the default implementation of IConfigurationProvider:
import com.indoona.openplatform.sdk.provider.impl.ConfigurationProvider;

.
.
.

// initialize a configuration provider with your app’s data
List<String> scope = new ArrayList<String>();
scope.add("basic");
scope.add("user_phone");
ConfigurationProvider confProvider = new ConfigurationProvider();
confProvider.init("my_client_id", "my_client_secret", "https://myappdomain/redirectUrl", scope);

// construct the provider locator’s unique instance with the
// obtained configuration provider
ProviderLocator.buildInstance(confProvider);

// add signature providers according to what specified at App Creation,
// to trust the sender on incoming messages
ProviderLocator.getInstance().setProvider(ProviderLocator.TAG_MD5_SIGNATURE_PROVIDER, new MD5SignatureProvider());

By default, the ProviderLocator singleton comes with an instance of ApacheApiProvider. If you are willing to use Google App Engine's HTTP framework instead, just add the code below to your app's initialization:


ProviderLocator.getInstance().setApiProvider(new GoogleAppEngineApiProvider());

Warning: Google App Engine's HTTP framework seems not to support the upload of files different from images towards a generic host. As a consequence, if your application needs to send movies, sound, or other kind of non-image files to indoona users, please rely on ApacheApiProvider.

Obtaining an Application Token

Once your application is initialized, you can obtain an OAuth token (app token) to perform requests towards the indoona API on behalf of the app itself.
The Authorization Provider comes with a method that fits this purpose and that can be invoked at any time:


import com.indoona.openplatform.sdk.model.AppAccessToken;

.
.
.

AppAccessToken appToken = ProviderLocator.getInstance()
								.getAuthorizationProvider()
								.getAppAccessToken();

With the app-level token you can invoke through the SDK all API that prescind from a particular connected user, as well as any API involving a specific user, provided that the corresponding user_id is specified as parameter.

Connecting users

In order to let indoona users connect from your website, as well as from your mobile site and apps, you can add a button like the one below (example in JSP):

<% @page import="com.indoona.openplatform.sdk.provider.ProviderLocator" %>
<% @page import="java.util.List" %>

<% @page language="java" contentType="text/html;" %>
<!DOCTYPE html>

<script>
	function openIndoonaConnectionForm(connectUrl) {
	 	// open the indoona connection URL in a new form with
	 	// a valid window size
	 	window.open(connectUrl);
 	}
</script>

<%
	ProviderLocator locator = ProviderLocator.getInstance();
	List<String> scopes = locator.getConfigurationProvider().getScopes();
	String state = "my_connection_state";
%>

<div id="indoona-connect-btn" class="btn-connect">
    <a onclick="openIndoonaConnectionForm('<%=locator.getAuthorizationProvider().getConnectUrlWithScopes(state, scopes)%>')">
        <img src="/images/indoona-button.png" />
    </a>
</div>

indoona provides standard images for these buttons at our Resources page.

Once users have inserted and validated their indoona credentials (telephone number and verification code) and set their connection scopes, they are sent to the redirect url specified at App Creation stage. In our example, the redirect url is mapped onto a Java Servlet in charge of completing the user connection task.


import com.indoona.openplatform.sdk.provider.ProviderLocator;
import com.indoona.openplatform.sdk.model.UserAccessToken;

.
.
.

@WebServlet("/redirectUrl")
public class ConnectServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {

			// get the received OAuth2 authorization code
			String code = request.getParameter("code");

			// request a user access token based on the received code
			UserAccessToken token = ProviderLocator.getInstance()
								.getAuthorizationProvider()
								.getUserAccessToken(code);

			// here you should store the user access token in your database
 			// for future use...
 			// if your application allows users to configure some preferences, you may
 			// want to redirect them to the management page you specified in the App
 			// Management dashboard

			response.sendRedirect("/managementUrl");
	}
}

Note:
The user token obtained in the example above can be used in two different ways to authenticate all further API calls:

  • directly include it as a parameter;
  • get the connected user's id by invoking token.getUserId(), and specify it as a parameter in conjunction with the app token.

Semantically, these are equivalent options, yet there are useful tips that it's worth considering: see our Guidelines to gain more insight into this matter. In any case, please keep in mind that both kind of tokens will expire or may be invalidated, so be prepared to properly handle refresh procedures.

If you want your application to appear in the indoona app directory, that will let indoona users connect directly from it, you must handle calls on the dedicated url you provided at App Creation stage. An otp (one time password) is delivered to your application as a parameter: you will use this otp to retrieve the actual connect url the user must be redirected to.

In our example, the url for connecting your App from the official directory is mapped onto a Java Servlet:


import com.indoona.openplatform.sdk.provider.ProviderLocator;

.
.
.

@WebServlet("/directoryConnectUrl")
public class DirectoryConnectServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {

			ProviderLocator locator = ProviderLocator.getInstance();

			String otp = request.getParameter("otp");

			// The application should check here whether the
			// currently logged user (if exists) is actually
			// *not connected* to indoona, otherwise prompt
			// users for confirming their identity or
			// logging in with another one

			String url = locator.getAuthorizationProvider()
				.getConnectUrlWithOtp("my_state", otp);
			response.sendRedirect(url);
	}

}

Managing the connection

In many cases, you will need to give users a way to manage their connection with your application. This may be about letting users tune their notification preferences or select contents you make available from your service, and so on.
To this purpose indoona Open Platform allows you to specify a management url in the developer dashboard, to be invoked from within the indoona mobile or web client. Typically you will make the same management url available from your website as well, provided that you properly handle different window sizes.

When the management url is called from a mobile or web client, it comes with two parameters:

  • otp: a one-time password associated to the requesting user, generated as from the previous paragraph;
  • fromapp: a boolean value that tells you whether the page was requested by an indoona mobile client, e.g. to decide about showing a button for disconnection (show the button when fromapp = 'false','0' or not given)

You should always validate a one time password received on the management url, to ensure that the requesting user is a valid one, possibly checking it against an existing web session. Verification is carried out by invoking a dedicated API, that returns the users's unique identifier. In this example, the management url is mapped onto the following servlet:


import com.indoona.openplatform.sdk.provider.ProviderLocator;
import com.indoona.openplatform.sdk.model.AppAccessToken;

.
.
.

@WebServlet("/managementUrl")
public class ManagementServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {

			ProviderLocator locator = ProviderLocator.getInstance();

			String otp = request.getParameter("otp");
			boolean fromApp = request.getParameter("fromapp");

			AppAccessToken appToken = locator
				.getAuthorizationProvider()
				.getAppAccessToken();
			String jsonResult = locator.getApiProvider()
				.invokeOtpVerifyApi(appToken, otp);
			String userId = JSONObject.fromObject(jsonResult)
				.get("user_id");

			// here you should check whether the obtained
			// user_id matches the one locally stored for
			// the currently logged user (if exists);
			// if yes, provide the management page’s content
			// (formatted according to fromApp's value),
			// otherwise you should raise an error
		}
}

Bring your entities into indoona

An important point of strenght of the indoona Open Platform is the possibility to bring any kind of entities from your domain into indoona, mapping them onto contacts that connected users will find in their indoona address book.

You may add contacts acting on behalf of your Application (e.g. a customer care contact available to all users), or let each connected user decide what to import on the management web page you declared at App Creation stage.

When you bring a new contact into indoona, you must provide display name and (optional) avatar url for that contact: these data will typically match corresponding ones in your domain, if existing, but you can decide otherwise.

In addition, for each contact you can specify a set of capabilities:

  • group_add: the contact can be invited into indoona groups;
  • interactive: the contact can process incoming messages (other than group notifications, that are always forwarded to your service).

Contacts can be removed or updated at any time.


// retrieve user access token for the involved user (e.g. from persistency)
UserAccessToken userToken = ...;

List caps = new ArrayList();
caps.add("group_add");
caps.add("interactive");

Contact smartLamp = new Contact("unique_numeric_id",
	"Smart Lamp (Living Room)",
	"https://myappdomain/resource/smartlamp.png",
	caps);

ProviderLocator.getInstance().getApiProvider()
		.invokeContactAddApi(userToken, smartLamp);

N.B.: your Application is endowed by default with a supercontact, named after your application, that is allowed to send messages to any connected user.

Follow our Guidelines to learn about best practices in working with contacts.

Send your first message

After you added one or more contacts to a user’s indoona address book, you can start sending messages to that user on behalf of them. You can send text messages, as well as stickers, locations, pictures, movies, audio notes and generic files.


// retrieve user access token for the involved user (e.g. from persistency)
UserAccessToken userToken = ...;

String sentMsgStr = ProviderLocator.getInstance()
		.getApiProvider().invokeTextMessageSendApi(
			userToken,
			"smart_lamp_id_in_my_domain",
			"user_id_of_the_connected_user",
			"Hi, I'm the smart lamp of your living room! I accept the following commands: 'on', 'off', 'more' and 'less' ");

// the API above returns a string representation of the message just sent:
// by means of the message factory, it can be inflated into a Java object,
// that in turn can be serialized/deserialzed to implement local message
// persistency
Message sentMsg = MessageFactory.getInstance().buildMessage(sentMsgStr);

Receive messages

Besides sending messages, your App can also receive messages from indoona. In order to enable message reception you must declare a valid endpoint URL, as specified in App Creation.
A message is delivered to your endpoint over a POST request with Content-Type set to application/x-www-form-urlencoded: the "data" parameter carries a JSON representation of the actual message; "signature" and "signature_method" parameters allow you to verifiy the (optional) message signature, according to the algorithms described in Message Reference.

In our example, the endpoint URL is associated to a dedicated Java servlet.


import com.indoona.openplatform.sdk.provider.ISignatureProvider;
import com.indoona.openplatform.sdk.provider.ISignatureProvider.SignatureMethod;
import com.indoona.openplatform.sdk.model.message.MessageFactory;
import com.indoona.openplatform.sdk.model.message.Message;
.
.

@WebServlet("/endPointUrl")
public class EndPointUrl extends HttpServlet {

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		// Extract message data:
		String data = request.getParameter("data");

		// Extract possible packet signature and signature method:
		String signatureMethod = request.getParameter("signature_method");
		String signature = request.getParameter("signature");

		boolean isDataSafe = true;

		if ((signature != null) && (signatureMethod != null)) {

			ISignatureProvider signatureProvider = ProviderLocator.getInstance()
				.getSignatureProvider(SignatureMethod
				.fromAlias(signatureMethod));

			try {
				isDataSafe = signatureProvider.verifyPacketSignature(data, signature);
			}
			catch (Exception e) {
				isDataSafe = false;
			}

		}

		if(isDataSafe){
			// Use the MessageFactory to build a Message instance with
			// the received JSON string:
			Message msg = MessageFactory.getInstance().buildMessage(data);

			// handle the received message according to your business logic
		}
	}
}

Talk in groups!

Every contact featured with the ‘group_add’ capability can be added to indoona groups by every user who owns it. Notice that contacts can be owned by more than one connected user, to reflect scenarios in which the corresponding entity in your domain is shared among multiple users.

When a contact is invited to a group, on the endpoint URL you will get a JSON message structured as follows:


{
	"id"		:	"21kj3h12oh3o",
	"type"		:	"group-invite",
	"timestamp"	:	"1434613275796",
	"sender"	:	"aewd558asta9c4sot4fkfj34l@indoona",
	"room"		:	"dcsurakhg9x4a1rlqxjuawazq",
	"owner"		:	"aewd558asta9c4sot4fkfj34l@indoona",
	"recipient"	:	"333@appdomain",
	"data"		:	{
						"subject"	: "My group",
						"avatar"	: "http://myavatar.com/my-group-avatar.jpg",
						"occupants"	: [
							{"id"	: "aewd558asta9c4sot4fkfj34l@indoona"},
							{"id"	: "333@appdomain"},
							{"id"	: "15@anotherappdomain"}
						],
						"policy": "open"
					}
}

The “room” value is the unique group’s identifier, while the “owner” value is the identifier of the inviting user, among the ones who own the contact.

Let inviteMsgStr be a string holding the JSON representation of a group invite message: like any other received message, it can be inflated to a serializable/deserializable object by means of the message factory:


Message inviteMsg = MessageFactory.getInstance()
			.buildMessage(inviteMsgStr);

// Your application will gather the received room id and specify it
// as the recipient resource every time it needs to post something
// to the corresponding group:
String roomId = ((GroupInviteMessage) inviteMsg).getRoom();

Similar messages are delivered to your service in correspondence of all events regarding that group (e.g. join/leave of other participants).

Now you are able to send your first message to the group on behalf of the contact just invited:


// retrieve user access token for the involved user (e.g. from persistency)
UserAccessToken userToken = ...;

String sentMsgStr = ProviderLocator.getInstance()
		.getApiProvider().invokeTextMessageSendApi(
			userToken,
			"smart_lamp_id_in_my_domain",
			roomId,
			"Hi everybody in the house from the smart lamp of the living room! I will notify you all when I get turned on or off, but only privileged people is allowed to tell me what to do!");

Follow our Guidelines to learn about best practices in working with messages.