Catégories
gwt Java

Hijacking GWT-RPC serialization

GWT, the Google Web Toolkit for client-side web programming, contains several client/server communication frameworks. The « traditional » one is called GWT-RPC. GWT-RPC encapsulates the serialization/deserialization of objects, and all the protocol plumbing.

This is usually fine for most use cases, but I needed to explicitely serialize objects on the client side, and deserialize them on the server side.

Use case : I need to call a servlet from my GWT application, to open a document (generated server-side) in a new windows – using window.open()

Solution : create a standard GWT-RPC, declaring a « fake » method containing all the parameter types that we need to explicitly serialize. This will generate a correct GWT « serialization policy ». Then, server side, implement the GWT Service servlet, and override the doGet with your code.

Create the standard GWT-RPC interfaces :

The interface declares a fake method, with the only goal to allow the type we want to add in the serialization process (here, we have OutputTypeEnum, RepositoryServiceEnum and FBBaseFilterPagingLoadConfig).

@RemoteServiceRelativePath("../fastbiz/service/recordUpload")
public interface RecordUploadService extends RemoteService {
 // Fake
 void fakeModel(OutputTypeEnum outputType, RepositoryServiceEnum repository, FBBaseFilterPagingLoadConfig config) throws FBException;
}

 
public interface RecordUploadServiceAsync {
 // Fake
 void fakeModel(OutputTypeEnum outputType,RepositoryServiceEnum repository,FBBaseFilterPagingLoadConfig config, AsyncCallback<Void> callback);
}

  • Client side :

// Creating factory. Interface of any remote service can be used
SerializationStreamFactory factory = (SerializationStreamFactory) GWT.create(RecordUploadService.class);

// Creating stream writer
SerializationStreamWriter writer = factory.createStreamWriter();

// Serializing object
String serializedOutputType = null;
String serializedRepositoryIdentifier = null;
try {
 writer.writeObject(outputType);
 // Getting serialized object content
 serializedOutputType = URL.encodeQueryString(writer.toString());

 writer = factory.createStreamWriter();
 writer.writeObject(listWindow.getRepositoryIdentifier());

 // Getting serialized object content
 serializedRepositoryIdentifier = URL.encodeQueryString(writer.toString());
 
 writer = factory.createStreamWriter();
 PagingLoadConfig config = listWindow.getConfig();
 writer.writeObject(config);
} catch (SerializationException e) {
 e.printStackTrace();
}

// Getting serialized object content
String serializedConfig = URL.encodeQueryString(writer.toString());
 
Window.open(Upload.EXPORT_BASE_URL + "?" + Upload.PARAM_OUTPUT_TYPE + "="
 + serializedOutputType + "&"
 + Upload.PARAM_REPOSITORY_IDENTIFIER + "="
 + serializedRepositoryIdentifier + "&"
 + Upload.PARAM_PAGING_LOAD_CONFIG + "="
 + serializedConfig, "_blank", null);

 

  • Server side :

You implement the service interface, and override the GET method that way:

 

public class RecordUploadServiceImpl extends RemoteServiceServlet implements com.fastbiz.client.service.RecordUploadService {
 
 private static final long serialVersionUID = 9107309941962048452L;
 
 @Override
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
 
 // Ensure the thread-local data fields have been initialized
 
 try {
  // Store the request & response objects in thread-local storage.
  synchronized (this) {
   if (perThreadRequest == null) {
    perThreadRequest = new ThreadLocal<HttpServletRequest>();
   }
   if (perThreadResponse == null) {
    perThreadResponse = new ThreadLocal<HttpServletResponse>();
   }
   perThreadRequest.set(request);
   perThreadResponse.set(response);
  }
  processGet(request, response);
 
 } catch (Throwable e) {
  // Give a subclass a chance to either handle the exception or
  // rethrow it
  doUnexpectedFailure(e);
 } finally {
  // null the thread-locals to avoid holding request/response
  perThreadRequest.set(null);
  perThreadResponse.set(null);
 }
 
 }
 
 protected void processGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  if (Upload.TYPE_LIST.equals(origin)) {
 
  // Initializing stream reader
  ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
  Thread.currentThread().getContextClassLoader(), this);
  RepositoryServiceEnum recordType;
  FilterPagingLoadConfig config;
  OutputTypeEnum outputType;
  try {
   // Filling stream reader with data
   streamReader.prepareToRead(request.getParameter(Upload.PARAM_OUTPUT_TYPE));
   // Reading deserialized object from the stream
   outputType = (OutputTypeEnum) streamReader.readObject();
  } catch (SerializationException e1) {
   throw new ServletException("Unable to deserialize parameters",
   new FBException(e1));
  }
  try {
   // Filling stream reader with data
   streamReader.prepareToRead(request.getParameter(Upload.PARAM_REPOSITORY_IDENTIFIER));
   // Reading deserialized object from the stream
   recordType = (RepositoryServiceEnum) streamReader.readObject();
  } catch (SerializationException e1) {
   throw new ServletException("Unable to deserialize parameters",
   new FBException(e1));
  }
  try {
   // Filling stream reader with data
   streamReader.prepareToRead(request.getParameter(Upload.PARAM_PAGING_LOAD_CONFIG));
   // Reading deserialized object from the stream
   config = (FilterPagingLoadConfig) streamReader.readObject();
  } catch (SerializationException e1) {
   throw new ServletException("Unable to deserialize parameters",
   new FBException(e1));
  }
 
 }
 
}
 
 @Override
 public void fakeBaseModel(OutputTypeEnum outputType, RepositoryServiceEnum repository, FBBaseFilterPagingLoadConfig config) throws FBException {
 }
 
}

