Showing posts with label C2DM. Show all posts
Showing posts with label C2DM. Show all posts

Friday, 2 December 2011

Error: The RequestFactory ValidationTool must be run for GWT 2.4

When loading up the CloudTasks tutorial as downloaded from the Google website, I encountered the following Runtime error:

SEVERE: Unexpected error
java.lang.RuntimeException: The RequestFactory ValidationTool must be run for the com.cloudtasks.shared.CloudTasksRequestFactory RequestFactory type

I found this article:

http://code.google.com/p/google-web-toolkit/wiki/RequestFactoryInterfaceValidation

Here's an excerpt:
The RequestFactory annotation processor will validate the RequestFactory interface declarations and ensure that the mapping of proxy properties and context methods to their domain types is valid. The manner in which the errors are reported depends on the method by which the annotation processor is invoked.
In addition to validating the interfaces, the annotation processor also generates additional Java types which embed pre-computed metadata that is required by the RequestFactory server components. Users of RequestFactorySource must also run the annotation processor in order to provide the client code with obfuscated type token mappings. In the client-only case, the server domain types are not required.
If the validation process is not completed, a runtime error message will be generated:
The RequestFactory ValidationTool must be run for the com.example.shared.MyRequestFactory RequestFactory type
In GWT 2.4, RequestFactory interfaces must be validated before they can be used by the RequestFactory server code or JVM-based clients. I followed the instructions for Eclipse as follows:
  • Right-click on Project and select Properties > Java Compiler > Annotation Processing
  • Tick "Enable project specific settings"
  • Then make sure that both "Enable annotation processing" and "Enable processing in editor" are ticked.
  • Go to Factory Path underneath Annotation Processing in the left-hand menu
  • Click the button "Add External Jar"
  • Add the requestfactory-apt.jar file from your Eclipse distribution
  • Press Apply and the project will re-build.
The images on the above link ask you to add this file from GWT_TOOLS, but the text says to use your distribution. I found my file under:

C:\dev\eclipse-3.5.2\plugins\com.google.gwt.eclipse.sdkbundle_2.4.0.relr35v201110112027\gwt-2.4.0

Monday, 28 November 2011

Calling your own AppEngine Service methods

To test the operation of this I'm going to take a leaf out of the Task example and implement a Create button on the GWT front-end of the server, to make sure everything is wired up and working.

Add a new button to the web-page com.yrdomain.yrproject.client.YrprojectWidget.ui.xml

 <g:Button ui:field="createButton">Create</g:Button>
 
Open file com.yrdomain.yrproject.client.YrprojectWidget.java

Add the UI element:

@UiField
Button createButton;
 
And in the constructor, fire off a createGift() request.

createButton.addClickHandler(new ClickHandler() {
        public void onClick(ClickEvent event) {
            createButton.setEnabled(false);
            MygiftwishlistRequest mygwlRequest = requestFactory.mygiftwishlistRequest();
            mygwlRequest.createGift().fire(new Receiver<GiftProxy>() {
                @Override
                public void onFailure(ServerFailure error) {
                    createButton.setEnabled(true);
                    setStatus(error.getMessage(), true);
                }
                @Override
                public void onSuccess(GiftProxy response) {
                    createButton.setEnabled(true);
                    setStatus(response.getGiftLocation(), false);
                  }
                
            });
        }
    });
 
The RPC wizard automatically took the Gift.java Entity and created a GiftProxy, which it used in YrprojectRequest.java and added an entry for it in the MyRequestFactory.java (both in the shared folder).
Update the createGift() method to make it return something interesting - ok, just a variation on the HelloWorldService:

@ServiceMethod
    public Gift createGift() {
        
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();
        String message;
        if (user == null) {
          message = "No one is logged in!\nSent from App Engine at " + new Date();
        } else {
          message = "Howdie, " + user.getEmail() + 
"!\nSent from createGift on the App Engine at " + 
new Date();
        }
       
        Gift porsche = new Gift();
        porsche.setGiftName("sports car");
        porsche.setGiftLocation(message);
        
        return porsche;
    }
 
When we run the project, we see that when we press the Create button on the web-page, it calls the createGift() function on YrprojectService.java as expected.

