Desarrollo de arquitectura multiplataforma
Unificación de interfaces Web, Móvil y Escritorio

Ignacio Baca Moreno-Torres
Alberto Mateos Checa
Alberto Jimenez Ruiz
Enero 2012

Introducción y planteamiento del problema

Idea Inicial

Aplicación de localización de surfistas

La aplicación debe tener los siguientes UI

Casos de uso usuario móvil

Casos de uso usuario web

Casos de uso usuario escritorio

Objetivos

Arquitectura del Sistema

Modulos del sistema


Artefactos del sistema (maven)


Modelo de datos del sistema


Servidor que debe hacer y como se ha implementado

¿Que debe hacer el servidor?

Modulos del servidor


Separación de responsabilidad , fachada de sesion

Separación de responsabilidad , capa de servicios

Separación de responsabilidad , capa de persistencia

Modulos, paquetes y clases

Nunca reinventar la rueda, usar herramientas disponibles

private static ThreadLocal<EntityManager> holder = 
           new ThreadLocal<EntityManager>() {
   @Override
   protected EntityManager initialValue() {
       throw new IllegalStateException(DomainContext.class.getName()
               + " no esta inicializado");
   }
};

org.inftel.socialwind.server.domain.DomainContext

Desarrollo centrado en test

@Test(expected = UserNotAuthenticatedException.class)
public final void testCurrentUserNotAuthenticated() {
  helper.setEnvIsLoggedIn(false);
  @SuppressWarnings("unused")
  Surfer current = SurferService.currentSurfer();
}

...socialwind.server.services.SurferServiceTest (main/test)

Cliente de Escritorio java SE y Swing

¿Que hace?

Arquitectura de la aplicación Swing

Persistencia, local y remota

Sincronización de evenews

Dos threads, uno para comunicación y otro para mostrar

// Blocking queue para esperar recibir mensajes
private final BlockingQueue<String[]> queue = new ArrayBlockingQueue<String[]>(10);
// offer, por si hay demasiados que se descarten
queue.offer(new String[] { title, message });
// take para que quede bloqueado hasta que reciba algo
String[] data = queue.take();

Cliente móvil

Por: Alberto Mateos Checa

Estructura de paquetes

MVC: Android vs JRE

MVC: Modelo

Uso de 3 modelos: hotspots, sesiones y perfil.


Hotspots Sesiones Perfil

MVC: Modelo

MVC: Modelo

Características

MVC: Modelo

MVC: Modelo

MVC: Controlador

MVC: Vista

MVC: Vista

MVC: Vista


Patrones utilizados

Tareas asíncronas

Objetivo: no congelar la UI

Uso de la clase AsyncTask

Client Web google web toolkit

Requisitos del Cliente Web

Objetivos del Cliente Web

Necesidad de una buena arquitectura:

Arquitectura del Cliente Web

Patrón Factory

Guarda Instancias estáticas de objetos pesados:

public class ClientFactoryImpl implements ClientFactory {
    /** Bus encargado de gestionar los distintos eventos de la aplicacion */
    private static EventBus eventBus;
    /** Vista encargada de mostrar el listados de playas */
    private static PlayasListView playasListView;
    /** Vista encargada de mostrar el perfil */
    private static PerfilView perfilView;    
    /** Objeto necesario para realizar la comunicacion con el servidor */
    private static SocialwindRequestFactory swrf;    
    /** Objeto necesario para realizar los cambios de actividad */
    private static PlaceController placeController;

Patrón Activity/Place

Se encarga de realizar el cambio entre actividades. Una actividad es un componente MVP.

@UiHandler("opIntro")
void showPanelIntroduccion(ClickEvent event) {
  clientFactory.getPlaceController().goTo(new IntroduccionPlace());
}
public Activity getActivity(Place place) {
  if (place instanceof PlayasListPlace)
    return new PlayasListActivity((PlayasListPlace) place, clientFactory, false);
  else if (place instanceof PerfilPlace)
    return new PerfilActivity((PerfilPlace) place, clientFactory);
           .....
}

Patrón Model-View-Presenter

Lleva a cabo la separación de responsabilidades.

La parte Presenter:
La parte View:

Patrón Model-View-Presenter

La parte Model:
public void onCargarListadoPlayas() {
    SpotRequest sr = swrf.spotRequest();
    sr.findAllSpots().with("location").fire(new Receiver>() {
        public void onSuccess(List response) {
            playasListView.addPlayas(response);
        }
        public void onFailure(ServerFailure error) {
            System.out.println(error.getMessage());
        }
    });
}

Componentes visuales

Separación de Funcionalidad:

Componentes visuales

Estructura de un componente:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <ui:style>
    </ui:style>
    <g:HTMLPanel>
        <h2>Listado de Playas:</h2>
        <g:FlowPanel ui:field="fpPlayas"></g:FlowPanel>
    </g:HTMLPanel>
</ui:UiBinder> 

Componentes visuales

Funcionalidad de un componente:

public class SesionListViewImpl extends Composite implements SesionListView {
  private static SesionListImplUiBinder uiBinder = GWT.create(SesionListImplUiBinder.class);
  interface SesionListImplUiBinder extends UiBinder {}    
  @UiField FlowPanel fpsesiones;

Internacionalización

Con esta arquitectura internacionalizar textos es muy simple:

  1. Los textos se encapsulan con la etiqueta <ui:msg>.
  2. <ui:msg>Panel Principal del ejemplo</ui:msg>
    
  3. GWT crea un archivo properties con los textos del componente.
  4. Hay que incluir el módulo de Internacionalización.
  5. <module>
    	<inherits name='com.google.gwt.i18n.I18N'/>
    	<extend-property name='locale' values='en_US, es'/>
    	<set-property-fallback name='locale' value='es'/>
    </module>
    

Dudas

Gracias!