AI2 Component  (Version nb184)
TinyWebDB.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
26 
27 import android.os.Handler;
28 
29 import org.apache.http.NameValuePair;
30 import org.apache.http.message.BasicNameValuePair;
31 import org.json.JSONArray;
32 import org.json.JSONException;
33 
34 // When the component is installed in App Inventor, the Javadoc
35 // comments will become included in the automatically-generated system
36 // documentation, except for lines starting with tags (such as @author).
55 // The annotations here provide information to the compiler about
56 // integrating the component into App Inventor system. The following
57 // three annotations stipulate that TinyWeb DB will appear in the
58 // designer, that it will be an object in the App Inventor language,
59 // and say what Android system permissions it requires.
60 //
61 
62 
63 @DesignerComponent(version = YaVersion.TINYWEBDB_COMPONENT_VERSION,
64  description = "Non-visible component that communicates with a Web service to store and " +
65  "retrieve information.",
66  category = ComponentCategory.STORAGE,
67  nonVisible = true,
68  iconName = "images/tinyWebDB.png")
69 @SimpleObject
70 @UsesPermissions(permissionNames = "android.permission.INTERNET")
71 public class TinyWebDB extends AndroidNonvisibleComponent implements Component {
72 
73  private static final String LOG_TAG = "TinyWebDB";
74  private static final String STOREAVALUE_COMMAND = "storeavalue";
75  private static final String TAG_PARAMETER = "tag";
76  private static final String VALUE_PARAMETER = "value";
77  private static final String GETVALUE_COMMAND = "getvalue";
78 
79  private String serviceURL;
80  private Handler androidUIHandler;
81 
87  public TinyWebDB(ComponentContainer container) {
88  super(container.$form());
89  // We use androidUIHandler when we set up operations (like
90  // postStoreVaue and getStoreValue) that run asynchronously in a
91  // separate thread, but which themselves want to cause actions
92  // back in the UI thread. They do this by posting those actions
93  // to androidUIHandler.
94  androidUIHandler = new Handler();
95  // We set the initial value of serviceURL to be the
96  // demo Web service.
97  serviceURL = "http://tinywebdb.appinventor.mit.edu/";
98  }
99 
100  // The two procedures below give the getter and setter for the
101  // TinyWebDB component's ServiceURL property. Each one has
102  // a @SimpleProperty annotation to indicate that it's a property in
103  // the language (and blocks will be generated for it). The setter
104  // also has a @DesignerProperty that makes this property appear in the
105  // Properties listed with the component in the designer. Here we've
106  // stipulated that the property should appear with a default value:
107  // the URL of the App Inv Tiny DB demonstration Web service. Note
108  // that this default specifies what should be shown in the designer:
109  // it does not automatically set the value of ServiceURL by itself,
110  // which is why we explicitly set the variable serviceURL above
111  // where the component is created.
112 
117  category = PropertyCategory.BEHAVIOR,
118  description = "The URL of the web service database.")
119  public String ServiceURL() {
120  return serviceURL;
121  }
122 
128  defaultValue = "http://tinywebdb.appinventor.mit.edu")
130  public void ServiceURL(String url) {
131  serviceURL = url;
132  }
133 
134  // StoreValue (and GetValue below) show how use the
135  // event-driven style we recommend for operations that communicate
136  // over the Web. For each operation, there's (a) The function the
137  // user calls (e.g., StoreValue); (b) A non-user visible function
138  // that that runs asynchronously to do the actual communication,
139  // wait for the result, and signal an event when the result is
140  // obtained; (c) the event handler for that result (e.g., ValueStored)
141 
142  // Here's part (a): The component function itself. All it does is arrange
143  // for part (b) to run in a separate thread.
144 
153  @SimpleFunction(description = "Asks the Web service to store the given value under the given tag")
154  // The @SimpleFunction annotation arranges for this to be a
155  // function (StoreValue) associated with the component.
156  public void StoreValue(final String tag, final Object valueToStore) {
157  final Runnable call = new Runnable() {
158  public void run() { postStoreValue(tag, valueToStore); }};
160  }
161 
162  // Here's part (b): The actual communication, which runs
163  // asynchronously. It uses postCommand, from the WebServiceUtil
164  // library. PostCommand here takes four arguments: (1) The URL of
165  // the Web service; (2) The name of the command to be posted to the
166  // Web service; (3) parameters for the command; (4) an
167  // AsyncCallbackPair, which specifies an onSuccess callback and an
168  // onFailure callback.
169 
170  // The onSuccess callback is called with the response from the Web
171  // server. Here, for postStoreValue, we ignore the response, and
172  // simply signal a ValueStored event.
173 
174  // The onFailure callback is called with an error message. It calls
175  // WebServiceError, which will signal a WebServiceError event for the
176  // application.
177 
178  private void postStoreValue(String tag, Object valueToStore) {
179  // The commented-out Log.w command writes a message to the
180  // AppInventor Web server log. It's useful to include these
181  // commands to aid in debugging while the component is being
182  // developed, and then commenting them out when the component is
183  // deployed.
184  // Log.w(LOG_TAG, "postStoreValue: sending tag = " +
185  // tag + " and value = " + valueToStore);
186  // Here we define the AsyncCallbackPair, myCallback.
188  public void onSuccess(String response) {
189  // the result here will be the JSON-encoded list ["STORED", tag, value]
190  // but the component ignores this
191  // Log.w(LOG_TAG, "postStoreValue: got result " + result);
192  androidUIHandler.post(new Runnable() {
193  public void run() {
194  // Signal an event to indicate that the value was
195  // stored. We post this to run in the Applcation's main
196  // UI thread, rather than in the separate thread where
197  // postStoreValue is running.
198  ValueStored();
199  }
200  });
201  }
202  public void onFailure(final String message) {
203  // Pass any failure message from the Web service command back
204  // to the error handler.
205  androidUIHandler.post(new Runnable() {
206  public void run() {
207  WebServiceError(message);
208  }
209  });
210  }
211  };
212  try {
213  WebServiceUtil.getInstance().postCommand(serviceURL,
214  STOREAVALUE_COMMAND,
215  Lists.<NameValuePair>newArrayList(
216  new BasicNameValuePair(TAG_PARAMETER, tag),
217  new BasicNameValuePair(VALUE_PARAMETER,
218  JsonUtil.getJsonRepresentation(valueToStore))),
219  myCallback);
220  } catch (JSONException e) {
221  throw new YailRuntimeError("Value failed to convert to JSON.", "JSON Creation Error.");
222  }
223  }
224 
225  // Here's part (c): The event that gets signaled when the Web service
226  // replies that the store command succeeded. The application writer
227  // might want to specify a handler for this event, perhaps to show
228  // a confirmation to the end user.
229 
233  @SimpleEvent(description = "Event indicating that a StoreValue server request has succeeded.")
234  public void ValueStored() {
235  // invoke the application's "ValueStored" event handler.
236  EventDispatcher.dispatchEvent(this, "ValueStored");
237  }
238 
239  // The implementation of GetValue uses the same three-procedure
240  // event-driven strategy as for StoreVale. The main difference is
241  // that where StoreVale uses postCommand, GetValue uses
242  // postCommandReturningArray, which expects a JSON-encoded array as
243  // the respose from the Web service. For the Web service we're
244  // using the response should be the two-element array ["VALUE", value] (i.e.,
245  // the actual value tagged with "VALUE").
246 
247  // The onSuccess callback checks the response and signals an error
248  // if it was null. Otherwise it returns the second element of the
249  // response (i.e., the value). It also arranges to catch a JSON exception in
250  // case the result coming back from the service was garbled.
251 
252  // The onFailure callback signals a WebServiceError, just as with
253  // StoreValue.
254 
263  @SimpleFunction(description = "Sends a request to the Web service to get the value stored under "
264  + "the given tag. The Web service must decide what to return if there is no value stored "
265  + "under the tag. This component accepts whatever is returned.")
266  public void GetValue(final String tag) {
267  final Runnable call = new Runnable() { public void run() { postGetValue(tag); }};
269  }
270 
271  private void postGetValue(final String tag) {
272  // Log.w(LOG_TAG, "postGetValue: sending tag = " + tag);
274  public void onSuccess(JSONArray result) {
275  if (result == null) {
276  // Signal a Web error event to indicate that there was no response
277  // to this request for a value.
278  androidUIHandler.post(new Runnable() {
279  public void run() {
280  WebServiceError("The Web server did not respond to the get value request " +
281  "for the tag " + tag + ".");
282  }
283  });
284  return;
285  } else {
286  try {
287  // Log.w(LOG_TAG, "postGetValue: got result " + result);
288  // The Web service is designed to return the JSON encoded list ["VALUE", tag, value]
289  final String tagFromWebDB = result.getString(1);
290  String value = result.getString(2);
291  // If there's no entry with tag as a key then return the empty string.
292  final Object valueFromWebDB = (value.length() == 0) ? "" :
293  JsonUtil.getObjectFromJson(value, true);
294  androidUIHandler.post(new Runnable() {
295  public void run() {
296  // signal an event to indicate that a good value was returned. Note
297  // that the event handler takes the value as an argument.
298  GotValue(tagFromWebDB, valueFromWebDB);
299  }
300  });
301  } catch (JSONException e) {
302  // Signal a Web error event to indicate the the server
303  // returned a garbled value. From the user's perspective, there may be no practical
304  // difference between this and the "no response" error above, but application
305  // writers can create handlers to use these events as they choose.
306  androidUIHandler.post(new Runnable() {
307  public void run() {
308  WebServiceError("The Web server returned a garbled value " +
309  "for the tag " + tag + ".");
310  }
311  });
312  return;
313  }
314  }
315  }
316  public void onFailure(final String message) {
317  // Signal a Web error event to indicate that there was no response
318  // to this request for a value. Note that this needs to be posted to the UI
319  // thread to avoid a subsequent UI event causing an exception.
320  androidUIHandler.post(new Runnable() {
321  public void run() {
322  WebServiceError(message);
323  }
324  });
325  return;
326  }
327  };
328  WebServiceUtil.getInstance().postCommandReturningArray(
329  serviceURL,
330  GETVALUE_COMMAND,
331  Lists.<NameValuePair>newArrayList(new BasicNameValuePair(TAG_PARAMETER, tag)),
332  myCallback);
333  return;
334  }
335 
342  @SimpleEvent(description = "Indicates that a GetValue server request has succeeded.")
343  public void GotValue(String tagFromWebDB, Object valueFromWebDB) {
344  // Invoke the application's "GotValue" event handler
345  EventDispatcher.dispatchEvent(this, "GotValue", tagFromWebDB, valueFromWebDB);
346  }
347 
353  @SimpleEvent
354  public void WebServiceError(String message) {
355  // Invoke the application's "WebServiceError" event handler
356  // Log.w(LOG_TAG, "calling error event handler: " + message);
357  EventDispatcher.dispatchEvent(this, "WebServiceError", message);
358  }
359 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.TinyWebDB.TinyWebDB
TinyWebDB(ComponentContainer container)
Definition: TinyWebDB.java:87
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components
com.google.appinventor.components.runtime.TinyWebDB.WebServiceError
void WebServiceError(String message)
Definition: TinyWebDB.java:354
com.google.appinventor.components.runtime.TinyWebDB
Definition: TinyWebDB.java:71
com.google.appinventor.components.runtime.util.JsonUtil
Definition: JsonUtil.java:42
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.util.WebServiceUtil
Definition: WebServiceUtil.java:45
com.google.appinventor.components.runtime.collect
Definition: Lists.java:7
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.runtime.errors.YailRuntimeError
Definition: YailRuntimeError.java:14
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.util.AsynchUtil.runAsynchronously
static void runAsynchronously(final Runnable call)
Definition: AsynchUtil.java:23
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.collect.Lists
Definition: Lists.java:20
com.google.appinventor.components.runtime.TinyWebDB.ServiceURL
void ServiceURL(String url)
Definition: TinyWebDB.java:130
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.runtime.util.AsynchUtil
Definition: AsynchUtil.java:17
com.google
com
com.google.appinventor.components.runtime.errors
Definition: ArrayIndexOutOfBoundsError.java:7
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor.components.runtime.util.AsyncCallbackPair
Definition: AsyncCallbackPair.java:17
com.google.appinventor