Now we need to properly create Gifts and store them against the name of the user in the database.

RequestFactory implementation

This is a record of my implementation of the information contained in the following tutorial:

http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html

I started with the default implementation of a new AppEngine Connected Android Project and I have been following the tutorial on the Google website:

http://www.youtube.com/watch?v=M7SxNNC429U&feature=player_embedded#!

Sometimes I cut and paste text from the Google website, or change it around slightly, so credit to and acknowledgement to Google where I have done this. It just makes it easier as their terminology is correct.
I wanted to add some gifts (as its Christmas!) to my default AppEngine Connected Android Project. They chose Tasks, I thought Gifts would be more fun :)

First I created my Gift.java class - a simple class with three variables: giftName, giftLocation, giftPrice (all Strings for now) and the getters and setters.

Now the thing that makes this POJO interesting is that I added the annotation @Entity. This describes my class being able to be 'persisted to a data store such as a relational database or the Google App Engine Datastore'.

Gift.java

package com.yrdomain.yrproject.server;

import javax.persistence.*;
import javax.validation.constraints.*;

@Entity
public class Gift {

      private String giftName;
      private String giftLocation;
      private String giftPrice;

      @Id
      @Column(name = "id")
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;

      @Version
      @Column(name = "version")
      private Integer version;

    public String getGiftName() {
        return giftName;
    }
    public void setGiftName(String giftName) {
        this.giftName = giftName;
    }

    public String getGiftLocation() {
        return giftLocation;
    }
    public void setGiftLocation(String giftLocation) {
        this.giftLocation = giftLocation;
    }

    public String getGiftPrice() {
        return giftPrice;
    }
    public void setGiftPrice(String giftPrice) {
        this.giftPrice = giftPrice;
    }

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    public Integer getVersion() {
        return version;
    }
    public void setVersion(Integer version) {
        this.version = version;
    }
}
 
I was then able to use File > New > Google > RPC Service to generate me the necessary classes for the RPC service. Its an easy way to create the client-side code that does the basic CRUD operations. The wizard looks for all classes with persistence annotations (and finds Gift.java defined above) and uses this to create the following files in the same package:
  • GiftLocator.java
  • YrprojectService.java
  • YrprojectServiceLocator.java
and the proxy in the shared package:
  • GiftProxy.java
  • YrprojectRequest.java
and a new annotations package:
  • ServiceMethod.java
Lets have a look at some of these...

GiftProxy.java

The GiftProxy is the client-side representation (EntityProxy) of the server-side Gift (Entity). We associate it with the entity in the @ProxyFor annotation and link the persistence implementation with the GiftLocator class.
package com.yrdomain.yrproject.shared;

import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.ProxyForName;

@ProxyForName(value   = "com.yrdomain.yrproject.server.Gift",
              locator = "com.yrdomain.yrproject.server.GiftLocator")
public interface GiftProxy extends EntityProxy {

    String getGiftName();
    void setGiftName(String giftName);

    String getGiftLocation();
    void setGiftLocation(String giftLocation);
    String getGiftPrice();
    void setGiftPrice(String giftPrice);

    Long getId();

    Integer getVersion();
    void setVersion(Integer version);
}
 
GiftLocator.java

The generated file is for when you don't want to implement persistence code in an entity itself. However there are two small problems with the generated file, maybe because the API is so new?
Change Void id to Long id (Eclipse will suggest that you implement all unimplemented methods, which will in effect re-define this method)
@Override
    public Gift find(Class<? extends Gift> clazz, Long id) {
        // TODO Auto-generated method stub
        return null;
    }
Add the missing semi-colon :)
@Override
    public Long getId(Gift domainObject) {
    return domainObject.getId();
    }
 
YrprojectRequest.java

This is found with the GiftProxy.java class in the shared package. This is a RequestFactory service stub, and it must extend RequestContext and uses the @Service or @ServiceName annotations to name the associated service implementation class on the server. The methods in a service stub do not return entities directly, but rather return subclasses of com.google.web.bindery.requestfactory.shared.Request.
This allows the methods on the interface to be invoked asynchronously with Request.fire()from the client.
package com.yrdomain.yrproject.shared;

