You are on page 1of 50

1/50

Endpoints en Android
Hoy en da es comn que una aplicacin tenga que desarrollarse para distintas plataformas. Para evitar repetir
cdigo, se desarrollan interfaces grficas especficas para la plataforma y se conectan a la lgica, que se pone
en un servidor. Normalmente el cdigo en el servidor se conoce como servicio, que es un grupo de funciones
que no mantienen un estado, es decir, no tienen variables que compartir entre sus funciones.
Los endpoints son un tipo de servicios que pueden accederse desde Android, iOs y web Estn inspiradas por
una tecnologa previa, conocida como RMI y otra, conocida como RESTful. Corren en el servidor conocido
como App Engine. Android Studio incluye herramientas para manejar fcilmente esta tecnologa.

1. Planteamiento del Problema.


Realizar un programa web y otro mvil que reciben el nombre de 2 personas y devuelven un saludo que
incluya a las dos.
Se construyen dos programas. Uno con tecnologa Android y otro con tecnologa web, la generacin del
saludo se hace en un endpoint.
Los casos de uso y el anlisis son los mismos que en D202mvc.

2. Diseo.
Las pruebas son las mismas que en D202mvc.

2.1. Consideraciones.
1. El presentador se divide en dos. La parte encargada de recuperar los datos de la interfaz, se coloca en
los clientes web y Android. En el servidor web se coloca un presentador que es independiente de la
tecnologa utilizada para capturar los datos y realiza la lgica del negocio. Se utiliza el mecanismo
conocido como endpoint y se codifica en Java.
2. El endpoint recibe y devuelve datos en formato Json.
3. Android Studio genera artefactos de software especializados en la conexin al endpoint, que se
conocen como proxy, que significa representante. Ms informacin sobre este patrn de diseo se
puede encontrar en http://www.oodesign.com/proxy-pattern.html. Para las aplicaciones web, se
genera un objeto de JavaScript y para Android se generan clases de Java. Los dos tipos de artefactos
se parecen mucho a las clases usadas por el endpoint, pero su funcin solamente es enviar y recibir
datos. Tienen algunos cambios que facilitan el control de la conexin, generacin de datos en formato
Json y recuperacin de los datos recibidos en este formato.
Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

2/50

4. En web, el cdigo de los proxy debe descargarse del servidor App Engine y de otro sitio de Google.
5. Para Android, los proxy se generan desde Android Studio cada vez que se hace build al proyecto web
y son empacados en archivos jar.
6. Los proxy son generados desde un servicio web, por lo que es necesario tener acceso a Internet para
poder compilar los cdigos web y Android.

2.2. Diseo del Endpoint


2.2.1. Diagrama de Secuencia

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

3/50

2.2.2. Diagrama de Clases

2.3. Diseo Web


Se utiliza la Web Application Extension versin 2 (WAE2) para UML. Define algunos estereotipos que
normalmente se aplican a clases y a componentes de UML. Los estereotipos que se usan en este ejemplo son:
Client Page. Pginas web en formato HTML. Es una mezcla de datos, presentacin y lgica.

HTML Form. Es una coleccin de campos de entrada que son parte de una client page.

Script Library. Conjunto de funciones y variables de JavaScript.

Client Script Object. Un objeto de JavaScript que tiene definido un prototype y usualmente define
un nmero de variables y funciones miembro.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

4/50

2.3.1. Diagrama de Secuencia


En parecido al del ejemplo D202MVC, pero el objeto de tipo proxyEndpointSaludo crea un objeto de
tipo saluda, que se encarga de enviar la los datos a una instancia de EndpointSaludo para que realice el
clculo y devuelva el resultado. Los datos se envan de forma asncrona; es decir, liberando el hilo de
ejecucin al inicio del envo, permitiendo que se puedan realizar otras cosas en la interfaz del navegador web.
La transferencia se realiza en otro hilo. Cuando el resultado llega, se invoca otra vez de forma asncrona la
funcin recibeRespuestaSaludo, pero esta vez en el hilo de la interfaz grfica, para mostrar la respuesta del
endpoint.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

5/50

2.3.2. Diagrama de Clases


La pgina index se comporta como vista e incluye una forma, un script que funciona como presenter y otro,
llamado client, que coordina la conexin con el endpoint. Este ltimo script, solicita al presenter que
descargue los proxy que estn en el servidor web.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

6/50

2.4. Diseo Android


2.4.1. Clases Proxy Generadas por Android Studio.
El centro de las clases proxy es ProxyEndpointSaludo. Se crea utilizando instancias de su clase interna
Builder, a la cual se le configuran distintos parmetros de la conexin y al ejecutar el mtodo build, se crea
la instancia de ProxyEndpointSaludo.
Se crea tambin un paquete con las clases que sirven como modelo y son una versin modificada de las
usadas en el endpoint. Estn especializadas para manejas el formato JSON.
El mtodo saluda de ProxyEndpointSaludo, se parece al mtodo del endpoint. Recibe el parmetro de tipo
Nombres (en el diagrama que sigue no se muestra por cuestiones de espacio), pero devuelve un objeto de la
clase interna Saluda, el cual se encarga de conectarse al endpoint, invocar el mtodo saluda y recibir la
respuesta de tipo Saludo.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

7/50

2.4.2. Clases Android


