Android and GCM – Broadcast Yourself

At the 2012 Google IO conference, Google announced the release of Google Cloud Messaging, AKA GCM, as a replacement for its Cloud to Device Messaging (C2DM) protocol. Being new to cloud messaging, I was glad to have the chance to start with something fresh, knowing that I did not have to unlearn any earlier protocols to get this under my belt.

But I found very few examples, most of which only gave me partial explanations, so after battling through some basic GCM concepts and actually getting the thing to work, I’ve started putting together this tutorial of the fundamentals.

This tutorial will demonstrate the use of GCM for the broadcast of messages to an Android client from a Tomcat server. It will also make use of sending broadcastintents from a service and receiving those broadcastintents from an app’s activity. The end result will look something like this screenshot, where a web server displays a JSP page for a message to be transmitted, next to a DroidAtScreen capture of a broadcast message.

UPDATE:

I’ve uploaded two zip files – one containing the Android client and one containing the Tomcat service. You should be able to download either file, unzip to a folder, and then import the unzipped folder as a project in Eclipse.

NOTE: You’ll need to have an instance of Tomcat configure to work from within your Eclipse environment, or you’ll need to manually compile and deploy a WAR file from the provided source.

NOTE2: You’ll also need to update the Java code in the projects to make use of your PROJECT ID, Android device’s generated registration ID, and the browser key that’s returned when you generate your GCM project in Google’s console.

GCMClient.zip

GCMService.zip

Requirements:

You will need to have a running instance of Tomcat that you can control through Eclipse. You will also need an Android device, and a Google developer login ID. Finally, you must be running the latest (r20) version of the Eclipse ADT.

Some GCM Fundamentals

I’ll need to put a diagram here.

  1. A developer creates a Google APIs GCM project, which generates a project id and a “browser” id to be used in the project’s server-based code.
  2. An Android app registers the device its running on with GCM, using that project id, which generates a registration id. The app should transmit that registration id to the project’s server
  3. The project’s server code can now send a broadcast message to any registered devices by communicating with the GCM service, using the browser id. For java-based projects, there is a jar file that facilitates this communication.

The GCM Demo – The Big Picture

This project should be built in three steps:

  • Create a Google APIs project at the Google portal, and generate and save the project id and API keys.
  • Build and run the Android client app, letting it register with GCM, and capture the generated device app key.
  • Build the web service, using the browser key to send to GCM, and using the captured device app key to broadcast to your target device

Project Build, More Detail:

You’ll see a lot of steps listed below, which might seem daunting at first. But most of the pre-coding setup steps are quite straightforward, and you should be able to get your projects set up in about 20 minutes before you have to code anything.

Most of the information for steps 1 and 2 was taken verbatim from this
GCM Getting Started guide:

  1. Create a Google API Project at https://code.google.com/apis/console , and capture the PROJECT ID, which will be a 11-digit or so number. Store On the Google APIs Console page, select Services and turn Google Cloud Messaging ON.
  2. Create a Server and Browser API key. Contrary to the developer docs, the BROWSER key is the most important. But copy and store both keys in a cool dry place.
  3. From the Android SDK console, locate “Extras” and download the GCM libraries.
  4. Create an Android project, and add the gcm-client.jar to the projects \libs folder
  5. Set the appropriate permissions in your project’s manifest.
  6. In your Android app’s code you’ll be using GCMRegistrar to verify and register an Android device to receive messages from the project with the generated Google Project ID. This registration process will generate a lengthy registration id. For the purposes of this demo, you will want to capture that registration id. Optimally, your Android app’s code would send that key to your web service, which should then store that key for use in broadcasts.
  7. Add a receiver of type com.google.android.GCMBroadcastReceiver to your app’s manifest.
  8. To broadcast a message, your Tomcat web service’s code would create an instance of a Sender object, using the BROWSER key generated from step 2 above. It can then send messages to an array of devices, identified by the registration ids that each device generated in step 8.
  9. In order to receive GCM messages, your Android app will need a descendant of a GCMBaseIntentService that will respond to GCM messages sent by the GCMBroadcastReceiver. In this demo, this service will broadcast any received messages to a broadcast receiver, which will handle messages in our app’s main activity.
  10. Create a “Dynamic Web Application” with Tomcat as the server.
  11. Add the gcm-server.jar to the Web-Info/lib folder of your server project’s instance.

The Details:

Creating the Google Project

A condensed version of the steps found at GCM Getting Started will go here. The key point is to turn on GCM messaging for your project and to capture the project id and the generated API keys.
Create a Google APIs Project

Turn on GCM Messaging

Generate API Keys using “Simple API Access”

Creating the Android App Project

For the moment, I’ll post the source code. All java classes are in a package called com.avilyne.android.gcmclient .

The GCMIntentService is a descendant of GCMBaseIntentService. Note that the static PROJECT_ID string should come from the project id that you obtained from the Google APIs control panel while generating your project.

This service will receive messages sent to the Android device, via the “onMessage()” method.

package com.avilyne.android.gcmclient;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.support.v4.app.NotificationCompat.Builder;
import com.google.android.gcm.GCMBaseIntentService;

public class GCMIntentService extends GCMBaseIntentService {

    private static final String PROJECT_ID = "xxx";
    
	private static final String TAG = "GCMIntentService";
	
	public GCMIntentService()
	{
		super(PROJECT_ID);
		Log.d(TAG, "GCMIntentService init");
	}
	

	@Override
	protected void onError(Context ctx, String sError) {
		// TODO Auto-generated method stub
		Log.d(TAG, "Error: " + sError);
		
	}

	@Override
	protected void onMessage(Context ctx, Intent intent) {
		
		Log.d(TAG, "Message Received");
		
		String message = intent.getStringExtra("message");
		
		sendGCMIntent(ctx, message);
		
	}

	
	private void sendGCMIntent(Context ctx, String message) {
		
		Intent broadcastIntent = new Intent();
		broadcastIntent.setAction("GCM_RECEIVED_ACTION");
		
		broadcastIntent.putExtra("gcm", message);
		
		ctx.sendBroadcast(broadcastIntent);
		
	}
	
	
	@Override
	protected void onRegistered(Context ctx, String regId) {
		// TODO Auto-generated method stub
		// send regId to your server
		Log.d(TAG, regId);
		
	}

	@Override
	protected void onUnregistered(Context ctx, String regId) {
		// TODO Auto-generated method stub
		// send notification to your server to remove that regId
		
	}

}

