views:

3880

answers:

7

I just start playing with GWT I'm having a really hard time to make GWT + JAVA + JDO + Google AppEngine working with DataStore. I was trying to follow different tutorial but had no luck. For example I wend to these tutorials: TUT1 TUT2

I was not able to figure out how and what i need to do in order to make this work. Please look at my simple code and tell me what do i need to do so i can persist it to the datastore:

1. ADDRESS ENTITY

package com.example.rpccalls.client;

import java.io.Serializable;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

public class Address implements Serializable{

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private int addressID;
 @Persistent private String address1;
 @Persistent private String address2;
 @Persistent private String city;
 @Persistent private String state;
 @Persistent private String zip;

 public Address(){}

 public Address(String a1, String a2, String city, String state, String zip){
  this.address1 = a1;
  this.address2 = a2;
  this.city = city;
  this.state = state;
  this.zip = zip;
 }

 /* Setters and Getters */
}

2. PERSON ENTITY

package com.example.rpccalls.client;

import java.io.Serializable;
import java.util.ArrayList;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable
public class Person implements Serializable{

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Key key;
 @Persistent private String name;
 @Persistent private int age;
 @Persistent private char gender;
 @Persistent ArrayList<Address> addresses;

 public Person(){}

 public Person(String name, int age, char gender){
  this.name = name;
  this.age = age;
  this.gender = gender;
 }

 /* Getters and Setters */
}

3. RPCCalls

package com.example.rpccalls.client;

import java.util.ArrayList;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;


public class RPCCalls implements EntryPoint {

 private static final String SERVER_ERROR = "An error occurred while attempting to contact the server. Please check your network connection and try again.";

 private final RPCCallsServiceAsync rpccallService = GWT.create(RPCCallsService.class);

 TextBox nameTxt = new TextBox();
 Button btnSave = getBtnSave();

 public void onModuleLoad() {

  RootPanel.get("inputName").add(nameTxt); 
  RootPanel.get("btnSave").add(btnSave);
 }



 private Button getBtnSave(){

  Button btnSave = new Button("SAVE");

  btnSave.addClickHandler(
    new ClickHandler(){
     public void onClick(ClickEvent event){
      saveData2DB(nameTxt.getText());
     }
    } 
  );
  return btnSave;
 }

 void saveData2DB(String name){  
  AsyncCallback<String> callback = new AsyncCallback<String>() {
   public void onFailure(Throwable caught) {
          Window.alert("WOOOHOOO, ERROR: " + SERVER_ERROR);
    // TODO: Do something with errors.
        }

        public void onSuccess(String result) {
          Window.alert("Server is saying: ' " + result + "'");
        }

  };

  ArrayList<Address> aa = new ArrayList<Address>();
  aa.add(new Address("123 sasdf","", "Some City", "AZ", "93923-2321"));
  aa.add(new Address("23432 asdf", "Appt 34", "Another City", "AZ", "43434-4432"));

  Person p = new Person();
  p.setName(name);
  p.setAge(23);
  p.setGender('m');
  p.setAddresses(aa);

  // !!!!!!!!!!!!!!!!!!  SERVER CALL !!!!!!!!!!!!!!!!!!
  rpccallService.saveName(p, callback);
  // !!!!!!!!!!!!!!!!!!  SERVER CALL !!!!!!!!!!!!!!!!!!

 }
}

4. RPCCallsService

package com.example.rpccalls.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("calls")
public interface RPCCallsService extends RemoteService {

 String saveName(Person p);

}

5. RPCCallsServiceAsync

package com.example.rpccalls.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface RPCCallsServiceAsync {

 void saveName(Person p, AsyncCallback<String> callback);

}

6. **RPCCalls.gwt.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd"&gt;
<module rename-to='rpccalls'>          
  <inherits name='com.google.gwt.user.User'/>
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <entry-point class='com.example.rpccalls.client.RPCCalls'/>
</module>

I tried to add Key class and everything else in those tutorials but it looks like i'm missing something.

Here is my error: alt text

or before i was getting this error:

Key cannot be resolved to a type

