rhoconnect 3.0.5 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/CHANGELOG.md +7 -1
  2. data/Gemfile.lock +9 -9
  3. data/README.md +4 -4
  4. data/Rakefile +41 -28
  5. data/bench/bench_runner.rb +4 -5
  6. data/bench/benchapp/Gemfile +1 -1
  7. data/bench/benchapp/Gemfile.lock +26 -27
  8. data/bench/benchapp/config.ru +4 -0
  9. data/bench/benchapp/log/passenger.3000.log +1 -0
  10. data/bench/benchapp/log/passenger.9292.log +59 -0
  11. data/bench/benchapp/settings/settings.yml +2 -0
  12. data/bench/benchapp/tmp/pids/passenger.3000.pid.lock +0 -0
  13. data/bench/benchapp/tmp/pids/passenger.9292.pid.lock +0 -0
  14. data/bench/distr_bench/distr_bench +7 -0
  15. data/bench/distr_bench/distr_bench_main +99 -0
  16. data/bench/distr_bench/run_distr_client.sh +12 -0
  17. data/bench/distr_bench/run_test_query_script.sh +50 -0
  18. data/bench/lib/bench/bench_result_processor.rb +90 -0
  19. data/bench/lib/bench/cli.rb +35 -2
  20. data/bench/lib/bench/logging.rb +2 -0
  21. data/bench/lib/bench/runner.rb +4 -2
  22. data/bench/lib/bench/session.rb +3 -1
  23. data/bench/lib/bench/statistics.rb +41 -1
  24. data/bench/lib/bench/timer.rb +7 -0
  25. data/bench/lib/bench/utils.rb +8 -0
  26. data/bench/lib/bench.rb +35 -9
  27. data/bench/lib/testdata/0-data.txt +0 -0
  28. data/bench/lib/testdata/1-data.txt +0 -0
  29. data/bench/lib/testdata/10-data.txt +15 -0
  30. data/bench/lib/testdata/2-data.txt +3 -0
  31. data/bench/lib/testdata/25-data.txt +39 -0
  32. data/bench/lib/testdata/250-data.txt +353 -0
  33. data/bench/lib/testdata/3-data.txt +4 -0
  34. data/bench/lib/testdata/5-data.txt +7 -8
  35. data/bench/lib/testdata/50-data.txt +70 -0
  36. data/bench/lib/testdata/500-data.txt +711 -0
  37. data/bench/prepare_bench +45 -0
  38. data/bench/run_bench.sh +3 -3
  39. data/bench/run_blob_script.sh +1 -1
  40. data/bench/run_cud_script.sh +1 -1
  41. data/bench/run_query_md_script.sh +1 -1
  42. data/bench/run_query_only_script.sh +1 -1
  43. data/bench/run_query_script.sh +1 -1
  44. data/bench/run_test_query_script.sh +30 -0
  45. data/bench/run_test_source_script.sh +21 -0
  46. data/bench/scripts/test_query_script.rb +64 -0
  47. data/bench/scripts/test_source_script.rb +27 -0
  48. data/bin/rhoconnect-benchmark +13 -0
  49. data/doc/client-objc.txt +236 -1
  50. data/doc/client.txt +132 -0
  51. data/doc/extending-rhoconnect-server.txt +79 -0
  52. data/doc/install.txt +1 -1
  53. data/doc/java-plugin.txt +291 -0
  54. data/doc/rest-api.txt +3 -1
  55. data/installer/unix-like/create_texts.rb +73 -16
  56. data/installer/unix-like/pre_install.sh +1 -1
  57. data/installer/unix-like/rho_connect_install_constants.rb +1 -1
  58. data/installer/utils/constants.rb +2 -2
  59. data/installer/utils/nix_install_test.rb +85 -75
  60. data/installer/utils/package_upload/repos.rake +82 -2
  61. data/installer/windows/rhosync.nsi +5 -5
  62. data/lib/rhoconnect/api/application/queue_updates.rb +14 -0
  63. data/lib/rhoconnect/api/source/set_db_doc.rb +3 -1
  64. data/lib/rhoconnect/server.rb +9 -17
  65. data/lib/rhoconnect/version.rb +1 -1
  66. data/rhoconnect.gemspec +1 -1
  67. data/spec/api/admin/get_api_token_spec.rb +6 -0
  68. data/spec/api/source/set_db_doc_spec.rb +13 -0
  69. data/spec/server/server_spec.rb +27 -1
  70. data/tasks/redis.rake +2 -2
  71. metadata +73 -48
  72. data/examples/simple/dump.rdb +0 -0
  73. data/installer/utils/package_upload/repos.rb +0 -83