Catégories
gwt Java

GWT et encoding UTF-8

Le framework ajax GWT est conçu pour fonctionner avec des données UTF-8. Cela peut avoir quelques conséquences pour les applications internationales au niveau des caractères spéciaux (accents français par exemple…).

Ayant butté longuement sur un problème de cette nature, je vous en livre la solution ici.

Mon application GWT réalise des appels RPC sur un serveur Tomcat. La première chose à faire, comme cela est bien précisé sur divers sites, est d’encoder systématiquement tous les fichiers sources de votre application en UTF-8, et non pas en Cp1252 par exemple. Dans Eclipse : click droit sur le projet dans le Package Explorer, puis Properties, puis Resource, sélectionnez Text file encoding : Other : UTF-8. Sur un autre IDE ou éditeur de fichier, vous trouverez l’équivalent.

La deuxième chose à faire, concerne la compilation du code pour le serveur (pour les servlets appelées par GWT) : il faut préciser javac -encoding utf8

Personnellement, j’utilise ant pour mes compilations, et donc la commande suivante :

<javac srcdir="${src.dir}" destdir="${dest.dir}" classpathref="server.lib" mce_href="server.lib" encoding="UTF-8">
<include name="..." />
</javac>

Catégories
Java

« Could not save master table » dans Eclipse

Suite à une erreur disque, Eclipse ne pouvait plus démarrer sur mon workspace.

L’erreur affichée était « Could not save master table ».

Après quelques recherches, la solution a été pour moi de supprimer le fichier %WORKSPACE%\.metadata\plugins\org.eclipse.core.resources\

safetable\org.eclipse.core.resources

Et après cela…. de réinsérer tous mes projets du workspace…

Du moins, les projets n’étaient pas cassés, c’est toujours ça!

Si cela peut servir à quelqu’un, welcome!

Catégories
ERP Java

ofbiz : l’ERP Open source

Depuis quelques temps, je m’intéresse de près à l’outil Open Source Ofbiz.

Il s’agit d’un ERP écrit en java sous licence Apache. Son orientation fonctionnelle actuelle est très orientée e-commerce : réalisation de sites web marchand. Il gère également le suivi facturation, stock, logistique, comptabilité derrière le site marchand lui-même.

Particularité intéressante, il dispose d’un module POS (Point Of Sale : Point de Ventes). Ce module offre la possibilité de gérer des appareils distants, temporairement connectés au système (donc synchronisés à la demande) pour la saisie de commande (PDA, caisses enregistreuses, …). Ce module POS est développé en client java swing, alors que le reste de l’application est bien entendu en web HTML conforme.

L’application existe depuis un certain nombre d’années (2001), bénéficie d’une comunauté de développeurs assez active, et de nombreux utilisateurs clients de prestige dans le monde. De plus, le projet a été très récemment rattaché à la fondation Apache en tant que projet TLP (Top Level Project). C’est un signe très clair de la qualité de l’architecture du projet et de son code. De plus, cette affiliation est pour moi fondamentale : elle signifie que ce produit est utilisable dans le cadre de projets clients (possibilité de vendre des modules complémentaires ou des produits s’appuyant sur l’outil).