En el sistema operativo Android, es obligatorio que la conexin a Internet se lleve a cabo en un hilo distinto
al que utiliza la interfaz grfica. Esto se puede hacer con una instancia de AsyncTask, que tiene un mtodo
de nombre doInBackground, el cual se ejecuta en un hilo aparte. Cuando se obtiene la respuesta del
servidor, se invoca en el hilo de la interfaz grfica el mtodo onPostExecute, que recibe el valor devuelto por
doInBackground.
La conexin al servidor puede tardarse lo suficiente para rotar el dispositivo; si esto sucede, la activity que
inici la transferencia se destruye y se crea otra en su lugar. Para evitar esto, se usa un fragment de tipo
retain instance.
Un fragment representa el comportamiento de una parte de la interfaz de usuario de una activity. Puede tener
asociada una vista y conservarse despus de rotar la interfaz grfica.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

8/50

2.4.3. Diagrama de Secuencia

3. Componentes
Los componentes son partes modulares de un sistema con contenido encapsulado y cuya manifestacin (o
implementacin) es reemplazable. Las clases que se utilizan para comunicar los mdulos estn marcados con
conectores circulares.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

9/50

4.Deployment

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

10/50

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

11/50

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

12/50

7. Implementacin
7.1. Creacin de Proyecto en App Engine
Entra a https://console.developers.google.com para dar de alta un proyecto. Cuando no has iniciado sesin,
aparece una pgina de inicio de sesin. Cambia el idioma a Espaol (Latinoamrica).

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

13/50

Si tienes una cuenta de Google, sala para entrar a sesin. Si no la tienes, Haz clic en Crear cuenta.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

14/50

Introduce los datos solicitados y sigue las instrucciones. El nmero de celular es +52 1 seguido de los 10
dgitos del nmero telefnico.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

15/50

Al acceder a tu cuenta, aparece la pantalla de inicio de sesin. Si te ofrece una prueba gratis, tienes la opcin
de rechazarla (DISMISS) o aceptar (SIGN UP FOR FREE TRIAL). En este caso se selecciona DISMISS.

Cuando no tienes proyectos, te aparece una ventana para crear uno.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

16/50

Otra opcin es el combo de proyectos. Haz clic en l y luego en Create a project...

Elige un nombre nico para la aplicacin. La url del proyecto comienza con este identificador, seguida de
.appspot.com. No debe llevar espacios, mnimo 6 caracteres. El sitio verifica automticamente que el
nombre sea vlido. El nombre de proyecto que aqu se muestra es solo de ejemplo. Tu debes seleccionar
un identificador diferente. Lee los Terms of Service y si ests de acuerdo con ellos, selecciona I accept
these terms. Finalmente haz clic en Create.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

17/50

Aparece la pgina del proyecto.

Crea un proyecto de Android Studio de acuerdo al proyecto de App Engine.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

18/50

La activity tambin es Empty Activity. En Activity Name introduce ActivitySaludo y en Layout Name
teclea view_saludo.
Selecciona el men File > New > New Module...

Selecciona Google Cloud Module y haz clic en Next.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

19/50

El Module Type es App Engine Java Endpoints Module, el module name es backend, el Package Name es
el nombre del proyecto de app engine al revs y le agregamos .backend. Por ejemplo, si la url es
gpgendpoints.appspot.com, el package name es com.appspot.gpgendpoints.backend. Haz clic en Finish.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

20/50

Se genera el proyecto y empieza la descarga de archivos. En este punto se necesita conexin a Internet la
primera vez que creas un mdulo web.

7.2. Edicin del archivo build.gradle.


Antes de empezar a capturar el cdigo del proyecto, hay que editar el archivo build.gradle del mdulo
backend. El cdigo fuente aparece en la seccin 8. Al terminar de editarlo, hay que hacer clic en Sync Now.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

21/50

7.3. Ejecucin local del mdulo backend


Una vez que hayas capturado todo el cdigo correspondiente al mdulo backend, selecciona la configuracin
backend en la barra de herramientas.

Haz clic en Run 'backend'.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

22/50

Empieza la ejecucin.

Abre http://localhost:8080 en un navegador y ah est la pgina.

7.4. Pruebas de Unidad del Endpoint


En la carpeta src del mdulo backend, crea un folder de nombre test. En su interior crea el paquete que diste
de alta al crear el mdulo backend. En su interior hay que crear las clases EndpointSaludoTest,
EndpointsTestSuite y NombresTest.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

23/50

Las clases de este endpoint no necesitan ningn recurso del servidor web, por lo que para ejecutar las
pruebas, solo hay que hacer clic derecho sobre EndpointsTestSuite y ejecutar Run 'EndpointsTestSuite'. No
es necesario levantar el servidor web.

7.5. Pruebas de Unidad En el Navegador.


Una forma muy sencilla de probar la pgina web es usar el plugin Selenium IDE para Firefox. Se puede
instalar de la url https://addons.mozilla.org/es/firefox/addon/selenium-ide/ haciendo clic en + Agregar a
Firefox.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

24/50

Se descarga.

Haz clic en Instalar.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

25/50

Haz clic en Reiniciar ahora.

Abre Selenium IDE

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

26/50

Aparece la interfaz de Selenium IDE.

Hay que crear una Test Suite que a su vez contiene Test Case que se guardan de forma individual.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

27/50

A continuacin se muestra la Test Suite que corresponde al diseo de pruebas. Se corre con el mnimo de
velocidad porque el endpoint se tarda en responder.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

28/50

Si quieres ejecutar todas las pruebas de la suite, haz clic en Play entire test suite o si quieres ejecutar solo
una prueba haz doble clic en la que quieres y luego haz clic en Play current test case.

7.6. Ejecucin del Proyecto en Internet


