java - Problemas al pasar objetos de clase a través de GWT RPC
google-app-engine gwt-rpc (8)
Aquí hay una respuesta mejor: problema de uso de GWT Simple RPC: código incluido
Básicamente, puede agregar parámetros a su archivo APPNAME.gwt.xml para que el compilador proporcione al compilador una ruta a la clase del servidor.
He revisado el tutorial de Google Web Toolkit StockWatcher con Eclipse y el complemento de Google , y estoy intentando hacer algunos cambios básicos para poder entender mejor el marco RPC.
Modifiqué el método "getStocks" en la clase del servidor StockServiceImpl para que devuelva una matriz de objetos Stock en lugar de objetos String. La aplicación compila perfectamente, pero Google Web Toolkit está devolviendo el siguiente error:
"No hay ningún código fuente disponible para el tipo com.google.gwt.sample.stockwatcher.server.Stock; ¿Olvidaste heredar un módulo requerido?"
Modo alojado de Google Web Toolkit http://i44.tinypic.com/a47r83.jpg
Parece que las clases del lado del cliente no pueden encontrar una implementación del objeto Stock, aunque la clase se haya importado. Como referencia, aquí hay una captura de pantalla de mi jerarquía de paquetes:
Jerarquía de paquetes de Eclipse http://i43.tinypic.com/14tr5gk.jpg
Sospecho que me falta algo en web.xml, pero no tengo idea de qué se trata. ¿Alguien puede señalarme en la dirección correcta?
EDITAR: Olvidé mencionar que la clase Stock es persistente, por lo que debe permanecer en el servidor.
Después de mucho ensayo y error, logré encontrar una manera de hacer esto. Puede que no sea la mejor manera, pero funciona. Con suerte, esta publicación puede ahorrarle a alguien más mucho tiempo y esfuerzo.
Estas instrucciones asumen que ha completado tanto el tutorial básico de StockWatcher como las modificaciones de Google App Engine StockWatcher .
Crear una implementación del lado del cliente de la clase de stock
Hay un par de cosas a tener en cuenta acerca de GWT:
- Las clases del lado del servidor pueden importar clases del lado del cliente, pero no viceversa (generalmente).
- El lado del cliente no puede importar ninguna biblioteca de Google App Engine (es decir, com.google.appengine.api.users.User)
Debido a los dos elementos anteriores, el cliente nunca puede implementar la clase Stock que creamos en com.google.gwt.sample.stockwatcher.server. En su lugar, crearemos una nueva clase Stock del lado del cliente llamada StockClient.
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;
}
}
Modifique las clases de cliente para usar StockClient [] en lugar de String []
Ahora hacemos algunas modificaciones simples a las clases de clientes para que sepan que la llamada RPC devuelve StockClient [] en lugar de 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:
Agrega una importación:
import com.google.gwt.sample.stockwatcher.client.StockClient;
El resto del código permanece igual, excepto addStock, loadStocks y 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());
}
});
}
Modificar la clase StockServiceImpl para devolver StockClient []
Finalmente, modificamos el método getStocks de la clase StockServiceImpl para que traduzca las clases Stock del lado del servidor en las clases StockClient del lado del cliente antes de devolver la matriz.
StockServiceImpl.java
import com.google.gwt.sample.stockwatcher.client.StockClient;
Necesitamos cambiar ligeramente el método addStock para que se devuelva la ID generada:
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();
}
Todos los demás métodos permanecen igual, excepto 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]);
}
Resumen
El código anterior funciona perfectamente para mí cuando se implementa en Google App Engine, pero desencadena un error en el modo alojado de 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
Avíseme si encuentra el mismo problema o no. El hecho de que funcione en Google App Engine parece indicar un error en el modo hospedado.
El compilador de GWT no sabe acerca de Stock, porque no está en una ubicación en la que se ve. Puedes moverlo a la carpeta del cliente, o si tiene más sentido, dejarlo donde está y crear un ModuleName.gwt.xml que hace referencia a cualquier otra clase que desee y obtiene su archivo Main.gwt.xml para heredar de eso.
eg: DomainGwt.gwt.xml
<module>
<inherits name=''com.google.gwt.user.User''/>
<source path="javapackagesabovethispackagegohere"/>
</module>
y:
<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>
Entrando en la respuesta de rustyshelf arriba ...
En mi caso, necesitaba editar el archivo ModuleName.gwt.xml y agregar lo siguiente:
<source path=''client''/>
<source path=''shared''/>
Creé mi proyecto con el asistente Nuevo-> Proyecto de aplicación web pero desmarqué la opción Generar código de muestra del proyecto . Luego creé el paquete compartido. Si no hubiera desmarcado eso, el paquete se habría creado para mí y el archivo xml se modificó según lo anterior.
GWT necesita el archivo .java además del archivo .class. Además, Stock debe estar en la ubicación "cliente" de un módulo GWT.
Hay una solución mucho más simple y fácil para eso. Si desea enviar un objeto de su clase diseñada a medida desde el lado del servidor al lado del cliente, debe definir esta clase personalizada en el paquete compartido .
Por ejemplo, para su caso, solo tiene que llevar la clase Stock.java (arrastrando y soltando) a
com.google.gwt.sample.stockwatcher.shared
paquete. Sin embargo, desde la captura de pantalla de la jerarquía de su paquete, parece que ha eliminado este paquete compartido. Simplemente vuelve a crear este paquete y suelta Stock.java dentro de él y deja que comience el juego.
Obtuve el mismo problema y la salida "mvn gwt: compilar" no fue muy útil. En cambio, cuando intenté implementarlo en tomcat (a través del plugin maven tomcat: mvn tomcat: deploy) recibí mensajes de error útiles.
Algunas cosas que tuve que arreglar:
- Hacer que el objeto que se envía desde el cliente al servidor implemente Serializable
- Agregue un constructor de arca vacía a ese mismo objeto
Sí, es seguro que necesitamos usar la serialización para obtener los objetos del servidor para el cliente. Estos modile? La configuración de archivos no funcionará para usar la clase Stock en el lado del cliente.
En su caso, solo tiene Stock de una clase y puede crear StockClient en el lado del cliente. Es fácil. Pero cuál será la solución si alguien tiene más clases. Algo así como las propiedades de esta clase son también otro tipo de clases.
Ejemplo: stock.getEOD(date).getHigh();
getEOD
devolverá otra clase con la fecha dada y esa clase tiene el método getHigh
.
¿Qué hacer en casos tan grandes? No creo que crear todas las clases implementando serialización en el lado del cliente sea bueno para eso. Luego tenemos que escribir el código tanto en el servidor como en el cliente. todas las clases dos veces.