data/doc/client.txt CHANGED
@@ -83,6 +83,20 @@ Rhoconnect Client is a library to add sync data capability to your applications.
83
83
  - (void) clearObjectNotification;
84
84
  //add object to track changes: create, update, delete
85
85
  - (void) addObjectNotify: (int) nSrcID szObject:(NSString*) szObject;
86
+
87
+ //To process 'create-error' errors from server:
88
+ //Action may be "delete" or "recreate"
89
+ //"delete" just remove object from client, "recreate" will push this object to server again at next sync.
90
+ - (void) onCreateError: (RhoConnectNotify*)notify action: (NSString*)action;
91
+
92
+ //To process 'update-error' errors from server:
93
+ //Action may be "retry" or "rollback".
94
+ //"retry" will push update object operation to server again at next sync, "rollback" will write rollback objects(comes from server) to client database.
95
+ - (void) onUpdateError: (RhoConnectNotify*)notify action: (NSString*)action;
96
+
97
+ //To process 'delete-error' errors from server:
98
+ //Action may be "retry" - will push delete object operation to server again at next sync.
99
+ - (void) onDeleteError: (RhoConnectNotify*)notify action: (NSString*)action;
86
100
 
87
101
  #### RhomModel
88
102
 
@@ -151,6 +165,11 @@ This class provides access to sync notification objects as describe [here](/rhod
151
165
  @property(assign) NSString* error_message;
152
166
  @property(assign) NSString* callback_params;
153
167
 
168
+ - (Boolean) hasCreateErrors;
169
+ - (Boolean) hasUpdateErrors;
170
+ - (Boolean) hasDeleteErrors;
171
+ - (Boolean) isUnknownClientError;
172
+
154
173
  #### RhoConnectObjectNotify
155
174
 
156
175
  This class provides access to sync object notification as describe [here](/rhodes/synchronization#notifications):
@@ -192,6 +211,7 @@ See [rhoconnect-client\Samples\ObjectiveC\store](http://github.com/rhomobile/rho
192
211
 
193
212
  #### Initialization of Schema model
194
213
 
214
+ :::cplusplus
195
215
  RhomModel* product = [[RhomModel alloc] init];
196
216
  product.name = @"Product_s";
197
217
  product.model_type = RMT_PROPERTY_FIXEDSCHEMA;
@@ -203,9 +223,13 @@ To create schema model tables edit database schema file:
203
223
  * Edit file syncdb.schema : add create table statment for each schema model. Note that mandatory object column should be in each table:
204
224
 
205
225
  "object" varchar(255) PRIMARY KEY
226
+
227
+ ** NOTE: Do not use NOT NULL statement in column definitions. RhoConnect client delete object attributes by setting value to null, when all object attributes become nulls, object deleted. So objects with NOT NULL columns will not be deleted. **
228
+
206
229
 
207
230
  * Open 'Copy bundle resources' build phase in project target and point it to local project db folder
208
231
 
232
+
209
233
  #### Login
210
234
 
211
235
  :::cplusplus
@@ -241,6 +265,114 @@ To create schema model tables edit database schema file:
241
265
 
242
266
  [sclient addObjectNotify: [[item objectForKey:@"source_id"] intValue] szObject:[item valueForKey:@"object"] ];
243
267
 
268
+ #### Processing unknown-client error
269
+ Unknown client error return by server after resetting server database, removing particular client id from database or any other cases when server cannot find client id(sync server unique id of device).
270
+ Note that login session may still exist on server, so in this case client does not have to login again, just create new client id.
271
+ Processing of this error contain 2 steps:
272
+
273
+ * When unknown client error is come from server, client should call database_client_reset and start new sync, to register new client id.
274
+
275
+ * If login session also deleted or expired on the server, then customer has to login again.
276
+
277
+ Example:
278
+ :::cplusplus
279
+
280
+ - (void)syncAllCalback:(RhoConnectNotify*) notify
281
+ {
282
+ NSString* status = notify.status;
283
+ NSString* error = notify.error_message;
284
+ int err_code = notify.error_code;
285
+
286
+ NSLog(@"syncAll DONE, status: '%s' , error_msg: '%s' , error_code: %d",
287
+ [status cStringUsingEncoding: NSUTF8StringEncoding],
288
+ [error cStringUsingEncoding: NSUTF8StringEncoding],
289
+ err_code
290
+ );
291
+
292
+ if ( [notify.status compare:@"in_progress"] == 0) {
293
+
294
+ } else if ([notify.status compare:@"complete"] == 0) {
295
+
296
+ [[RhoConnectEngine sharedInstance].syncClient clearNotification];
297
+
298
+ } else if ([notify.status compare:@"error"] == 0) {
299
+
300
+ if([notify isUnknownClientError]) {
301
+ [[RhoConnectEngine sharedInstance].syncClient database_client_reset];
302
+ [[RhoConnectEngine sharedInstance].syncClient setNotification: @selector(syncAllCalback:) target:self];
303
+ [[RhoConnectEngine sharedInstance].syncClient syncAll];
304
+ } else if( err_code == RHO_ERR_CLIENTISNOTLOGGEDIN
305
+ || err_code == RHO_ERR_UNATHORIZED) {
306
+
307
+ NSLog(@"GO TO LOGIN PAGE!");
308
+ // real code to trigger view transition goes here..
309
+ }
310
+ }
311
+ }
312
+
313
+ #### Processing server errors
314
+ * create-error:
315
+ has to be handled in sync callback. Otherwise sync will stop on this model. To fix create errors you should call Model.on_sync_create_error or SyncEngine.on_sync_create_error:
316
+
317
+ * update-error:
318
+ If not handled, local modifications, which were failing on server, will never sync to server again.
319
+ So sync will work fine, but nobody will know about these changes.
320
+
321
+ * delete-error:
322
+ If not handled, local modifications, which were failing on server, will never sync to server again.
323
+ So sync will work fine, but nobody will know about these changes.
324
+
325
+ Example:
326
+ :::cplusplus
327
+
328
+ - (void)syncAllCalback:(RhoConnectNotify*) notify
329
+ {
330
+ NSString* status = notify.status;
331
+ NSString* error = notify.error_message;
332
+ int err_code = notify.error_code;
333
+
334
+ NSLog(@"syncAll DONE, status: '%s' , error_msg: '%s' , error_code: %d",
335
+ [status cStringUsingEncoding: NSUTF8StringEncoding],
336
+ [error cStringUsingEncoding: NSUTF8StringEncoding],
337
+ err_code
338
+ );
339
+
340
+ if ( [notify.status compare:@"in_progress"] == 0) {
341
+
342
+ } else if ([notify.status compare:@"complete"] == 0) {
343
+
344
+ [[RhoConnectEngine sharedInstance].syncClient clearNotification];
345
+
346
+ } else if ([notify.status compare:@"error"] == 0) {
347
+
348
+ if([notify isUnknownClientError]) {
349
+ [[RhoConnectEngine sharedInstance].syncClient database_client_reset];
350
+ [[RhoConnectEngine sharedInstance].syncClient setNotification: @selector(syncAllCalback:) target:self];
351
+ [[RhoConnectEngine sharedInstance].syncClient syncAll];
352
+ } else if( err_code == RHO_ERR_CLIENTISNOTLOGGEDIN
353
+ || err_code == RHO_ERR_UNATHORIZED) {
354
+
355
+ NSLog(@"GO TO LOGIN PAGE!");
356
+ // real code to trigger view transition goes here..
357
+ }else
358
+ {
359
+ //This is mandatory:
360
+ if([notify hasCreateErrors]) {
361
+ [[RhoConnectEngine sharedInstance].syncClient onCreateError: notify action:@"delete"];
362
+ }
363
+ //These are optional:
364
+ /*
365
+ if([notify hasUpdateErrors]) {
366
+ [[RhoConnectEngine sharedInstance].syncClient onUpdateError: notify action:@"rollback"];
367
+ }
368
+ if([notify hasDeleteErrors]) {
369
+ [[RhoConnectEngine sharedInstance].syncClient onDeleteError: notify action:@"retry"];
370
+ }
371
+ */
372
+ }
373
+ }
374
+ }
375
+
244
376
  #### Run SQL query on Schema model
245
377
 
246
378
  :::cplusplus
@@ -0,0 +1,79 @@
1
+ Extending Rhoconnect Application with custom routes
2
+ ===
3
+
4
+ Extending Rhoconnect Application with custom routes
5
+ ---
6
+
7
+ You can provide custom routes support in your Rhoconnect application while you still fully utilize the powerful features provided by the default Rhoconnect server implementation. To do this, create a custom server class inherited from `Rhoconnect::Server` and define all of your custom routes in this extended server class.
8
+
9
+ The following example illustrates how to add a sample `my_custom_route' to the existing Rhoconnect application.
10
+
11
+ 1) create `my_server.rb` class in your Rhoconnect application's root directory:
12
+
13
+ :::ruby
14
+ class MyServer < Rhoconnect::Server
15
+ get '/my_custom_route' do
16
+ if current_user
17
+ send_file 'public/my_file.png'
18
+ end
19
+ nil
20
+ end
21
+ end
22
+
23
+ The above custom route implementation will respond to client's GET request, verifies the current_user (which will be extracted from the Rhoconnect session cookie)
24
+ and returns a static PNG file (which can be later used in BLOB syncs)
25
+
26
+
27
+ 2) Replace the default server instance with the new extended one in the `config.ru` file of your Rhoconnect application:
28
+
29
+ :::ruby
30
+ # Setup the url map
31
+ run Rack::URLMap.new \
32
+ "/" => MyServer.new,
33
+ "/resque" => Resque::Server.new, # If you don't want resque frontend, disable it here
34
+ "/console" => RhoconnectConsole::Server.new # If you don't want rhoconnect frontend, disable it here
35
+
36
+
37
+ Using the New Custom route in BLOB syncs
38
+ ---
39
+
40
+ After you create the new custom routes and replace the default server instance with the new extended one, you can reference and use them from standard Rhodes applications.
41
+ For example, suppose you have a Rhodes app with a Product model. To use a custom route in BLOB syncs, define the following property in the Rhodes Product model (file: `app/Product/product.rb`):
42
+
43
+ :::ruby
44
+ # The model has already been created by the framework, and extends Rhom::RhomObject
45
+ # You can add more methods here
46
+ class Product
47
+ include Rhom::PropertyBag
48
+
49
+ # Uncomment the following line to enable sync with Product.
50
+ enable :sync
51
+
52
+ #add model specifc code here
53
+ property :my_custom_blob_field, :blob, :overwrite
54
+ end
55
+
56
+ To see the static image, modify the `app/Product/show.erb` file for the Product model in your Rhodes application:
57
+
58
+ :::html
59
+ <li>
60
+ <div class="itemLabel">My Static Field Image</div>
61
+ <div class="itemValue"><img src="<%=Rho::RhoApplication::get_blob_path(@product.my_custom_field)%>"></img></div>
62
+ </li>
63
+
64
+ Then, modify your Rhoconnect source adapter (in `rhoconnect_app/sources/product.rb`) to provide BLOB's url for the `my_custom_blob_field`:
65
+
66
+ :::ruby
67
+ def query(params=nil)
68
+ parsed = JSON.parse(RestClient.get("#{@base}.json").body)
69
+
70
+ @result={}
71
+ parsed.each do |item|
72
+ item["product"]["my_custom_field-rhoblob"] = "http://localhost:9292/my_custom_route"
73
+ @result[item["product"]["id"].to_s] = item["product"]
74
+ end if parsed
75
+ end
76
+
77
+ This way, your `my_custom_field` would be getting the BLOB image data from the custom route defined in your Rhoconnect extension server.
78
+
79
+ For more information on BLOB syncs, see [this section](http://docs.rhomobile.com/rhoconnect/blob-sync).
data/doc/install.txt CHANGED
@@ -17,7 +17,7 @@ You can now skip to the [generating a new application instructions](/rhoconnect/
17
17
 
18
18
  3. Ruby Web Server - We test with [thin](http://code.macournoyer.com/thin/), and [passenger](http://www.modrails.com/). WEBrick, the web server that ships with ruby, is known to cause issues with HTTP headers/cookies and is ***not*** recommended.
19
19
 
20
- 4. [Redis 2.4.1+](http://redis.io/) - RhoConnect includes a simple [rake task](/rhoconnect/command-line#rake-tasks) `redis:install` to install redis, covered in the [Rake Tasks section](/rhoconnect/command-line#rake-tasks). Alternatively, you can [install redis directly](http://redis.io/download).
20
+ 4. [Redis 2.4.x](http://redis.io/) - RhoConnect includes a simple [rake task](/rhoconnect/command-line#rake-tasks) `redis:install` to install redis, covered in the [Rake Tasks section](/rhoconnect/command-line#rake-tasks). Alternatively, you can [install redis directly](http://redis.io/download).
21
21
 
22
22
  ### Installing RhoConnect Gem
23
23
 
@@ -0,0 +1,291 @@
1
+ RhoConnect-Java
2
+ ===
3
+
4
+ RhoConnect-Java library is designed for the [RhoConnect](http://rhomobile.com/products/rhoconnect/) App Integration Server.
5
+
6
+ Using the RhoConnect-Java plugin, your [Spting 3 MVC](http://www.springsource.org/) application's data will transparently synchronize with a mobile application built on the [Rhodes framework](http://rhomobile.com/products/rhodes), or any of the available [RhoConnect clients](http://rhomobile.com/products/rhoconnect/).
7
+
8
+ ## Getting started
9
+
10
+ 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 Maven2 for build and dependency management and some database to persist the data. The database is accessed by a Data Access (DAO) layer.
11
+
12
+ For testing and evaluation purposes you can use [RhoconnectJavaSample](https://github.com/shurab/RhoconnectJavaSample) application as a starting point before continuing with the following steps.
13
+
14
+ ### Adding Dependencies to Your Maven 2 Project
15
+
16
+ Add dependencies to your Apache Maven 2 project object model (POM): apache common beanutils, and Jackson JSON mapper. In the RhoconnectJavaSample application, this code is in the pom.xml file.
17
+
18
+ :::xml
19
+ <!-- apache commons beanutils -->
20
+ <dependency>
21
+ <groupId>commons-beanutils</groupId>
22
+ <artifactId>commons-beanutils</artifactId>
23
+ <version>1.8.3</version>
24
+ </dependency>
25
+ <!-- Jackson JSON Mapper -->
26
+ <dependency>
27
+ <groupId>org.codehaus.jackson</groupId>
28
+ <artifactId>jackson-mapper-asl</artifactId>
29
+ <version>1.9.0</version>
30
+ <type>jar</type>
31
+ <optional>false</optional>
32
+ </dependency>
33
+
34
+ ### Updating Your Servlet XML Configuration File
35
+
36
+ Update your servlet xml configuration file to include rhoconnect-java metadata: the packages, converters, and beans. In the RhoconnectJavaSample, this code is in comments in the src/main/webapp/WEB-INF/spring-servlet.xml file.
37
+
38
+ :::xml
39
+ <!-- rhoconnect-java plugin packages -->
40
+ <context:component-scan base-package="com.rhomobile.rhoconnect.controller" />
41
+
42
+ <!-- rhoconnect-java plugin converters -->
43
+ <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
44
+ <property name="order" value="1" />
45
+ <property name="messageConverters">
46
+ <list>
47
+ <ref bean="stringHttpMessageConverter"/>
48
+ <ref bean="jsonConverter" />
49
+ </list>
50
+ </property>
51
+ </bean>
52
+
53
+ <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
54
+ <property name="supportedMediaTypes" value="application/json" />
55
+ </bean>
56
+
57
+ <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
58
+ </bean>
59
+
60
+ <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
61
+ <property name="messageConverters">
62
+ <list>
63
+ <ref bean="jsonConverter" />
64
+ <ref bean="stringHttpMessageConverter"/>
65
+ </list>
66
+ </property>
67
+ </bean>
68
+
69
+ <!-- rhoconnect-java plugin beans -->
70
+ <bean id="rhoconnect" class = "com.rhomobile.rhoconnect.RhoconnectImpl" />
71
+
72
+ <bean id="rhoconnectClient" class = "com.rhomobile.rhoconnect.RhoconnectClient" init-method="setAppEndpoint" >
73
+ <property name="restTemplate"><ref bean="restTemplate"/></property>
74
+ <property name="endpointUrl" value="your_rhoconnect_server_url" />
75
+ <property name="appEndpoint" value="your_spring_app_url" />
76
+ <property name="apiToken" value="rhoconnect_api_token" />
77
+ </bean>
78
+
79
+ The `setAppEndpoint` method in the `rhoconnectClient` bean in the above code sample is a main point in establishing the communication
80
+ link between the `Rhoconnect` server and the Spring 3 MVC application. It has the following properties that you need to configure.
81
+
82
+ <table border="1">
83
+ <tr>
84
+ <td><code>endpointUrl</code></td>
85
+ <td>rhoconnect server's url, for example <code>http://localhost:9292</code>.</td>
86
+ </tr>
87
+ <tr>
88
+ <td><code>appEndpoint</code></td>
89
+ <td>your Spring 3 MVC app url, for example <code>http://localhost:8080/contacts</code>.</td>
90
+ </tr>
91
+ <tr>
92
+ <td><code>apiToken</code></td>
93
+ <td>rhoconnect server's api_token, for example <code>sometokenforme</code>.</td>
94
+ </tr>
95
+ </table>
96
+
97
+
98
+ ### Adding rhoconnect-java to the Build Path
99
+
100
+ You must add the rhoconnect-java jar to your apache maven 2 build classpath. In the RhoconnectJavaSample application, you would add this code to the pom.xml file, putting in the path to your rhoconnect-java jar into the systemPath.
101
+
102
+ :::xml
103
+ <dependency>
104
+ <groupId>rhoconnect-java</groupId>
105
+ <artifactId>rhoconnect-java</artifactId>
106
+ <version>0.0.1</version>
107
+ <scope>system</scope>
108
+ <!-- Set absolute path to your rhoconnect-java jar -->
109
+ <systemPath>/path-to-jar-directory/rhoconnect-java-0.0.1.jar</systemPath>
110
+ </dependency>
111
+
112
+
113
+ ### Establishing communication from the RhoConnect server to java back-end application
114
+
115
+ You need to establish communication from the RhoConnect instance to your java back-end application by implementing Rhoconnect interface.
116
+
117
+ :::java
118
+ package com.rhomobile.rhoconnect;
119
+ import java.util.Map;
120
+
121
+ public interface Rhoconnect {
122
+ boolean authenticate(String login, String password, Map<String, Object> attribures);
123
+
124
+ Map<String, Object> query_objects(String resource, String partition);
125
+ Integer create(String resource, String partition, Map<String, Object> attributes);
126
+ Integer update(String resource, String partition, Map<String, Object> attributes);
127
+ Integer delete(String resource, String partition, Map<String, Object> attributes);
128
+ }
129
+
130
+ For example, `RhoconnectJavaSample` application implementation is based on `contactService` API:
131
+
132
+ :::java
133
+ /**
134
+ * RhoconnectImpl.java
135
+ */
136
+ package com.rhomobile.rhoconnect;
137
+
138
+ import java.util.HashMap;
139
+ import java.util.Iterator;
140
+ import java.util.List;
141
+ import java.util.Map;
142
+
143
+ import org.apache.commons.beanutils.BeanUtils;
144
+ import org.springframework.beans.factory.annotation.Autowired;
145
+
146
+ import com.rhomobile.contact.form.Contact;
147
+ import com.rhomobile.contact.service.ContactService;
148
+
149
+ public class RhoconnectImpl implements Rhoconnect {
150
+
151
+ @Autowired
152
+ private ContactService contactService;
153
+
154
+ @Override
155
+ public boolean authenticate(String login, String password, Map<String, Object> attribures) {
156
+ // TODO: your authentication code goes here ...
157
+ return true;
158
+ }
159
+
160
+ @Override
161
+ public Map<String, Object> query_objects(String resource, String partition) {
162
+ Map<String, Object> h = new HashMap<String, Object>();
163
+ List<Contact> contacts = contactService.listContact();
164
+ Iterator<Contact> it = contacts.iterator( );
165
+ while(it.hasNext()) {
166
+ Contact c =(Contact)it.next();
167
+ h.put(c.getId().toString(), c);
168
+ }
169
+ return h;
170
+ }
171
+
172
+ @Override
173
+ public Integer create(String resource, String partition, Map<String, Object> attributes) {
174
+ Contact contact = new Contact();
175
+ try {
176
+ BeanUtils.populate(contact, attributes);
177
+ int id = contactService.addContact(contact);
178
+ return id;
179
+ } catch(Exception e) {
180
+ e.printStackTrace();
181
+ }
182
+ return null;
183
+ }
184
+
185
+ @Override
186
+ public Integer update(String resource, String partition, Map<String, Object> attributes) {
187
+ Integer id = Integer.parseInt((String)attributes.get("id"));
188
+ Contact contact = contactService.getContact(id);
189
+ try {
190
+ BeanUtils.populate(contact, attributes);
191
+ contactService.updateContact(contact);
192
+ return id;
193
+ } catch(Exception e) {
194
+ e.printStackTrace();
195
+ }
196
+ return null;
197
+ }
198
+
199
+ @Override
200
+ public Integer delete(String resource, String partition, Map<String, Object> attributes) {
201
+ String objId = (String)attributes.get("id");
202
+ Integer id = Integer.parseInt(objId);
203
+ contactService.removeContact(id);
204
+ return id;
205
+ }
206
+ }
207
+
208
+ ### Establishing communication from java back-end application to the RhoConnect server
209
+
210
+ You also must to establish the communication from your java back-end application to the RhoConnect instance by autowiring your data access (DAO) service layer with RhoconnectClient bean and inserting notifications hooks there.
211
+
212
+ :::java
213
+ package com.rhomobile.rhoconnect;
214
+
215
+ public class RhoconnectClient {
216
+ // ...
217
+ public boolean notifyOnCreate(String sourceName, String partition, Object objId, Object object);
218
+ public boolean notifyOnUpdate(String sourceName, String partition, Object objId, Object object);
219
+ public boolean notifyOnDelete(String sourceName, String partition, Object objId);
220
+ // ...
221
+ }
222
+
223
+ For example, `RhoconnectJavaSample` application uses the following implementation:
224
+
225
+ :::java
226
+ package com.rhomobile.contact.service;
227
+ import java.util.List;
228
+
229
+ import org.springframework.beans.factory.annotation.Autowired;
230
+ import org.springframework.stereotype.Service;
231
+ import org.springframework.transaction.annotation.Transactional;
232
+
233
+ import com.rhomobile.contact.dao.ContactDAO;
234
+ import com.rhomobile.contact.form.Contact;
235
+
236
+ import com.rhomobile.rhoconnect.RhoconnectClient;
237
+ //import org.apache.log4j.Logger;
238
+
239
+ @Service
240
+ public class ContactServiceImpl implements ContactService {
241
+
242
+ @Autowired
243
+ private ContactDAO contactDAO;
244
+
245
+ @Autowired
246
+ private RhoconnectClient client;
247
+
248
+ //private static final Logger logger = Logger.getLogger(ContactServiceImpl.class);
249
+
250
+ private static final String sourceName = "Contact"; // name of DAO model
251
+
252
+ // Data partitioning: i.e. your user name for filtering data on per user basis
253
+ private static final String partition = "your_partition";
254
+
255
+ @Transactional
256
+ public int addContact(Contact contact) {
257
+ int id = contactDAO.addContact(contact);
258
+ client.notifyOnCreate(sourceName, partition, Integer.toString(id), contact);
259
+ return id;
260
+ }
261
+
262
+ @Transactional
263
+ public void updateContact(Contact contact) {
264
+ contactDAO.updateContact(contact);
265
+ client.notifyOnUpdate(sourceName, partition, Integer.toString(contact.getId()), contact);
266
+ }
267
+
268
+ @Transactional
269
+ public void removeContact(Integer id) {
270
+ contactDAO.removeContact(id);
271
+ client.notifyOnDelete(sourceName, partition, Integer.toString(id));
272
+ }
273
+
274
+ @Transactional
275
+ public List<Contact> listContact() {
276
+ return contactDAO.listContact();
277
+ }
278
+
279
+ @Transactional
280
+ public Contact getContact(Integer id) {
281
+ return contactDAO.getContact(id);
282
+ }
283
+ }
284
+
285
+ For more information about RhoConnect partitions, please refer to the [RhoConnect docs](http://docs.rhomobile.com/rhosync/source-adapters#data-partitioning).
286
+
287
+
288
+ ## Meta
289
+ Created and maintained by Alexander Babichev.
290
+
291
+ Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
data/doc/rest-api.txt CHANGED
@@ -337,6 +337,7 @@ Return content of a given document (client or source).
337
337
 
338
338
  ### `set_db_doc`
339
339
  Sets the content of the specified server document. Data should be either a string or hash of hashes. Data type should be set accordingly.
340
+ If `append` flag is set to `true` , the data is appended to the current doc (if it exists) instead of replacing it.
340
341
 
341
342
  :::ruby
342
343
  RestClient.post(
@@ -345,7 +346,8 @@ Sets the content of the specified server document. Data should be either a strin
345
346
  :api_token => token,
346
347
  :doc => doc,
347
348
  :data => data,
348
- :data_type => data_type
349
+ :data_type => data_type,
350
+ :append => false
349
351
  }.to_json,
350
352
  :content_type => :json
351
353
  )