【问题标题】:Problems passing class objects through GWT RPC通过 GWT RPC 传递类对象的问题
【发布时间】:2010-10-31 19:51:31
【问题描述】:

我已经使用 EclipseGoogle Plugin 浏览了 Google Web Toolkit StockWatcher Tutorial,并且我正在尝试对其进行一些基本的更改,以便更好地理解 RPC 框架。

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    com.google.gwt.sample.stockwatcher.shared

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

    【讨论】:

      【解决方案2】:

      关闭上面 rustyshelf 的答案...

      就我而言,我需要编辑 ModuleName.gwt.xml 文件并添加以下内容:

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

      我使用新建->Web 应用程序项目 向导创建了我的项目,但未选中生成项目示例代码 选项。然后我创建了共享包。如果我没有取消选中,则会为我创建包并根据上述修改 xml 文件。

      【讨论】:

        【解决方案3】:

        是的,可以肯定的是,我们需要使用序列化来获取服务器对象到客户端。这些模块??文件设置无法在客户端使用 Stock 类。

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

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

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

        遇到这么大的情况怎么办?我不认为在客户端创建所有实现序列化的类对此有好处。然后我们必须在服务器和客户端中编写代码。所有课程两次。

        【讨论】:

          【解决方案4】:

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

          我必须解决的一些问题:

          1. 使从客户端发送到服务器的对象实现可序列化
          2. 为同一个对象添加一个空参数构造函数

          【讨论】:

            【解决方案5】:

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

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

            【讨论】:

              【解决方案6】:

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

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

              创建 Stock 类的客户端实现

              关于 GWT 有几点需要牢记:

              1. 服务器端类可以导入客户端类,但反之亦然(通常)。
              2. 客户端无法导入任何 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;
                }
              }
              

              修改客户端类以使用 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 Hosted Mode 中触发错误:

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

              【讨论】:

              • 供其他人评论:如果您想要 GAE 生成的 JDO id (@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)) 的 Key 或 Long 的情况如何?那可以在客户端对象中吗?如果不是,我们如何唯一识别客户端对象?
              • 好问题 dfrankow。我不得不在另一个项目中处理这个问题,所以我修改了上面的代码,以便将 ID 返回给 StockClient 类。基本思路是需要修改StockServiceImpl的addStock方法,返回ID为Long,然后需要添加代码更新StockClient类的ID。
              • 这是 DTO 方法。我不喜欢这种模式,因为它迫使开发人员为单个实体维护两个类。但遗憾的是,这似乎是使可持久对象与 GWT 一起使用的更简单方法......
              【解决方案7】:

              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>
              

              【讨论】:

              • 我在听你的回答时遇到了一些麻烦,rustyshelf - 你是说我应该在 com.google.gwt.sample.stockwatcher 中创建 Stock.gwt.xml,然后在 StockWatcher.gwt 中继承它。 xml?我用 试过了,但它似乎没有帮助。我似乎找不到任何关于 gwt.xml 文件如何工作的文档。
              【解决方案8】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-01-26
                • 1970-01-01
                • 2013-12-16
                • 2012-09-06
                • 1970-01-01
                • 1970-01-01
                • 2017-10-31
                • 1970-01-01
                相关资源
                最近更新 更多