RhoConnect-Java library is designed for the RhoConnect Application Integration Server.
Using the RhoConnect-Java plugin, your Spring 3 MVC application’s data will transparently synchronize with a mobile application built on the Rhodes framework, or any of the available RhoConnect clients.
We assume that you have a complete java end-to-end application using Spring 3.0 MVC as the front end technology and Hibernate as backend ORM. For this application we will also use Maven for build and dependency management and some database to persist the data. The database is accessed by a Data Access (DAO) layer.
Copy rhoconnect-java and rhoconnect-java-api jar files to your PC. You can also create target rhoconnect-java plugin jar from sources by cloning rhoconnect-java repository
$ git clone git@github.com:rhomobile/rhoconnect-java.git
and executing in cloned project directory the following commands:
$ mvn package
The archived rhoconnect-java-x.y.z.jar file will be created in the project java-plugin/target/ directory. The accompanying rhoconnect-java-api-x.y.z.jar file will be created in the project java-api/target/ directory.
For testing and evaluation purposes you can use RhoconnectJavaSample application as a starting point before continuing with the following steps.
Add dependencies to your Apache Maven project object model (POM): log4j, Apache common beanutils, and Jackson JSON mapper. In the RhoconnectJavaSample application, this code is in the pom.xml file.
<!-- Log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.9</version> <type>jar</type> <optional>false</optional> </dependency> <!-- Apache Commons Beanutils and Jackson JSON Mapper required by rhoconnect-java plugin --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.0</version> <type>jar</type> <optional>false</optional> </dependency>
You must also add the rhoconnect-java and rhoconnect-java-api jars to your Maven project. At this moment they are not available in Maven public repositories and you need install the jar manually into your Maven’s local repository.
$ mvn install:install-file -Dfile=/path-to-jar/rhoconnect-java-api-1.0.0.jar -DgroupId=com.msi.rhoconnect.api \ -DartifactId=rhoconnect-java-api -Dversion=1.0.0 -Dpackaging=jar $ mvn install:install-file -Dfile=/path-to-jar/rhoconnect-java-1.0.2.jar \ -DgroupId=com.rhomobile.rhoconnect -DartifactId=rhoconnect-java -Dversion=1.0.2 -Dpackaging=jar
Now, the rhoconnect-java
jar library is included into your Maven local repository.
In the RhoconnectJavaSample application, you would add this code to the pom.xml file.
<!-- Rhoconnect-java plugin --> <dependency> <groupId>com.rhomobile.rhoconnect</groupId> <artifactId>rhoconnect-java</artifactId> <version>1.0.1</version> <type>jar</type> </dependency> <!-- Rhoconnect-java-api and its dependencies --> <dependency> <groupId>com.msi.rhoconnect.api</groupId> <artifactId>rhoconnect-java-api</artifactId> <version>1.0.0</version> <type>jar</type> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-bundle</artifactId> <version>1.17</version> </dependency> <dependency> <groupId>com.googlecode.json-simple</groupId> <artifactId>json-simple</artifactId> <version>1.1.1</version> </dependency>
Update your servlet xml configuration file to include rhoconnect-java metadata: the packages, converters, and beans. In the RhoconnectJavaSample, the following code is added to src/main/webapp/WEB-INF/spring-servlet.xml file.
<!-- rhoconnect-java plugin packages --> <context:component-scan base-package="com.rhomobile.rhoconnect.controller" /> <!-- rhoconnect-java plugin converters --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="order" value="1" /> <property name="messageConverters"> <list> <ref bean="stringHttpMessageConverter"/> <ref bean="jsonConverter" /> </list> </property> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json" /> </bean> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> </bean> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <ref bean="jsonConverter" /> <ref bean="stringHttpMessageConverter"/> </list> </property> </bean> <!-- rhoconnect-java plugin beans --> <bean id="dispatcher" class = "com.rhomobile.rhoconnect.RhoconnectDispatcher"></bean> <bean id="rhoconnectClient" class = "com.rhomobile.rhoconnect.RhoconnectClient" init-method="setAppEndpoint" > <property name="endpointUrl" value="your_rhoconnect_server_url" /> <property name="appEndpoint" value="your_spring_app_url" /> <property name="apiToken" value="my-rhoconnect-token" /> </bean> <!--The following bean handles the application's authentication routine and should be implemented by you --> <bean id="authenticate" class = "com.rhomobile.contact.ContactAuthenticate" />
The setAppEndpoint
method in the rhoconnectClient
bean in the above code sample is a main point in establishing the communication
link between the Rhoconnect
server and the Spring 3 MVC application. It has the following properties that you need to configure.
endpointUrl |
rhoconnect server's url, for example http://localhost:9292
|
appEndpoint |
your Spring 3 MVC app url, for example http://localhost:8080/contacts
|
apiToken |
rhoconnect server's api_token, for example my-rhoconnect-token
|
The authenticate
bean will be called by rhoconnect server to authenticate users, and has to implement Rhoconnect
interface provided by rhoconnect-java plugin:
package com.rhomobile.rhoconnect; import java.util.Map; public interface Rhoconnect { String authenticate(String userName, String password, Map<String, Object> attributes); }
For example:
package com.rhomobile.contact; import java.util.Map; import org.apache.log4j.Logger; import com.rhomobile.rhoconnect.Rhoconnect; public class ContactAuthenticate implements Rhoconnect { private static final Logger logger = Logger.getLogger(ContactAuthenticate.class); @Override public String authenticate(String login, String password, Map<String, Object> attributes) { logger.debug("ContactAuthenticate#authenticate: implement your authentication code!"); // TODO: your authentication code goes here ... // Return null value if authentication fails. // Otherwise, returned value is data partitioning: i.e. user name for filtering data on per user basis //return login; // But if you want your data to be partitioned by ‘app’ (i.e. the data will be shared among all users), // you should return string "app": it will instruct Rhoconnect to partition the data accordingly. return "app"; } }
You need to establish communication from the RhoConnect instance to your java back-end application by mixing RhoconnectResource interface in your data access (DAO) service class:
package com.rhomobile.rhoconnect; import java.util.Map; public interface RhoconnectResource { Map<String, Object> rhoconnectQuery(String partition); Integer rhoconnectCreate(String partition, Map<String, Object> attributes); Integer rhoconnectUpdate(String partition, Map<String, Object> attributes); Integer rhoconnetDelete(String partition, Map<String, Object> attributes); }
To help rhoconnect-java plugin correctly do mapping of model name to service bean you should take into account the following conventions:
ServiceImpl
suffix (i. e. ContactServiceImpl
for model Contact
)Data partitioning in your code should be based on filtering data for unique user (i.e. your user name) or per application (shared dataset for all users). For more information about RhoConnect partitions, please refer to the RhoSync docs.
You also must to establish the communication from your java back-end application to the RhoConnect instance by auto-wiring RhoconnectClient
bean into your DAO service class and inserting notifications hooks into data access (create/update/delete) methods.
RhoconnectClient
bean is provided by rhoconnect-java plugin and responds to the following methods you have to call:
Example for RhoconnectJavaSample
application:
package com.rhomobile.contact.service; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.rhomobile.contact.dao.ContactDAO; import com.rhomobile.contact.form.Contact; import com.rhomobile.rhoconnect.RhoconnectClient; import com.rhomobile.rhoconnect.RhoconnectResource; @Service public class ContactServiceImpl implements ContactService, RhoconnectResource { //private static final Logger logger = Logger.getLogger(ContactServiceImpl.class); @Autowired private ContactDAO contactDAO; @Autowired private RhoconnectClient client; private static final String sourceName = "Contact"; // name of DAO model @Transactional public int addContact(Contact contact) { int id = contactDAO.addContact(contact); client.notifyOnCreate(sourceName, Integer.toString(id), contact); return id; } @Transactional public void updateContact(Contact contact) { contactDAO.updateContact(contact); client.notifyOnUpdate(sourceName, Integer.toString(contact.getId()), contact); } @Transactional public void removeContact(Integer id) { contactDAO.removeContact(id); client.notifyOnDelete(sourceName, Integer.toString(id)); } @Transactional public List<Contact> listContact() { return contactDAO.listContact(); } @Transactional public Contact getContact(Integer id) { return contactDAO.getContact(id); } @Override @Transactional public Map<String, Object> rhoconnectQuery(String partition) { Map<String, Object> h = new HashMap<String, Object>(); List<Contact> contacts = listContact(); Iterator<Contact> it = contacts.iterator( ); while(it.hasNext()) { Contact c =(Contact)it.next(); h.put(c.getId().toString(), c); } return h; } @Override @Transactional public Integer rhoconnectCreate(String partition, Map<String, Object> attributes) { Contact contact = new Contact(); try { BeanUtils.populate(contact, attributes); int id = addContact(contact); return id; } catch(Exception e) { e.printStackTrace(); } return null; } @Override @Transactional public Integer rhoconnectUpdate(String partition, Map<String, Object> attributes) { Integer id = Integer.parseInt((String)attributes.get("id")); Contact contact = getContact(id); try { BeanUtils.populate(contact, attributes); updateContact(contact); return id; } catch(Exception e) { e.printStackTrace(); } return null; } @Override @Transactional public Integer rhoconnetDelete(String partition, Map<String, Object> attributes) { String objId = (String)attributes.get("id"); Integer id = Integer.parseInt(objId); removeContact(id); return id; } }
Click here to download full source code of Contact manager application with rhoconnect-java plugin.
The rhoconnect-java project contains the java-api
module that provides a Java API to all resources available in Rhoconnect.
The rhoconnect-java-api
is a generic Java library for communicating with RhoConnect RESTful API and it might be used to implement java client apps or components of back-end servers.
This library also is used by RhoConnect-Java plugin to directly access RhoConnect resources.
The library has the following dependencies:
Every usage of API call follows this pattern:
ClientResponse response = NameOfResource.method(serverUrl, params); // Every call returns as a response Jersey object // To get status code call `response.getStatus()` // To get body as a jason string call `response.getEntity(String.class)`
POST /rc/v1/system/login
Login to the RhoConnect server and get API token.
public static ClientResponse login(String url, String password);
POST /rc/v1/system/reset
Reset the server: flush db and re-bootstrap server.
public static ClientResponse reset(String url, String token);
GET /rc/v1/system/license
Returns license information of the currently used license.
public static ClientResponse license(String url, String token);
GET /rc/v1/system/appserver
Returns the url of the plugin’s backend from the RhoConnect server.
public static ClientResponse getAppserver(String url, String token);
POST /rc/v1/system/appserver
Saves the url of the plugin’s backend to the RhoConnect server.
public static ClientResponse setAppserver(String url, String token, String adapterUrl)
GET /rc/v1/system/stats
Retrieves stats for a given metric key.
public static ClientResponse statsMetricKey(String url, String token, String metricKey, int start, int finish)
Retrieves a list of metric keys matching a given pattern. This supports ‘glob’ or ‘*’ style pattern matching.
public static ClientResponse statsMetricPattern(String url, String token, String pattern);
GET /rc/v1/store/:doc
Return content of a given document stored in Redis.
public static ClientResponse get(String url, String token, String docname);
POST /rc/v1/store/:doc
Sets the content of the specified server document. Data should be a string. If append flag is set to true , the data is appended to the current doc (if it exists) instead of replacing it.
public static ClientResponse set(String url, String token, String docname, String data, boolean append);
GET /rc/v1/users
List users registered with this RhoConnect application.
public static ClientResponse list(String url, String token);
POST /rc/v1/users
Create a user in this RhoConnect application.
public static ClientResponse create(String url, String token, String login, String password);
DELETE /rc/v1/users/:user_id
Delete User and all associated devices from the RhoConnect application.
public static ClientResponse delete(String url, String token, String userId);
PUT /rc/v1/users/:user_id
Update attributes for a user on this RhoConnect application. userAttributes is a hash of attribute/value pairs: {:a_user_specific_attribute => a_user_specific_attribute_value}.
public static ClientResponse update(String url, String token, String userId, JSONObject userAttributes);
GET /rc/v1/users/:user_id
Returns the information for the specified user.
public static ClientResponse show(String url, String token, String userId);
GET /rc/v1/users/:user_id/clients
List clients (devices) associated with given user.
public static ClientResponse listClients(String url, String token, String userId);
DELETE /rc/v1/users/:user_id/clients/:client_id
Deletes the specified client (device) for the given user.
public static ClientResponse deleteClient(String url, String token, String userId, String clientId);
GET /rc/v1/users/:user_id/sources/:source_id/docnames
Return list of document keys associated with given source and user. If userId set to ‘*’, this call will return list of keys for ‘shared’ documents.
public static ClientResponse sourcesDocnames(String url, String token, String userId, String sources);
POST /rc/v1/users/ping
Sends PUSH message to all devices of the specified user(s). pingParams is a hash of the following attribute/pairs: { “user_id”:[array_of_users], “sources”:“source_name”, “message”: “hello world”, “vibrate”: 2000, “sound”: “hello.mp3” }
public static ClientResponse ping(String url, String token, JSONObject pingParams);
GET /rc/v1/users/:user_id/sources/:source_id/docs/:doc
Return content of a given source document for the specified user.
public static ClientResponse getSourcesDocs(String url, String token, String userId, String sourceId, String docname);
POST /rc/v1/users/:user_id/sources/:source_id/docs/:doc
Sets the content of the specified source document for the given user. Data should be either a string or hash of hashes. If append flag is set to true , the data is appended to the current doc (if it exists) instead of replacing it.
public static ClientResponse setSourcesDocs(String url, String token, String userId, String sourceId, String docname, String data, boolean append); public static ClientResponse setSourcesDocs(String url, String token, String userId, String sourceId, String docname, JSONObject data, boolean append);
GET /rc/v1/sources/type/:partition_type
Return list of source adapters for this RhoConnect application for the given partition type. Partition type is one of PartitionType.App, PartitionType.User, PartitionType.All.
public static ClientResponse getSources(String url, String token, PartitionType partitionType);
GET /rc/v1/sources/:source_id
Return attributes associated with a given source.
public static ClientResponse getAttributes(String url, String token, String sourceId);
PUT /rc/v1/sources/:source_id
Updates attributes associated with a given source. Attributes defined as a hash of attribute/value pair(s).
public static ClientResponse setAttributes(String url, String token, String sourceId, JSONObject sourceParams);
GET /rc/v1/clients/:client_id
Returns client (device) attributes, such as device_type, device_pin, device_port.
public static ClientResponse getAttributes(String url, String token, String clientId);
GET /rc/v1/clients/:client_id/sources/:source_id/docnames
Returns list of document keys associated with particular client for a given source.
public static ClientResponse getSourcesDocnames(String url, String token, String clientId, String sourceId);
GET /rc/v1/clients/:client_id/sources/:source_id/docs/:doc
Return content of a given source document for the specified client.
public static ClientResponse getSourceDocument(String url, String token, String clientId, String sourceId, String docname);
POST /rc/v1/clients/:client_id/sources/:source_id/docnames
Sets the content of the specified source document for the given client. Data should be either a string or hash of hashes. If append flag is set to true, the data is appended to the current doc (if it exists) instead of replacing it.
public static ClientResponse setSourcesDocnames(String url, String token, String clientId, String sourceId, String data, boolean append); public static ClientResponse setSourcesDocnames(String url, String token, String clientId, String sourceId, JSONObject data, boolean append);
POST /app/v1/:source_name/push_objects
Push new objects or object updates to RhoConnect.
public static ClientResponse pushObjects(String url, String token, String userId, String sourceName, JSONObject data);
POST /app/v1/:source_name/push_deletes
Delete objects from RhoConnect.
public static ClientResponse deleteObjects(String url, String token, String userId, String sourceName, JSONArray objectIds);
PUT /rc/v1/readstate/users/:user_id/sources/:source_id
Sets source poll interval to “current time plus x seconds”. Calling method with refreshTime = null will trigger a refresh on the sync request for the source.
public static ClientResponse setPollInterval(String url, String token, String userId, String sourceName, Integer refreshTime);
import com.msi.rhoconnect.api.SystemResource; import com.msi.rhoconnect.api.UserResource; import com.sun.jersey.api.client.ClientResponse; import org.json.simple.JSONArray; import org.json.simple.JSONValue; public class JavaClient { static String URL = "http://localhost:9292"; public static void main(String[] args) { try { ClientResponse response = SystemResource.login(URL, ""); if(response.getStatus() == 200) { String token = response.getEntity(String.class); System.out.println("Login to server. Got API token: " + token); // List users registered with this RhoConnect application response = UserResource.list(URL, token); JSONArray users = (JSONArray)JSONValue.parse(response.getEntity(String.class)); System.out.println(users); // Create "testuser" in this RhoConnect application response = UserResource.create(URL, token, "testuser", "password"); // Make sure that 'testuser' is registered response = UserResource.list(URL, token); users = (JSONArray)JSONValue.parse(response.getEntity(String.class)); if(users.contains("testuser")) System.out.println("User 'testuser' created"); System.out.println(users); // .... // Delete "testuser" from this rhoconnect app response = UserResource.delete(URL, token, "testuser"); if (response.getStatus() == 200) System.out.println("User 'testuser' deleted"); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Created and maintained by Alexander Babichev.
Released under the MIT License.