import java.util.List;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.ServiceName;

@ServiceName(value = "com.yrdomain.yrproject.server.YrprojectService", locator = "com.yrdomain.yrproject.server.YrprojectServiceLocator")
public interface YrprojectRequest extends RequestContext {

    Request<GiftProxy> createGift();
    Request<GiftProxy> readGift(Long id);
    Request<GiftProxy> updateGift(GiftProxy gift);
    Request<Void> deleteGift(GiftProxy gift);
    Request<List<GiftProxy>> queryGifts();
}
 
YrprojectService.java

package com.yrdomain.yrproject.server;

import java.util.List;
import com.yrdomain.yrproject.annotation.ServiceMethod;

public class YrprojectService {

    @ServiceMethod
    public Gift createGift() {
        // TODO Auto-generated method stub
        return null;
    }
    @ServiceMethod
    public Gift readGift(Long id) {
        // TODO Auto-generated method stub
        return null;
    }
    @ServiceMethod
    public Gift updateGift(Gift gift) {
        // TODO Auto-generated method stub
        return null;
    }
    @ServiceMethod
    public void deleteGift(Gift gift) {
        // TODO Auto-generated method stub
    }
    @ServiceMethod
    public List<Gift> queryGifts() {
        // TODO Auto-generated method stub
        return null;
    }
}
 
You'll notice that the Service class above imports a generated annotation class too:


package com.yrdomain.yrproject.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation on method specifying that the method is a service method
 * and needs to have the corresponding request factory code
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ServiceMethod {
}
 
YrprojectServiceLocator.java

package com.yrdomain.yrproject.server;

import com.google.web.bindery.requestfactory.shared.ServiceLocator;

public class YrprojectServiceLocator implements ServiceLocator {

    @Override
    public Object getInstance(Class<?> clazz) {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
 
That's enough for one post... next, making it do stuff!

Thursday, 24 November 2011

FATAL EXCEPTION: IntentService due to NullPointerException on PendingIntent.getActivity()

When sending a message to your Android device you use the default GWT template provided:

C2DM web service

You must have used a registered email address (see previous post) otherwise you'll get a Failure. If all is fine you'll get a Success: message sent message.
However, just because all things are rosy this side, doesn't mean that your phone has picked up the message:

11-24 17:37:35.586: E/AndroidRuntime(1339): FATAL EXCEPTION: IntentService[role@yrdomain.com]
11-24 17:37:35.586: E/AndroidRuntime(1339): java.lang.NullPointerException
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at android.app.PendingIntent.getActivity(PendingIntent.java:195)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at com.example.yourname.Util.generateNotification(Util.java:120)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at com.example.yourname.MessageDisplay.displayMessage(MessageDisplay.java:44)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at com.example.yourname.C2DMReceiver.onMessage(C2DMReceiver.java:82)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at com.google.android.c2dm.C2DMBaseReceiver.onHandleIntent(C2DMBaseReceiver.java:112)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at android.os.Handler.dispatchMessage(Handler.java:99)
11-24 17:37:35.586: E/AndroidRuntime(1339):     at android.os.Looper.loop(Looper.java:137)
11-24 17:37:35.586: E/AndroidRuntime(1339):   
at android.os.HandlerThread.run(HandlerThread.java:60)

This is due to the default code that comes with the standard New install of the Android Cloud to Device Messaging (C2DM):

    /**
     * Display a notification containing the given string.
     */
    public static void generateNotification(Context context, String message) {
        int icon = R.drawable.status_icon;
        long when = System.currentTimeMillis();

        Notification notification = new Notification(icon, message, when);
        notification.setLatestEventInfo(context, "C2DM Example", message,
                PendingIntent.getActivity(context, 0, null, PendingIntent.FLAG_CANCEL_CURRENT));
        notification.flags |= Notification.FLAG_AUTO_CANCEL;

        SharedPreferences settings = Util.getSharedPreferences(context);
        int notificatonID = settings.getInt("notificationID", 0);

        NotificationManager nm = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify(notificatonID, notification);

        SharedPreferences.Editor editor = settings.edit();
        editor.putInt("notificationID", ++notificatonID % 32);
        editor.commit();    

    }
 
Besides the fact that they really should be using the new Notification.Builder (introduced in Honeycomb API 11), this piece of code gives a NullPointerException because of the null value given where an Intent is expected.

PendingIntent.getActivity(context, 0, null, PendingIntent.FLAG_CANCEL_CURRENT));
 
All you have to do is add an Intent:

PendingIntent.getActivity(context, 0, new Intent(Util.DUMMY_PENDING_INTENT)
PendingIntent.FLAG_CANCEL_CURRENT));