This is the MainActivity.java, which registers the device during its onCreate() method, and also registers a BroadcastReceiver to receive messages from our GCMIntentService class.

package com.avilyne.android.gcmclient;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.TextView;

import com.google.android.gcm.GCMRegistrar;

public class MainActivity extends Activity {

	// Replace the xxx with the project id generated from the Google console when
	// you defined a Google APIs project.
	private static final String PROJECT_ID = "xxx";

	// This tag is used in Log.x() calls
	private static final String TAG = "MainActivity";

	// This string will hold the lengthy registration id that comes
	// from GCMRegistrar.register()
	private String regId = "";

	// These strings are hopefully self-explanatory
	private String registrationStatus = "Not yet registered";
	private String broadcastMessage = "No broadcast message";

	// This intent filter will be set to filter on the string "GCM_RECEIVED_ACTION"
	IntentFilter gcmFilter;

	// textviews used to show the status of our app's registration, and the latest
	// broadcast message.
	TextView tvRegStatusResult;
	TextView tvBroadcastMessage;

	// This broadcastreceiver instance will receive messages broadcast
	// with the action "GCM_RECEIVED_ACTION" via the gcmFilter
	
	// A BroadcastReceiver must override the onReceive() event.
	private BroadcastReceiver gcmReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {

			broadcastMessage = intent.getExtras().getString("gcm");

			if (broadcastMessage != null) {
				// display our received message
				tvBroadcastMessage.setText(broadcastMessage);
			}
		}
	};

	// Reminder that the onCreate() method is not just called when an app is first opened,
	// but, among other occasions, is called when the device changes orientation.
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		tvBroadcastMessage = (TextView) findViewById(R.id.tv_message);
		tvRegStatusResult = (TextView) findViewById(R.id.tv_reg_status_result);

		// Create our IntentFilter, which will be used in conjunction with a
		// broadcast receiver.
		gcmFilter = new IntentFilter();
		gcmFilter.addAction("GCM_RECEIVED_ACTION");

		registerClient();

	}

	// This registerClient() method checks the current device, checks the
	// manifest for the appropriate rights, and then retrieves a registration id
	// from the GCM cloud.  If there is no registration id, GCMRegistrar will
	// register this device for the specified project, which will return a
	// registration id.
	public void registerClient() {

		try {
			// Check that the device supports GCM (should be in a try / catch)
			GCMRegistrar.checkDevice(this);

			// Check the manifest to be sure this app has all the required
			// permissions.
			GCMRegistrar.checkManifest(this);

			// Get the existing registration id, if it exists.
			regId = GCMRegistrar.getRegistrationId(this);

			if (regId.equals("")) {

				registrationStatus = "Registering...";

				tvRegStatusResult.setText(registrationStatus);

				// register this device for this project
				GCMRegistrar.register(this, PROJECT_ID);
				regId = GCMRegistrar.getRegistrationId(this);

				registrationStatus = "Registration Acquired";

				// This is actually a dummy function.  At this point, one
				// would send the registration id, and other identifying
				// information to your server, which should save the id
				// for use when broadcasting messages.
				sendRegistrationToServer();

			} else {

				registrationStatus = "Already registered";

			}
			
			
		} catch (Exception e) {
			
			e.printStackTrace();
			registrationStatus = e.getMessage();
			
		}

		Log.d(TAG, registrationStatus);
		tvRegStatusResult.setText(registrationStatus);
		
		// This is part of our CHEAT.  For this demo, you'll need to
		// capture this registration id so it can be used in our demo web
		// service.
		Log.d(TAG, regId);

	}

	private void sendRegistrationToServer() {
		// This is an empty placeholder for an asynchronous task to post the
		// registration
		// id and any other identifying information to your server.
	}

	// If the user changes the orientation of his phone, the current activity
	// is destroyed, and then re-created.  This means that our broadcast message
	// will get wiped out during re-orientation.
	// So, we save the broadcastmessage during an onSaveInstanceState()
	// event, which is called prior to the destruction of the activity.
	@Override
	public void onSaveInstanceState(Bundle savedInstanceState) {

		super.onSaveInstanceState(savedInstanceState);

		savedInstanceState.putString("BroadcastMessage", broadcastMessage);

	}

	// When an activity is re-created, the os generates an onRestoreInstanceState()
	// event, passing it a bundle that contains any values that you may have put
	// in during onSaveInstanceState()
	// We can use this mechanism to re-display our last broadcast message.
	
	@Override
	public void onRestoreInstanceState(Bundle savedInstanceState) {
		
		super.onRestoreInstanceState(savedInstanceState);

		broadcastMessage = savedInstanceState.getString("BroadcastMessage");
		tvBroadcastMessage.setText(broadcastMessage);

	}

	// If our activity is paused, it is important to UN-register any
	// broadcast receivers.
	@Override
	protected void onPause() {
		
		unregisterReceiver(gcmReceiver);
		super.onPause();
	}
	
	// When an activity is resumed, be sure to register any
	// broadcast receivers with the appropriate intent
	@Override
	protected void onResume() {
		super.onResume();
		registerReceiver(gcmReceiver, gcmFilter);

	}

    // There are no menus for this demo app.  This is just
	// boilerplate code.
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	// NOTE the call to GCMRegistrar.onDestroy()
	@Override
	public void onDestroy() {

		GCMRegistrar.onDestroy(this);

		super.onDestroy();
	}

}

Here is the screen layout. It’s not pretty at all, but serves its purpose. It will show the status of the registration process, and will also show any received messages.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
     >

    <TextView
        android:id="@+id/tv_reg_status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding_medium"
        android:text="@string/registry_status"
        />
     <TextView
        android:id="@+id/tv_reg_status_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding_medium"
        android:text="@string/registry_status_result"
        />

         <TextView
        android:id="@+id/tv_message_label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding_medium"
        android:text="@string/broadcast_label"
        tools:context=".MainActivity" />
     
       
    <TextView
        android:id="@+id/tv_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding_medium"
        android:text="@string/broadcast_result"
        tools:context=".MainActivity" />

</LinearLayout>