Primero hay que detener la ejecucin del mdulo backend. Haz clic en Stop 'backend'.

En Android Studio se selecciona el mdulo backend y el men Build > Deploy Module to App Engine

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

29/50

Aparece el cuadro de dilogo Deploy to App Engine. Selecciona Deploy to y haz clic en Sign In.

Aparece un cuadro de dilogo indicando que se abre una ventana del navegador.

Introduce el usuario y contrasea de Google en el navegador y haz clic en Siguiente.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

0/50

Introduce la contrasea y haz clic en Iniciar sesin.

Haz clic en Permitir.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

31/50

Si todo sale bien, se recibe una pgina que avisa la entrada en Android Studio. Cerrar la ventana del
navegador.

Vuelve a abrir el cuadro combinado Deploy to. Si no aparecen los proyectos de la cuenta, se hace clic en
refrescar (cono azul abajo a la derecha). Selecciona el proyecto de App Engine creado en este ejercicio.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

32/50

Haz clic en Deploy.

Empieza del despliegue en el servidor. Cuando termina, lo avisa. Si el despliegue falla, vuelve a intentarlo.

Abre una ventana de navegador e ingresa la url del proyecto web, que en este caso es
http://rickendpoints.appspot.com/ y aparece el sitio en Internet.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

33/50

7.7. Limpieza del Proyecto.


Cuando termines de usar un proyecto, lmpialo para que ocupe menos espacio en disco y en tu respaldo.
Selecciona el men Build > Clean Project.

8. Cdigo
8.1. Cdigo Web

build.gradle
1
2
3
4
5
6
7
8

// If you would like more information on the gradle-appengine-plugin please


