Saturday, June 14, 2014

Lecture 25 - The Cloud: Running on the Real Cloud

Lecture 25 - The Cloud: Running on the Real Cloud

We carry on discussing App Engine and discuss how to deploy your application to the Google App Engine (i.e. cloud).

What this lecture will teach you

  • Deploy your application to Google App Engine

Deploying to Google App Engine

There are two steps to deploy your application to the cloud: 1. create an application on the cloud, and 2. upload your project to the cloud. In what follows, we show you how to do these two steps.

Creating an Application

  1. Login to Google App Engine's Application Overview, click "Create Application".
  1. Input the "Application Identifier" and click "Check Availability" to make sure it is available. Input the "Application Title" in the following text box. Your application's url will be http://.appspot.com. In the example showing below, the url is http://cs65gcmdemo.appspot.com.
  1. After creating the application, you can access your application's dash board by clicking the "dashboard" link on the result page.

Deploying an Application

Before deploying your application, you need to make sure your project is compiled using Java 1.7. To do this, right click your project and click "Properties". Go to "Project Facets", and make sure the facet names "Java" is 1.7 and checked.
  1. In eclipse, right click your project and click "Properties". Go to "Google" -> "App Engine", input your application id and version as shown below. It is up to you to decide the version number.
  1. Right click your project, go to "Google" -> "Deploy to App Engine", a login window will open. Login to your Gmail account to proceed.
 
  1. After login, click "Deploy" in the next window to start deploying your application to Google App Engine.
A progress bar will be showing.
  1. After deploying the application, go to the dashboard. Go to "Versions", and make the version you just deployed default.
  1. You can access your application using the URL now.

Lecture 26 -- Publishing Apps

Lecture 26 -- Publishing Apps

Once you have design, coded and tested your Android app it's time to publish it on Google Play. In what follows, we discuss the necessary minimum steps to clean up your app, ready it for publishing and the ship it to the global marketplace. It is difficult to make a make a splash these days with over 800,000 apps on play but you could be the next mobile success so dream big.

Resources

The best resources for understanding the publication process are as follows:

Workflow

The figure below shows the complete workflow for an Android app from conception to publishing it on Play. We've skipped a number of important phases when developing MyRuns -- we've not done functional or unit testing of any kind -- that's bad. We haven't fully tested all the edge cases. Setting that aside for the moment an app as a best practice, your application should meet all of your release criteria for functionality, performance, and stability before you begin the publishing process.
In what follows, we describe an abbreviated set of steps to release your app; these are:
  • Step 1: Preparing your app for release
  • Step 2: Signing your app
  • Step 3: Uploading your app to play

Step 1: Preparing your app for release

We first discuss the package name. Then you need to make sure all debug information in your code is turned off or removed. Finally, if your app has a cloud side you need to prepare the server side and test with the phone app.

Choose a good package name

Over the past 7 weeks we have all used the same set of package names to reflect the incremental MyRuns app; edu.dartmouth.cs.myruns1, etc. The problem is everyone has used the same package name and that won't work at app distribution time. This is acceptable in the debug phase of application development but when you are going to release the app, you have to use a unique package name for Google to identify your app. That means you can't use "edu.dartmouth.cs.myruns5" as your package name to release on Play. You have to use another package name that is unique and hence avoids name conflicts. That's probably a reason why people use domain name to be their package name. Make sure you choose a package name that is suitable over the life of your application. You cannot change the package name after you distribute your application to users. As you know you set the package name in application's manifest file.

Turn off all logging and debugging in your code

