多くの試行錯誤の後、私はこれを行う方法を見つけることができました。最善の方法ではないかもしれませんが、うまくいきます。この投稿が他の誰かの時間と労力を大幅に節約できることを願っています.
これらの手順は、基本的な StockWatcher チュートリアルとGoogle App Engine StockWatcher の変更の両方を完了していることを前提としています。
Stock クラスのクライアント側実装を作成する
GWT については、次の点に注意してください。
- サーバー側のクラスはクライアント側のクラスをインポートできますが、その逆はできません (通常)。
- クライアント側は Google App Engine ライブラリをインポートできません (つまり、com.google.appengine.api.users.User)
上記の両方の項目により、クライアントは、com.google.gwt.sample.stockwatcher.server で作成した Stock クラスを実装できません。代わりに、StockClient という新しいクライアント側の Stock クラスを作成します。
StockClient.java:
package com.google.gwt.sample.stockwatcher.client;
import java.io.Serializable;
import java.util.Date;
public class StockClient implements Serializable {
private Long id;
private String symbol;
private Date createDate;
public StockClient() {
this.createDate = new Date();
}
public StockClient(String symbol) {
this.symbol = symbol;
this.createDate = new Date();
}
public StockClient(Long id, String symbol, Date createDate) {
this();
this.id = id;
this.symbol = symbol;
this.createDate = createDate;
}
public Long getId() {
return this.id;
}
public String getSymbol() {
return this.symbol;
}
public Date getCreateDate() {
return this.createDate;
}
public void setId(Long id) {
this.id = id;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
}
String[] の代わりに StockClient[] を使用するようにクライアント クラスを変更する
ここで、RPC 呼び出しが String[] ではなく StockClient[] を返すことを認識できるように、クライアント クラスに簡単な変更を加えます。
StockService.java:
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.sample.stockwatcher.client.NotLoggedInException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@RemoteServiceRelativePath("stock")
public interface StockService extends RemoteService {
public Long addStock(String symbol) throws NotLoggedInException;
public void removeStock(String symbol) throws NotLoggedInException;
public StockClient[] getStocks() throws NotLoggedInException;
}
StockServiceAsync.java:
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.sample.stockwatcher.client.StockClient;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface StockServiceAsync {
public void addStock(String symbol, AsyncCallback<Long> async);
public void removeStock(String symbol, AsyncCallback<Void> async);
public void getStocks(AsyncCallback<StockClient[]> async);
}
StockWatcher.java:
1 つのインポートを追加します。
import com.google.gwt.sample.stockwatcher.client.StockClient;
addStock、loadStocks、displayStocks を除いて、他のすべてのコードは同じままです。
private void loadStocks() {
stockService = GWT.create(StockService.class);
stockService.getStocks(new AsyncCallback<String[]>() {
public void onFailure(Throwable error) {
handleError(error);
}
public void onSuccess(String[] symbols) {
displayStocks(symbols);
}
});
}
private void displayStocks(String[] symbols) {
for (String symbol : symbols) {
displayStock(symbol);
}
}
private void addStock() {
final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
// Stock code must be between 1 and 10 chars that are numbers, letters,
// or dots.
if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
Window.alert("'" + symbol + "' is not a valid symbol.");
newSymbolTextBox.selectAll();
return;
}
newSymbolTextBox.setText("");
// Don't add the stock if it's already in the table.
if (stocks.contains(symbol))
return;
addStock(new StockClient(symbol));
}
private void addStock(final StockClient stock) {
stockService.addStock(stock.getSymbol(), new AsyncCallback<Long>() {
public void onFailure(Throwable error) {
handleError(error);
}
public void onSuccess(Long id) {
stock.setId(id);
displayStock(stock.getSymbol());
}
});
}
StockClient[] を返すように StockServiceImpl クラスを変更する
最後に、StockServiceImpl クラスの getStocks メソッドを変更して、配列を返す前にサーバー側の Stock クラスをクライアント側の StockClient クラスに変換するようにします。
StockServiceImpl.java
import com.google.gwt.sample.stockwatcher.client.StockClient;
生成された ID が返されるように、addStock メソッドを少し変更する必要があります。
public Long addStock(String symbol) throws NotLoggedInException {
Stock stock = new Stock(getUser(), symbol);
checkLoggedIn();
PersistenceManager pm = getPersistenceManager();
try {
pm.makePersistent(stock);
} finally {
pm.close();
}
return stock.getId();
}
getStocks を除いて、他のすべてのメソッドは同じままです。
public StockClient[] getStocks() throws NotLoggedInException {
checkLoggedIn();
PersistenceManager pm = getPersistenceManager();
List<StockClient> stockclients = new ArrayList<StockClient>();
try {
Query q = pm.newQuery(Stock.class, "user == u");
q.declareParameters("com.google.appengine.api.users.User u");
q.setOrdering("createDate");
List<Stock> stocks = (List<Stock>) q.execute(getUser());
for (Stock stock : stocks)
{
stockclients.add(new StockClient(stock.getId(), stock.getSymbol(), stock.getCreateDate()));
}
} finally {
pm.close();
}
return (StockClient[]) stockclients.toArray(new StockClient[0]);
}
概要
上記のコードは、Google App Engine にデプロイすると完全に機能しますが、Google Web Toolkit のホスト モードではエラーが発生します。
SEVERE: [1244408678890000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.google.gwt.sample.stockwatcher.client.StockClient[] com.google.gwt.sample.stockwatcher.client.StockService.getStocks() throws com.google.gwt.sample.stockwatcher.client.NotLoggedInException' threw an unexpected exception: java.lang.NullPointerException: Name is null
同じ問題が発生したかどうかをお知らせください。Google App Engine で動作するという事実は、ホスト モードのバグを示しているようです。