// refer to the github page
// https://github.com/GoogleCloudPlatform/gradle-appengine-plugin
buildscript {
repositories {
jcenter()
}
dependencies {

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

34/50
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
}
}
repositories {
jcenter();
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
test { systemProperty "file.encoding", "utf-8" }
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
compile 'com.google.appengine:appengine-endpoints:1.9.18'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
compile 'javax.servlet:servlet-api:2.5'
testCompile 'junit:junit:4.12'
}
appengine {
downloadSdk = true
appcfg {
oauth2 = true
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}

Constantes.java
1 package com.appspot.gpgendpoints.backend;
2
3 public class Constantes {
4
public static final String FORMATO_DEL_SALUDO = "Saludos a %1$s y a %2$s.";
5
public static final String MENSAJE_FALTA_EL_NOMBRE_1 = "Falta el nombre 1.";
6
public static final String MENSAJE_FALTA_EL_NOMBRE_2 = "Falta el nombre 2.";
7
private Constantes() {}
8 }

EndpointSaludo.java
1
2
3
4
5
6
7
8
9
10
11
12
13

package com.appspot.gpgendpoints.backend;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import static com.appspot.gpgendpoints.backend.Constantes.FORMATO_DEL_SALUDO;
import static com.appspot.gpgendpoints.backend.Constantes
.MENSAJE_FALTA_EL_NOMBRE_1;
import static com.appspot.gpgendpoints.backend.Constantes
.MENSAJE_FALTA_EL_NOMBRE_2;
@Api(name = "proxyEndpointSaludo", version = "v1",

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

35/50
14
namespace = @ApiNamespace(ownerDomain = "gpgendpoints.appspot.com",
15
ownerName = "gpgendpoints.appspot.com", packagePath = ""))
16 public class EndpointSaludo {
17
@ApiMethod(name = "saluda") public Saludo saluda(Nombres modelo)
18
throws ExceptionFaltaNombre {
19
valida(modelo.getNombre1(), MENSAJE_FALTA_EL_NOMBRE_1);
20
valida(modelo.getNombre2(), MENSAJE_FALTA_EL_NOMBRE_2);
21
final String saludo = construyeSaludo(modelo);
22
return new Saludo(saludo);
23
}
24
private void valida(String nombre, String mensajeDeError)
25
throws ExceptionFaltaNombre {
26
if (nombre == null || nombre.isEmpty()) {
27
throw new ExceptionFaltaNombre(mensajeDeError);
28
}
29
}
30
private String construyeSaludo(Nombres modelo) {
31
return String
32
.format(FORMATO_DEL_SALUDO, modelo.getNombre1(), modelo.getNombre2());
33
}
34 }

ExceptionFaltaNombre.java
1 package com.appspot.gpgendpoints.backend;
2
3 public class ExceptionFaltaNombre extends Exception{
4
public ExceptionFaltaNombre(String detailMessage) {
5
super(detailMessage);
6
}
7 }

Nombres.java
1 package com.appspot.gpgendpoints.backend;
2
3 public class Nombres {
4
private String nombre1;
5
private String nombre2;
6
public Nombres() {}
7
public Nombres(String nombre1, String nombre2) {
8
this.nombre1 = nombre1;
9
this.nombre2 = nombre2;
10
}
11
public String getNombre1() {
12
return nombre1;
13
}
14
public void setNombre1(String nombre1) {
15
this.nombre1 = nombre1;
16
}
17
public String getNombre2() {
18
return nombre2;
19
}
20
public void setNombre2(String nombre2) {
21
this.nombre2 = nombre2;
22
}
23 }

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

36/50

Saludo.java
1 package com.appspot.gpgendpoints.backend;
2
3 public class Saludo {
4
private String texto;
5
public Saludo() {}
6
public Saludo(String texto) {
7
this.texto = texto;
8
}
9
public String getTexto() {
10
return texto;
11
}
12
public void setTexto(String texto) {
13
this.texto = texto;
14
}
15 }

appengine-web.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
3
<application>myApplicationId</application>
4
<version>1</version>
5
<threadsafe>true</threadsafe>
6
<system-properties>
7
<property name="java.util.logging.config.file"
8
value="WEB-INF/logging.properties"/>
9
</system-properties>
10 </appengine-web-app>

logging.properties
1 .level = WARNING

web.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
3
<servlet>
4
<servlet-name>SystemServiceServlet</servlet-name>
5
<servlet-class>com.google.api.server.spi.SystemServiceServlet
6
</servlet-class>
7
<init-param>
8
<param-name>services</param-name>
9
<param-value>com.appspot.gpgendpoints.backend.EndpointSaludo
10
</param-value>
11
</init-param>
12
</servlet>
13
<servlet-mapping>
14
<servlet-name>SystemServiceServlet</servlet-name>
15
<url-pattern>/_ah/spi/*</url-pattern>
16
</servlet-mapping>
17
<welcome-file-list>
18
<welcome-file>index.html</welcome-file>
19
</welcome-file-list>
20
<security-constraint>
21
<display-name>Encriptamiento</display-name>

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

37/50
22
<web-resource-collection>
23
<web-resource-name>todas</web-resource-name>
24
<description>Encripta todas las url</description>
25
<url-pattern>/*</url-pattern>
26
</web-resource-collection>
27
<user-data-constraint>
28
<description>Todos los datos van encriptados.</description>
29
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
30
</user-data-constraint>
31
</security-constraint>
32 </web-app>

PresenterSaludo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

"use strict";
var /** @const {string} */ NO_ENCONTRADO =
" no se encuentra en el archivo html.";
var /** @const {string} */ ENDPOINT = "proxyEndpointSaludo";
function inicia() {
var forma = busca("forma");
forma.addEventListener("submit", clicEnSaludar, false);
}
function clicEnSaludar() {
/* Los campos relacionados con el endpoint los ponemos entre comillas para
* asegurar que Gloogle Closure Compiler no cambie los nombres al optimizar.*/
var modelo = {"nombre1": recuperaTexto("nombre1"),
"nombre2": recuperaTexto("nombre2")};
var saluda = window["gapi"]["client"][ENDPOINT]["saluda"](modelo);
saluda["execute"](recibeRespuestaSaludo);
}
/** Procesa la respuesta devuelta por endpointSaludo. En caso de error, la
* respuesta contiene el campo "error", que tiene un campo llamado "code", con
* un nmero de error y un campo "message" que describe el error con un texto.
* En caso de xito, se devuelve el campo "result", que contiene el objeto
* devuelto por el servidor.
* @param {Object} respuesta datos devueltos por el proxy al endpoint. */
function recibeRespuestaSaludo(respuesta) {
var error = respuesta["error"];
if (error) {
var mensaje = procesaError(error);
muestraRespuesta(mensaje);
} else {
var saludo = respuesta["result"];
// La respuesta contiene las propiedades que tienen get en Java.
muestraRespuesta(saludo["texto"]);
}
}
/** Obtiene el mensaje de error del objeto de error y registra en bitcora si no
* es un error de valdacin.
* @param {Object} error objeto con la propiedad "message" que decribe el
* problema.
* @returns {string} el texto que describe el error. */
function procesaError(error) {
var mensaje = error["message"];
var errorFaltaNombre = catchMensaje(
"com.appspot.gpgendpoints.backend.ExceptionFaltaNombre", mensaje);
if (errorFaltaNombre) {
return errorFaltaNombre;
} else {

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

38/50
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

console.error(mensaje);
return mensaje;
}
}
/** Si el mensaje empieza con el nombre de la excepcin, devuelve el el texto
* que sigue despus. Si el texto no empieza con el nombre de la excepcin,
* devuelve null.
* @param {string} nombreException nombre de la excepcin generada en el
* servidor.
* @param {string} texto mensaje recibido del servidor.
* @returns {?string} si el mensaje empieza con el nombre de la excepcin,
* devuelve el el texto que sigue despus. Si el texto no empieza con el nombre
* de la excepcin devuelve null. */
function catchMensaje(nombreException, texto) {
var busqueda = nombreException + ": ";
if (texto.indexOf(busqueda) >= 0) {
return texto.substring(busqueda.length);
} else {
return null;
}
}
/** Devuelve el value del atributo value de un input.
* @param {string} idInputText id html de un input text.
* @returns {string} el atributo value del input. */
function recuperaTexto(idInputText) {
var inputText = busca(idInputText);
return inputText.value;
}
/** Muestra el mensaje de la respuesta en el elemento con id salida.
* @param {string} respuesta valor recibido del servidor. */
function muestraRespuesta(respuesta) {
var salida = busca("salida");
salida.textContent = respuesta;
}
/** busca el elemento con el id mencionado. Si no lo encuentra, lanza un error.
* @param {string} id el id del elemento buscado.
* @returns {Element} el elemento en contrado
* @throws {Error} si el id no se encuentra en la pgina html. */
function busca(id) {
var element = document.getElementById(id);
if (element) {
return element;
} else {
throw new Error(id + NO_ENCONTRADO);
}
}
/* La funcin que se menciona en elemento script y que descarga client.js se
* define de la forma siguiente, para que Google Closure Compiler no le cambie
* el nombre al optimizar. */
window["descargaProxy"] = function () {
var apiName = ENDPOINT;
var apiVersion = 'v1';
var apiRoot = '//' + window.location.host + '/_ah/api';
window["gapi"]["client"]["load"](apiName, apiVersion, inicia, apiRoot);
};

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

39/50

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Saludo con Endpoints</title>
</head>
<body>
<h1>Saludo con Endpoints</h1>
<form id="forma" action="javascript:;">
<p>
<input type="text" id="nombre1" placeholder="Nombre 1" accesskey="1">
</p>
<p>
<input type="text" id="nombre2" placeholder="Nombre 2" accesskey="2">
</p>
<p><button id="saludar" type="submit">Saludar</button></p>
<p><output id="salida"></output></p>
</form>
<script src="js/PresenterSaludo.js"></script>
<!-- Carga la Google APIs Client Library para JavaScript Ms informacin:
https://developers.google.com/api-client-library/javascript/reference/referencedocs
-->
<script
src="https://apis.google.com/js/client.js?onload=descargaProxy"></script>
</body>
</html>

EndpointTestSuite.java
1
2
3
4
5
6
7
8
9
10
11
12

package com.appspot.gpgendpoints.backend;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* Lanza las pruebas de unidad individuales. Se utiliza para no tener que
* ejecutar las pruebas una a una.
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({NombresTest.class, EndpointSaludoTest.class})
public class EndpointsTestSuite {}

EndpointSaludoTest.java
1
2
3
4
5
6
7
8
9
10
11
12

package com.appspot.gpgendpoints.backend;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class EndpointSaludoTest {
public static final String FALTA_EL_NOMBRE_1 = "Falta el nombre 1.";
public static final String FALTA_EL_NOMBRE_2 = "Falta el nombre 2.";
private EndpointSaludo endpointSaludo;

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

40/50
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 }

@Before public void setUp() {


endpointSaludo = new EndpointSaludo();
}
@Test public void testP1() {
try {
endpointSaludo.saluda(new Nombres(null, null));
fail("Debera abortar porque falta el nombre 1.");
} catch (ExceptionFaltaNombre e) {
assertEquals(FALTA_EL_NOMBRE_1, e.getLocalizedMessage());
}
}
@Test public void testP2() {
try {
endpointSaludo.saluda(new Nombres("Pedro", null));
fail("Debera abortar porque falta el nombre 2.");
} catch (ExceptionFaltaNombre e) {
assertEquals(FALTA_EL_NOMBRE_2, e.getLocalizedMessage());
}
}
@Test public void testP3() {
try {
endpointSaludo.saluda(new Nombres(null, "Ana"));
fail("Debera abortar porque falta el nombre 1.");
} catch (ExceptionFaltaNombre e) {
assertEquals(FALTA_EL_NOMBRE_1, e.getLocalizedMessage());
}
}
@Test public void testP4() throws Exception {
final Saludo resultado =
endpointSaludo.saluda(new Nombres("Pedro", "Ana"));
assertEquals("Saludos a Pedro y a Ana.", resultado.getTexto());
}
@Test public void testP5() throws Exception {
final Saludo resultado =
endpointSaludo.saluda(new Nombres("Cuca", "Pepe"));
assertEquals("Saludos a Cuca y a Pepe.", resultado.getTexto());
}

NombresTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package com.appspot.gpgendpoints.backend;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/** Clase que sirve para probar la clase Test. */
public class NombresTest {
/**
* Mtodo que implementa una prueba. Debe llevar la anotacin org.junit
* .Test y
* ser public. Puede lanzar excepciones.
*/
@Test public void testNombres() {
final Nombres nombres = new Nombres("Pp", "Qk");
// Revisa que el objeto devuelva los valores recibidos en el constructor.
assertEquals("Pp", nombres.getNombre1());

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

41/50
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 }

assertEquals("Qk", nombres.getNombre2());
}
@Test public void testConstructorSinParmetrosSinSetters() {
final Nombres nombres = new Nombres();
assertNull(nombres.getNombre1());
assertNull(nombres.getNombre2());
}
@Test public void testConstructorSinParmetrosConSetNombre1() {
final Nombres nombres = new Nombres();
nombres.setNombre1("Pp");
assertEquals("Pp", nombres.getNombre1());
assertNull(nombres.getNombre2());
}
@Test public void testConstructorSinParmetrosConSetNombre2() {
final Nombres nombres = new Nombres();
nombres.setNombre2("Qk");
assertNull(nombres.getNombre1());
assertEquals("Qk", nombres.getNombre2());
}
@Test public void testConstructorSinParmetrosConNombres() {
final Nombres nombres = new Nombres();
nombres.setNombre1("Pp");
nombres.setNombre2("Qk");
assertEquals("Pp", nombres.getNombre1());
assertEquals("Qk", nombres.getNombre2());
}

8.2. Cdigo Android

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

42/50

AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <manifest
3
package="com.appspot.gpgendpoints.d203endpoints"
4
xmlns:android="http://schemas.android.com/apk/res/android">
5
<!-- Permiso para conectarse a Internet y usar endpoints. -->
6
<uses-permission android:name="android.permission.INTERNET"/>
7
<application
8
android:allowBackup="true"
9
android:icon="@mipmap/ic_launcher"
10
android:label="@string/app_name"
11
android:supportsRtl="true"
12
android:theme="@style/AppTheme">
13
<activity android:name=".ActivitySaludo">
14
<intent-filter>
15
<action android:name="android.intent.action.MAIN"/>
16
<category android:name="android.intent.category.LAUNCHER"/>
17
</intent-filter>
18
</activity>
19
</application>
20 </manifest>

colors.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3
<color name="colorPrimary">#3F51B5</color>
4
<color name="colorPrimaryDark">#303F9F</color>
5
<color name="colorAccent">#FF4081</color>
6 </resources>

dimens.xml (values)
1 <resources>
2
<dimen name="activity_horizontal_margin">16dp</dimen>
3
<dimen name="activity_vertical_margin">16dp</dimen>
4 </resources>

dimens.xml (values-w820dp)
1 <resources>
2
<dimen name="activity_horizontal_margin">64dp</dimen>
3 </resources>

strings.xml
1 <resources>
2
<string name="app_name">D203endpoints</string>
3
<string name="nombre1">Nombre 1</string>
4
<string name="nombre2">Nombre 2</string>
5
<string name="saludar">Saludar</string>
6
<string name="hint_saludo">Aqu aparece el saludo para Nombre 1 y Nombre 2.</string>
7
<string name="edit_text_no_encontrado">EditText no encontrado en el archivo xml.</string>
8 </resources>

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

43/50

styles.xml
1 <resources>
2
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
3
<item name="colorPrimary">@color/colorPrimary</item>
4
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
5
<item name="colorAccent">@color/colorAccent</item>
6
</style>
7 </resources>

view_frame.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <!-- Layout que permite superponer otras view. tools:layout es solo para que las
3 herramientas de desarrollo muestren lo que se puede colocar en su interior. -->
4 <FrameLayout
5
android:id="@+id/frame"
6
xmlns:android="http://schemas.android.com/apk/res/android"
7
xmlns:tools="http://schemas.android.com/tools"
8
android:layout_width="match_parent"
9
android:layout_height="match_parent"
10
tools:context=".ActivitySaludo"
11
tools:layout="@layout/view_saludo"/>

view_saludo.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout
3
xmlns:android="http://schemas.android.com/apk/res/android"
4
xmlns:tools="http://schemas.android.com/tools"
5
android:layout_width="match_parent"
6
android:layout_height="match_parent"
7
android:orientation="vertical"
8
android:paddingBottom="@dimen/activity_vertical_margin"
9
android:paddingLeft="@dimen/activity_horizontal_margin"
10
android:paddingRight="@dimen/activity_horizontal_margin"
11
android:paddingTop="@dimen/activity_vertical_margin"
12
tools:context="net.ramptors.d202mvc.ActivitySaludo">
13
<EditText
14
android:id="@+id/nombre1"
15
android:layout_width="match_parent"
16
android:layout_height="wrap_content"
17
android:hint="@string/nombre1"/>
18
<EditText
19
android:id="@+id/nombre2"
20
android:layout_width="match_parent"
21
android:layout_height="wrap_content"
22
android:hint="@string/nombre2"/>
23
<Button
24
android:id="@+id/saludar"
25
android:layout_width="match_parent"
26
android:layout_height="wrap_content"
27
android:text="@string/saludar"/>
28
<TextView
29
android:id="@+id/salida"
30
android:layout_width="wrap_content"
31
android:layout_height="wrap_content"

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

44/50
32
android:hint="@string/hint_saludo"/>
33 </LinearLayout>

ActivitySaludo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package com.appspot.gpgendpoints.d203endpoints;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
public class ActivitySaludo extends AppCompatActivity {
private FragmentSaludo fragment;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_frame);
// Obtiene una referencia al administrador de fragments.
final FragmentManager fm = getSupportFragmentManager();
fragment = (FragmentSaludo) fm.findFragmentById(R.id.frame);
if (fragment == null) {
fragment = new FragmentSaludo();
// Aade el fragment al ViewGroup con id frame.
fm.beginTransaction().add(R.id.frame, fragment).commit();
}
}
FragmentSaludo getFragment() {
return fragment;
}
}

