通过GWT RPC传递类对象的问题

时间:2009-06-06 02:48:28

标签: java google-app-engine gwt gwt-rpc

我使用StockWatcher TutorialEclipse运行了Google Web Toolkit Google Plugin,我正在尝试对其进行一些基本更改,以便我更好地理解RPC框架。

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

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

Google Web Toolkit Hosted Mode

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

Eclipse Package Hierarchy

我怀疑我在web.xml中遗漏了一些内容,但我不知道它是什么。有人能指出我正确的方向吗?

编辑:忘记提及Stock类是可持久的,因此它需要留在服务器端。

8 个答案:

答案 0 :(得分:23)

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

这些说明假设您已完成basic StockWatcher tutorialGoogle App Engine StockWatcher modifications

创建Stock Class的客户端实现

关于GWT,有几点需要注意:

  1. 服务器端类可以导入客户端类,但反之亦然(通常)。
  2. 客户端无法导入任何Google App Engine库(即com.google.appengine.api.users.User)
  3. 由于上述两个项目,客户端永远不能实现我们在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;
      }
    }
    

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

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

    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:

    添加一个导入:

    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类。

    StockServiceImpl.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中运行的事实似乎表明托管模式中存在错误。

答案 1 :(得分:4)

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

答案 2 :(得分:3)

GWT编译器不知道Stock,因为它不在它所查找的位置。你可以将它移动到客户端文件夹,或者如果它更有意义就把它放在原处并创建一个ModuleName.gwt .xml引用您想要的任何其他类,并从中继承您的Main.gwt.xml文件。

例如:DomainGwt.gwt.xml

<module>
    <inherits name='com.google.gwt.user.User'/>
    <source path="javapackagesabovethispackagegohere"/>
</module>

和:

<module rename-to="gwt_ui">
    <inherits name="com.google.gwt.user.User"/>
    <inherits name="au.com.groundhog.groundpics.DomainGwt"/>

    <entry-point class="au.com.groundhog.groundpics.gwt.client.GPicsUIEntryPoint"/>
</module>

答案 3 :(得分:1)

这里有一个更好的答案:GWT Simple RPC use case problem : Code included

基本上,您可以向APPNAME.gwt.xml文件添加参数,以便编译器为编译器提供服务器端类的路径。

答案 4 :(得分:1)

我遇到了同样的问题,“mvn gwt:compile”输出效果不是很好。 相反,当我尝试部署到tomcat(通过maven tomcat插件:mvn tomcat:deploy)时,我收到了有用的错误消息。

我必须解决一些问题:

  1. 使从客户端发送到服务器的对象实现Serializable
  2. 将空arg构造函数添加到同一个对象

答案 5 :(得分:0)

是的,我们确定需要使用序列化来获取客户端的服务器对象。这些莫代尔?文件设置无法在客户端使用Stock类。

在您的情况下,您只有一个类库存,您可以在客户端创建StockClient。这很容易。但是,如果有人有更多课程,那将是什么解决方案。类似于这个类的属性也是一些其他类型的类。

示例:stock.getEOD(date).getHigh();

getEOD将返回具有给定日期的另一个类,该类具有getHigh方法。

在这么大的案件中该怎么办?我不认为在客户端创建实现序列化的所有类都是有益的。然后我们必须在服务器和客户端编写代码。所有班级两次。

答案 6 :(得分:0)

以上关于rustyshelf的答案......

在我的情况下,我需要编辑ModuleName.gwt.xml文件并添加以下内容:

<source path='client'/>
<source path='shared'/>

我使用 New-&gt; Web应用程序项目向导创建了我的项目,但未选中生成项目示例代码选项。然后我创建了共享包。如果我没有取消选中,那么就会为我创建包,并根据上面的内容修改xml文件。

答案 7 :(得分:0)

有一个更加简单易行的解决方案。如果要将自定义设计类的对象从服务器端发送到客户端,则应在共享包中定义此自定义类。

例如,对于您的情况,您只需携带Stock.java类(通过拖放)进入

  

com.google.gwt.sample.stockwatcher.shared

包。但是,从您的包层次结构截图中,您似乎已删除此共享包。只需重新创建此包并将Stock.java放入其中并让游戏开始。