miércoles, 21 de septiembre de 2011

Using JSNI in GWT

Se puede combinar fácilmente código JavaScript directamente en el código de GWT. Es todo JavaScript al final. Eso es acerca de lo que JSNI se trata. Es un nombre similar al Java Native Interface (JNI), ya que utiliza la misma idea básica: declarar un método Java "nativo" y luego usar otro lenguaje para implementarlo. En el caso de JSNI, ese otro lenguaje es JavaScript. :)

A los ejemplos...

Podemos mostrar un alert como usualmente lo hacíamos con JavaScript .. alert('This is an alert!');


public class MyApp implements EntryPoint {

     public void onModuleLoad() {

         Window.alert(’Showing alert from GWT’);
         showAlert("Some string...");

     }

     public native void showAlert(String param)/*-{

         $wnd.alert('Showing alert from JavaScript' + param);

     }-*/;

}

También podemos hacer el camino inverso... invocar métodos de Java desde JavaScript utilizando JSNI

public class BlueApp implements EntryPoint {

     public void onModuleLoad() {

          setShowTrigger(this);

     }

     public void runApp() {

          Window.alert(’I am a GWT function’);

     }

     public native void setShowTrigger(BlueApp x)/*-{

          $wnd.showBlueApp = function () {

               x.@com.company.client.BlueApp::runApp()();

          };

     }-*/;

}

Cuando se carga el módulo se crea una función JavaScript llamada showBlueApp(), y nada más. Esa función puede ser invocada desde la página HTML, ahí se ejecutará el método runApp(). Este está referenciado en JavaScript con su path completo el cual incluye además el nombre del módulo. La variable x es la clase misma y es enviada como parámetro usando “this”. Una vez invocado el alert aparece.
La función showBlueApp debería ser invocada como cualquier otra función JavaScript desde el HTML.


onclick="showBlueApp()"
   
Links útiles: - Tutorial en PDF
              - GWT Blog Article

Saludos. =)


miércoles, 7 de septiembre de 2011

GWT MenuBar - Handling Keyboard Events

Este problema se me presentó ayer y me parece bueno aportar en esto ya que todo lo que sea Menu/MenuBar/MenuItem en GWT no parece tener buen manejo de handlers, buen.. en cuanto a MenuItem directamente no lo tiene.

El problema en sí es el siguiente: ¿Que pasa si yo quiero capturar eventos del teclado junto con el click en algún item del menu?... El constructor de MenuItem espera un Command el cual va a ser ejecutado cuando se hace click en dicho elemento, pero no da la posibilidad de capturar otros eventos como Doble click, o teclas como Ctrl., Alt., Shift, etc.