FragmentSaludo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

package com.appspot.gpgendpoints.d203endpoints;
import
import
import
import
import
import
import
import
import
import
import
import
import

android.os.AsyncTask;
android.os.Bundle;
android.support.annotation.IdRes;
android.support.annotation.NonNull;
android.support.annotation.Nullable;
android.support.v4.app.Fragment;
android.util.Log;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.Button;
android.widget.EditText;
android.widget.TextView;

import
import
import
import
import
import
import
import
import
import

com.appspot.gpgendpoints.proxyEndpointSaludo.ProxyEndpointSaludo;
com.appspot.gpgendpoints.proxyEndpointSaludo.model.Nombres;
com.appspot.gpgendpoints.proxyEndpointSaludo.model.Saludo;
com.google.api.client.extensions.android.http.AndroidHttp;
com.google.api.client.googleapis.json.GoogleJsonError;
com.google.api.client.googleapis.json.GoogleJsonResponseException;
com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
com.google.api.client.http.HttpRequest;
com.google.api.client.http.HttpRequestInitializer;

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

45/50
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import java.io.IOException;
public class FragmentSaludo extends Fragment {
private static final boolean SERVIDOR_LOCAL = true;
private static final String URL_LOCAL = "http://10.0.2.2:8080";
private static final String URL_REMOTA = "https://rickendpoints.appspot.com";
@SuppressWarnings("ConstantConditions")
private static final String URL = SERVIDOR_LOCAL ? URL_LOCAL : URL_REMOTA;
private static final String TAG = FragmentSaludo.class.getName();
private static final String RAIZ_NULA = "Raz nula.";
private static final String VIEW_NO_ENCONTRADA =
"View no encontrada en el archivo xml.";
private static final String EXCEPTION_FALTA_NOMBRE =
"com.appspot.gpgendpoints.backend.ExceptionFaltaNombre";
private static final String ESPERANDO_SALUDO = "Esperando saludo.";
@Nullable private ProxyEndpointSaludo proxy;
private AsyncTask<Nombres, Void, String> task;
/**
* Se invoca al aadir el fragment a la activity con el fragment manager. Si
* el fragment declara en onActivityCreated que retiene su instancia, no se
* destruye la instancia al rotar el dispositivo; en este caso, no se vuelve a
* invocar este mtodo.
* @param savedInstanceState map que almacena el contenido guardado antes de
* rotar el dispositivo. Las view que tienen asignado un id guardan
* automticamente su contenido en l.
*/
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
proxy = creaProxyEndpointSaludo();
}
/**
* Infla la vista al aadir el fragmento o rotar el dispositivo.
* @param inflater inflador por utilizar.
* @param container vista a la que se aade la vista del dispositivo.
* @param savedInstanceState guarda los valores respaldados.
* @return la vista asociada al fragment.
*/
@Nullable @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.view_saludo, container, false);
}
/**
* Se ejecuta una vez que la vista del fragment se ha creado.
* @param view vista asociada al fragment.
* @param savedInstanceState guarda los valores respaldados.
*/
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final Button saludar = (Button) busca(R.id.saludar);
saludar.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
clicEnSaludar();
}
});

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

