通过 GWT RPC 传递类对象时出现问题

2022-09-02 09:44:17

我已经使用EclipseGoogle插件运行了Google Web Toolkit StockWatcher教程,并且我正在尝试对其进行一些基本更改,以便我可以更好地理解RPC框架。

我已经修改了 StockServiceImpl 服务器端类上的 “getStocks” 方法,以便它返回 Stock 对象数组而不是 String 对象。应用程序编译完美,但 Google Web 工具包返回以下错误:

“没有源代码可用于类型 com.google.gwt.sample.stockwatcher.server.Stock;您是否忘记继承所需的模块?

Google Web Toolkit Hosted Mode

客户端类似乎找不到 Stock 对象的实现,即使该类已被导入也是如此。作为参考,这是我的包层次结构的屏幕截图:

Eclipse Package Hierarchy

我怀疑我在网络中遗漏了一些东西.xml,但我不知道它是什么。任何人都可以为我指出正确的方向吗?

编辑:忘了提一下 Stock 类是持久的,所以它需要留在服务器端。


答案 1

经过多次尝试和错误,我设法找到了一种方法来做到这一点。这可能不是最好的方法,但它有效。希望这篇文章可以节省其他人大量的时间和精力。

这些说明假定您已完成 StockWatcher 基本教程Google App Engine StockWatcher 修改

创建 Stock 类的客户端实现

关于GWT,有几件事要记住:

  1. 服务器端类可以导入客户端类,但反之亦然(通常)。
  2. 客户端无法导入任何Google App Engine库(即.com google.appengine.api.users.User)

由于上述两项,客户端永远无法实现我们在 com.google.gwt.sample.stockwatcher.server 中创建的 Stock 类。相反,我们将创建一个名为 StockClient 的新客户端 Stock 类。

股票客户.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;
  }
}

修改客户端类以使用 StockClient[] 而不是 String[]

现在我们对客户端类进行一些简单的修改,以便它们知道 RPC 调用返回 StockClient[] 而不是 String[]。

库存服务.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);
}

股票观察家.java:

添加一个导入:

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());
        }
    });
}

修改 StockServiceImpl 类以返回 StockClient[]

最后,我们修改 StockServiceImpl 类的 getStocks 方法,以便它在返回数组之前将服务器端 Stock 类转换为客户端 StockClient 类。

股票服务简介.java

import com.google.gwt.sample.stockwatcher.client.StockClient;

我们需要稍微更改 addStock 方法,以便返回生成的 ID:

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中工作的事实似乎表明托管模式下存在错误。


答案 2

GWT 除了.class文件外,还需要.java文件。此外,Stock 需要位于 GWT 模块的“客户端”位置。


推荐