La solución que encontré puede que no sea la mejor, pero resuelve el problema (:

Primero que nada creamos la clase MenuBarAdv que extiende de MenuBar


/**
 * MenuBarAdv.java
 * 06/09/2011 12:28:52
 * @author Cristian N. Miranda
 */
package ar.com.gwt.menu.events.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.MenuBar;

/**
 * @author Cristian N. Miranda
 */
public class MenuBarAdv extends MenuBar {

 private boolean ctrlKeyPressed;
 
 /**
  * Constructor
  */
 public MenuBarAdv() {
  super(false);
 }

 /**
  * Creates an empty menu bar.
  * 
  * @param vertical
  *            < code >true< /code > to orient the menu bar vertically
  */
 public MenuBarAdv(boolean vertical) {
  super(vertical, GWT.< Resources > create(Resources.class));
 }

 @Override
 public void onBrowserEvent(Event event) {
  MenuItemEvents.setCurrentMenuBarAdv(this);
  switch (DOM.eventGetType(event)) {
  case Event.ONCLICK: {
   if(event.getCtrlKey()){
    ctrlKeyPressed = true;
   }else{
    ctrlKeyPressed = false;
   }
   break;
  }
  }
  super.onBrowserEvent(event);
 }

 /**
  * Get Ctrl. Key Pressed Status
  * @author Cristian N. Miranda
  * @return
  */
 public boolean getCtrlKeyPressed(){
  return this.ctrlKeyPressed;
 }
 
}

Ahí es donde capturamos los eventos que se generen sobre el MenuBar con el método onBrowserEvent() ...

Luego en nuestro EntryPoint..

package ar.com.gwt.menu.events.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.RootPanel;

public class MenuItemEvents implements EntryPoint {
 
 private static MenuBarAdv currentMenuBarAdv;
 
 private final MenuBarAdv menutop = new MenuBarAdv();
 private final MenuBarAdv menutwo = new MenuBarAdv(true);
 
 public void onModuleLoad() {
 
  // A command for general use
  Command command = new Command(){
   @Override
   public void execute() {
    Window.alert("Ctrl. key pressed: " + String.valueOf(currentMenuBarAdv.getCtrlKeyPressed()));
   }
  };
  
  // Top-level menu
  menutop.addStyleName("demo-MenuItem");

  // Item to fire a command
  MenuItem fireone = new MenuItem("Fire One", command);

  // Level-two menu - vertical=true
  
  menutwo.addStyleName("demo-MenuItem");

  // Items for menu two
  MenuItem firetwo = new MenuItem("Fire Two", command);
  MenuItem firethree = new MenuItem("Fire Three", command);
  MenuItem firefour = new MenuItem("Fire Four", command);
  
  // Assemble the menu system
  menutop.addItem(fireone);            // Adds existing item
  menutop.addItem("SubMenu", menutwo); // Creates item and adds menutwo
  menutwo.addItem(firetwo);
  menutwo.addItem(firethree);
  menutwo.addItem(firefour);
  
  RootPanel.get().add(menutop);
  
 }
 
 /**
  * Obtains the current MenuBar clicked
  * @author Cristian N. Miranda
  * @param menuBarAdv
  */
 public static void setCurrentMenuBarAdv(MenuBarAdv menuBarAdv){
  currentMenuBarAdv = menuBarAdv;
 }
 
}

Ahi es donde obtenemos la instancia del MenuBarAdv clickeado y verificamos los flags de las teclas o cualquier otro evento deseado.. todo dentro de nuestro método execute() del Command para cada MenuItem.

Post en Google groups haciendo referencia al problema: http://code.google.com/p/google-web-toolkit/issues/detail?id=3559 (Titulado: MenuItem should better use ClickHandler instead of Command)
Código de ejemplo: http://code.google.com/p/gwt-menuitem-keyboard-events/downloads/list

Cualquier cosa avisan.
Saludos.

CKEditor Wrapper

Wrapper para GWT del editor de HTML "CKEditor" http://ckeditor.com
Es muy fácil de integrar a cualquier proyecto.. pueden seguir los pasos en el proyecto de Google code. (http://code.google.com/p/gwt-ckeditor/). Además de seguir todos los pasos, a los autores se les pasó por alto lo siguiente, para hacer que funcione correctamente (incluyendo en Dev mode) hay que agregar lo siguiente en nuestro App.html..

<script type="text/javascript" language="javascript" src="cksample/ckeditor/ckeditor_source.js"></script>
Les va a quedar algo así..



Acá un proyectito de ejemplo con los fuentes del Wrapper en caso de que no quieran usar la librería: http://code.google.com/p/gwt-ckeditor-wrap/downloads/list

Cualquier cosa chiflan.
Saludos. (:

martes, 30 de agosto de 2011

Java DAO Helper

Para mantener vivo el blog... :)
Este DAO Helper sirve para cualquier aplicación Java. La idea es tener algo de fácil uso para poder acceder a la base de datos de nuestra aplicación. Para poder utilizarlo necesitamos el driver específico para nuestra base de datos. En este ejemplo vamos a usar MySQL.

Modo de uso:

  1. Crear una aplicación Java.
  2. Añadir DaoHelper.java y DBConnection.java
  3. Personalizar la clase DBConnection.java con los datos de conexión de nuestra base de datos.
  4. Desde cualquier lado de nuestra aplicación podemos hacer lo siguiente...
try{
     
      QueryParameters params = new QueryParameters() {
         public void setParameters(PreparedStatement pst)
              throws SQLException {
              pst.setString(1, "Cristian");
         }
      };

      String query = "SELECT * FROM USERS WHERE USERNAME = ?";
      List< String > users = new DaoHelper< String >().executeQuery(
                query, params, new ResultReader< String >() {
          public String getResult(ResultSet result) 
                throws SQLException {
                return result.getString("nick");
          }
      });
   
   catch(SQLException e){
       e.printStackTrace();
   }

Link al proyecto: http://code.google.com/p/java-dao-helper/
Link para descargar los componentes: http://code.google.com/p/java-dao-helper/downloads/list

Saludos.

jueves, 18 de agosto de 2011

Dispatcher - Client/Server communication

Introducción al tema según Google...
All GWT applications run as JavaScript code in the end user's web browser. Frequently, though, you'll want to create more than just a standalone client-side application. Your application will need to communicate with a web server, sending requests and receiving updates.Each time traditional web applications talk to the web server, they fetch a completely new HTML page. In contrast, AJAX applications offload the user interface logic to the client and make asynchronous calls to the server to send and receive only the data itself. This allows the UI of an AJAX application to be much more responsive and fluid while reducing an application's bandwidth requirements and the load on the server.
     Resumiendo, lo que seguramente vamos a necesitar alguna vez, por ejemplo, es obtener información de una base de datos. Esto lo hacemos desde la capa server de nuestra aplicación con GWT. Lo que vamos a ver a continuación es una forma de efectuar la comunicación cliente-servidor para poder, luego de obtenidos los datos, manipularlos en la capa client.

     El ejemplo que armé lo único que hace es incrementar un contador cada vez que se clickea un botón, pero esta tarea la efectúa en la capa server.

Primero vamos a crear la clase IncrementCounterAction, la cuál nos va a servir de transporte para los parámetros.

/**
 * Action 
 * @author Cristian
 */
public class IncrementCounterAction implements Action< IncrementCounterResult > {
    private int amount;

    /** For serialization only. */
    IncrementCounterAction() {
    }

    public IncrementCounterAction( int amount ) {
        this.amount = amount;
    }

    public int getAmount() {
        return amount;
    }
}

Luego creamos la clase IncrementCounterResult, la cúal, como su nombre indica, va a contener los resultados de la operación.

/**
 * Result
 * @author Cristian
 */
public class IncrementCounterResult implements Result {
    private int amount;
    private int current;

    /** For serialization only. REQUIRED! **/
    protected IncrementCounterResult(){}

    public IncrementCounterResult( int amount, int current ) {
        this.amount = amount;
        this.current = current;
    }

    public int getAmount() {
        return amount;
    }

    public int getCurrent() {
        return current;
    }
}

Definimos nuestro IncrementCounterHandler, el cuál se encargará de realizar la operación de incremento al counter ya en la capa server.

/**
 * Handler 
 * @author Cristian
 */
public class IncrementCounterHandler implements ActionHandler< IncrementCounterAction ,  IncrementCounterResult > {

    private int current = 0;

    public Class< IncrementCounterAction > getActionType() {
        return IncrementCounterAction.class;
    }

    public synchronized IncrementCounterResult execute( IncrementCounterAction action, ExecutionContext context ) throws ActionException {
        current += action.getAmount();
        return new IncrementCounterResult( action.getAmount(), current );
    }

    public synchronized void rollback( IncrementCounterAction action, IncrementCounterResult result, ExecutionContext context ) throws ActionException {
        // Reset to the previous value.
        current = result.getCurrent() - result.getAmount();
    }
}

Luego registramos el Handler en la clase HandlerRegistry.

/**
 * Handler Registry 
 * @author Cristian
 */
public class HandlerRegistry {
	
	static InstanceActionHandlerRegistry register() {
		
		InstanceActionHandlerRegistry registry = new DefaultActionHandlerRegistry();
		
		registry.addHandler(new IncrementCounterHandler());
		
		return registry;
		
	}

}

Y ya estamos en condiciones de, desde la capa client, realizar el request.

/**
 * Main Module
 * @author Cristian
 */
public class GWTDispatcher implements EntryPoint {
	
	HorizontalPanel main = new HorizontalPanel();
	
	public void onModuleLoad() {
		
		main.setSpacing(10);
		
		Label label = new Label("Click 'Ok' to increment the counter");
		Button button = new Button("Ok");
		button.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				Dispatcher.execute( new IncrementCounterAction( 1 ), new AsyncCallback< IncrementCounterResult >() {
		            public void onFailure( Throwable e ) {
		                Window.alert( "Error: " + e.getMessage() );
		            }
		            public void onSuccess( IncrementCounterResult result ) {
		                Window.alert( "Incremented by " + result.getAmount() + ", new total is " + result.getCurrent() );
		            }
		        });
			}
		});
		
		main.add(label);
		main.add(button);
		
		RootPanel.get("nameFieldContainer").add(main);
		
	}
}



     Para verlo en acción pueden descargar el código desde acá

Saludos.

miércoles, 17 de agosto de 2011

GWT Prototype App - Part 1

Buenas!!
   
     Hoy les traigo un prototipo de aplicación con GWT 2.3 que va a ir evolucionando con el tiempo... o eso espero.

     Esta entrega contiene:
  • Dispatcher (comunicación cliente-servidor)
  • DaoHelper (Base de datos)
  • Drag and drop
  • ClientBundle
La idea era armar un esqueleto para tener a mano cuando se necesite. Se le van a ir agregando más cosas, pero para una primer versión me pareció bastante completo :)
El Dispatcher y el DaoHelper no están implementados pero si disponibles ahi para el uso desde cualquier lado (Dispatcher -> client , DaoHelper -> server).
     Mas adelante voy a subir el ejemplito aislado del Dispatcher y del DaoHelper, el que se anima lo extrae de este ejemplo :)

Vista previa...  Sino, click acá para abrirlo en un browser...



El proyecto lo subí a un repo que tengo en GitHub para que puedan clonarlo con Git :)
URL del repo: click acá
Para la gente anti-git: click acá

Saludos :)