46/50
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

}
/**
* Se invoca cuando el fragment y la activity a la que se agrega han sido
* creados.
* @param savedInstanceState guarda los valores respaldados.
*/
@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Permite que el fragmento se conserve al rotar el dispositivo.
setRetainInstance(true);
}
AsyncTask<Nombres, Void, String> getTask() {
return task;
}
@NonNull Button getSaludar() {
return (Button) busca(R.id.saludar);
}
@NonNull TextView getSalida() {
return (TextView) busca(R.id.salida);
}
public void clicEnSaludar() {
if (proxy != null) {
final Nombres modelo = new Nombres();
modelo.setNombre1(recuperaTexto(R.id.nombre1));
modelo.setNombre2(recuperaTexto(R.id.nombre2));
task = new AsyncTask<Nombres, Void, String>() {
/**
* Realiza en un hilo aparte la conexin al servidor.
* @param modelo la posicin 0 contiene el modelo que se enva al
* servidor.
* @return la respuesta del servidor.
*/
@Override protected String doInBackground(Nombres... modelo) {
try {
final ProxyEndpointSaludo.Saluda saluda =
proxy.saluda(modelo[0]);
final Saludo saludo = saluda.execute();
return saludo.getTexto();
} catch (GoogleJsonResponseException e) {
return procesaError(e);
} catch (IOException e) {
return procesaError(e);
}
}
/**
* Se ejecuta en el hilo de la interfaz grfica.
* @param respuesta valor devuelto por el servidor.
*/
@Override protected void onPostExecute(String respuesta) {
muestraRespuesta(respuesta);
}
};
task.execute(modelo); // Empieza la ejecucin de la tarea.
}
}
@NonNull View busca(@IdRes int idView) {
if (getView() == null) {
throw new NullPointerException(RAIZ_NULA);
} else {

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

47/50
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

final View view = getView().findViewById(idView);


if (view == null) {
throw new NullPointerException(VIEW_NO_ENCONTRADA);
} else {
return view;
}
}
}
private void muestraRespuesta(@Nullable String respuesta) {
if (isAdded()) {
final TextView salida = (TextView) busca(R.id.salida);
salida.setText(respuesta == null ? "" : respuesta);
}
}
@NonNull private String recuperaTexto(@IdRes int idEditText) {
final EditText editText = (EditText) busca(idEditText);
return editText.getText().toString();
}
/**
* Obtiene el mensaje de error del objeto de error y registra en bitcora si
* no es un error de valdacin.
* @param e excepcin que decribe el problema.
* @return el texto que describe el error.
*/
@NonNull private String procesaError(GoogleJsonResponseException e) {
final String mensaje = getMessage(e);
final String errorFaltaNombre =
catchMensaje(EXCEPTION_FALTA_NOMBRE, mensaje);
if (errorFaltaNombre == null) {
Log.e(TAG, ESPERANDO_SALUDO, e);
return mensaje;
} else {
return errorFaltaNombre;
}
}
/**
* Obtiene el mensaje de error del objeto de error y registra en bitcora.
* @param e excepcin que decribe el problema.
* @return el texto que describe el error.
*/
@NonNull private String procesaError(IOException e) {
Log.e(TAG, ESPERANDO_SALUDO, e);
return getIOExceptionMessage(e);
}
/**
* Obtiene el mensaje de error para una excepcin del tipo
* GoogleJsonResponseException.
* @param e excepcin que se procesa.
* @return mensaje asociado con la excepcin.
*/
@NonNull private String getMessage(GoogleJsonResponseException e) {
final GoogleJsonError details = e.getDetails();
return details == null || details.isEmpty() ? getIOExceptionMessage(e) :
details.getMessage();
}
/**
* Obtiene el mensaje de error para una excepcin del tipo IOException.
* @param e excepcin que se procesa.
* @return mensaje asociado con la excepcin.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

48/50
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 }

*/
@NonNull private String getIOExceptionMessage(IOException e) {
final String localizedMessage = e.getLocalizedMessage();
return localizedMessage == null || localizedMessage.isEmpty() ?
e.toString() : localizedMessage;
}
/**
* Si el mensaje empieza con el nombre de la excepcin, devuelve el el texto
* que sigue despus. Si el texto no empieza con el nombre de la excepcin,
* devuelve null.
* @param nombreException nombre de la excepcin generada en el servidor.
* @param texto mensaje recibido del servidor.
* @return si el mensaje empieza con el nombre de la excepcin, devuelve el el
* texto que sigue despus. Si el texto no empieza con el nombre de la
* excepcin devuelve null.
*/
@Nullable private String catchMensaje(String nombreException, String texto) {
final String busqueda = nombreException + ": ";
if (texto.contains(busqueda)) {
return texto.substring(busqueda.length());
} else {
return null;
}
}
@NonNull private ProxyEndpointSaludo creaProxyEndpointSaludo() {
final JacksonFactory jacksonFactory = new JacksonFactory();
final HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
final HttpRequestInitializer httpRequestInitializer =
new HttpRequestInitializer() {
@Override public void initialize(HttpRequest solicitudHttp)
throws IOException {
}
};
final ProxyEndpointSaludo.Builder builder =
new ProxyEndpointSaludo.Builder(httpTransport, jacksonFactory,
httpRequestInitializer);
builder.setApplicationName(getString(R.string.app_name));
builder.setRootUrl(URL + "/_ah/api/");
// Solo se usa GZip al conectarse al servidor remoto.
builder.setGoogleClientRequestInitializer(
new GoogleClientRequestInitializer() {
@Override
public void initialize(AbstractGoogleClientRequest<?> request)
throws IOException {
if (SERVIDOR_LOCAL) {
request.setDisableGZipContent(true);
}
}
});
return builder.build();
}