Make sure you deactivate logging and disable the debugging option (you haven't used the debugging option) before you build your application for release. You can deactivate logging by removing calls to Log methods in your source files. You can disable debugging by removing the android:debuggable attribute from the tag in your manifest file, or by setting the android:debuggable attribute to false in your manifest file. Also, make sure you remove any log files or static test files that were created in your project for debugging purposes.

Clean up the project directories

Clean up your project directories. Clean up your project and make sure it conforms to the directory structure described in Android Projects. At a minimum you should do the following cleanup tasks -- check all these in ProjectExplorer, as shown below:
  • Review the contents of your lib/ and src/ directories. The src/directory should contain only the source files for your application (.java and .aidl files). The src/ directory should not contain any .jar files.
  • Look in your project's res/ directory for old drawable files, layout files, and values files that you are no longer using and delete them.
  • Check your lib/ directory for test libraries and remove them if they are no longer being used by your application.
  • Review the contents of your assets/ directory and your res/raw/ directory for raw asset files and static files that you need to update or remove prior to release.

Manifest settings

Review and update your manifest settings. Verify that the following manifest items are set correctly:
  • <uses-permission> element: You should specify only those permissions that are relevant and required for your application.
  • android:icon and android:label attributes: You must specify values for these attributes, which are located in the element.
  • android:versionCode and android:versionName attributes.
   android:versionCode="1"
   android:versionName="1.0" 
We recommend that you specify values for these attributes, which are located in the element. For more information see Versioning your Application.
There are several additional manifest elements that you can set if you are releasing your application on Google Play. For example, the android:minSdkVersion and android:targetSdkVersion attributes, which are located in the element.
    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

Update URLs for servers and services

If you are using a server in your team project (we use servers in MyRun6), you need to change the server address to the one that you tend to use for the public. Implement Licensing (if you are releasing on Google Play). If you are releasing a paid application through Google Play, consider adding support for Google Play Licensing. Licensing lets you control access to your application based on whether the current user has purchased it. Using Google Play Licensing is optional even if you are releasing your app through Google Play.

Step 2: Signing your app

The Android system requires that all installed applications are digitally signed with a certificate whose private key is held by the application's developer. While debugging, the build tools sign your application automatically with a special debug key that is created by the Android SDK build tools. When you are ready to release your application to users, you must sign it with a suitable private key on your own. You cannot publish an application that is signed with the debug key generated by the SDK tools.
If you are using Eclipse with the ADT plugin, you need to do the following in oder to sign your application.
  1. Select the project in the Package Explorer and select File > Export. Click Export Android Application.
  1. Open the Android folder, select Export Android Application, and click Next
The Export Android Application wizard now starts, which will guide you through the process of signing your application. Because you have not created a key before, you have got to create it.
  1. Choose a destination to save the release version of the apk. Complete the Export Wizard and your application will be compiled, signed, aligned, and ready for distribution.

Uploading your app to play

If you have gone through the steps above, you should have a release-ready version of apk for the public now. You can release it on website, email or Google Play store. You can simply email the apk to people to install -- not Play store needed for that. You can offer the app from a website; or best, upload to Play and complete the workflow. We will discuss the website and Play options below.
Releasing through a website. If you do not want to release your app on a marketplace like Google Play, you can make the app available for download on your own website or server, including on a private or enterprise server. To do this, you must first prepare your application for release in the normal way. Then all you need to do is host the release-ready APK file on your website and provide a download link to users.
When users browse to the download link from their Android-powered devices, the file is downloaded and Android system automatically starts installing it on the device. However, the installation process will start automatically only if the user has configured their settings on your phone to allow the installation of apps from unknown sources.
Although it is relatively easy to release your application on your own website, it can be inefficient. For example, if you want to monetize your application you will have to process and track all financial transactions yourself and you will not be able to use Google Play's In-app Billing service to sell in-app products. In addition, you will not be able to use the Licensing service to help prevent unauthorized installation and use of your application.
Releasing through an App Marketplace. If you want to distribute your apps to the broadest possible audience, releasing through an app marketplace such as Google Play is ideal.
The steps are as follows:
  1. Go to the publish page
  2. Read the agreement, review the distribution of countries where you can distribute and sell applications -- you need a credit card and $25.00. For this class we will publish your project apps through our internal account.
  1. Now you can add your first application to the Play store. Before publishing you need to upload the signed apk and add some descriptions in the store listing section to your app.
You have to fill the requested information in the store listing before publish it. Things you need to specify include:
  • text description of the app and how great it is
  • graphic asset (one high resolution icon and at least two screenshots)
  • categorization (e.g., social nets, games)
  • developer's contact details, etc.

You are done! You are Android

That is it. You are done. Done with the taught part of the course. You have completed the following and should be proud of your new skills. These are very marketable -- or your money back.
We have covered the complete workflow (minus testing) for the MyRuns app -- soup to nuts.
Next the project phase. You will build an amazing app that uses Bio data and release it via Play.

Lecture 25 - The Cloud: Google Cloud Messaging for Android (GCM)

Lecture 25 - The Cloud: Google Cloud Messaging for Android (GCM)

We carry on discussing App Engine and support for building cloud services.

What this lecture will teach you

  • Using HttpURLConnection to make Android interact with App Engine web servers
  • Google Cloud Messaging (GCM)
  • Sending messages from the cloud to Android using GCM

Resources

The key resource and the most up to date information can be viewed here: Google Cloud Messaging for Android.

Demo code

In this lecture we will use a demo that implements a simple demo app that comprises native android app on the phone and the cloud side service. The demo uses Google Cloud Messaging (GCM) between the client running on the phone and the cloud side. Download and import the Google Cloud Messaging Demo.
In MyRuns6 you need to store and query ExerciseEntry data using GCM. The demo code provides a framework for doing that lab. You need to add the app side code to MyRuns and build the server side out. The above demo code includes two ADT projects: GCMDemoServer -- the server side; and GCMDemoAndroid -- the Android side. Go through the example. Understand how it works. Now thinking about the requirements of MyRuns6.

Google Cloud Messaging Demo

Google Cloud Messaging (GCM) is the communications and data transfer glue between your Android app running on your phone and the backend cloud services that we discussed in the last lecture. It's the glue that makes the cloud work.
In what follows we discuss the architecture and services offered by GCM.
This demo shows how to send text from both Android App and browsers. Previous messages will be saved in datastore. When user submitted a post from browser, the server will send a message to registered devices, notifying them there are updates, then the App will update the post list from the server.
The following screen shots show the App posts a message to the server, then we can get that message from browser. We use browser to post a message, the app receive that message automatically.
  • Post a message from the phone:
  • After refreshing the browser, you can see the message just posted.
  • Post a message from the browser, the message will show on your phone automatically.

How the Demo Code works

The following diagram shows the main components of the system. In what follows, we briefly show how each component works together.
The server side consists 5 servlets, 1 jsp and a Datastore.
  1. query.do This servlet is used by browser to get all posts. It receives requests from user's browser, read posts data from the datastore, put the data in the request object, then redirects to main.jsp.
  2. main.jsp This jsp page generates HTML for all the posts retrieved by query.do servlet.
  3. get_history.do This servlet is used by the smartphone app to get all posts. Similar to query.do, it receives requests from user's browser first, then return all post records to the app in text format.
  4. post.do This servlet is used by both browser and smartphone app to post new messages (posts). It can determine the source of the request (i.e. smartphone app or browser). If the request is from the smartphone app, it returns, otherwise it redirects to sendmsg.do.
  5. sendmsg.do This servlet sends a message to smartphone app to notify that there are updates. It redirect to query.do after sending the message.
  6. register.do This servlet receives smartphone app's registration, and save the registration to the datastore.

Run the Demo Code

To run the demo code, you need the following things:
  1. Your "Project Number" and server API Key from the Google API Console
  2. Both your phone and computer are connected to the same WiFi network. Dartmouth Secure is a working one
  3. Your computer's IP Address and port (which is 8888)
  4. Important, make sure you start the server before starting your Android app side.
In what follows, we show you how to set up the environment, run the demo code and how to debug problems.

Obtain Your Computer's IP Address

If you use MAC, open the terminal, enter command "ifconfig". It lists all your network interfaces. You are only interested in WiFi, which will be listed as en1. Find the line starts with "inet", where you can find your IP address. The IP address of the example below is 10.31.236.168.
If you use Windows, go to command line, type "ipconfig", you can find your IP Address under "Wireless LAN adapter Wi-Fi".

GCMDemoServer

The API key is defined in edu.dartmouth.cs.gcmdemo.server.Globals. You need to change it to your own key, as shown below.
Open the server run configuration in Eclipse's "Run Configurations" dialog, add "-a 0.0.0.0" to "Program arguments" in "Arguments" tab.
If you get errors after importing the project, open the project's properties (from Project->Properties), go to Google->App Engine, check "Use specific SDK" to allow eclipse to refresh app engine libraries under your project directory.

GCMDemoAndroid

First, you need to make sure Google Play Services is correctly imported to your project.
Then, you need to add the complete URL (i.e., IP address: port address -- for example http://129.170.21.157:8888) to res/values/stings.xml as shown below. But you have to us the IP address from your own computer as discussed before. The port address will always be 8888. Recall that to make your demo work your laptop (which runs the server) and your Android phone have to be using the same WiFi networks (e.g., Dartmouth Secure). In summary, get your IP address, update the string server_adr in res/values/stings.xml. And make sure your devices are under the same WiFi network.
Finally, change SENDER_ID defined in edu.dartmouth.cs.gcmdemo.android.PostActivity to your Project Number.

Android app talking to a web server

In previous lecture, we use browser to visit the web server. However, the Android app needs to be able to connect to the server directly and exchange data. In this scenario, the app issue HTTP POST or GET request to the server, the server sends back the result to the app. The server and the app need to use the set of data format to exchange data. For example, the app can put the request's parameters in the request's header as parameters, and the server returns the data in JSON, or xml format.

Sending a HTTP request

A HTTP request contains a header and content. The header defines various parameters including method (i.e. POST/GET), host, requesting path, protocol and more, the request body contains the parameters when the request's method is POST.
To construct and send an HTTP request to a web server and receive response from that server in Android app, you can use a HttpURLConnection object. You can implement such method like this:
    public static String post(String endpoint, Map<String, String> params)
            throws IOException {
        URL url;
        try {
            url = new URL(endpoint);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + endpoint);
        }
        StringBuilder bodyBuilder = new StringBuilder();
        Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
        // 1. constructs the POST body using the parameters
        while (iterator.hasNext()) {
            Entry<String, String> param = iterator.next();
            bodyBuilder.append(param.getKey()).append('=')
                    .append(param.getValue());
            if (iterator.hasNext()) {
                bodyBuilder.append('&');
            }
        }
        String body = bodyBuilder.toString();
        byte[] bytes = body.getBytes();
        HttpURLConnection conn = null;
        try {
            // 2. open the HTTP connection
            conn = (HttpURLConnection) url.openConnection();
            // 3. set header parameters
            // 3.1 Sets the flag indicating this connection allows output
            conn.setDoOutput(true);
            // 3.2 Sets the flag indicating this connection does not use cache
            conn.setUseCaches(false);
            // 3.3 Sets the length of the body
            conn.setFixedLengthStreamingMode(bytes.length);
            // 3.4 Sets the method to POST
            conn.setRequestMethod("POST");
            // 3.5 Sets Content-Type
            conn.setRequestProperty("Content-Type",
                    "application/x-www-form-urlencoded;charset=UTF-8");
            // 4. post the request
            OutputStream out = conn.getOutputStream();
            out.write(bytes);
            out.close();

            // handle the response
            int status = conn.getResponseCode();
            if (status != 200) {
                throw new IOException("Post failed with error code " + status);
            }

            // Get Response
            InputStream is = conn.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            String line;
            StringBuffer response = new StringBuffer();
            while ((line = rd.readLine()) != null) {
                response.append(line);
                response.append('\n');
            }
            rd.close();
            return response.toString();

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
In the post method shown above, it takes URL and parameters stored in a Map as its input. It sends the HTTP request to the URL with all the parameters using POST, then return server's responses in string. To construct a request:
  1. converts all the parameter to a string in the format of "param1_name=param1_value&param2_name=param2_value", where param1_name and param2_name are the name of two parameters, param1_value and param2_value are the values for param1_name and param2_name.
  2. opens the HTTP connection
  3. sets header parameters including method, content-type, the length of the body etc.
  4. send the parameter string as the request's body using a OutputStream which is obtained from the connection object.
After sending the request, we can check the connection's status code. If the status code is "200", then we can read server's response from connection's InputStream.
Finally, remember to close the connection when you are done.
You can find some information on HTTP headers from here.

Examples from the Demo

This demo shows how to send text from both Android app and browsers. The app can post messages to the server, as well as pull all messages from the server. In what follows, we show you how to do both tasks.
The servlet for receiving messages on the server is mapped to "/post.do". It takes two parameters: "post_text" and "from". "post_text" is the content of the message and "from" indicates if the message is from an app or a browser. When the user send a message to the server, it starts an AsyncTask because network operations are not allowed on UI thread. post() method described in previous section is used to send the request. Since we don't care server's response, the retured string is ignored.
    private void postMsg(String msg) {
        new AsyncTask<String, Void, String>() {

            @Override
            protected String doInBackground(String... arg0) {
                String url = getString(R.string.server_addr) + "/post.do";
                String res = "";
                // construct the request parameters
                Map<String, String> params = new HashMap<String, String>();
                params.put("post_text", arg0[0]);
                params.put("from", "phone");
                try {
                    // send post request to the server
                    res = ServerUtilities.post(url, params);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                return res;
            }

            @Override
            protected void onPostExecute(String res) {
                mPostText.setText("");
                // retrieve all messages from the server
                refreshPostHistory();
            }

        }.execute(msg);
    }
After a message is sent to the server, the app refreshes its message list by retrieving all messages from the server. The server uses servlet get_history.do to handle this request. The app send a post request to the server, the server returns the result, and finally, the app shows the result in mHistoryText which is a TextView.
    private void refreshPostHistory() {
        new AsyncTask<Void, Void, String>() {

            @Override
            protected String doInBackground(Void... arg0) {
                String url = getString(R.string.server_addr)
                        + "/get_history.do";
                String res = "";
                Map<String, String> params = new HashMap<String, String>();
                try {
                    res = ServerUtilities.post(url, params);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                return res;
            }

            @Override
            protected void onPostExecute(String res) {
                if (!res.equals("")) {
                    mHistoryText.setText(res);
                }
            }

        }.execute();
    }

Google Cloud Messaging

Google Cloud Messaging for Android (GCM) is a service that allows you to send data from your server to your users' Android-powered device. This could be a lightweight message telling your app there is new data to be fetched from the server (for instance, a movie uploaded by a friend), or it could be a message containing up to 4kb of payload data (so apps like instant messaging can consume the message directly). (From Google Cloud Messaging for Android)

Setup Development Environment

The detailed process of creating a new Android application that uses the GCM requires several steps. In what follows we provide a more truncated set of steps with some screen dumps to help you along. In short, you should enable Google Cloud Messaging for Android in your Google API console, add Google Play Services to your app and set up required permissions.

Step 1 Enable GCM in your Google API console

  1. Go to Google API console, select the project you want to use, go to "APIs & auth" -> "APIs", turn the Google Cloud Messaging for Android toggle to ON.
  2. Go to "APIs & auth" -> "Credentials", create a new "Server key", input "0.0.0.0/0" to the "Accept requests from these server IP addresses" text box. This step allows any server to use the API key.
  3. Remember your server application API key, and "Project Number" which can be found by clicking "Overview" in the left column. Project Number will be used by the Android app and the API key will be used by the server.

Step 2 Specify settings in the Application Manifest

GCM is a part of Google Play Services. It requires to setup the following in the manifest to work properly:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 
     [Snip code]
         ........
         ........
   
     <permission
        android:name="edu.dartmouth.cs.whereami_5.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <permission android:name="com.example.gcm.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />

     [Snip code]
         ........
         ........
    <application ...>
        ........
        ........
         <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        ........
        ........
    </application>
</manifest>
com.example.gcm.permission.C2D_MESSAGE is a custom permission, in which com.example.gcm is your app's package name.

Write the Android Application

In what follows, we show you how to implement GCM in your Android app.

Register in Main Activity

The app need to register itself to GCM when it runs for the first time. It will get a regid from the GCM server (not from your own server) after it successfully registered. You need to save the regid to SharedPref. It does not need to register again next time it the app is already registered. You can check this by reading regid from SharedPref. In what follows, we discuss how to register to GCM step by step.
  1. Get a gcm instance and register using the instance. SENDER_ID is a String constant. Its value is your "Project Number".
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
regid = gcm.register(SENDER_ID);
  1. Store the regid to SharedPref.

    private void storeRegistrationId(Context context, String regId) {
        final SharedPreferences prefs = getGCMPreferences(context);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(PROPERTY_REG_ID, regId);
        editor.commit();
    }
  1. Send the regid to the backend. Your server needs to know the app's regid. It is used by the server as device identifier which uniquely identifies a client. In the example code, the server implemented a servlet called register, which take the regid as its only parameter, to handle registration.

Receive a message

To receive a message from GCM, you need to implement a GcmBroadcastReceiver. It is the mechanism GCM uses to deliver message. The following code is the implementation of such broadcast receiver. It starts an intent service called GcmIntentService to handle incoming messages.

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Explicitly specify that GcmIntentService will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
    }
}
You also need to define this broadcast receiver in manifest:

<application ......>
        ......
        <receiver
            android:name=".GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="edu.dartmouth.cs.gcmdemo.android" />
            </intent-filter>
        </receiver>
        ......
</application>
As mentioned in the previous step, you need to implement an intent service called GcmIntentService. In its onHandleIntent method, you need to handle various message types as shown below:
    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        // The getMessageType() intent parameter must be the intent you received
        // in your BroadcastReceiver.
        String messageType = gcm.getMessageType(intent);

        if (!extras.isEmpty()) {  // has effect of unparcelling Bundle
            /*
             * Filter messages based on message type. Since it is likely that GCM
             * will be extended in the future with new message types, just ignore
             * any message types you're not interested in, or that you don't
             * recognize.
             */
            if (GoogleCloudMessaging.
                    MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
                //handle send error in here
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_DELETED.equals(messageType)) {
                //handle delete message on server in here
            } else if (GoogleCloudMessaging.
                    MESSAGE_TYPE_MESSAGE.equals(messageType)) {
                // If it's a regular GCM message, do some work.
                String message = (String) extras.get("message");
                Intent i = new Intent();
                i.setAction("GCM_NOTIFY");
                i.putExtra("message", message);
                sendBroadcast(i);
            }
        }
        
        // Release the wake lock provided by the WakefulBroadcastReceiver.
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }
When it receives a message with type GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE, it sends a broadcast message. You can implement a broadcast receiver in your activity to receive and process the message.

Write the Server Application

Write Servlet to Handle Device Registrations

Since sending messages from server needs devices' registration ID, server needs to get regIDs from devices then store them in the datastore. You need to write servlets to handle devices' registration and unregistration requests. In the example code, we implement a register servlet. The servlet's doPost method firstly get the regid from the parameters, then save the regid to the datastore:
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException {
        String regId = req.getParameter(PARAMETER_REG_ID);

        if (regId != null && !regId.equals("")) {
            RegDatastore.register(regId);
        }
        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType("text/plain");
        resp.setContentLength(0);
    }
The register method in RegDatastore creates a datastore entity, then saves it to the datastore:
    public static void register(String regId) {
        Entity entity = new Entity(ENTITY_KIND_DEVICE, regId);
        entity.setProperty(DEVICE_REG_ID_PROPERTY, regId);
        mDatastore.put(entity);
    }

Sending Messages to a Device

To send a message to a device, the server need to find the corresponding regid first. If we want to send a message to all of our clients, we need to get all regids from the datastore:
    public static List<String> getDevices() {
        List<String> devices;

        Query query = new Query(ENTITY_KIND_DEVICE);
        Iterable<Entity> entities = mDatastore.prepare(query).asIterable();
        devices = new ArrayList<String>();
        for (Entity entity : entities) {
            String device = (String) entity.getProperty(DEVICE_REG_ID_PROPERTY);
            devices.add(device);
        }

        return devices;
    }
Then, the application server issues a POST request to https://android.googleapis.com/gcm/send. The format of the POST request is as follows:
Content-Type:application/json
Authorization:key=AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA

{ "collapse_key": "score_update",
  "time_to_live": 108,
  "delay_while_idle": true,
  "data": {
    "score": "4x8",
    "time": "15:16.2342"
  },
  "registration_ids":["4", "8", "15", "16", "23", "42"]
}
The first two lines are the request's headers. It specifies the content type to be JSON, and the API key to be the one you obtained from Google API console. The content is a JSON format structure. You can find detained specification from here. The GCM server responds with http status code 200 when success.

Troubleshooting

If the demo cannot run properly, you can follow the steps below to troubleshoot the problem:
  1. Can you open the website using your external IP?
To check this, open your browser, enter the URL which uses the external IP instead of the loopback IP (i.e. 127.0.0.1 or localhost). For exampleL http:// 10.31.238.27:8888. If it did not open, it means either you are not using the correct external IP address in the URL, or you have not set the IP parameter in the server's "Run Configurations"
  1. Can you post messages from the phone?
If you cannot post messages from the phone but can do so from the browser, it means your phone and your computer may connected to different WiFi networks that they cannot communicate directly. To fix this, you need to check both your phone's and your computer's WiFi connections.
There is another possibility is that your network firewall blocks incoming connections to your server. You can try to solve this problem by disabling the firewall.
  1. Can you receive messages when you post messages from the browser?
If you post a message from the browser and the app on your phone is open, but the app did not refresh and got the latest message, you need to check if you are using the correct "Project Number" "server API Key" pair: they must belong to the same project.
If it did not work, you can clear the app's data, as well as the server's data. To remove the datastore, stop the web server, go to war/WEB-INF/appengine-generated under the project directory, remove local_db.bin and datastore-indexes-auto.xml, restart the server.
  1. Datastore Management
App engine development server provides an admin console where you can view your data in Datastore. Open your browser and open "http://127.0.0.1/_ah/admin", we will see the interface of the console and you can view the data and update the data.
  1. When you run the server and native app you can view the console for the server and the logcat for the app. The console selection can be shown below by clicking on the terminal with upside down triangle next to it.
  1. You may unable to start the server because the port is using by other process. You can learn it from the console's output as shown below.
First thing you need to do is to check all your consoles to see if you have started another web app. As the example shown below, you can select the console 5, and terminate that server by clicking the stop button.
If you could not find the project, you can kill the process in the terminal:
$ lsof -i -P | grep 8888
java      15672  atc   65u  IPv6 0x512e2b17d9d3315d      0t0  TCP *:8888 (LISTEN)
$ kill 15672 
The first command tries to find which process is using the port 8888, which is the second column of the output (pid). Then you can use "kill" with the pid to terminate the process.

Lecture 23 - The Cloud: Google App Engine (GAE)

Lecture 23 - The Cloud: Google App Engine (GAE)

Before we can run a simple Google App Engine (GAE) application, we need to setup local GAE development environment. We will run GAE applications locally. You can also deploy your applications to GAE to provide services to users all over the world. GAE provides a general platform for cloud sides services where native Android apps such as MyRuns can build out the app framework to, for example, store runs in the cloud and allow other users to share data or allow the user to view the data through the web; for example, using a laptop based browser for better viewing and visualization. Getting to know how to use, GAE or App Engine for short, is the final tool for your Android toolkit -- note, there are many other components to Android that we have not have time to cover in detail. What we have done in the class is to focus on a core set of components and skill development that makes your new skills very marketable, or your money back.

What this lecture will teach you

  • Resources
  • Two demos
  • What is the App Engine?
  • App Engine features
  • Setting up the Google App Engine development environment
  • Creating a new web app
  • First App Engine project demo
  • App engine datastore

Resources - not much available

The problem with Google App Engine for teaching is that it's fluid. The course book has no information on the App Engine. The best up to date information is Google App Engine on the developers site. Much of the information we have gathered for these notes comes from the developers site and other related documentation -- cited by links in our notes.
A short tutorial on App Engine.

Demo code

Two demo projects are given out:
The first demo code: first App Engine project allows you to submit a string to web server; the server echo's the string back.
In the second demo code: google app engine project, ancestor queries are used for strong consistency. Data operations are defined in edu.dartmouth.cs.gae_sample.data.ContactDatastore.query_result.jsp is used for displaying query results. QueryServlet put query result list in request object's attribute, query_result.jsp use get the result list from the request object, then generate the result Web page. Data operation servlets(add, delete, update) will redirect browser to QueryServlet so that user can see the operation result immediately.
Don't worry that you don't understand the technical text in the demo description -- come back to it once you have read the notes and thought about the lecture.

What is Google App Engine

Mobile apps require cloud services for many different things: maintain a social network, gaming services, storage services, etc. Many mobile clients interact with these backend services (aka the cloud) through a set of APIs. Whenever you write server code and deploy a server there are a number of infrastructure and configuration issues that you need to take care of such as scaling, access, deploying server side code, protocol interaction between the clients and server.
Google App Engine is a Platform as a Service (PaaS) that manages many of these server side issues. It provides a number of APIs to allow you to deploy servers on the Google cloud and not worry about scaling and other server side issues. It offers different types of storage (e.g., Data Store -- which we will use in MyRuns6) and task management.
The App Engine allows you to interact with services via a web browser (as the demo code in these notes do) or via a native Android app such as MyRuns.
So the App Engine takes away many of the problems of hosting servers on the Internet and provides a set of tools to build scalable services. While the PR is that you just upload your application and your are ready to go -- it is not as simple as that. In these notes we discuss how to set up your ADT environment and set up a simple web application that uses a http based servlet that the client interacts.
Google App Engine also includes a simulation environment where you can deploy your application to a local cloud (that looks and feels like you have actually deployed to Google infrastructure). The local deployment is a good place to start to debug your client and server side of the application.
We will discuss how all the parts of a service are captured in the project folder in ADT and how they are deployed.
While the notes below are targeted to a thin slice through the App Engine you can find more out from the Google App Engine developers site.
There is little of no information in the course book. This is because the App Engine environment is still fluid. We have updated these notes from last year and we have checked they are current and work.

App Engine features

Take a look through the key features of the App Engine APIs and environment. We will use a limited set of these features in the class:
Others not included but important if you wanted to manage a scalable service are:

Setting up the Google App Engine Development Environment

To set up the GAE development environment, you need to firstly install the GAE Java SDK, then install Google Plugin for eclipse. Also, you also need to make sure your eclipse is using JDK instead of JRE if you want to develop JSP in your project.

Install the JDK

Google Plugin for eclipse, which is used to develop GAE project in eclipse, requires JDK 1.7. If your version of JDK is not 1.7, you can go to Java SE download site to download and install the latest version of JDK 1.7.
After you installed the JDK, you need to set it up in your eclipse. In eclipse, go to "ADT"->"Preferences", in the left column, find "Java" -> "Installed JREs", check if you have selected the latest JDK you installed.
If not, you need to add JDK to eclipse. To do this, click "Add..." button, select "Standard VM" in the following window and click "Next". Input the path to your JDK in "JRE Home" in the following window, as shown below. Click "Finish" when you are done, and remember to select the jdk you just added in the previous window.

Install the GAE Java SDK

  1. Go to GAE SDK download site, click "Google App Engine SDK for Java", download the package to your computer.
  1. Uncompress the file
  2. Remember the path to the uncompressed GAE SDK

Setup eclipse

Before setting up your eclipse (or ADT), you need to know the version of your install eclipse. Then use corresponding eclipse update site URL to install Google Plugin for Eclipse.
  1. Open eclipse, go to "ADT->About ADT" and then click on the Eclipse icon as shown below and you can see the version of Eclipse (4.3.1 in the example below)
the version is shown like the screenshot below.
  1. Go to [GAE plugin installation instruction](GAE SDK download site, you can find the eclipse update site URL list under "Update sites" section. In our example, since our eclipse's version is 4.3.1, we should "Google Plugin for Eclipse 4.3 (Kepler)". Yours could be different if you have a different version of Eclipse for example 4.2 you would use "Google Plugin for Eclipse 3.8/4.2 (Juno)".
So the Google Plugging version for 4.3 would be:
https://dl.google.com/eclipse/plugin/4.3
  1. Following this instruction to install the plugin. NOTE: You only need to install Google Plugin for Eclipse. DON'T update "Developer Tools"
  2. Go to "ADT"->"Preferences", in the left column, find "Google" -> "App Engine", click "Add..." as shown below
Input the path where you install the GAE SDK. Click OK to close the window.

Creating a new web app

To create a new web app, you need to make sure your eclipse's Java compiler compliance level is 1.7. Open eclipse preference by clicking "ADT"->"Preferences", go to "Java"->"Compiler", select "1.7" in "Compiler compliance level" as shown below.
In what follows, we show you how to create a web app project in eclipse called firstgaeprj.
  1. Click "File"->"New"->"Other...", find "Web Application Project" under "Google", click "Next >"
  1. Input "Project name" and "Package" in the following windows. In our example, the project name is firstgaeprj and the package is edu.dartmouth.cs.firstgaeprj. We don't need Google Web Toolkit, so we can uncheck "Use Google Web Toolkit". Click "Finish" when you are done.
  1. The project structure is shown below. It creates index.html in war, which is web page which users open when they visit the website. It is defined in segment in web.xml. A servlet is created in edu.dartmouth.cs.firstgaeprj called FirstgaeprjServlet. It is defined in web.xml and mapped to "/firstgaeprj".
The content of web.xml is shown below.
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
        <servlet>
                <servlet-name>Firstgaeprj</servlet-name>
                <servlet-class>edu.dartmouth.cs.firstgaeprj.FirstgaeprjServlet</servlet-class>
        </servlet>
        <servlet-mapping>
                <servlet-name>Firstgaeprj</servlet-name>
                <url-pattern>Firstgaeprj</url-pattern>
        </servlet-mapping>
        <welcome-file-list>
                <welcome-file>index.html</welcome-file>
        </welcome-file-list>
</web-app>
It is important that the servlet Firstgaeprj is embedded correctly in the index.html (or other html, servlet, jsp etc), with defined in < servlet-mapping>.
  1. Create a Servlet
To create a servlet, you need to create a subclass of javax.servlet.http.HttpServlet, then define the servlet in web.xml.
Note, when you a Web Application Project it will create a Servlet for you. However, in MyRuns you will have to create a number of other servlets so we show you how to create a new servlet here. If you chose to write all the code for the first demo project you could just use the servlet created when the project is created. Anyway, back to creating a new servlet:
  • Create servlet class You can specify the super class in "New Java Class" as follows.
Once you have created the servlet class the template will be in the src folder and look like the following:

package edu.dartmouth.cs.myfirstgae;

import javax.servlet.http.HttpServlet;

public class DemoServlet extends HttpServlet {

}
You need to implement doGet() method and doPost() method to handle both GET and POST requests (the demo code already has this done).
package edu.dartmouth.cs.firstgaeprj;

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DemoServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;

        public void doGet(HttpServletRequest req, HttpServletResponse resp)
                        throws IOException {
                resp.setContentType("text/plain");
                resp.getWriter().println("Hello, world");
        }

        public void doPost(HttpServletRequest req, HttpServletResponse resp)
                        throws IOException {
                resp.setContentType("text/plain");
                resp.getWriter().println("Hello, world");
        }
}
Then you have to define the servlet in web.xml. Open web.xml, define the servlet and its mapping as follows:
.....
        <servlet>
                <servlet-name>DemoServlet</servlet-name>
                <servlet-class>edu.dartmouth.cs.firstgaeprj.DemoServlet</servlet-class>
        </servlet>
        <servlet-mapping>
                <servlet-name>DemoServlet</servlet-name>
                <url-pattern>DemoServlet</url-pattern>
        </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
.....
To complete the project you would need to write the html file -- index.html is created by default. You can look at the html code in the first demo code below to see an example; it's in AppEngineServlet.html. In the demo code there is also a CCS file which we don't really need in the demo but it's there if you fancy being stylistic.
Note, after you started the server on your machine, you can access this servlet from URL: http://127.0.0.1:8888/DemoServlet

Project structure

The project structure is shown as follows:
The src folder holds the source code.
The META-INF is an internal Java meta directory that you will typically not update. See META-INF for more information.
There are a number of libraries folders in the project: App Engine SDK (not shown in the figure above) and the JRE (Java Runtime Environment).
There are a number of important files under the war (Web application ARchive) directory. The war: "is a JAR file used to distribute a collection of JavaServer Pages, Java Servlets, Java classes, XML files, tag libraries, static web pages (HTML and related files) and other resources that together constitute a web application". This is a bundle that contains all the files to run your web application.
There a couple of important files and in the war directory, in particular the WEB-INF directory includes: appengine-web.xml and web.xml
The web.xml defines the structure of the web application for example declares servlets and servlet mappings (URLs to servlet), filters,
As stated "If the web application is only serving JSP ( JavaServer Pages) files, the web.xml file is not strictly necessary. If the web application uses servlets (ours does), then the servlet container uses web.xml to ascertain to which servlet a URL request will be routed". This is the case in our simple demo. "The web.xml is also used to define context variables which can be referenced within the servlets and it is used to define environmental dependencies which the deployer is expected to set up".
The appengine-web.xml "includes the registered ID of your application (Eclipse creates this with an empty ID for you to fill in later), the version number of your application, and lists of files that ought to be treated as static files (such as images and CSS) and resource files (such as JSPs and other application data).” (GAE document)

First App Engine project demo

Download the code for a simple demo called first App Engine project

Import and run the app

Import the project into eclipse just like importing a general project, as shown below.
To start the server, right click the project, click "Run As" -> "Web Application".
You will see a lot of log showing in the console window. When it says "INFO: Dev App Server is now running", you can open "127.0.0.1:8888" from your web browser to view the web application. If you want to stop the server, click the stop button shown in the screenshot below. Note, "http://localhost:8888" is the same as "http://127.0.0.1:8888" -- either will work to start your service. Also, make sure that you stop the executing servlet when done.
In this a simple web app that allows you to submit a string to web server; the server echo's the string back, as shown below.
After you type 'Hello!' into the text box (below) and clicked button "GET" you will see:

GET and POST

When you click the "GET" and "POST" buttons your browser will submit the text in the text box (to the left of the button) to the web server in either "GET" method or "POST" method. GET and POST are two commonly used methods for a request-response between a client and server. GET requests data from a specified resource -- read server. POST submits data to be processed to a specified resource -- read server. GET and POST are part of the HTTP protocol that make the web tick -- HTTP was invented, yes!, by and English man -- see HTTP Methods: GET vs. POST for more info.

Forms

The AppEngServlet.html file uses forms to set up the input buttons in the webpage, get user input and submit the form for processing by the servlet -- the server side form handler (i.e., AppEngServletDemo) as shown below. We look at the html and resulting URL below.
<!doctype html>

<html>
  <head>
    <title>Google App Engine Servlet Example</title>
  </head>
  <body>

    <h1>Google App Engine Servlet Example</h1>

    <form name="input" action="AppEngServletDemo" method="get">
        User Input: <input type="text" name="user_input">
        <input type="submit" value="GET">
    </form>
  
    <form name="input" action="AppEngServletDemo" method="post">
        User Input: <input type="text" name="user_input">
        <input type="submit" value="POST">
    </form>
    
  </body>
</html>
So for example, the first form corresponds to the GET button. It displays User Input: and expects text input associated with the variable name "user_input".
Once submitted when the user enters some text and clicks GET then the action is taken to do a HTTP GET on the servlet defined in the action (i.e., AppEngServletDemo). The “action” field of the form defines where to issue the POST/GET request:
This code tells the browser when the form is submitted, it should issue a GET request, defined by the method="get", to "AppEngServletDemo". It uses relative path in this case. If the web page locates at http://127.0.0.1/index.html, then it issues the request to http://127.0.0.1/AppEngServletDemo
If you enter the text "Hello" and click GET then the URL captures the complete user input and the servlet name that services the GET as shown below in the URL.

Java Servlet

Java Servlet technology provides Web developers with a simple, consistent mechanism for extending the functionality of a Web server. A servlet can almost be thought of as an applet that runs on the server side. A servlet is a Java class in Java EE that conforms to the Java servlet API, a protocol by which a Java class may respond to requests. Servlets could in principle communicate over any client–server protocol, but more often they are used with the HTTP protocol. Checkout Java Servlet Technology Overview and Java Servlet for more information on servlets.
A servlet needs to extend javax.servlet.http.HttpServlet, and override doGet() and doPost() methods which will handle GET requests and POST requests, respectively.
User requests can be retrieved from HttpServletRequest object. It contains anything that comes from the (user) client (i.e., browser, applications). You can add an intermediate result to it, then redirect the request to other servlets or JSPs. You can use the getParameter(String paramName) method to get parameters, and use setAttribute(String attrName, Object val) and getAttribute(String attrName) to set and get attributes from request object, respectively.

First App Engine project demo work flow

This project contains a static web page AppEngServlet.html and a servlet mapped to url /AppEngServletDemo. When the user visits the website, the server sends back AppEngServlet.html because it is the default welcome file defined in web.xml. The user can issue POST or GET request to /AppEngServletDemo through the webpage. The work flow is shown below.
To summarize the workflow:
  • the user loads the URL localhost:8888 into the browser and a static page (i.e., AppEngServlet.html) is loaded into the browser using HTTP from the localhost:8888 server page;
  • the user inputs some data (e.g., Hello) and clicks GET or POST;
  • the form's action invokes the servlet at AppEngServletDemo at localhost:8888;
  • the servlet builds a HTML in response which is the response to the original GET command; and finally,
  • the HTML is rendered inside the user's browser and displayed.

Java servlet implementation

Let's look at the implementation of a simple servlet:
public class AppEngServletDemo extends HttpServlet {
    private static final long serialVersionUID = 7224390555085474606L;

    //GET method
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException, ServletException {
        String str = req.getParameter("user_input");
        
        PrintWriter writer = resp.getWriter();
        writer.write("<html>\n");
        writer.write("<b>");
        writer.write("doGet(): " + str + ", Time: " + Calendar.getInstance().getTime().toString());
        writer.write("</b>");
        writer.write("</html>");
    }
    
    //POST method
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws IOException, ServletException {
        String str = req.getParameter("user_input");
        
        PrintWriter writer = resp.getWriter();
        writer.write("<html>\n");
        writer.write("<b>");
        writer.write("doPost(): " + str + ", Time: " + Calendar.getInstance().getTime().toString());
        writer.write("</b>");
        writer.write("</html>");
    }
}
If you want to forward the request to another servlet:
//url is the dest servlet's url
getServletContext().getRequestDispatcher(url).forward(req, resp);
If you want to tell the browser to redirect to another url:
resp.sendRedirect(url);

Servlet configuration

Servlets need to be declared in web.xml, as follows:
<!-- declare servlet -->
  <servlet>
    <servlet-name>AppEngServletDemo</servlet-name>
    <servlet-class>edu.dartmouth.cs.appengine.server.AppEngServletDemo</servlet-class>
  </servlet>
<!-- map servlet to urls-->  
  <servlet-mapping>
    <servlet-name>AppEngServletDemo</servlet-name>
    <url-pattern>/AppEngServletDemo</url-pattern>
  </servlet-mapping>

Second demo: Using App Engine Datastore

The demo code: google app engine project allows the client to store data using the App Engine datastore services. The App Engine Datastore provides a NoSQL schemaless object Datastore, with a query engine and atomic transactions. In the demo code, data operations are defined in edu.dartmouth.cs.gae_sample.data.ContactDatastore.query_result.jsp is used for displaying query results. QueryServlet put query result list in request object's attribute, query_result.jsp use get the result list from the request object, then generate the result Web page. Data operation servlets (add, delete, update) will redirect browser to QueryServlet so that user can see the operation result immediately.
Data objects in the App Engine Datastore are known as entities. An entity has one or more named properties, each of which can have one or more values. Entities of the same kind need not have the same properties, and an entity's values for a given property need not all be of the same data type. Each entity in the Datastore has a key that uniquely identifies it. The key consists of the following components:
  • The kind of entity, which categorizes it for the purpose of Datastore queries
  • An identifier for the individual entity, which can be either a key name string or an integer numeric ID
  • An optional ancestor path locating the entity within the Datastore hierarchy
When you import the project you will see the following files and directories:
Consider the following example:
//get datastore object from DatastoreServiceFactory
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

//declare an entity with Kind "Employee" and KeyName "asalieri"
Entity employee = new Entity("Employee", "asalieri");

//set entity's properties
employee.setProperty("firstName", "Antonio");
employee.setProperty("lastName", "Salieri");
Date hireDate = new Date();
employee.setProperty("hireDate", hireDate);
employee.setProperty("attendedHrTraining", true);

//put this entity to datastore
datastore.put(employee);

Browser UI

The webpage allows the user to add, delete, update and query entities. If the user clicks on Query Result OK without entering a name then all records will be returned. The user can update an entry already saved in the datastore or delete it, as shown in the example below.

Workflow

Let's think about the workflow of the browser and the servlets -- recall there are 4 servlets, one for each query type. If you inspect the code then the query workflow is as follows:
  • the user loads the URL localhost:8888 into the browser. The browser sends a GET request to the servlet /query.do because it is defined as the welcome file in web.xml;
  • the request invokes the servlet on the server;
  • the servlet parses the request, gets a parameter called “name”, which is the query condition;
  • the servlet retrieves data from the Datastore with the condition.
  • if the condition is empty, it retrieves all the records from the Datastore, otherwise it only retrieves the record whose name is the same as the condition. The results are stored in an ArrayList;
  • the servlet adds the ArrayList to the request object then forward to the query_result.jsp;
  • the query_result.jsp gets the ArrayList from the request object, then generates the html with all the data in ArrayList; and finally,
  • the server sends the generated html back to user’s browser which displays the html

Welcome file

Note, that the* welcome file* (web.xml) in the first demo is a html file and in the second demo is a servlet. When a user visits the website without specifying which type of file (html or servlet), the server uses the welcome file to fulfill the request. So in the second demo, when a user visits localhost:8888, the server knows it should use query.do to respond the request.
<welcome-file-list>
       <welcome-file>query.do</welcome-file>
</welcome-file-list>

Servlets and JavaServer Pages

The design uses a number of servlets that handle each function. The basic mechanism is that each servlet updates the datastore and sets the results in the request (req.setAttribute("result", result)) before forwarding the request to the "query_result.jsp" as shown in the code from query().

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException, ServletException {
        String name = req.getParameter("name");
        ArrayList<Contact> result = ContactDatastore.query(name);
        req.setAttribute("result", result);
        getServletContext().getRequestDispatcher("/query_result.jsp").forward(
                req, resp);
    }
The query_result.jsp executes taking the request and response as input and generates dynamic html which is returned to the user. A snippet of the query_result.jsp is shown below:

    <b>
        ---------------------------------------------------------------------<br>
        <%
            ArrayList<Contact> resultList = (ArrayList<Contact>) request
                    .getAttribute("result");
            if (resultList != null) {
                for (Contact contact : resultList) {
        %> Name:<%=contact.mName%>&nbsp; Address:<%=contact.mAddress%>&nbsp;
        PhoneNumber:<%=contact.mPhoneNumber%>&nbsp; &nbsp;&nbsp; <a
        href="/delete.do?name=<%=contact.mName%>">delete</a> <br> <%
    }
    }
 %>
        ---------------------------------------------------------------------<br>
    </b> Add new contact:
    <br>
    <form name="input" action="/add.do" method="post">
        Name: <input type="text" name="name"> Address: <input
            type="text" name="addr"> Phone: <input type="text"
            name="phone"> <input type="submit" value="Add">
    </form>
    ----------
See How JSP Works: Servlets and JavaServer Pages for more information if these concepts are new to you.
Note, the first time the welcome file will invoke query with null entered in the query. This will get all previously saved entries in the datastore and create the dynamic html using the method above. The html page will be returned to the user who will see something like as follows:
The user can then select any of the forms that were presented (add, update, query -- and delete, which is a special case because it is presented as a hyperlink in the page.

Workflow revisited

When the user visit the website first: since query.do is the welcome file, query.do is invoked. It redirects to query_result.jsp, where HTML is generated and returned to the user. So the main interface is generated by query_result.jsp.
When the user is viewing the dynamic html created by query_result.jsp: query_result.jsp defines three forms to handle add, update and query. Each form submits the form to the corresponding servlet; that is, when user clicks the add form, it issues a POST request to the servlet add.do. It’s the same with update and query.
Delete is different. When query_result.jsp is generating html, it generates a delete hyperlink for each record. When the user click such hyperlink, it issues a GET request to the delete servlet.
All servlet eventually redirect to query_result.jsp to show the same interface as a result.

The admin console is running at http://localhost:8888/_ah/admin

The App Engine environment supports an admin console where you can inspect what is happening when your web app is running in the cloud. To get access to the console simply set the URL of your browser to http://localhost:8888/ah/admin (i.e., open a different tab to your running app http://localhost:8888 and enter http://localhost:8888/ah/admin). An example of the console for this demo code is shown below.

Using mlogger to display debug messages to the console.

Like log.d() you can use logging to write various status messages to the console (not the CatLog) for code running on the cloud as shown in the snippet of code. Look for the printed lined log statement in the console:

May 05, 2014 8:57:37 AM edu.dartmouth.cs.gae_sample.data.ContactDatastore add
INFO: contact exists
The relevant code snippet found in the ContactDatastore java file is shown below:

    \\ get the logger
        private static final Logger mLogger = Logger
            .getLogger(ContactDatastore.class.getName());

        .......
    public static boolean add(Contact contact) {
        if (getContactByName(contact.mName, null) != null) {
  
                       \\ write to the log file
            mLogger.log(Level.INFO, "contact exists");
            return false;
        }

Basic operations

The following are the basic operations:
  • Add an entity
  //declare an entity with Kind "Employee" and KeyName "asalieri"
  Entity employee = new Entity("Employee", "asalieri");
  //set a property
  employee.setProperty("lastName", "Salieri");
  //put this entity to datastore
  datastore.put(employee);
  • Retrieving an entity
  //declare the key for the entity you are trying to retrieve
  Key employeeKey = KeyFactory.createKey(“Employee”, “asalieri”);
  //get the entity using the key
  Entity employee = datastore.get(employeeKey);
  • Updating an entity
  //declare the key for the entity you are trying to modify
  Key employeeKey = KeyFactory.createKey(“Employee”, “asalieri”);
  //modify employee’s properties in here
  employee.setProperty("lastName", "Sal");
  //if employeeKey exits in the datastore, put() will update that entity 
  datastore.put(employee);
  • Deleting an entity
  //declare the key for the entity you are trying to modify
  Key employeeKey = KeyFactory.createKey(“Employee”, “asalieri”);
  datastore.delete(employee);

Entity Ancestor Paths

Entities in the Datastore form a hierarchically structured space similar to the directory structure of a file system. When you create an entity, you can optionally designate another entity as its parent; the new entity is a child of the parent entity. An entity without a parent is a root entity. The association between an entity and its parent is permanent and cannot be changed once the entity is created. The Datastore will never assign the same numeric ID to two entities with the same parent, or to two root entities (those without a parent).
  //parent entity
  Entity employee = new Entity("Employee");
  datastore.put(employee);
  
  //use employee.getKey() as address' parent key
  Entity address = new Entity("Address", "addr1", employee.getKey());
  datastore.put(address);

Datastore queries

A Datastore query retrieves entities from the App Engine Datastore that meet a specified set of conditions:
  • An entity kind to which the query applies
  • Zero or more filters based on the entities' property values, keys, and ancestors
  • Zero or more sort orders to sequence the results
  • A query retrieves all entities of the given kind that satisfy all of the given filters, sorted in the specified order.
Consider the following example:
  //declare a query for kind "Person"
  Query q = new Query("Person");
  //set filter for property "height"
  q.setFilter("height",
    Query.FilterOperator.GREATER_THAN_OR_EQUAL,
    minHeight);
  //use PreparedQuery to execute query
  PreparedQuery pq = mDatastore.prepare(q);
  //get query results as a list of entity
  List<Entity> results = 
    pq.asList(FetchOptions.Builder.withDefaults());
A query with an ancestor filter limits its results to the specified entity and its descendants:
  //set employeeKey as addrQuery's ancestor
  Query addrQuery = new Query(“Address").setAncestor(employeeKey);
Datastore queries have the following limitations:
  • Entities lacking a property name in the query are ignored.
  • Filtering on unindexed properties returns no results.
  • Inequality filters are limited to at most one property.
  • Ordering of query results is undefined when no sort order is specified.
  • Properties used in inequality filters must be sorted first.
  • Queries inside transactions must include ancestor filters.
  • JOIN is not supported
  • And more…

Structuring Data for Strong Consistency

Datastore queries can deliver results at two consistency levels:
  • Strongly consistent queries guarantee the freshest results; but may take longer to complete. Ancestor queries (those within an entity group) are strongly consistent by default.
  • Eventually consistent queries generally run faster; but may occasionally return stale results. Non-ancestor queries are always eventually consistent.

Second App Engine project demo work flow

The following flow charts shows how different servlet and JSP coordinate to fulfill user's query, add, delete and update requests.
  • Query
  • Add a record
  • Delete a record
  • Update a record