Here is the AndroidManifest.xml file. Note the permissions, and note the .GCMIntentService entry and the GCMBroadcastReceiver entry within the application section.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.avilyne.android.gcmclient"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    
    <permission
        android:name="com.avilyne.android.gcmclient.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.avilyne.android.gcmclient.permission.C2D_MESSAGE" />
    
    <!-- receives GCM messages -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <!-- GCM connects to Google services -->
    <uses-permission android:name="android.permission.INTERNET" />
        
    <!-- GCM requires a Google account -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    
    <uses-permission android:name="android.permission.READ_OWNER_DATA" />
    
    <!-- wake the processor if a GCM message is received -->
	<uses-permission android:name="android.permission.WAKE_LOCK" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="com.google.android.gcm.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="com.avilyne.android.gcmclient" />  
            </intent-filter>   
        </receiver>
        <service
            android:name=".GCMIntentService">
        </service>
    </application>

</manifest>

Run the Android app on your device to capture the registration id

When you run the app on your device, the captured registry id will show in Eclipse’s LogCat area:

Highlight that line, and press the file icon (just to the right of the “verbose” drop-down shown in the picture above. Your highlighted text will be saved to a file. Once the file is saved in a location where you can find it, open it up in a text editor. It will look something like this:

The highlighted area shown above is your device’s registry id for this project. Make plans to copy that regid to a const string in a servlet.

Creating the Tomcat service project.


  • Create a “Dynamic Web Project” with Tomcat. For my demo I named the project “GCMService”, so when I run the server project,the resulting URL will be http://192.168.1.5:8080/GCMService . That IP address is the address of my development machine on my local lan.
  • Add “gcm-server.jar” to the WEB-INF/lib folder, and configure the project’s build path to include that jar.
  • Also add “json-simple-1.1.1.jar” to the WEB-INF/lib folder, and again update the project’s build path. This jar can be found in the GCM demo project that comes downloaded with the rest of the GCM files. I got tripped up by JSON parse error messages until I read online to add this jar.
  • In the src folder, create a servlet named “GCMBroadcast.java” with a “doPost()” method. I placed my servlet in a package called “com.avilyne.gcm”, and the code is shown here.

Listed here is the source code for the GCMBroadcast servlet.

package com.avilyne.gcm;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.MulticastResult;
import com.google.android.gcm.server.Sender;

/**
 * Servlet implementation class GCMBroadcast
 */
@WebServlet("/GCMBroadcast")
public class GCMBroadcast extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	// The SENDER_ID here is the "Browser Key" that was generated when I
	// created the API keys for my Google APIs project.
	private static final String SENDER_ID = "AIzaSyChuyZMLIIl-B6vcIH8mbmzo3E7c5ivjXc";
	
	// This is a *cheat*  It is a hard-coded registration ID from an Android device
	// that registered itself with GCM using the same project id shown above.
	private static final String DROID_BIONIC = "APA91bEju-eB74DWRChlVt5gh7YfIVzNOr8gRYPisFbmcwBPlMJeGTYmdF7cYR3oL-F9KqmTey016drxmWAkYa4WQv9pQ_KvRzI1VUkql6ObbYGPkV7UBsm6pYoBw0dEk3veh60v3lVhDtLztWIbDc3XqtjU_fE_0g";
		
	// This array will hold all the registration ids used to broadcast a message.
	// for this demo, it will only have the DROID_BIONIC id that was captured 
	// when we ran the Android client app through Eclipse.
	private List<String> androidTargets = new ArrayList<String>();
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public GCMBroadcast() {
    	
        super();

        // we'll only add the hard-coded *cheat* target device registration id 
        // for this demo.
        androidTargets.add(DROID_BIONIC);
        
    }
    
    // This doPost() method is called from the form in our index.jsp file.
    // It will broadcast the passed "Message" value.
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// We'll collect the "CollapseKey" and "Message" values from our JSP page
		String collapseKey = "";
		String userMessage = "";
		
		try {
			userMessage = request.getParameter("Message");
			collapseKey = request.getParameter("CollapseKey");
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		// Instance of com.android.gcm.server.Sender, that does the
		// transmission of a Message to the Google Cloud Messaging service.
		Sender sender = new Sender(SENDER_ID);
		
		// This Message object will hold the data that is being transmitted
		// to the Android client devices.  For this demo, it is a simple text
		// string, but could certainly be a JSON object.
		Message message = new Message.Builder()
		
		// If multiple messages are sent using the same .collapseKey()
		// the android target device, if it was offline during earlier message
		// transmissions, will only receive the latest message for that key when
		// it goes back on-line.
		.collapseKey(collapseKey)
		.timeToLive(30)
		.delayWhileIdle(true)
		.addData("message", userMessage)
		.build();
		
		try {
			// use this for multicast messages.  The second parameter
			// of sender.send() will need to be an array of register ids.
			MulticastResult result = sender.send(message, androidTargets, 1);
			
			if (result.getResults() != null) {
				int canonicalRegId = result.getCanonicalIds();
				if (canonicalRegId != 0) {
					
				}
			} else {
				int error = result.getFailure();
				System.out.println("Broadcast failure: " + error);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}

		// We'll pass the CollapseKey and Message values back to index.jsp, only so
		// we can display it in our form again.
		request.setAttribute("CollapseKey", collapseKey);
		request.setAttribute("Message", userMessage);
		
		request.getRequestDispatcher("index.jsp").forward(request, response);
				
	}

}

  • Create an index.jsp file in the WebContent folder. Shown below is the index.jsp, which provides a user interface for keying in a broadcast message, and will post the captured message to a servlet mapped to “GCMBroadcast”.
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<%
    // retrieve our passed CollapseKey and Message parameters, if they exist.
    String collapseKey = "GCM_Message";
    String message  = "Generic Broadcast Message";

    Object collapseKeyObj =  request.getAttribute("CollapseKey");
    
    if (collapseKeyObj != null) {
    	collapseKey = collapseKeyObj.toString();
    }
    
    Object msgObj =  request.getAttribute("Message");
    
    if (msgObj != null) {
    	message = msgObj.toString();
    }
    
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Android GCM Broadcast</title>

</head>
<body>

    <h2>GCM Broadcast Demo</h2>
	
    <form action="GCMBroadcast" method="post">
		<label>Broadcast Message </label>
		<br /><input type="text" name="CollapseKey" value="<%=collapseKey %>" />
		<br/><textarea name="Message" rows="3" cols="60" ><%=message %> </textarea> 
		<br/><input type="submit" name="submit" value="Submit" />
		</form>	
	</body>
</html>

Create (or edit, if it already exists) a web.xml file in the WEB-INF folder. The web.xml file will contain a servlet mapping for the GCMBroadcast servlet, and will list index.jsp as a default page.

Use this as a starting point for your web.xml file:

<?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_3_0.xsd" 
    id="WebApp_ID" 
    version="3.0">
    <servlet>
        <servlet-name>GCMBroadcast</servlet-name>
        <servlet-class>com.avilyne.gcm.GCMBroadcast</servlet-class>
    </servlet> 
    <servlet-mapping>
        <servlet-name>GCMBroadcast</servlet-name>
        <url-pattern>/GCMBroadcast</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    
</web-app>

Sending a Message

Here is the server-side code that actually sends a message to a list of registered devices.

Understanding .collapseKey(“KEY_NAME”). Suppose you want to broadcast a message with the latest score of a Redsox Yankees game. The message content might be “Bottom of the third, Yankees 4, Redsox 1″, but the client never receives the message because his device is out of range. When the client gets back in range, the score has changed, and is now “Top of the fifth, Yankees 4, Redsox 6″. You’d probably only want the client device to receive the latest score, and not be bombarded with a blast of older broadcasts.

In this case, you’d define a String key – perhaps “Latest_Score”, and use that key to “collapse” any stack of messages. If you broadcast a message using a key, and then later broadcast a new message using that same key, if the client device was out of range for the first message but in range for the second message, the client device will only receive the second message.

That’s what .collapseKey(“…”) refers to.


		// Instance of com.android.gcm.server.Sender, that does the
		// transmission of a Message to the Google Cloud Messaging service.
		Sender sender = new Sender(SENDER_ID);
		
		// This Message object will hold the data that is being transmitted
		// to the Android client devices.  For this demo, it is a simple text
		// string, but could certainly be a JSON object.
		Message message = new Message.Builder()
		
		// If multiple messages are sent using the same .collapseKey()
		// the android target device, if it was offline during earlier message
		// transmissions, will only receive the latest message for that key when
		// it goes back on-line.
		.collapseKey("GCM_Message")
		.timeToLive(30)
		.delayWhileIdle(true)
		.addData("message", userMessage)
		.build();
		
		try {
			// use this for multicast messages.  The second parameter
			// of sender.send() will need to be an array of register ids.
			MulticastResult result = sender.send(message, androidTargets, 1);
			
			if (result.getResults() != null) {
				int canonicalRegId = result.getCanonicalIds();
				if (canonicalRegId != 0) {
					
				}
			} else {
				int error = result.getFailure();
				System.out.println("Broadcast failure: " + error);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}

		// We'll bring ourselves back to index.jsp, so we can continue the fun.
		response.sendRedirect("index.jsp");

Broadcasting a Message

Before one broadcasts a message, this demo Android app will simply mention that no broadcast message has been received:

On the JSP page, type a message and send it.

Receiving the Message on your Android Device

Assuming that network connections are good, your device should receive that message. Note that the device does not have to have any connection to your web server.

This is a rough draft of this tutorial. It will be refined and enhanced.

121 thoughts on “Android and GCM – Broadcast Yourself”

    1. Thanks! This is an incomplete tutorial, with some big gaps. But if this helped at all, that is fantastic.

  1. can you share the your source kode to project ?

    when ı try start Tomcat service project.ı got this error can you help me ??

    com.google.android.gcm.server.InvalidRequestException: HTTP Status Code: 404(

    Not Found

    Not Found
    Error 404

    )
    at com.google.android.gcm.server.Sender.sendNoRetry(Sender.java:367)
    at com.google.android.gcm.server.Sender.send(Sender.java:261)
    at com.avilyne.gcm.GCMBroadcast.doPost(GCMBroadcast.java:102)
    at com.avilyne.gcm.GCMBroadcast.doGet(GCMBroadcast.java:58)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    1. Do you get this error when your Tomcat service first starts, or when the service attempts to send a broadcast message? The 404 message is very odd, since the “Sender” class, provided by Google’s own GCM jar files, provides the actual URL.

      It looks like someone had the same issue with this code (or a variation of it), and posted a request on StackOverflow, at
      http://stackoverflow.com/questions/11948785/gcm-returns-404-error

      In that person’s implementation, there is also a doGet(…), and I wonder if what’s happening is the passing of the doGet() to the doPost() causes issues because parameters passed in a GET are formatted in a different way than the parameters passed in a POST.

      Something like that. I’ll play around with this to see if I can reproduce this same error.

  2. when i type my REG ID inside web page and type msg , i always get No webpage was found for the web address …/WebContent/GCMBroadcast please help me and i didn’t find method doget() where is it ??

    1. Before you type anything in, what do you see when you start the Tomcat server app and go to http://localhost:8080/GCMService/ ?
      The GCMBroadcast servlet was only coded with a doPost() method. The JSP page sends data to GCMBroadcast using POST.

      By REG ID, are you referring to your Android device’s registration ID? I might need to get more information before I can be of much help.

      Mark

  3. when i type http://localhost:8080/GCMService/ i get “HTTP Status 404 – /GCMService/” description The requested resource (/GCMService/) is not available.

    By REG ID , yes i’m referring to my android devise reg id .

    thanks for ur help Dear . really i need to run this example

    1. Can you check the properties for your project, under “Web Project Settings”? If you click on “Web Project Settings”, you should see a screen with one entry labelled “Context root:”, and in the edit control there, you should have “GCMService”.

      Also, can you send your web.xml? If your context root is ok, then there may be an issue with the mapping of the default index.jsp file in the welcome-file-list section.

  4. i didn’t Create a “Dynamic Web Project” with Tomcat.with name“GCMService”, because u just ask to create the project only but no description what is inside or how will we use it ?? or what is code inside ?? so i didn’t create it. so if this is important kindly describe what is code inside or if any classes ???

    Thaanks

    1. Mortaa,

      When I first wrote this post, I skimmed over the Tomcat service project, and I should have had more information on how to do that.

      You’ll need to have a version of Eclipse that can run J2EE projects, and to configure a link between Eclipse and an existing Tomcat server.

    1. Thank you very much. I need to add more to this tutorial, but I’m glad you could get something out of it.

  5. Thanks a lot..this tutorial helps me a lot to understand GCM.. I tried this code and its work perfectly fine… Thanks once again..

  6. Hi! First of all great tutorial, but i got some issues.
    My API Project ID is 12 digits(like “123456789110″) is this fine? because i put it in MainActivity and in the BaseIntent but when i run my app the logcat doesn’t give me anything only “resetting backoff for my.pack” and the UI shows me only Registration Acquired… can someone help me please??

    1. Yes, your project id should be 12 digits. At least, for the projects I’ve built, they’ve been consistently 12 digits. The demo app has a method called registerClient(). If you step through that method via the debugger, at what point does it fail? (My guess is that it would be GCMRegistrar.register(…)).

      One possibility might be the permissions in your manifest file. If the right permissions aren’t set, I can imagine the GCMRegistrar.register() returning with some issues. Then again, the GCMRegistrar.checkManifest() would throw an error.

      1. this is my LogCat:

        09-11 14:47:28.989: I/PackageManager(28501): Unpacking native libraries for /data/app/com.example.gcm-1.apk
        09-11 14:47:29.579: D/PackageManager(28501): Services: com.example.gcm.GCMIntentService
        09-11 14:47:29.579: I/ActivityManager(28501): Force stopping package com.example.gcm uid=10079
        09-11 14:47:29.589: D/PackageManager(28501): Activities: com.example.gcm.First
        09-11 14:47:29.589: D/PackageManager(28501): Permissions: com.example.gcm.permission.C2D_MESSAGE
        09-11 14:47:30.009: D/PackageManager(28501): New package installed in /data/app/com.example.gcm-1.apk
        09-11 14:47:30.119: W/PackageManager(28501): Unknown permission android.permission.READ_OWNER_DATA in package com.example.gcm
        09-11 14:47:30.379: D/Launcher.SWidgetPkgMgr(4961): addPackage=com.example.gcm
        09-11 14:47:32.359: I/ActivityManager(28501): Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0×10000000 cmp=com.example.gcm/.First } from pid 6858
        09-11 14:47:32.409: I/ActivityManager(28501): Start proc com.example.gcm for activity com.example.gcm/.First: pid=6871 uid=10079 gids={3003}
        09-11 14:47:32.559: D/GCMRegistrar(6871): resetting backoff for com.example.gcm

        1. I wonder if something’s happening in your manifest file. I see this “Unknown permission android.permission.READ_OWNER_DATA in package com.example.gcm” in the log you posted.

  7. Thanks dude, I had problems with the collapse key initially, every time I sent a message from my server the android device would receive it but would display do_not_collapse as the name of the collapse key.
    Great job!

  8. Thanks for this tutorial!
    I followed it and seems that everything works (server side and client side), but when I send the message, my phone doesn’t get anything. Do you have any idea??
    can you share the project code source???
    Thanks a lot for your attention.

    1. I should be able to post the source code – two projects, tomcat server and android client, in a few days. Hopefully, that will help.

      Mark

  9. When i run the program i just got Registration acquired but registry id is not shown in Eclipse’s LogCat area: please help

  10. hey man,when ı send message it give me “java.lang.IllegalArgumentException: argument cannot be null” on Sender. What ı solve this prob?

    1. how to upload my server pgm’s war file in web? is there any free web hosting? and how to make server pgm i android?

  11. Hey thanks for the application. Its Working… I have few queries

    1. How to invoke the server code to pass the regID to it from Client’s MainActivity

    2. If i close my emulator and meanwhile i send a message then will the emulator receive it when its up?

    1. Regarding the server code for the RegID – I punted on that. I’d do a POST from the Android client, sending the RegID that the client received when it registered, but I also might want to send some other identifying information – perhaps a user id or email address. On the server side, I’d write another handler for the transmitted POST message that would capture the passed RegID (and optional user id) and store it in a database.

      I haven’t tried it, but the GCM documents say that, if your device is out of range to receive a broadcast, and later comes in range, that broadcast will be transmitted to the newly reappeared device. By that logic, it would be likely that your emulator would receive your GCM message when you power it up again. But you might need to re-open your GCM client app. I’d like to hear what you find out if you do any experimentation on this.

  12. Did it exactly step by step
    but: “Conversion to Dalvik format failed with error 1″
    and it is because of the “gcm.jar” and I need it in order for the whole thing to work, can’t delete it. any solution?

  13. I followed your code and executed but I am getting register :http error 400 and I got the output in emulator registration status as Registration Acquired. I observed the LogCat there registrationId is Null. I am trying for two days but I am not able to register the device. what is the problem happening internally?. Please help me out.

    1. It sounds like you’re running this on an emulator. When I built this demo, I steered clear of the emulator. The documentation seems to imply that an emulator might work, but I didn’t want to chance it. If you haven’t already, can you try running this on an Android device?

  14. Http Status : 500
    java.lang.ClassNotFoundException: com.google.android.gcm.server.Sender
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1676)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1521)
    at com.logiciel.gcm.GCMBroadcast.doPost(GCMBroadcast.java:80)

  15. Thank you for this tutorial,
    it wa very helpful for me.
    One things, if my app is opened I receive the message, no problem..
    if it’s closed, the message in sent but no badge appear over the screen…

    Can you help me?
    Thank you again and sorry for my english

    1. In order for a badge to show at the top of your screen, you’ll have to make use of the Notification Manager. That might be something for another tutorial, but in short, when your background service receives a GCM message, it can post a notification, with an icon and text, to the top of your device’s screen. You can also specify an Activity to be run if the user selects the item from the expanded Notification area.

      That’s the idea, anyway. Check out at Lars Vogel’s tutorial on notifications. That can be found at http://www.vogella.com/articles/AndroidNotifications/article.html .

  16. Hi Awesome tutorial!!
    I am pretty new to servlet and POST. Can you direct me to a good example for this. I want to implement the function:
    private void sendRegistrationToServer() {
    // This is an empty placeholder for an asynchronous task to post the
    // registration
    // id and any other identifying information to your server.
    }
    Any help would be great! Thanks again for this great example!

  17. I’ve developed using your steps. Everything is fine individually.
    What i mean by this is, client is good when i run that alone, server is good when i’m running it alone.

    Now the problem is, When i’m sending the message from server onto the android device its not publishing the broadcasting message. (FYI: Here i’m using the AVD ((Android Virtual Device) we get in eclipse ide for mobile development) for android demonstration).
    Please let me know, if i’m not clear on my question. but i would like to know what’s wrong on my implementation.

    1. I would strongly suggest using a real Android device for this project, as it may be difficult to find the AVD, which is sharing the same IP address as the host computer.

  18. Hi,
    i run all the steps in this great tutorial but when i tried to submit
    a message from the web page i’m getting exception in the server log :
    “java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode”
    can anyone please suggest for a solution ?

  19. Relly NIce tutorial…
    Hi
    can u tell me what is this private static final String DROID_BIONIC in GCMBroadCast calss telll me detail how we will do when we are working on emulator

    1. The DROID_BIONIC String is a (temporary) hard-coded string that identifies my own Android phone. In a real world situation, one would never do that. The Android app, once registered on a user’s device, should send its identification string to a server, which should collect and store it. The server would then retrieve that stored identifier to use in a broadcast.

      I punted on that part of this demo. Perhaps I should write a follow-up that demonstrates a POST of the identifier to a server, and the storage of that id in a NoSQL database. I’d probably use Google App Engine for that.

  20. Hi Expert
    Thanks for you sharing. I have implemented it with your step successfully.
    Now I want to push message back from Android App to server site, do you have any idea about this?

  21. I am not able to get the registry id in Eclipse’s Log Cat area,its showing error
    registration error:SERVICE_NOT_AVAILABLE
    please help me out….

    1. I might need more information on this. Are you running the app in an emulator, or on a real device. Also, does your device have internet connectivity?

      I’d opt for running the app on a real device. Emulators are quirky, and you’ll encounter IP address challenges when you attempt to communicate between an emulator and a server on the same machine.

      1. thanks for reply…..
        i am running on emulators ,yes my LAPTOP has internet connectivity .
        its also showing error
        registration error:INVALID SENDER.
        1.not getting RegistrationId.
        2.I didnt write the server side code .
        how i will check whether i my code is reaching to GCM?

  22. i am running on emulators ,yes my LAPTOP has internet connectivity .
    its also showing error
    registration error:INVALID SENDER.
    1.not getting RegistrationId.
    2.I didnt write the server side code .
    how i will check whether i my code is reaching to GCM?

  23. Excellent tutorial, but it turns out that I have a problem I always get to the id is not equal to null the value generated by the API

    1. I might need more info and clarification in order to help you. Can you point a particular place in the code where this error occurs?

      1. Hi this my logcat normally i should got the registerid in my case it’s null and the Registration error: INVALID_SENDER

        01-10 15:47:47.185: D/GCMRegistrar(308): resetting backoff for com.test.example.gtest
        01-10 15:47:47.225: V/GCMRegistrar(308): Registering app com.test.example.gtest of senders 864678147863
        01-10 15:47:47.285: D/MainActivity(308): Registration Acquired
        01-10 15:47:53.205: V/GCMBroadcastReceiver(308): onReceive: com.google.android.c2dm.intent.REGISTRATION
        01-10 15:47:53.205: V/GCMBroadcastReceiver(308): GCM IntentService class: com.test.example.gtest.GCMIntentService
        01-10 15:47:53.215: V/GCMBaseIntentService(308): Acquiring wakelock
        01-10 15:47:53.265: V/GCMBaseIntentService(308): Intent service name: GCMIntentService-864678147863-1
        01-10 15:47:53.265: D/GCMIntentService(308): GCMIntentService init
        01-10 15:47:53.295: E/GCMRegistrar(308): internal error: retry receiver class not set yet
        01-10 15:47:53.295: V/GCMRegistrar(308): Registering receiver
        01-10 15:47:53.315: D/GCMBaseIntentService(308): handleRegistration: registrationId = null, error = INVALID_SENDER, unregistered = null
        01-10 15:47:53.315: D/GCMBaseIntentService(308): Registration error: INVALID_SENDER
        01-10 15:47:53.315: D/GCMIntentService(308): Error: INVALID_SENDER
        01-10 15:47:53.325: V/GCMBaseIntentService(308): Releasing wakelock

  24. thank so much
    But I have a problem: i am running on emulators and it’s OK, but when I’m running on my mobile device it not ok (i known that’s client on my mobile device could not connect to the GCMService)
    Plz help me

    1. Well, my first question is: can your device connect to the internet? Your device never connects directly to the GCMService Tomcat servlet.

  25. Hi… Nice work done
    I was trying this example but on the server side i am facing this issue
    java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
    at com.google.android.gcm.server.Sender.sendNoRetry(Sender.java:362)
    at com.google.android.gcm.server.Sender.send(Sender.java:261)
    at com.avilyne.gcm.GCMBroadcast.doPost(GCMBroadcast.java:88)

    Also on the AVD i am getting INVALID_SENDER Error.. The logcat looks like this

    01-11 06:47:38.576: V/GCMBroadcastReceiver(1582): onReceive: com.google.android.c2dm.intent.REGISTRATION
    01-11 06:47:38.594: V/GCMBroadcastReceiver(1582): GCM IntentService class: com.avilyne.android.gcmclient.GCMIntentService
    01-11 06:47:38.644: V/GCMBaseIntentService(1582): Acquiring wakelock
    01-11 06:47:38.864: W/Trace(1582): Unexpected value from nativeGetEnabledTags: 0
    01-11 06:47:38.864: W/Trace(1582): Unexpected value from nativeGetEnabledTags: 0
    01-11 06:47:38.954: D/GCMIntentService(1582): GCMIntentService init
    01-11 06:47:38.984: W/Trace(1582): Unexpected value from nativeGetEnabledTags: 0
    01-11 06:47:39.014: W/Trace(1582): Unexpected value from nativeGetEnabledTags: 0
    01-11 06:47:39.014: D/GCMBaseIntentService(1582): handleRegistration: registrationId = null, error = INVALID_SENDER, unregistered = null
    01-11 06:47:39.034: D/GCMBaseIntentService(1582): Registration error: INVALID_SENDER
    01-11 06:47:39.044: D/GCMIntentService(1582): Error: INVALID_SENDER
    01-11 06:47:39.044: V/GCMBaseIntentService(1582): Releasing wakelock

    1. There might be an issue with the registration on the server side. There’s a key value that Google generated when you registered your project in the Google console. Can you check that the key value in your project matches the one that Google generated?

  26. hi if i have to publish an app using the android gcm… will i have to run my pc always…. or will i have to host my site online… I know i am stating the obvious…. just checking….

    1. You should aim to have your server-side hosted on-line. One option might be to implement your server-side code in an instance of Google App Engine.

  27. Hi,
    In my case, message sent to GCM server but mobile is not able receive messsage.am sending no of devices to messgaes

    after sent message to server it giving like
    MulticastResult(multicast_id=7389675129380894085,total=2,success=2,failure=0,canonical_ids=0,results: [[ messageId=0:1358414204037677%a26459d300000031 ], [ messageId=0:1358414204037560%a26459d300000031 ]]

    but mobile is not able to receive messages (Registration id ‘s are correct and appkey using serverkey)

    1. It looks like your server side code is working correctly, and you mentioned that the registration ids are correct. One weak link in this tutorial is the manual capture of the mobile client key. I have a feeling your server is broadcasting to *somebody*, but it’s not you.

      Is your client mobile device able to connect to the internet?

      1. yes .mobile is connected to internet . nothing change but now messages are received at mobile side but happening only sometimes not all messages .please help me here

        1. along with i used these parameters collapskeysome string value 1 and delayidlewith(true)
          and timetolive is 3 and below method i followed
          send(message,registrationid’s,1)

        2. Since you’re getting some messages on your mobile side, the communication setup between your own server code and GCM sounds like it’s working correctly. But your device’s connectivity sounds like it’s sporadic.

          One option worth experimenting with is the .timeToLive() parameter of the Message. In this demo it’s set to 30 seconds, but the default timeToLive() value is 4 weeks (!) . Comment out that parameter and re-run the project to see if your device can receive your broadcasts more consistently.

      2. yes . in mobile internet working fine..even if net fulctuated the message should reach after some period . which is given in the time to live parameter i given there 3 and along with i used these parameters delaywith idle as true and collpasekey as 1.i checked today the message is reached to mobile sometimes .

  28. can you help me mark for this problem this is my logcat
    01-10 15:47:47.185: D/GCMRegistrar(308): resetting backoff for com.test.example.gtest
    01-10 15:47:47.225: V/GCMRegistrar(308): Registering app com.test.example.gtest of senders 864678147863
    01-10 15:47:47.285: D/MainActivity(308): Registration Acquired
    01-10 15:47:53.205: V/GCMBroadcastReceiver(308): onReceive: com.google.android.c2dm.intent.REGISTRATION
    01-10 15:47:53.205: V/GCMBroadcastReceiver(308): GCM IntentService class: com.test.example.gtest.GCMIntentService
    01-10 15:47:53.215: V/GCMBaseIntentService(308): Acquiring wakelock
    01-10 15:47:53.265: V/GCMBaseIntentService(308): Intent service name: GCMIntentService-864678147863-1
    01-10 15:47:53.265: D/GCMIntentService(308): GCMIntentService init
    01-10 15:47:53.295: E/GCMRegistrar(308): internal error: retry receiver class not set yet
    01-10 15:47:53.295: V/GCMRegistrar(308): Registering receiver
    01-10 15:47:53.315: D/GCMBaseIntentService(308): handleRegistration: registrationId = null, error = INVALID_SENDER, unregistered = null
    01-10 15:47:53.315: D/GCMBaseIntentService(308): Registration error: INVALID_SENDER
    01-10 15:47:53.315: D/GCMIntentService(308): Error: INVALID_SENDER
    01-10 15:47:53.325: V/GCMBaseIntentService(308): Releasing wakelock

  29. Hi,
    Awesome Tutorial. Everything is working fine. I have one query. Once my device becomes idle for sometime, as in the screen lock happens, after that when i try to send a message it says not registered. I have to then open the app once again. Why is this happening? Like for example whatsapp messaging has a service which is constantly running and it automatically restarts when the phone restarts. How do i do that?

    1. You’ll need to create a BroadcastReceiver that responds to the broadcast message android.intent.action.BOOT_COMPLETED. In your app’s manifest, you’ll therefore need to add the permission “android.permission.RECEIVE_BOOT_COMPLETED”.

      You’d start your background service when your BroadcastReceiver’s “onReceive(…)” method is called. That background service can then be running even when your app is no longer active.

  30. i am getting error such as no route to host exception..i followed the whole procudeure mentioned above..but not able to receive mesg to my emulator.please help me thank you

    1. Could you try running the Android client app on an actual device? It sounds like your emulator can’t connect to the outside world, and that may be a configuration issue on your computer. By running the app on a device, you can bypass that issue, and it would be a more real-to-life test situation anyway.

  31. I am trying the server-side code in netbeans-7.2.1 version. but, it is not successfully implemented. If u post the server-end procedures for netbeans, it will be very helpful for me. Pls post for netbeans by step to step graphically screenshot…………@admin

    1. I’m sorry that I am not running Netbeans at this time. But if I ever do have a reason to transition to netbeans, I will make it a point to come up with a tutorial that shows how I did it.

  32. instead of hard code value of PROJECT_ID , is there any way to initialize dynamically using email account?

        private static final String PROJECT_ID = “xxx”;

    1. The PROJECT_ID value is used to associate a particular client device (the hardware) with a particular server+client project, and is not associated with a user’s account. It’s not the same as the tokens that are used by authentication protocols like OAuth.

      Mark

  33. Hi. when i run the GCMService server the esclipse give me an error:

    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/GCMService]]
    at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/GCMService]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    … 7 more
    Caused by: java.lang.IllegalArgumentException: The servlets named [GCMBroadcast] and [com.avilyne.gcm.GCMBroadcast] are both mapped to the url-pattern [/GCMBroadcast] which is not permitted
    at org.apache.catalina.deploy.WebXml.addServletMapping(WebXml.java:335)
    at org.apache.catalina.startup.ContextConfig.processAnnotationWebServlet(ContextConfig.java:2412)
    at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2125)
    at org.apache.catalina.startup.ContextConfig.processAnnotationsFile(ContextConfig.java:2080)
    at org.apache.catalina.startup.ContextConfig.processAnnotationsFile(ContextConfig.java:2073)
    at org.apache.catalina.startup.ContextConfig.processAnnotationsFile(ContextConfig.java:2073)
    at org.apache.catalina.startup.ContextConfig.processAnnotationsFile(ContextConfig.java:2073)
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1300)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:878)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:369)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5179)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more

    Fev 21, 2013 1:30:53 PM org.apache.catalina.core.ContainerBase startInternal
    SEVERE: A child container failed during start
    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more

    Fev 21, 2013 1:30:53 PM org.apache.catalina.startup.Catalina start
    SEVERE: Catalina.start:
    org.apache.catalina.LifecycleException: Failed to start component [StandardServer[8005]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardService[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 9 more
    Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 11 more

    Fev 21, 2013 1:30:53 PM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 314 ms

    what is wrong? can you help me please?

  34. hey i found this very useful, however i am trying to develop a appstore portal from where the app on clicking download has to install in the target android device any tips on how i can use this gcm in that…??

    thanks

    1. Perhaps the message that’s sent via your GCM broadcast can include a URL to the app in the Google Play store. But your users will still need to have an app already installed that you’ve created to handle GCM requests.

  35. I got this error :

    HTTP Status 500 – Error instantiating servlet class com.avilyne.gcm.GCMBroadcast

    1. Most likely, this error comes from a conflict between the path to the GCMBroadcast class in your actual code, and the defined path in the XML files for the service.

  36. Hey Mark, thanks for the tutorial. I ran the client app on my phone and it shows that it is registered with GCM. But I have a problem while running the GCMService. It shows java.net.UnknownHostException : android.googleapis.com
    could you please help me with this?

    1. It sounds like your server can’t communicate with Google’s GCM service. One way to diagnose this would be to open up a terminal on your server and try to ping the google server. You might be behind a firewall, but that’s only a first guess.

  37. Hey Mark! Great tutorial, I hadn’t fired up my Tomcat server in awhile ha! As you have admitted pulling the Registered ID by hand is just not going to work in large scale development of an app. GCM has a register function it is just a matter of storing it. I look forward to the JDBC/MySQL/Android/GCM/Tomcat Tutorial. Keep up the good work.

    Kyle

    1. Certainly a production system would receive and store a user’s registration ID, along with other credentials, so that it can be used in the push process. But including a data persistence mechanism (and an appropriate POST mechanism from the client app to our server) would have made this an even longer tutorial.

      If I had been smart, I would have made this a 2 – or 3 part tutorial. I’ll see if I can expand this some day.

  38. Hi..Good tutorial..

    I made an application that uses the GCM service on mobile phones and everything works pretty well, except for some non-response of the Google server every now and then ..

    If I download the same app on a tablet, I do not understand why, I do not receive response from the server of Google when I register the device.. I have to set different permissions between Mobile and tablet for the GCM service? There are some little things to fix?

    Could it be my custom receiver that non works in the Tablet?

    1. I’d have to hear more about your device and it’s capabilities before saying anything. The permissions should be the same for the tablet as for your phone. I’ll apologize in advance before asking the next questions, as I’m just trying to think of all possibilities (no matter how remote):

      Are you able to generate a separate registration id on your tablet? If so, are you using that tablet’s registration id (the one that was manually copied from within Eclipse in the tutorial) in your web service when you send a message to GCM?

  39. When I get registration id in device I want to send that registration id to server and then send message.because when i have to test on various device every time i cannot copy the registrationid from device and paste in server code ? Please can you provide source code for that?rest of the code is awesome.

    1. Someday, when I get the chance, I’ll write a follow-up that demonstrates the POST of the registration id, along with a user id and maybe password, to a Tomcat web service. On the Tomcat side, I might also show the saving of the reg id, user id, password to a database.

      Then again, maybe the whole server side should be written in Node.js, using MongoDB as the database. Much less code.

  40. Thank you very much ! It really helped me so much !
    I have trouble, I want to back to Tomcat RigID, but do not know where to start ….

    1. I think you’re looking to do two things: send the new registration id from the user’s Android device to your Tomcat server, and then store that for future use.

      Regarding the first part: I would very likely want to include a user id (and probably password) from the user, and send that with the registration id. So I’d build an user interface to collect that in the Android app. I’d then want to send that information to a new Tomcat service via a POST, where the user id, password, registration id, and any other collected info would be submitted in key-value pairs.

      On the Tomcat side, I’d need to create a service that would respond to a POST request, retrieving those same pairs of info. I put together a tutorial on basic communication between an Android client and a Tomcat web service earlier last year, as an example of this, and I know there are others out there.

      Regarding the second part: The collected info would need to be persisted (stored) in some sort of database. For a relational database like MySQL, you’d need to create a table with a structure that matches the information you are storing for the user, and you’d need to write code that adds that data to your table. Typically there’s a java layer between your collected data and the database itself. Another option might be to store the data in a NoSQL database like MongoDB.

      But that’s a possible subject for a future blog post.

      I hope this helps.

      Mark

  41. Mark Hello, I created a button to send my REGID, using the POST method, the server did receive, thank you very much.
    Only a thin REGID thrown into my DB perfect.
    The part of the user will eventually come back to add the function of this part.

  42. i am getting this exception

    cannot retry due to server authentication, in streaming mode
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)

    1. Try to trace the value being passed to .getInputStream() that might be causing the “Unknown Source” response. That’s where I’d start.

  43. Hey,
    for some reason I cannot import your GCM Service file into Eclipse (works fine for the client). Are maybe the java files missing?
    Regards,
    Jonas

        1. Hi mark,

          At the beginning u mentioned that this was an incomplete tutorial. is it updated andif yes where can i find. How to do u handle the failure of messages. Moreover did u try implementing the new Google cloud messaging. Do u have a demo for both client and server for that and where ..Do let me know..

          thanks for your help

          1. An update to this tutorial is long overdue. I might put together some server-side code using JRuby, Rails and a REST library some day. Just need time to write it.

  44. Respected Sir,
    Sir, I have completed same code for client and server in two different projects in eclipse both runs and displays screens properly but when I type the message and click the submit to send this message to client.It gives error HTTP status 404 resource is not available. So Plz help to solve this error and send message to client.
    Regards,
    Pallavi Pathak

    1. It sounds like there may be an issue with the servlet mapping in the appropriate XML file, but I’d need to know more details of the error you’re getting.

  45. Thanks for the tutorial but I am getting following error on the tomcat server

    java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1126)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318)
    at com.google.android.gcm.server.Sender.sendNoRetry(Sender.java:362)
    at com.google.android.gcm.server.Sender.send(Sender.java:261)
    at com.temp.prj.web.GCMBroadcast.doPost(GCMBroadcast.java:88)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)

    I generated the Browser Key from the API Access screen on the Google apis Console
    I entered http://192.168.1.110:8080/* as Accept requests from these HTTP

    Am I missing anything ?

  46. Pingback: Homepage

Comments are closed.