What is the best solution to make this working?

A: 

Another option would be to implement a DTO ( Data Transfer Object ) that you are using in the client instead of using the persistent objects directly. Or, you could go to JPA instead of JDO. In the example data class in the appengine JPA docs the Id is a Long instead of that Key implementation http://code.google.com/appengine/docs/java/datastore/usingjpa.html

digitaljoel
That's the tutorial i tried to follow (TUT2 link on the top). Try to read my post first and then respond.Thanks tho.
Maksim
Sorry about the original answer, you are correct it was not helpful. I read the question, but didn't follow the links.
digitaljoel
Yeah, i was just able to create JPA to work but how do i pass objects from/to GWT?
Maksim
You'll want to checkout the GWT developer's guide on server communication at http://code.google.com/webtoolkit/doc/1.6/DevGuideServerCommunication.html Their method in the example passes a String as the parameter and the return type, but you can pass and return any object that adheres to their serialization rules which are also in that same document.
digitaljoel
That said, you'll want to look at rustyshelf's answer to your TUT1 link on top about where to put the types you are using for client/server communication and how to get GWT to see them.
digitaljoel
this post has some good info about passing (or not passing) persistent objects from the server to the client. From the items there, it looks like you will definitely want to use DTOs http://stackoverflow.com/questions/749522/google-web-toolkit-gwt-google-app-engine-gae-detached-data-persistence
digitaljoel
A: 

Could it be that you forgot to create the implementation for the RPCCallsService? I can't see it from the list of files that you have.

You should have a file called RPCCallsServiceImpl.java in RPCCalls/src/com/example/rpccalls/server/, it is the implementation file for the interface RPCCallsService.java.

It will look something like this:

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.example.rpccalls.client.RPCCallsService;

public class RPCCallsServiceImpl extends RemoteServiceServlet implements RPCCallsService {

  // Factory to get persistence manager object later
  private static final PersistenceManagerFactory PMF = JDOHelper.getPersistenceManagerFactory("transactional-optional");

  public String saveName(Person p) {
    // Data Store need persistence manager object for writing to it 
    PersistenceManager pm = PMF.getPersistenceManager();

    // Recommended way to save an object to the data store
    try {
      pm.makePersistent(p);
    } finally {
      pm.close();
    }

    // You want it to return string
    return p.getName();
  }

}

Hopefully this help you to solve the problem. Cheers :)

SamChandra
+3  A: 

The second tutorial you've referenced has a section on shadowing the com.google.appengine.api.datastore.Key class, since it's not available to GWT:

Since I'm not doing anything with the Key class on the client I'm going to stub it out. This actually requires a few steps and involves the super-src feature of GWT XML module files.

You might want to take a look at the GWT documentation, which states that

The heart of GWT is a compiler that converts Java source into JavaScript

, therefore you need to have the source code available to use a given class in the client code.

Robert Munteanu
This is a common mistake since you can basically have java.lang and java.util classes on the client side of your app. In this way you wont be able to send Key or User objects to the client side for example.
Otavio
The second tutorial is a dastardly hack. Is there any better way?
dfrankow
+4  A: 

Sriran Narayan says to String-encode the Key to get it to pass through GWT's RPC mechanism:

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class SomeDomainClass implements Serializable {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
String id;

dfrankow
+1  A: 

Once you're fed up with JDO, take a look at objectify. I've found it to be a lot nicer to work with, and it has full GWT interop without DTOs.

Sudhir Jonathan
A: 

You can use the Key class in GWT code by adding these additional jar files:

http://www.resmarksystems.com/code/

  • appengine-utils-client-1.0.jar
  • appengine-utils-server-1.0.jar

This basically gives the GWT compiler a GWT-friendly version of the Key and other AppEngine classes. (like Text, Blob and User..)

To use:

  • Add the appengine-utils-client-1.0.jar anywhere in your build path.
  • Put the appengine-utils-server-1.0.jar in your WEB-INF/lib folder.
  • Add the following to your GWT module:
    • < inherits name="com.resmarksystems.AppEngineDataTypes"/>
nick