ActivitySaludoTest.java
1 package com.appspot.gpgendpoints.d203endpoints;
2
3 import android.support.annotation.IdRes;
4 import android.test.ActivityInstrumentationTestCase2;

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

49/50
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

import android.test.TouchUtils;
import android.widget.EditText;
import android.widget.TextView;
import java.util.concurrent.ExecutionException;
public class ActivitySaludoTest
extends ActivityInstrumentationTestCase2<ActivitySaludo> {
public static final String FALTA_EL_NOMBRE_1 = "Falta el nombre 1.";
public static final String FALTA_EL_NOMBRE_2 = "Falta el nombre 2.";
public ActivitySaludoTest() {
super(ActivitySaludo.class);
}
@Override protected void setUp() throws Exception {
super.setUp();
getActivity();
}
public void testP1() throws ExecutionException, InterruptedException {
TouchUtils.clickView(this, getActivity().getFragment().getSaludar());
getActivity().getFragment().getTask().get();
assertEquals(FALTA_EL_NOMBRE_1, getTextoSalida());
}
public void testP2() throws ExecutionException, InterruptedException {
asignaTexto(R.id.nombre1, "Pedro");
TouchUtils.clickView(this, getActivity().getFragment().getSaludar());
getActivity().getFragment().getTask().get();
assertEquals(FALTA_EL_NOMBRE_2, getTextoSalida());
}
public void testP3() throws ExecutionException, InterruptedException {
asignaTexto(R.id.nombre2, "Ana");
TouchUtils.clickView(this, getActivity().getFragment().getSaludar());
getActivity().getFragment().getTask().get();
assertEquals(FALTA_EL_NOMBRE_1, getTextoSalida());
}
public void testP4() throws ExecutionException, InterruptedException {
asignaTexto(R.id.nombre1, "Pedro");
asignaTexto(R.id.nombre2, "Ana");
TouchUtils.clickView(this, getActivity().getFragment().getSaludar());
getActivity().getFragment().getTask().get();
assertEquals("Saludos a Pedro y a Ana.", getTextoSalida());
}
public void testP5() throws ExecutionException, InterruptedException {
asignaTexto(R.id.nombre2, "Pepe");
asignaTexto(R.id.nombre1, "Cuca");
TouchUtils.clickView(this, getActivity().getFragment().getSaludar());
getActivity().getFragment().getTask().get();
assertEquals("Saludos a Cuca y a Pepe.", getTextoSalida());
}
private void asignaTexto(@IdRes int idEditText, String s) {
final EditText editText =
(EditText) getActivity().getFragment().busca(idEditText);
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
editText.requestFocus();
}
});
getInstrumentation().waitForIdleSync();
getInstrumentation().sendStringSync(s);

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

50/50
64
getInstrumentation().waitForIdleSync();
65
}
66
private String getTextoSalida() {
67
final TextView salida = getActivity().getFragment().getSalida();
68
return salida.getText().toString();
69
}
70 }

9. Bibliografa

http://www.codeproject.com/Articles/8058/Applying-Robustness-Analysis-on-the-Model-View-Con
Applying Robustness Analysis on the ModelViewController (MVC) Architecture in ASP.NET
Framework, using UML.

http://www.codeproject.com/Articles/42830/Model-View-Controller-Model-View-Presenter-and-Mod
Model View Controller, Model View Presenter, and Model View ViewModel Design Patterns.

http://martinfowler.com/eaaDev/PassiveScreen.html Passive View.

https://www.imaginanet.com/blog/patron-mvp.html Patrn MVP

http://www.uml-diagrams.org/

http://bouml.fr/doc/

https://cloud.google.com/appengine/docs

Android es propiedad de Google, al igual que Android Studio. Este ltimo est basado en IntelliJIDEA, que es propiedad de
JetBrains.

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin 4.0 Internacional. Para ver una copia de esta
licencia, visita http://creativecommons.org/licenses/by/4.0/.

You might also like