J’ai donc mis mon nez dans le code du produit après avoir chargé sous Eclipse le dernier SVN. L’architecture projet est clair et propre. On trouve pas mal de documentation disponible sur le site, ainsi que des vidéos de formation (!). Je les aie visionnées le week-end dernier, elles offrent une bonne approche du sujet et valent la peine d’être vues.

Globalement, l’application m’apparaît plus comme un excellent framework que comme une application utilisable en clientèle out-of-the-box. En effet, tous les fondements techniques sont parfaitement intégrés de façon beaucoup plus clairs et propres que dans la plupart des progiciels propriétaires que je connaisse, par contre, l’interface utilisateur n’est pas très attrayante telle quelle. Elle est cependant facilement modifiable, notamment par un web-designer (HTML, CSS).

Quelques fondements techniques sont à la base de ce projet, et me paraissent assez sain dans le cadre d’une application de gestion. Le framework est centré sur

  • le modèle de données (l’entity engine) 
  • le développement de services pour les règles métiers (accessibles par divers moyens techniques, asynchrones ou non, utilisation possible d’un moteur de workflow)
  • l’utilisation de langages de templates au choix pour le frontal utilisateur

Tout cela n’est qu’un aperçu très bref du produit, et je vais désormais m’atteler à la rélisation d’une petite application de démonstration, avec interface Ajax.

Catégories
Java

Premiers retours d’expérience Eclipse Europa

La dernière version d’Eclipse viens de sortir. Il s’agit de la version 3.3, baptisé Europa par la fondation Eclipse. Celle-ci est sortie le 29 juin 2007.

Voici l’annonce : Eclipse Ships Largest-Ever Release of Leading Open Source Software Development Platform

Je travaille actuellement pour l’un de mes clients sur un projet java sous Eclipse, utilisant notamment WTP (Web Tools Platform), pour réaliser des web services (client et serveur). Je suis particulièrement intéressé par cette nouvelle version, car celle-ci inclus la version 2.0 de WTP (la précédente était sur 1.5). J’ai donc migré mes projets vers Europa et WTP 2.0.

Premier point qui m’intéresse tout particulièrement, c’est le passage à la version Axis 1.4 sur WTP 2.0, alors que l’on en était à Axis 1.3 précédemment => très utile pour la compatibilité avec les partenaires web services!

Sur le plan ergonomique, pour ce que j’en ai utilisé sur mon projet, aucune grosse révolution. Je retrouve mes petits sans problème.

La stabilité du produit…l’utilisation de WTP 1.5 provoquait de nombreux plantages d’Eclipse sur mon PC portable, dûs à des problèmes de mémoire. Eclipse fermait brutalement, parfois avec un petit message pour prévenir, mais pas toujours! Mon portable dispose de 1Go de RAM, j’avais bien entendu correctement configuré le fichier eclipse.ini en fonction (paramètres -Xmx256m -Xmx792m). Désormais, plus aucun problème de plantage sauvage à signaler! C’est toujours ça de pris…

Par contre…si le produit ne plante plus, j’ai de gros problèmes de stabilité dans mes projets web. Il m’arrive très régulièrement que mes projets soient marqués comme non valides, avec des erreurs de classpath notamment (des classes non trouvées par Eclipse provoquent des plantages de compilation), sans raison réelle. Solution = soit fermer/réouvrir le projet, soit fermer Eclipse et le réouvrir, soit forcer un rebuild du projet. Dans le même ordre de problème, j’utilise la gestion des serveurs de WTP, et aie configuré un serveur Tomcat 4.1. La publication ne se fait pas toujours bien : je suis parfois obligé de stopper Tomcat, faire un clean, republier mes projets (opération étrangement très longue). De plus, j’ai détecter un bug sur la publication : mon projet contient un répertoire de classe que j’ai ajouté à mon build path, et que j’ai coché comme exportable : ce répertoire n’est pas exporté dans la publication du projet sur le serveur. Je le copie donc à la main dans le répertoire de travail d’Eclipse (dans le répertoire du workspace sous .metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps).

Enfin, un autre problème que j’ai eu sur Europa : tous mes liens vers les URL de software updates avaient disparus! Impossible de faire les mises à jour automatiques, et comme je ne connaissais pas les URL, j’ai refait une installation d’Eclipse et exporté ces liens pour ne plus avoir cette mésaventure.

En résumé pour le moment, du pour et du contre. Les problèmes que j’ai rencontré et qui me font perdre beaucoup de temps existaient déjà largement en version précédente, donc je reste sur Europa et j’espère que des correctifs ne tarderont pas à sortir.