You could use an existing one declared in Util.java, or you can create a new one like I did here called DUMMY_PENDING_INTENT. It doesn't matter because we are not going to use the Intent to start an activity, it's just an example to display a message on the notification bar. In a real program you would launch your app, just as an email notification launches your email app, etc.

Wednesday, 23 November 2011

Your First Connect to the Cloud Error: PHONE_REGISTRATION_ERROR

When you set up your new project you added in your role email address as in this image. This is the same role email address that you used to sign up for Android Cloud to Device Messaging. If you haven't done this yet then go to http://code.google.com/android/c2dm/signup.html and fill in the form and wait for the confirmation email before proceeding.

Configure C2DM

You build the projects and launch in Debug for the first time. All Android devices have the concept of a Google email address and password that is usually set up on the first day you have the phone. Thereafter you don't have to supply your email address and password every time you connect to a Google service. However, if you try to connect to the cloud using the role email address that you used to register with C2DM then you'll get the following error:

C2DM Connect to the Cloud Screen

11-23 15:39:11.444: E/C2DM(629): Registration error PHONE_REGISTRATION_ERROR
11-23 15:39:31.584: I/dalvikvm(629): threadid=3: reacting to signal 3
11-23 15:39:31.623: D/dalvikvm(629): threadid=1: still suspended after undo (sc=1 dc=1)
11-23 15:39:31.623: I/dalvikvm(629): Wrote stack traces to '/data/anr/traces.txt'
11-23 15:39:41.677: D/AndroidRuntime(629): Shutting down VM
11-23 15:39:41.677: W/dalvikvm(629): threadid=1: thread exiting with uncaught exception (group=0x409951f8)
11-23 15:39:41.854: E/AndroidRuntime(629): FATAL EXCEPTION: main
11-23 15:39:41.854: E/AndroidRuntime(629): java.lang.RuntimeException: Error receiving broadcast Intent { act=comexample.yourname.UPDATE_UI flg=0x10 } in com.example.yournameActivity$1@41058dd8
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:737)
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.os.Handler.handleCallback(Handler.java:605)
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.os.Handler.dispatchMessage(Handler.java:92)
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.os.Looper.loop(Looper.java:137)
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.app.ActivityThread.main(ActivityThread.java:4340)
11-23 15:39:41.854: E/AndroidRuntime(629):     at java.lang.reflect.Method.invokeNative(Native Method)
11-23 15:39:41.854: E/AndroidRuntime(629):     at java.lang.reflect.Method.invoke(Method.java:511)
11-23 15:39:41.854: E/AndroidRuntime(629):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
11-23 15:39:41.854: E/AndroidRuntime(629):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
11-23 15:39:41.854: E/AndroidRuntime(629):     at dalvik.system.NativeStart.main(Native Method)
11-23 15:39:41.854: E/AndroidRuntime(629): Caused by: java.lang.NullPointerException
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.app.PendingIntent.getActivity(PendingIntent.java:195)
11-23 15:39:41.854: E/AndroidRuntime(629):     at com.example.yourname.Util.generateNotification(Util.java:120)
11-23 15:39:41.854: E/AndroidRuntime(629):     at com.example.yournameActivity$1.onReceive(yournameActivity.java:81)
11-23 15:39:41.854: E/AndroidRuntime(629):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:728)
11-23 15:39:41.854: E/AndroidRuntime(629):     ... 9 more

This is because you are trying to connect without a proper gmail address. If you have connected up your live device, choose another email address such as your personal one to test. If you are using the simulator, you need to go into the Settings and add another google account - again add your personal one. You'll then have success :)

C2DM Contacting server

C2DM Successful response from App Engine