1. REST Drupal
Talking to Drupal from Javascript, iOS and Android
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 1
Sunday, November 18, 12
2. About me I pity the fool
who doesn’t use
Drupal
Hi, I’m Alexandru Badiu.
I’m a software engineer, amateur game
developer and part time Mister T
impersonator.
Drupal user for 9 years, board member in
Drupal Romania.
I work for Demotix.
Twitter @voidberg
Web ctrlz.ro
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 2
Sunday, November 18, 12
3. REST Drupal
Mobile is pretty big now days
Drupal 8 Web services initiative
For Drupal 7 there’s the Services module
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 3
Sunday, November 18, 12
4. YASS
Oh boy, yet another Services session!
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 4
Sunday, November 18, 12
5. Nope
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 5
Sunday, November 18, 12
6. In this session
Doing “web services” without Services
Doing “web services” without Drupal’s slowness
Using Services
... With Javascript
... With Objective-C
... With Java
Uploading files and cursing at Apple and Google
Some memes
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 6
Sunday, November 18, 12
7. Sorry :(
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 7
Sunday, November 18, 12
8. 1
Services without
services
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 8
Sunday, November 18, 12
9. Why would you do that?
One word: Drupal is slow
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 9
Sunday, November 18, 12
10. Services without services
Sometimes you really don’t need services
Getting a list of news and showing some details
about them.
Showing nearby businesses on a map.
Users submitting anonymous tips or comments.
Use JSON and don’t shoot yourself in the foot
by using XML or plists.
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 10
Sunday, November 18, 12
11. However
Services is not the slowest thing, Drupal is
Shameless plug: Drupal NoBootstrap
Small php library which allows you to be able to use some of
Drupal's functions without bootstrapping Drupal.
Used it for Solr powered instant Google like map of businesses
Used it for lightning fast autocomplete
Used it for coupon claiming app
https://github.com/voidberg/Drupal-NoBootstrap
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 11
Sunday, November 18, 12
12. 2
Services
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 12
Sunday, November 18, 12
13. Services
A long time ago in a Drupalcamp far, far away
Last year I was complaining about Services
Clunky way of connecting (session, nonce, timestamp)
Inconsistent responses
Bugs in json_server
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 13
Sunday, November 18, 12
14. Services 3
Services 3 is much much nicer
REST based
No more nonce and other crap
Session is kept in the HTTP calls
Consistent responses
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 14
Sunday, November 18, 12
15. Services 3
REST in a nutshell
Resource based
HTTP based
CRUD
GET /endpoint/resource/id
POST /endpoint/resource + (post data)
DELETE /endpoint/resource/id
PUT /endpoint/resource/id + (post data)
GET /endpoint/resource
GET /services/comment?parameters[nid]=123¶meters[timestamp]=123456:123600
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 15
Sunday, November 18, 12
16. Services 3
REST in a nutshell
Actions
Do not target a specific resource
POST /services/apachesolr/reindex
Targeted actions
Target a specific resource
POST /services/node/123/unpublish
Relationships
GET /services/node/123/comments
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 16
Sunday, November 18, 12
17. 3
Drupanium
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 17
Sunday, November 18, 12
18. Drupanium
Distribution targeted at building mobile apps
For Titanium Studio
Should be possible to use with Cordova
Looks abandoned
Simple js code to grab
Decent list of core services resources
http://drupanium.org/api
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 18
Sunday, November 18, 12
19. 4
iOS
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 19
Sunday, November 18, 12
20. Android
Drupal iOS SDK
The library formerly known as KBDrupalConnect
Uses AFNetworking
Implements all core service resources
Can use OAuth
Uses blocks
https://github.com/workhabitinc/drupal-ios-sdk
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 20
Sunday, November 18, 12
21. Drupal iOS SDK
Copy AFNetworking code and Drupal iOS SDK code in project
Update Settings.m with url, endpoint etc
Profit!
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 21
Sunday, November 18, 12
27. File upload
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 27
Sunday, November 18, 12
28. File upload
AFNetworking uses the iOS API for http calls
The API has a bug since iOS 3
When uploading files on 3G you get a OOM error
Solution is to throttle the upload which the API has no support for
AFNetworking is still working on this
I ended up using ASIHttpRequest just for file upload with throttled
upload on 3G
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 28
Sunday, November 18, 12
30. 5
Android
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 30
Sunday, November 18, 12
31. Android
DrupalCloud is not working anymore
Hasn’t been updated to Services 3
Never really liked the architecture
Dandy might work, I was scared
No documentation at all
Source code seems over engineered
Not sure it works with Services 3 (issue queue says it doesn’t)
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 31
Sunday, November 18, 12
32. Android
So I built my own
Android Drupal SDK
https://github.com/voidberg/
Android-Drupal-Sdk
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 32
Sunday, November 18, 12
33. First step
Let’s choose a HTTP Client
It’s a mess
HttpUrlConnection
Been since the dark ages
Had some serious bugs before Froyo
Still behind the apache lib
Apache HTTP Client
Best option so far IMHO
Has been deprecated
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 33
Sunday, November 18, 12
34. We have a winner
Android Asynchronous Http Client
http://loopj.com/android-async-http/
You’ll be in select company - Instagram
HTTP requests happen outside the UI thread
Requests use a threadpool to cap concurrent resource usage
Multipart file uploads with no additional third party libraries
Automatic smart request retries optimized for spotty mobile connections
Automatic gzip response decoding support for super-fast requests
Built-in response parsing into JSON with JsonHttpResponseHandler
Persistent cookie store, saves cookies into your app’s SharedPreferences
Uses the Apache HTTP Client
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 34
Sunday, November 18, 12
35. Using it
ServicesClient client;
client = new ServicesClient("http://www.example.com", "api/
mobile");
client.setBasicAuth("username","password/token");
PersistentCookieStore cookieStore;
cookieStore = new PersistentCookieStore(this);
client.setCookieStore(cookieStore);
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 35
Sunday, November 18, 12
36. Using it
SystemServices ss;
ss = new SystemServices(client);
JsonHttpResponseHandler connectHandler = new JsonHttpResponseHandler() {
@Override
public void onSuccess(JSONObject response) {
JSONObject user = response.getJSONObject("user");
int uid = user.getInt("uid");
}
@Override
public void onFailure(Throwable e, JSONObject response) {
// System.Connect call failed
}
@Override
public void onFinish() {
}
};
ss.Connect(connectHandler);
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 36
Sunday, November 18, 12
37. Using it
UserServices us;
us = new UserServices(client);
JsonHttpResponseHandler loginHandler = new JsonHttpResponseHandler() {
@Override
public void onSuccess(JSONObject response) {
boolean error = false;
JSONObject user = response.getJSONObject("user");
}
@Override
public void onFailure(Throwable e, JSONObject response) {
// Username or password were incorrect
}
@Override
public void onFinish() {
activity.hideProgressDialog();
}
};
activity.showProgressDialog("Logging you in");
us.Login("username", "password");
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 37
Sunday, November 18, 12
38. Extending it
public class DemotixServices {
private ServicesClient client;
public DemotixServices(ServicesClient c) {
client = c;
}
public void Register(String fname, String lname, String name, String email, String
password, AsyncHttpResponseHandler responseHandler) {
JSONObject params = new JSONObject();
try {
params.put("name", name);
params.put("mail", email);
params.put("pass", password);
params.put("field_firstname", fname);
params.put("field_lastname", lname);
params.put("legal_accept", "1");
params.put("from_mobile", "1");
} catch (JSONException e) {
e.printStackTrace();
}
client.post("demotix_services_user/register", params, responseHandler);
}
}
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 38
Sunday, November 18, 12
39. File upload
InputStream myInputStream = blah;
RequestParams params = new RequestParams();
params.put("secret_passwords", myInputStream, "passwords.txt");
File myFile = new File("/path/to/file.png");
RequestParams params = new RequestParams();
try {
params.put("profile_picture", myFile);
} catch(FileNotFoundException e) {}
client.post(...)
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 39
Sunday, November 18, 12
40. File upload
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 40
Sunday, November 18, 12
41. File upload
Turns out that the library tries to read all data before uploading
For large files this is a problem as you’ll run out of memory
immediately
Solution: go low level and build the multi part yourself by
attaching FileInputStreams
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 41
Sunday, November 18, 12
42. File upload
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 900 * 1000);
HttpConnectionParams.setSoTimeout(httpParams, 900 * 1000);
HttpClient httpclient = ServicesClient.client.getHttpClient();
HttpContext httpContext = ServicesClient.client.getHttpContext();
HttpPost httppost = new HttpPost("http://www.example.com/api/mobile/service/upload");
httppost.setParams(httpParams);
MultipartEntity entity = new MultipartEntity();
// Add parameters to the post body
entity.addPart("param1", new StringBody(param1.toString(),"application/json",
Charset.forName("UTF-8")));
// Add file to post body
InputStream istream1 = new FileInputStream("file1.jpg");
entity.addPart("file1", new InputStreamBody(istream1, "file1"));
httppost.setEntity(entity);
HttpResponse response = httpclient.execute(httppost, httpContext);
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 42
Sunday, November 18, 12
43. File upload
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 43
Sunday, November 18, 12
44. File upload
Nope
There is no easy way of getting the progress of an upload
We fake it: use CustomMultiPartEntity instead of MultiPartEntity
This class notifies a listener of the bytes read so far
Calculate an approximation of the size of the post data
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 44
Sunday, November 18, 12
45. File upload
CustomMultiPartEntity postEntity = new CustomMultiPartEntity(new
CustomMultiPartEntity.ProgressListener() {
@Override
public void transferred(long num) {
updateUploadProgress((int) ((num / (float) totalUploadSize) * 100));
}
});
try {
postEntity.addPart("param1", new StringBody(param1.toString(),"application/json",
Charset.forName("UTF-8")));
totalUploadSize = postEntity.getContentLength();
for (Map.Entry<String, String> entry : files.entrySet()) {
try {
InputStream istream = new FileInputStream(entry.getValue());
postEntity.addPart(entry.getKey(), new InputStreamBody(istream,
entry.getKey()));
totalUploadSize += new File(entry.getValue()).length();
} catch (FileNotFoundException e) {}
}
httppost.setEntity(postEntity);
}
catch (UnsupportedEncodingException e) {}
HttpResponse response = httpclient.execute(httppost, httpContext);
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 45
Sunday, November 18, 12
46. Another caveat
Varnish has issues by default
You have a rule that does “return post” for, well, POST calls
Upload will fail with a socket exception
Add another rule that does “return stream” for POST calls made
to your upload url
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 46
Sunday, November 18, 12
47. Thanks!
Questions?
Alexandru Badiu.
Twitter @voidberg
Web http://ctrlz.ro
Email andu@ctrlz.ro
D.O http://drupal.org/user/8662
Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 47
Sunday, November 18, 12