Author Archives: Gustavo

Go como usar Goroutines

Un Goroutine es un hilo de ejecución ligero manejado por GO.

Gorutine son funciones o métodos que se ejecutan simultáneamente.
Podemos entenderlas como “hilos ligeros” con un costo mucho menor que un hilo tradicional.

Supongamos que tienes dos funciones

func numbers() {
	for i := 0; i < 5; i++ {
		fmt.Printf("%d ", i)
		time.Sleep(100 * time.Millisecond)
	}
}

func characters() {
	for i := 'a'; i <= 'f'; i++ {
		fmt.Printf("%c ", i)
		time.Sleep(200 * time.Millisecond)
	}
}

Si llamas a estas funciones de la forma habitual tendrías este resultado.

func main() {

	// without goroutine
	numbers()

	characters()

	fmt.Scanln() // wait, press a key
	fmt.Println("done")
}

Observa cómo la ejecución de estas funciones es síncrona

go run rutine.go
0 1 2 3 4 a b c d e f
done

Como implementar Goroutine

Si deseas invocar estas funciones en una Goroutine debes usar “go” por delante de la llamada a la función de esta manera:

func main() {
	// goroutine
	go numbers()

	go characters()

	fmt.Scanln() // wait, press a key
	fmt.Println("done")
}

En la salida se ve como se van ejecutando ambas funciones de manera simultánea, lo que evidencia esta ejecución en paralelo de ambas rutinas.

go run rutine.go
0 a 1 2 b 3 c 4 d e f

Conclusión:

Cuando ejecutas este código sin Goroutine vemos que las funciones se ejecutan una primero y luego la otra, sincrónicamente.
Utilizando Goroutine, anteponiendo “go” por delante a la llamada, puedes observar que la salida de las funciones se va intercalando mostrando que se ejecutan al mismo tiempo.

Compartir esto:

Java 8 Stream map flatMap y filter

Stream map te permite leer cada elemento y transformar los valores que se encuentran en el Stream entregandote otro Stream con el resultado para seguir realizando operaciones.

Cómo usar Stream map

Como puedes ver en el ejemplo a continuación map() recibe una función como parámetro para realizar alguna transformación sobre cada elemento y devuelve otro Stream sobre el cual puedes seguir trabajando.

Observa en el ejemplo a continuación transformamos cada elemento en un String en mayúscula y luego lo colocamos en una nueva lista.

List<String> fruits = Arrays.asList(
    "apple", "banana", "cherry", "lemon", "peach");

List<String> fruitsUpper = fruits.stream().map(String::toUpperCase)
    .collect(Collectors.toList());
System.out.print(fruitsUpper);
// [APPLE, BANANA, CHERRY, LEMON, PEACH]

Otro ejemplo en donde el resultado será las tres primeras letras de cada String.

List<String> fruitsFirstLetters= fruits.stream().map(f -> f.substring(0,3)).collect(Collectors.toList());
System.out.println(fruitsFirstLetters);
// [app, ban, che, lem, pea]

Podemos realizar transformaciones de objetos en algo distinto, recibiendo en la lambda expresión el objeto como parámetro y haciendo algo con él. Por ejemplo, extraer valor de un atributo de un objeto.

List<User> users = Arrays.asList(new User("Hamilton"), new User("Thompson"),
       new User("Stallman"), new User("Torvalds"));

List<String> stringNames = users.stream().map(user -> user.getName()).map(String::toUpperCase)
       .collect(Collectors.toList());
System.out.println(stringNames);
// [HAMILTON, THOMPSON, STALLMAN, TORVALDS]

Cómo usar Stream flatMap

Podemos encontrarnos con estructuras algo más complejas. Por ejemplo, una lista con otra
lista.

List<List<String>> programmers = Arrays.asList(
       Arrays.asList("Margaret", "Hamilton"),
       Arrays.asList("Ken", "Thompson"),
       Arrays.asList("Richard", "Stallman"),
       Arrays.asList("Linus", "Torvalds"));

Si obtienes un Stream de esta lista obtendrías otro Stream con una lista

Stream<List<String>> streamWithList = programmers.stream();

Con flatMap lo que haces es un “flatten”, aplanar el resultado. Por ejemplo, aquí aplanamos List y nos quedamos con un Stream “plano”.

{ {"Margaret","Hamilton"}, {"Ken","Thompson"}, ... } -> flatMap -> {"Margaret","Hamilton","Ken","Thompson", ...}
List<String> flatList = programmers.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(flatList);
// [Margaret, Hamilton, Ken, Thompson, Richard, Stallman, Linus, Torvalds]

Cómo usar Stream filter

Filter aplica un predicado sobre cada elemento y si cumple la condición lo agrega a un nuevo Stream.
Aquí transformas un Stream en otro nuevo Stream con solo elementos que empiezan con “b”

Stream<String> streamWithB = fruits.stream().filter(f -> f.startsWith("b"));
List<String> listWithB = streamWithB.collect(Collectors.toList());
System.out.println(listWithB);
// [banana]

Conclusión

Has visto cómo crear un Stream y transformar cada dato del Stream en algo distinto usando map. También cómo aplanar un Stream complejo para obtener un Stream plano y sobre él seguir realizando operaciones.
Por último, cómo filtrar resultados aplicando un predicado para los elementos que cumplan con tu condición.

Código de ejemplo:
/j8/streams

Compartir esto:

Java 8 Variable Scope en Lambda

Variable Scope en Lambdas

El acceso a variables dentro de lambdas puede ser algo confuso, pero es sencillo si lo ves con un ejemplo.

Observa este código en el cual accedemos a los parámetros “text” y “count” desde la expresión lambda.

public static void showText(String text, int count) {
   Runnable r = () -> {
       for (int i = 0; i < count; i++) {
           System.out.println(text);
       }
   };
   new Thread(r).start();
}

Cuando llamamos al método showText este ejecutará un nuevo hilo con la expresión lambda y retornará de inmediato. ¿Cómo es que estas variables no se pierden dentro de la expresión lambda? Decimos que la expresión lambda ha capturado estos valores de las variables.

Estas variables son llamadas variables libres porque no son parámetros de la expresión lambda en sí misma y no están tampoco definidas dentro del código lambda.

Una expresión lambda tiene tres ingredientes:
-El bloque de código
-Parámetros
-Valores de variables libres.

Restricciones sobre variables en Lambdas

El valor de estas variables libres puede ser capturado por la lambda expresión pero tienen la restricción de que estás variables NO pueden ser modificadas dentro de la expresión lambda.

En nuestro ejemplo no podríamos modificar el valor de la variable “count”. Piensa que si diferentes trabajos ejecutan este código de forma concurrente estarían todos modificando esta contador “count”. Debemos asegurar la expresión lambda siempre “threadsafe”.

public static void showText_notWork(String text, int count) {
   Runnable r = () -> {
       while (count > 0) {
           System.out.println(text + " " + count);
           count--; // Error.  Can't change !!
       }
   };
   new Thread(r).start();
}

Dijimos que NO podemos modificar una variable compartida pero observa que si podemos modificar el valor dentro de un objeto compartido. No tendremos problemas de compilación pero nuestro código no será threadsafe por lo que el resultado puede ser impredecible si múltiples ejecuciones ocurren de forma concurrente.

En este ejemplo la asignación de la variable “data” nunca la estamos cambiando pero si estamos modificando su contenido.

    public static void repeatMessage_noThreadSafe() {
        List<String> data = new ArrayList<>();
        data.add("file1");
        // more into data...
        for (String p : data) {
            new Thread(() -> {
                if (p.startsWith("file9")) {
                    data.add("xfile9"); // changing shared object
                }
            }).start();
        }
    }

Conclusión:

Vimos una breve explicación sobre el scope de las variables en expresiones lambda y porqué no es posible modificarlas.

Código de ejemplo:
/j8/variableScope

Compartir esto:

Java 8 Constructor por Referencia

Java Constructor por Referencia

En Java 8 se han introducido importantes cambios como lambdas, interfaces funcionales y métodos por referencia.

Veamos ahora cómo hacer uso de constructores por referencia.

Si no has leido el articulo sobre métodos por referencia te recomiendo que le des un vistazo porque tiene mucho en común con el artículo a continuación. La diferencia es que aquí llamas al “new” en vez de llamar a un método.

Partamos de un ejemplo para que se vea claramente.

Tenemos este User con tres constructores, uno por default, otro que recibe el “name” y el último que recibe “name” y “password”. Deseamos crear varios user partiendo de una lista de nombres.

public class User {

   private String name;
   private String password;

   public User() {
   }

   public User(String name) {
       this.name = name;
   }

   public User(String name, String pass) {
       this.name = name;
       this.password = pass;
   }
//...

La lista de nombres predefinidos que queremos utilizar para crear los User.

List<String> userNames = new ArrayList<>();
userNames.add("jose");
userNames.add("luis");
userNames.add("lucas");

Como llamar al constructor por referencia

Para este ejemplo nos valdremos de stream().map() que nos permite convertir un objeto en algo diferente. En nuestro “mapeamos” un String en un User.
Luego usaremos stream.collect para recoger todos los elementos en una lista de Users.
Por último los iteramos para mostrar el resultado.

Lo importante aquí es observar que estamos llamando al constructor por referencia cuando escribimos User::new.
¿Pero cuál constructor llamará .map(..) si tenemos varios constructores en la clase User.
Aquí el compilador elige por iferenencia el constructor que tenga un solo parámetro String porque es el único que aplica.

Stream<User> userStream = userNames.stream().map(User::new);
List<User> userList = userStream.collect(Collectors.toList());
userList.forEach(System.out::println);

//.. output..
User{name='jose'}
User{name='luis'}
User{name='lucas'}

En conclusión vemos que así como usamos métodos por referencia tambien podemos hacerlo para constructores del modo User::new. Además el compilador elige el constructor correspondiente por inferencia.

Código de ejemplo:
j8/constructReference

Referencias:
Lambdas
Interfaces funcionales Métodos por referencia

Compartir esto:

Java 8 Optional

Java 8 Optional

Evitar los Nullpointer es uno de los problemas con los que habitualmente nos encontramos los programadores en Java. Continuamente realizamos operaciones para verificar que el valor no sea null o tener la precaución de devolver valores not null y a pesar de esto es habitual el fallo por los null pointer.

Java 8 introduce Optional, este es un wrapper que nos ayuda a prevenir los nullpointer tan comunes en Java. Optional nos da un métodos adecuados para validar si el valor contenido esta null o no.

Java 8 Optional

Como crear un Optional para un objeto

La forma correcta de crear un Optional para un objeto no nulo es la siguiente.

User user = new User("gustavo"); 
Optional<User> optUser = Optional.of(user);

Si utilizas Optional.of con un valor null obtendras un nullpointer inmediatamente en el metodo of.
Esto resulta en la ventaja de no esperar hasta usar el User para encontrarse con la excepción.

Por ejemplo, aqui tendremos un null pointer en Optional, antes de hacer uso de User nulo.

User user = null; 
Optional<User> optUser = Optional.of(user); // NullPointerException here
… 
user.getName(); 

// ...
Exception in thread "main" java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.<init>(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)
	at j8.optional.OptionalExample.main(OptionalExample.java:9)

Si sabes que el objeto User está null o es posible que este nulo y quieres devolver un Optional debes utilizar el método Optional.ofNullable(..);

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

Como utilizar Optional en Java

Dijimos que el uso de Optional es determinar si un objeto está nul o no, aunque es mejor decir que Optional te dirá si el objeto está presente.

A continuación vas a crear un Optional para un objeto User y vas a determinar si está presente.

   // logic to create User 
   User user = new User("john");

   // Create Optional for User
   Optional<User> optUser = Optional.ofNullable(user);

   if (optUser.isPresent()) {
       System.out.println(optUser.get().getName());
   }

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

}

Tambien puedes utilizar <a href="http://gustavopeiretti.com/java-8-usar-expresiones-lambda/">lambdas</a> con el metodo ifPresent(..)



   User user = new User("john");
   Optional<User> optUser = Optional.ofNullable(user);

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

}

Como utilizar Optional orElse / orElseGet en Java

Lo que hace orElse es devolver el valor si está presente o el valor que se pasa por parámetro si está ausente. Es decir usa un valor DEFAULT si el objeto no esta presente.

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElse(User.EMPTY_USER).getName());

// output 
// EMPTY_USER

En el caso de orElseGet también se devuelve un valor por default si el valor no está presente, solo que aquí el valor se devuelve utilizando la interfaz funcional Supplier.
Lo positivo aquí es que esta función solo será llamada si el valor no esta presente evitando procesamiento. En nuestro ejemplo es bastante simple, pero en los casos en que la ejecución de la función sea costosa este método es recomendable.

// orElseGet
User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElseGet(new Supplier<User>() {
   @Override
   public User get() {
       return User.EMPTY_USER;
   }
}).getName());

// better with j8 lambdas !
System.out.println(optUser.orElseGet(() -> User.EMPTY_USER).getName());

Tirando una expection con Optional.orElseThrow

Vimos que usando orElse / orElseGet lo que buscas es devolver otro valor por default si el valor dentro de Optional está ausente. Usando orElseThrow lo que deseas en cambio es tirar una Exception si este valor no está.

// orElseThrow
User nullUser = null;
Optional<User> optUser = Optional.ofNullable(nullUser);
optUser.orElseThrow(IllegalArgumentException::new);

// output
Exception in thread "main" java.lang.IllegalArgumentException
	at java.util.Optional.orElseThrow(Optional.java:290)
	at j8.optional.OptionalExample.main(OptionalExample.java:35)

Como usar Optional.filter en Java

Optional.filter te permite filtrar elementos que no deseas. Suponiendo que solo deseas imprimir algún resultado para un determinado User, en este ejemplo el User cuyo nombre es “john”.

User user = new User("john");
Optional<User> optUser = Optional.ofNullable(user);

// filter + ifPresent
optUser.filter(u -> "john".equals(u.getName()))
       .ifPresent(u -> System.out.println(u.getName()));

// filter + isPresent
if (optUser.filter(u -> "john".equals(u.getName()))
       .isPresent()) {
   System.out.println("john is present");
}


Como usar Optional.map y Optional.flatMap

Optional.map transforma o ‘mapea’ un valor en otro devolviendote otro Optional para el tipo de dato.

Por ejemplo si deseamos obtener un Optional con la edad Integer) del User

Optional<Integer> userAge = optUser.map(User::getAge);

Ahora siendo el valor devuelto con “Optional.map” también otro Optional podemos aplicarle filtros

// filter + map
if(optUser.map(User::getAge).filter(age -> age >= 18).isPresent()) {
   System.out.print("The user is older");
}

¿Cuál es la diferencia entre map y flatMap?

Debes usar map si la función te retorna un objeto, en cambio si la función retorna un Optional usas flatMap

// filter + flatMap
optUser.flatMap(User::getOptionalEmail).ifPresent(email -> sendEmail(email));

private static void  sendEmail(String email) {
   System.out.println("Sending email to " + email);
}


Siempre es conveniente utilizar Optional en vez de validar los nulos en la forma tradicional
Optional nos provee de herramientas para evitar los típicos errores con nulos, aunque es necesario usarlo correctamente porque si no validamos antes que el objeto este presente podemos incurrir facilmente en otro tipo de error.

Si olvidamos verificar si el objeto está presente y hacemos uso de get directamente tendremos una exception.

String data = null;
Optional<String> opt = Optional.ofNullable(data);
opt.get().toString();

Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.util.Optional.get(Optional.java:135)

Optional, además de facilitarnos el control de nulos o no presentes nos provee de funcionalidades como filter, map, orElse, orElseThrow que nos resuelven muchas de las tareas habituales. Puedes consultar este ejemplo en github, espero te sirva.

Compartir esto:

Java 8 como usar fechas y horas con la api java.time

Java 8 nos trae al fin una nueva api para el manejo de fechas. Nos encontramos dentro del paquete
java.time con nuevas clases para resolver los problemas con fechas como LocalDate, horas con LocalTime o la combinación de fecha y hora con LocalDateTime. También incluye como es debido dentro de esta api el uso de zonas horarios con ZonedDateTime.
Además los conceptos de Period para determinar el periodo entre dos fechas y Duration para determinar la duración entre dos horas.

Aquí te mostraré pequeños ejemplos para conocer esta api a fin de que luego en el uso ya te encuentres familiarizado con ellas.

Java 8 Date Time

LocalDate

LocalDate representa la fecha sin la hora.

LocalDate localDate = LocalDate.now();
System.out.println(localDate.toString());

LocalDate localDateOf = LocalDate.of(2017, 10, 10);
System.out.println(localDateOf.toString()); // 2017-10-10

Puedes además sumar o restar días facilmente,

LocalDate datePlus = localDateOf.plusDays(7);
System.out.println(datePlus.toString());  // 2017-10-17

LocalDate dateMinus = localDateOf.minusDays(7);
System.out.println(dateMinus.toString()); // 2017-10-03

Determinar cuál es fecha esta es anterior o posterior respecto a otra

boolean isBefore = LocalDate.of(2017, 10, 10)
                .isBefore(LocalDate.of(2017, 8, 20));
System.out.println(isBefore); // false

boolean isAfter = LocalDate.of(2017, 10, 10)
                .isAfter(LocalDate.of(2017, 8, 20));
System.out.println(isAfter); // true

LocalTime

LocalTime es similar a LocalDate en su uso y representa la hora sin la fecha.

LocalTime localTime = LocalTime.now();
System.out.println(localTime);

LocalTime hour = LocalTime.of(6, 30);
System.out.println(hour); // 06:30

Sumar o restar horas o cualquier otro tipo de unidad como segundos

LocalTime localTimePlus = hour.plus(1, ChronoUnit.HOURS);
System.out.println(localTimePlus); // 07:30
LocalTime localTimeMinus = hour.minus(60, ChronoUnit.SECONDS);
System.out.println(localTimeMinus); // 06:29

También podemos comparar para saber si alguna hora es mayor o no que otra.

boolean isBeforeHour = LocalTime.parse("08:30")
.isBefore(LocalTime.parse("10:20"));
System.out.println(isBeforeHour); // true

LocalDateTime

LocalDateTime es la combinación del la fecha y la hora. Al igual que con LocalDate y LocalTime puedes crear instancias

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
System.out.println(localDateTimeOf); // 2017-08-20T08:30

Igual que como vimos en LocalDate y LocalTime, puedes sumar o restar facilmente utilizando diferentes unidades de tiempo

LocalDateTime localDateTimePlus = localDateTimeOf.plusDays(5);
System.out.println(localDateTimePlus); // 2017-08-25T08:30
LocalDateTime localDateTimeMinus = localDateTimePlus.minusMinutes(10);
System.out.println(localDateTimeMinus); // 2017-08-25T08:20

ZonedDateTime

Si necesitas trabajar con zonas horarias puedes utilizar esta clase que te provee el manejo de fechas con hora para la zona que determines. La lista de zonas disponibles las puedes consultar desde la clase ZoneId

ZoneId.getAvailableZoneIds().forEach(z -> System.out.println(z)); // list of all zones
ZoneId zoneId = ZoneId.of("America/Panama");

Para ‘moverse’ a otra zona horaria, por ejemplo Tokyo haces uso de de LocalDateTime en conjunto con ZoneDateTime pasando la zona “Asia/Tokyo”

LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTimeOf, zoneId);
System.out.println(zonedDateTime); // 2017-08-20T08:30-05:00[America/Panama]
ZonedDateTime tokyoDateTime = localDateTimeOf.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(tokyoDateTime); // 2017-08-20T08:30+09:00[Asia/Tokyo]

Period

Con la clase Period puedes obtener la diferencia entre dos fechas o utilizarlo para modificar valores de alguna fecha.

LocalDate startLocalDate = LocalDate.of(2017, 10, 10);
LocalDate endLocalDate = startLocalDate.plus(Period.ofDays(10));  // 2017-10-20
int diffDays = Period.between(startLocalDate, endLocalDate).getDays();
System.out.println(diffDays); // 10

Duration

Duration es el equivalente a Period pero para las horas.

LocalTime startLocalTime = LocalTime.of(8, 30);
LocalTime endLocalTime = startLocalTime.plus(Duration.ofHours(3));  // 11:30

long diffSeconds = Duration.between(startLocalTime, endLocalTime).getSeconds();
System.out.println(diffSeconds); // 10800 seconds

Código completo

Te dejo copiado todo el ejemplo completo para tu uso. Te recomiendo lo copies y en tu ID para que lo veas más claro. Espero te sirva.

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

public class DateTimeExample {

    public static void main(String[] args) {

        // LocalDate
        localDateExample();

        // LocalTime
        localTimeExample();

        // LocalDateTime
        localDateTimeExample();

        // ZonedDateTime
        zonedDateTimeExample();

        // Period
        periodExample();

        // Duration
        durationExample();

    }


    private static void localDateExample() {
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate.toString());

        LocalDate localDateOf = LocalDate.of(2017, 10, 10);
        System.out.println(localDateOf.toString()); // 2017-10-10

        LocalDate datePlus = localDateOf.plusDays(7);
        System.out.println(datePlus.toString());  // 2017-10-17

        LocalDate dateMinus = localDateOf.minusDays(7);
        System.out.println(dateMinus.toString()); // 2017-10-03

        boolean isBefore = LocalDate.of(2017, 10, 10)
                .isBefore(LocalDate.of(2017, 8, 20));
        System.out.println(isBefore); // false


        boolean isAfter = LocalDate.of(2017, 10, 10)
                .isAfter(LocalDate.of(2017, 8, 20));
        System.out.println(isAfter); // true
    }

    private static void localTimeExample() {
        LocalTime localTime = LocalTime.now();
        System.out.println(localTime);

        LocalTime hour = LocalTime.of(6, 30);
        System.out.println(hour); // 06:30


        LocalTime localTimePlus = hour.plus(1, ChronoUnit.HOURS);
        System.out.println(localTimePlus); // 07:30

        LocalTime localTimeMinus = hour.minus(60, ChronoUnit.SECONDS);
        System.out.println(localTimeMinus); // 06:29

        boolean isBeforeHour = LocalTime.parse("08:30")
                .isBefore(LocalTime.parse("10:20"));
        System.out.println(isBeforeHour); // true
    }

    private static void localDateTimeExample() {
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);


        LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
        System.out.println(localDateTimeOf); // 2017-08-20T08:30


        LocalDateTime localDateTimePlus = localDateTimeOf.plusDays(5);
        System.out.println(localDateTimePlus); // 2017-08-25T08:30

        LocalDateTime localDateTimeMinus = localDateTimePlus.minusMinutes(10);
        System.out.println(localDateTimeMinus); // 2017-08-25T08:20
    }


    private static void zonedDateTimeExample() {
        ZoneId.getAvailableZoneIds().forEach(z -> System.out.println(z)); // list of all zones

        ZoneId zoneId = ZoneId.of("America/Panama");
        LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTimeOf, zoneId);
        System.out.println(zonedDateTime); // 2017-08-20T08:30-05:00[America/Panama]

        ZonedDateTime tokyoDateTime = localDateTimeOf.atZone(ZoneId.of("Asia/Tokyo"));
        System.out.println(tokyoDateTime); // 2017-08-20T08:30+09:00[Asia/Tokyo]
    }


    private static void periodExample() {
        LocalDate startLocalDate = LocalDate.of(2017, 10, 10);
        LocalDate endLocalDate = startLocalDate.plus(Period.ofDays(10));  // 2017-10-20

        int diffDays = Period.between(startLocalDate, endLocalDate).getDays();
        System.out.println(diffDays); // 10
    }

    private static void durationExample() {
        LocalTime startLocalTime = LocalTime.of(8, 30);
        LocalTime endLocalTime = startLocalTime.plus(Duration.ofHours(3));  // 11:30

        long diffSeconds = Duration.between(startLocalTime, endLocalTime).getSeconds();
        System.out.println(diffSeconds); // 10800 seconds
    }

}

Compartir esto:

Java – como usar ExecutorService

Java ExecutorService

ExecutorService nos simplifica las tareas asincronas proveyendo un pool de hilos que son manejados por esta API abstrayendonos del trabajo de crearlos y asignarles tareas a estos hilos.

java executor service

Utilizando la factory class Executors

La forma más común para crear una instancia de ExecutorService es a través de la factory class Executors.

Por ejemplo si deseas crear pool con 10 threads lo puedes hacer de este modo:

ExecutorService executor = Executors.newFixedThreadPool(10);

Si quieres crear un pool con un solo hilo en los que las tareas queden en cola y se ejecuten de forma secuencial:

ExecutorService executor = Executors.newSingleThreadExecutor();

Creando instancias directamente

Puedes directamente crear instancias de cualquiera de las implementaciones de ExecutorService, observa que esta es una interfaz con múltiples implementaciones.

Por ejemplo para crear un pool de 10 hilos del mismo modo que lo hace la factory class Executors.newSingleThreadExecutor().

ExecutorService executor = new ThreadPoolExecutor(10, 10,
       0L, TimeUnit.MILLISECONDS,
       new LinkedBlockingQueue<Runnable>());

¿Cómo ejecutar tareas con ExecutorService?

Usando ExecutorService.execute

ExecutorService en su método execute ejecuta cualquier implementacion de Runnable, por lo que su ejecución la haces de este modo:

Runnable command = new Runnable() {
   @Override
   public void run() {
       doLongWork();
   }
};
executor.execute(command);

Usando lambdas puedes cambiar este código por uno más limpio. Copia este codigo completo y pruebalo localmente.

package executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceExample {

   public static void main(String[] args) {

       ExecutorService executor = Executors.newFixedThreadPool(10);
       executor.execute(() -> doLongWork("hi 1"));
       executor.execute(() -> doLongWork("hi 2"));
       executor.execute(() -> doLongWork("hi 3"));
   }

   private static void doLongWork(String hola) {
       System.out.println("Running " + hola);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }

}

Usando ExecutorService.submit

EjecutorService.execute es un método void por lo que no puedes conocer el resultado, pero ExecutorService puede también retornar el resultado de tu ejecución si utilizas el método submit.

ExecutorService executor = Executors.newFixedThreadPool(10);
Future<String> future = executor.submit(() -> doLongWork("hi with future 1"));
future.get();

Future.get te devuelve el resultado de la ejecución de tu metodo luego de que este ya ha terminado.

Prueba este ejemplo completo

package executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceFutureExample {

   public static void main(String[] args) throws Exception {

       ExecutorService executor = Executors.newFixedThreadPool(10);
       Future<String> future = executor.submit(() -> doLongWork("hi with future 1"));
       System.out.println(future.get());
      
   }

   private static String doLongWork(String msg) {
       System.out.println("Running " + msg);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "done " + msg;
   }

}

Usando ExecutorService.invokeAny

ExecutorService.invokeAny recibe una lista de Callable, ejecuta todas las task y devuelve el resultado de una ejecución correcta

ExecutorService executor = Executors.newFixedThreadPool(10);

Collection<Callable<String>> callables = new ArrayList<>();
callables.add(() -> doLongWork("hi! 1"));
callables.add(() -> doLongWork("hi! 2"));
String oneResult = executor.invokeAny(callables);

Usando ExecutorService.invokeAll

ExecutorService.invokeAll recibe una también una lista de Callable, ejecuta todas las task y pero devuelve el resultado de todas las ejecuciones

package executor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceFutureAnyExample {

   public static void main(String[] args) throws Exception {

       ExecutorService executor = Executors.newFixedThreadPool(10);

       Collection<Callable<String>> callables = new ArrayList<>();
       callables.add(() -> doLongWork("hi! 1"));
       callables.add(() -> doLongWork("hi! 2"));

       List<Future<String>> result = executor.invokeAll(callables);

       for (Future f : result) {
           System.out.println("f.isDone :  " + f.isDone() + " f.get : " + f.get());
       }

   }

   private static String doLongWork(String msg) {
       System.out.println("Running " + msg);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "finish " + msg;
   }

}

Como terminar ExecutorService

Por defecto ExecutorService se queda ‘escuchando’ nuevas tareas, se queda vivo para recibir nuevas tareas y asignarlas a su pool. Si deseas finalizarlo para que no reciba nuevas tareas puedes ejecutar su método shutdown() para “solicitarle” la terminación. Una vez terminadas la tareas en ejecución se finalizará el servicio.
También puedes requerir la finalización inmediata usando shutdownNow(), aunque esto último no esta garantizado. Este método te devuelve la lista de tareas que no finalizaron si deseas conocerlas.

ExecutorService executor = Executors.newFixedThreadPool(10);
...
List<Runnable> runnableList = executor.shutdownNow();

Compartir esto:

Go Tipos Básicos

Go Types – Go Tipos básicos

¿Cuáles son los tipos básicos definidos en Go?

Go define diferentes tipos básicos entre los que puedes encontrar booleanos, string, numéricos enteros (con signo o sin signo), flotantes (números reales) y números complejos.

Este recuadro te muestra los tipos básicos definidos en Go

Tipo booleano:
bool

Tipo string:
string


Enteros con signo:
int         32-bit y 64-bit según el sistema
int8        8-bit (-128 to 127)
int16       16-bit (-32768 to 32767)
int32       32-bit (-2147483648 to 2147483647)
int64       64-bit (-9223372036854775808 to 9223372036854775807)

Enteros sin signo:
uint        32-bit y 64-bit según el sistema
uint8       8-bit (0 to 255)
uint16      16-bit (0 to 65535)
uint32      32-bit (0 to 4294967295)
uint64      64-bit integers (0 to 18446744073709551615)
uintptr    

Flotantes:
float32     32-bit 
float64     64-bit 

Complejos:
complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte // alias for uint8
rune // alias for int32 represents a Unicode code point

Los tipos int, uint y uintptr suelen tener 32 bits de ancho en sistemas de 32 bits y 64 bits de ancho en sistemas de 64 bits.
En general cuando necesites usar un entero utiliza siempre int a menos que tengas un motivo específico para usar enteros de tamaño definido o sin signo.

package main

import (
	"fmt"
	"math"
)

func main() {

	// Boolean
	var b bool = true
	if (b) {
		fmt.Println("is true")
	}

	// String
	var a string = "Hello World"
	fmt.Println(a)

	// Integer number
	var i int = 100
	fmt.Println(i + 99)

	// Float number
	var f float64 = 1.343435345563459
	fmt.Println(f)

	var fmax = math.MaxFloat64
	fmt.Println(fmax)

	// Complex number
	var c complex64 = 1.04i
	fmt.Println(c)

}

El resultado de este ejemplo

$ go run basictypes.go
is true
Hello World
199
1.343435345563459
1.7976931348623157e+308
(0+1.04i)

Compartir esto:

Go Errors

Go Errors

¿Cómo manejar los errores en Go?

Go utilizar como concepto de error cualquier devolución a través de alguna implementación de la interfaz “error” provista por el lenguaje

type error interface {
      Error() string
}

Para usar esta interfaz y devolver un error puedes utilizar una implementación ya provista por Go sobre esta interfaz haciendo uso de la función errors.New() del paquete errors.

Una vez creado error lo retornas y luego evaluas si es no es nulo.
Por convención el error es el último valor retornado si devuelves más de un valor.

package main

import (
	"fmt"
	"errors"
)

func addTwo(value int) (int, error) {
	if (value < 0) {
		// business rules .. I can not work with negatives
		return value, errors.New(fmt.Sprintf("something went wrong value is %d:", value))
	} else {
		return value + 2, nil
	}
}

func main() {

	v, err := addTwo(-1);
	if err != nil {
		// do something with this error
		fmt.Print(err)
	} else {
		fmt.Print(v)
	}

}


El resultado de este código es

$ go run errors.go
something went wrong value is -1:

Compartir esto:

Go Punteros

Go Punteros (Pointers)

¿Qué son los punteros en Go?

Un puntero es una referencia al valor almacenado en memoria.

Para obtener la referencia al puntero del valor debes usar el símbolo “&” por delante y para obtener la desreferencia usas el símbolo “*

Por defecto Go envia los argumentos como valor, si deseas que los argumentos sean enviados por referencia debes usar un puntero.

Hagamos una prueba. Crea dos métodos, el primero “addOne” que reciba un valor entero al cual le sumas un valor. Este argumento será recibido por valor. No se usan punteros aquí.

func addOne(value int) {
      value = value + 1
}

El segundo método a crear es “addOnePointer” que haga lo mismo pero que reciba el argumento como como puntero para desreferenciarlo de su posición de memoria a la variable. Este argumento será recibido como referencia.

func addOnePointer(value *int) {
      *value = *value + 1
}

Haciendo una prueba simple puedes observar que en el primer método “func addOne(value int) “ el argumento se recibe por valor como una copia por lo cual la variable original no es modificada.

En el segundo método func addOnePointer(value *int) en donde indicamos usar el puntero, el valor se recibe por referencia y la variable original si es modifica, ya que estamos accediendo a la posición de memoria y modificandola allí.

package main

import "fmt"

// value param
func addOne(value int) {
      value = value + 1
}

// pointer param, desreference param
func addOnePointer(value *int) {
      *value = *value + 1
}

func main() {
      i := 1
      fmt.Println("Value of i is:", i)

      addOne(i)
      fmt.Println("addOne:", i)

      // using memory address of i (pointer)
      addOnePointer(&i)
      fmt.Println("addOnePointer:", i)

}

El resultado de la prueba es el siguiente:

go run pointers.go
Value of i is: 1
addOne: 1
addOnePointer: 2

Compartir esto:

Go Interfaces

Go Interfaces

¿Qué son las interfaces en Go?

En Go una Interface es un conjunto o colección de métodos que deberán ser implementados.

Por ejemplo, suponemos que deseas una interfaz para figuras geométricas IShape que defina un método de cálculo del área para todos los tipos de figuras

type IShape interface {
      area() float64
}

Ahora lo que haces es implementar esa interface que calcula el área para diferentes figuras.

Defines un struct Rectangle, Square y Triangle con sus campos:

// Create a Rectangle struct
type Rectangle struct {
      Lenght float64
      Width  float64
}

// Create a Square struct
type Square struct {
      SideLength float64
}

// Create a Triangle struct
type Triangle struct {
      Base   float64
      Height float64
}

Implementas el método de la interfaz para cada uno. Observa que la implementación de la interfaz está implícita. Si vienes de Java como es mi caso esto puede resultar confuso; en Java debes explícitamente indicar que tu clase implementa determinada interfaz.

El método tiene un receptor para Rectangle y el nombre del método es el de la interfaz, por lo que implícitamente estamos implementando para Rectangle el método de la interfaz. Lo mismo para el resto de figuras Square y Triangle.

// Implement IShape.area() for Rectangle
func (r Rectangle) area() float64 {
      return r.Lenght * r.Width
}

// Implement IShape.area() for Square
func (s Square) area() float64 {
      return s.SideLength * 2
}

// Implement IShape.area() for Triangle
func (t Triangle) area() float64 {
      return t.Base * t.Height / 2
}

Creas una función que reciba la interfaz y muestre y devuelva el resultado

func ShowArea(s IShape) string {
      return fmt.Sprintf("Area %f", s.area())
}

Tu código completo para este ejemplo luce así:

package main

import (
      "fmt"
)


// Interface
type IShape interface {
      area() float64
}

// Create a Rectangle struct
type Rectangle struct {
      Lenght float64
      Width  float64
}

// Create a Square struct
type Square struct {
      SideLength float64
}

// Create a Triangle struct
type Triangle struct {
      Base   float64
      Height float64
}

// Implement IShape.area() for Rectangle
func (r Rectangle) area() float64 {
      return r.Lenght * r.Width
}

// Implement IShape.area() for Square
func (s Square) area() float64 {
      return s.SideLength * 2
}

// Implement IShape.area() for Triangle
func (t Triangle) area() float64 {
      return t.Base * t.Height / 2
}

// Function to show Area
func ShowArea(s IShape) string {
      return fmt.Sprintf("Area %f", s.area())
}

func main() {
      // creating a Rectangle
      fmt.Println("Rectangle", ShowArea(Rectangle{10, 50}))
      fmt.Println("Square", ShowArea(Square{40}))
      fmt.Println("Triangle", ShowArea(Triangle{20,30}))
}

Ejecutando tu código:

$ go run interfaces.go
Rectangle Area 500.000000
Square Area 80.000000
Triangle Area 300.000000

Compartir esto:

Go conceptos básicos con ejemplos

Una de las cosas que más me gustan de Go es su simplicidad de código y su rapidez tanto en el aprendizaje como en su ejecución.

Te explico los conceptos básicos de Go para iniciarte en este lenguaje. Go es un lenguaje simple de aprender y si ya conoces cualquier otro lenguaje en pocas horas puedes asimilar su semántica.

Estas son las sintaxis iniciales que deberías comprender, con ejemplos muy simples, para iniciarte en Go.

Go Variables

¿Como crear variables en Golang?

La palabra reservada para crear variables en Golang es “var” a continuación va el nombre de la variable y le sigue el tipo.

var hello string = "hello word"

Puedes declarar múltiples variables para el mismo tipo

var hello1, hello2, hello3 string = "hello word"

En golang todas las variables tienen siempre un valor predefinido

var s string
fmt.Println(s=="") // imprime "true"
var i int
fmt.Println(i==0) // imprime "true

Golang cuenta con tipado dinámico por lo que es capaz de inferir el tipo de dato

i := 123  // entero
name := "Gustavo" // string

Ejemplos de variables:

package main

import "fmt"

func main() {

	// var string
	var hello string = "hello word"
	fmt.Println(hello)

	// var int
	var age int = 30;
	fmt.Println("Age ", age);

	// var float
	var fb float64 = 30.12345678901234567890;
	fmt.Println("Float fb ", fb);

	// multiple assignations
	var hi, word string = "hi", "word"
	fmt.Println(hi, word)

	// default value of variable int
	var defaultInt int
	fmt.Println("Value of defaultInt", defaultInt)

	// default value of boolean
	var defaultBoolean bool
	fmt.Println("Value of defaultBoolean", defaultBoolean)

	// short variable declarations
	great := "great work";
	fmt.Println(great);

	// type inference
	var b = true
	fmt.Println("Value of b", b)

}
$ go run variables.go
hello word
Age  30
Float fb  30.123456789012344
hi word
Value of defaultInt 0
Value of defaultBoolean false
great work
Value of b true

Go constantes

¿Cómo crear constantes en Golang?

En golang puedes crear constantes usando la palabra reservada “const”.

const s string = "hello"

Aquí van algunos ejemplos de uso de constantes:

package main

import "fmt"

// declare constant
const (
	MaxAge = 30
	MinAge = 15
)

func main() {

	// declare constant
	const months = 12

	// use constant
	var age int = 30
	fmt.Println("Age in months ", months * age);

	// use constant
	fmt.Println("Max age", MaxAge)
	fmt.Println("Min Age", MinAge)

}

$ go run constant.go
Age in months  360
Max age 30
Min Age 15

Go Arreglos

¿Cómo crear arreglos en golang?

En golang un array tiene esta forma.

var i [5]int

Primero el keyword var, luego el nombre de la variable y la cantidad de elementos para el tipo definido. Los arreglos tienen un tamaño predefinido y no es posible redimensionarlos, una vez creado el arreglo con su tamaño este conserva su dimensión y no es factible cambiarlo en tiempo de ejecución.

package main

import "fmt"

func main() {

	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println("Position 0 ", a[0])
	fmt.Println("Values of a", a)

	// array with assignment
	fibonacci := [11]int{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}
	fmt.Println("Values of fibonacci", fibonacci)

	// array with 3 position and two assignment
	array := [3]int{0, 1}
	fmt.Println("Values of array", array)


	// array multi-dimensional
	var twoDimensionArray [2][3]int
	twoDimensionArray[0][0] = 1
	twoDimensionArray[0][1] = 2
	twoDimensionArray[0][2] = 3

	twoDimensionArray[1][0] = 4
	twoDimensionArray[1][1] = 5
	twoDimensionArray[1][2] = 6

	fmt.Println("Values of twoDimensionArray", twoDimensionArray)

}

Position 0  Hello
Values of a [Hello World]
Values of fibonacci [0 1 1 2 3 5 8 13 21 34 55]
Values of array [0 1 0]
Values of twoDimensionArray [[1 2 3] [4 5 6]]

Go Slices

¿Qué son los Slices en Golang?

Los slices podemos pensarlos como “tipo de arreglo” pero de tamaño dinámicos. A diferencia de los arreglos que no puedes redimensionar, los slices sí es factible cambiarlos en tamaño.

En un arreglo debes declarar el tamaño de antemano, en cambio un slice lo defines de esta forma:

var colors []string

Observa que no hemos indicado el tamaño.

package main

import "fmt"

func main() {

	// slide without data
	var colors []string
	fmt.Println("Colors without data", colors)

	colors = append(colors, "red")
	fmt.Println("Colors data", colors)

	// slice
	cities := []string{"buenos aires", "sao paulo", "new york"}
	fmt.Println("Cities", cities)

	// slice using make
	m := make([]int, 5)
	fmt.Printf("Slice m - lenght=%d capacity=%d values %v", len(m), cap(m), m)
	fmt.Println()

	mc := make([]int, 0, 5)
	fmt.Printf("Slice mc - lenght=%d capacity=%d values %v", len(mc), cap(mc), mc)
	fmt.Println()

}

..

$ go run slices.go
Colors without data []
Colors data [red]
Cities [buenos aires sao paulo new york]
Slice m - lenght=5 capacity=5 values [0 0 0 0 0]
Slice mc - lenght=0 capacity=5 values []

Go For

¿Cómo crear un for en Golang?

Go maneja los ciclos de manera muy simple en vista de que tiene solo un tipo de ciclo definido por el loop “for”.

El loop “for” en Golang luce parecido al “for” en java o en “C” con la diferencia de que no usas paréntesis y son requeridas las llaves {}

El for en Golang se compone de
-Inicialización de la variable
-La condición por la cual el loop continua su ejecución o no.
-Una instrucción que se ejecuta en cada iteración

package main

import "fmt"

func main() {

	// for
	for i := 0; i < 5; i++ {
		fmt.Println(i)
	}

	// for continued
	sum := 1
	for ; sum < 100; {
		sum += sum
	}
	fmt.Println("sum", sum)

	// for like while
	sumb := 1
	for sumb < 1000 {
		sumb += sumb
	}
	fmt.Println("sumb", sumb)

	// infinite for
	x := 0
	for {
		// do something in a loop and break manually
		x++
		if (x >= 10) {
			fmt.Println("infite loop break")
			break
		}
	}
}

$ go run for.go
0
1
2
3
4
sum 128
sumb 1024
infite loop break

Go Range

¿Qué es un Range en Golang?

En Golang “range” te permite iterar los elementos de distintas estructuras de datos. Por ejemplo puedes usar “range” para iterar un arreglo, un slice, un map, una cadena de texto.

package main

import "fmt"

func main() {

	// create array and range from this array
	var numbers = []int{2, 4, 6, 8}
	for i, v := range numbers {
		fmt.Println("index of array", i, "value or array", v)
	}

	// create slice and range from this slice
	nums := []int{2, 4, 6}
	sum := 0
	for i, v := range nums {
		sum += v
		fmt.Println("index of slice", i, "sum", sum)
	}

	// create a map and range from this map
	fruits := map[string]string{"o": "orange", "b": "banana"}
	for k, v := range fruits {
		fmt.Println("key of map", k, "value of map", v)
	}

	// create string and range from this string
	s := "golan hello word"
	for i, c := range s {
		fmt.Println("index of character", i, "character", string(c))
	}

}

$ go run range.go
index of array 0 value or array 2
index of array 1 value or array 4
index of array 2 value or array 6
index of array 3 value or array 8
index of slice 0 sum 2
index of slice 1 sum 6
index of slice 2 sum 12
key of map o value of map orange
key of map b value of map banana
index of character 0 character g
index of character 1 character o
index of character 2 character l
index of character 3 character a
index of character 4 character n
index of character 5 character  
index of character 6 character h
index of character 7 character e
index of character 8 character l
index of character 9 character l
index of character 10 character o
index of character 11 character  
index of character 12 character w
index of character 13 character o
index of character 14 character r
index of character 15 character d

Go If/Else

¿Cómo usar if/else en Golang

En Go para las declaraciones “if” no son requeridos los paréntesis (), pero si son obligatorias las llaves {}.

package main

import (
	"fmt"
)

func main() {

	// common if statement
	s := "hi"
	if s == "hi" {
		fmt.Println("Hello")
	} else if s == "bye" {
		fmt.Println("See you")
	} else {
		fmt.Println("How’s it going")
	}

	// if statement with operation
	x := 10
	n := 180
	if v := x + n; v <= 100 {
		fmt.Println("value is less or equal than 100 ", v)
	} else if v <= 200 {
		fmt.Println("value is less or equal than 200 ", v)
	} else {
		fmt.Println("the value is greater than 200", v)
	}

	// if without else
	i := 8
	j := 4
	if i % j == 0 {
		fmt.Println("It is divisible")
	}
}

$ go run ifelse.go
Hello
value is less or equal than 200  190
It is divisible

Go Switch

¿Cómo usar Switch en Golang?

Switch es otro control condicional que te permite controlar el flujo de tu código.

Observa que en Golang para el switch no es necesario agregar break como sucede por ejemplo en Java. El break esta implicito. Puedes agregar opcionalmente “default” para que en el caso de que ninguna condición se cumpla. Además, es posible comparar más de un valor en un “case”.

package main

import (
	"fmt"
	"time"
)

func main() {

	// basic switch - 'break' it is not necessary
	value := 4
	switch value {
	case 1:
		fmt.Println("value is one")
	case 2:
		fmt.Println("value is two")
	case 3:
		fmt.Println("value is three")
	case 4, 5:
		fmt.Println("value is four or five")
	default:
		fmt.Println("I do not recognize the value")
	}

	// switch without condition, like if
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon")
	default:
		fmt.Println("Go home")
	}
}

$ go run switch.go
value is four or five
Good afternoon

Go Maps

¿Cómo crear Mapas en Golang?

Al igual que en otros lenguajes un mapa relaciona un key con un valor.
Así creas un mapa en Golang con un key entero que represente un id, y un valor que represente un el nombre de un producto:

// map literal
var x = map[int]string{
      1: "coffee",
      2: "te",
      3: "chocolate",
}

// adding more elements
x[4] = "cappuccino"
fmt.Println("key value of map x", x)


// retrieve one element
product := x[2]
fmt.Println("Product of key 2", product)

// delete element
delete(x, 2)

// check if element exist
element, okExist := x[1]
if(okExist) {
      fmt.Println("Element with id 1 is",element)
}

key value of map x map[1:coffee 2:te 3:chocolate 4:cappuccino]
Product of key 2 te
Element with id 1 is coffee

Para crear un map vacío en Golang usas la función make

// create a map using make
m := make(map[string]string)
m["key1"] = "value1"
m["key2"] = "value2"
fmt.Println("key value of map m", m)
key value of map m map[key1:value1 key2:value2]

Go Funciones

¿Cómo crear funciones en Golang

En Golang las funciones pueden recibir cero o más argumentos y retornar cualquier cantidad de elementos acordes al tipo.

package main

import "fmt"

// function receive two arg and return one
func plus(a int, b int) int {
      return a + b
}

// function receive two arg and return two
func hello(h string, w string) (string, string) {
      return h, w
}

func main() {
      fmt.Println("plus", plus(2, 6))
      fmt.Println(hello("hello", "golang"))
}

La salida de estas funciones

$ go run functions.go
plus 8
hello golang

Go Structs (estructuras)

¿Qué son y cómo crear structs en Golang?

Un Struct en Golang define una estructura de fields / campos. Si vienes de Java lo puedes pensar como una clase que no permite herencia pero que sí permite composición. El acceso a los campos es directo desde el struct, es decir, no requieres getters y setters.

package main

import "fmt"

type User struct {
      Name     string
      LastName string
}

func main() {

      u := User{"Albert", "Einstein"}
      fmt.Println(u)

      fmt.Println(u.Name, "was a genius");
}
$ go run structs.go
{Albert Einstein}
Albert was a genius

Go Métodos

¿Cómo se escribe un método en Golang?

En Golang un método es un tipo de función con un receptor definido como argumento.
Observa que a diferencia de las funciones comunes este receptor va entre el keyword “func” y el nombre del método.

Por ejemplo, si tienes un Struct User que representa un usuario

type User struct {
	Name     string
	LastName string
}

puedes definir un método cuyo receptor sea ese User

func (u User) CheckGenius() bool {
      return u.Name == "Albert"
}

que luego debes usar de este modo

func main() {

	u := User{"Albert", "Einstein"}


	// using method throw User.CheckGenius
	fmt.Println("Method CheckGenius", u.Name, u.CheckGenius())


}


Así queda el código, en Golang escribes este método afuera del Struct.

package main

import "fmt"

type User struct {
      Name     string
      LastName string
}

// this is a method
func (u User) CheckGenius() bool {
      return u.Name == "Albert"
}

// this is a common function
func CheckGenius(u User) bool {
      return u.Name == "Albert"
}

func main() {

      u := User{"Albert", "Einstein"}

      // using method throw User.CheckGenius
      fmt.Println("Method CheckGenius", u.Name, u.CheckGenius())

      // using a common function
      fmt.Println("Function CheckGenius", CheckGenius(u))

}

Go Imports

¿Cómo importas en Go?

Para importar en Go puedes hacerlo agrupado o linea por linea

Import linea por linea

package main

import "fmt"
import "time"

Import agrupado (recomendado).

package main

import (
	"fmt"
	"time"
)

func main() {
	h, m, s := time.Now().Clock()
	fmt.Println("Hour: ", h)
	fmt.Println("Minute: ", m)
	fmt.Println("Second: ", s)
}
$ go run imports.go
Hour:  13
Minute:  20
Second:  48

Go Packages

¿Cómo organiza Go sus archivos?

Go organiza sus programas en paquetes y se inicia su ejecución en el package main

package main

func main() {
      print("Hola, Mundo!")
}
$ go run packages.go
Hola, Mundo!

Go, por convención, usa como nombre de package el último elemento de la ruta de los import.

Por ejemplo, si importamos el paquete “math / rand”, esto incluye todos los archivos que se encuentran en el paquete rand.

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("Random number", rand.Intn(10))
}

Referencias:
Estos ejemplos están basados en la excelente Guia oficial de Go

Compartir esto:

Go Hola Mundo

¿Cómo escribir un hola mundo en Golang?

En Go escribes un ‘hola mundo’ de este modo:

package main

import "fmt"

func main() {
	fmt.Printf("hola mundo\n")
}

-Los paquetes se definen en la primer línea como “package”
-A continuación van los “import”
-El ‘main’ del programa se inicia con func main()

¿Como ejecutar hello-world.go?

$ go run hello-world.go
hola mundo

¿Cómo hacer un build de hello-world.go?

$ go build hello-world.go
$ ./hello-world
hola mundo

Compartir esto:

Como instalar Go en Mac OS

Para instalar Golang en Mac OS sigue estos pasos:

Descargar la versión de golang para Mac desde esta dirección:

https://golang.org/dl/

Al finalizar la descarga, ejecuta el archivo pkg descargado.

Sigue los pasos del instalador de go

 

Al finalizar la instalación go queda en la carpeta /usr/local/go

Crea tu espacio de trabajo

Go utiliza como convención la ruta /go dentro de tu home como espacio de trabajo para los programas.

mkdir $HOME/go

Agrega tu espacio de trabajo al path

Go busca usando la variable de entorno GOPATH la ubicación de tu workspace. Agrega esta variable de entorno en tu archivo bash_profile de así

 
vi ~/.bash_profile
export GOPATH=$HOME/go

Agrega Go al path

El instalador agrega automáticamente al PATH la carpeta /usr/local/go/bin .
Verifica que Go esté incluido en el PATH buscando la ruta /usr/local/go/bin

~ $ echo $PATH
< ...others paths... : > /usr/local/go/bin
~ $

Si no está entonces agrega go al path en tu archivo ~/.bash_profile

 
vi ~/.bash_profile
export PATH=$PATH:/usr/local/go/bin

Comprueba la instalación

Comprueba go escribiendo go version desde la consola.

~ $ go version
go version go1.9.1 darwin/amd64
~ $

También puedes ejecutar go env desde la consola para ver todas las variables de entorno establecidas

~ $ go env
...
..
GOPATH="/Users/gustavo/go"
GOROOT="/usr/local/go"
..
...
~ $

Referencia: golang

Compartir esto:

Strategy Pattern con Spring Boot

Strategy Pattern con Spring Boot

Un buen patrón para resolver la complejidad cuando debes implementar diferentes comportamientos según algún estado es el patrón strategy.

Un patrón strategy encapsula comportamiento que podemos intercambiar en ejecución. Es decir, cambiar el comportamiento o lógica de negocio acorde al estado del modelo o contexto en el que te se encuentra, evitando el uso extensivo de if o switch en tu código.

Es bastante simple. Necesitas una interfaz Strategy y diferentes implementaciones para la resolución de los diferentes comportamientos deseados.

Para este ejemplo suponemos que tenemos una entidad “User” que representa un usuario. Estos users son clasificados por tipo acorde a un enum “UserType”. El UserType determinará el valor del atributo limitCredit del User.

@Entity
public class User {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @NotBlank
   private String userName;

   @NotBlank
   private String lastName;

   private UserType type;

   private double limitCredit;


}

public enum UserType {
   NORMAL, FULL, GOLD;
}


Queremos que nuestro servicio se desentienda de esta decisión, por lo que crearemos diferentes estrategias para cada tipo de user.

Cómo crear el patrón Strategy

Defines la interfaz de la cual luego vas a implementar los diferentes comportamientos. En este ejemplo simple se trata de cambiar el limite bajo algún cálculo que suponemos complejo para los diferentes tipos de User.

public interface Strategy {
   void changeLimit(User user);
}

Las implementaciones de las estrategias para cada tipo UserType quedan así:

public class StrategyOperationNormal implements Strategy {
   @Override
   public void changeLimit(User user) {
       // a complex calculation.. 
       user.setType(UserType.NORMAL);
       user.setLimitCredit(1000D);
   }
}

public class StrategyOperationFull implements Strategy {
   @Override
   public void changeLimit(User user) {
       user.setType(UserType.FULL);
       user.setLimitCredit(5000D);
   }
}
public class StrategyOperationGold implements Strategy {

   @Override
   public void changeLimit(User user) {
       user.setType(UserType.GOLD);
       user.setLimitCredit(20000D);
   }

}

Usar un Factory que nos devuelva el Strategy

Para simplificar aún más el uso de Strategy en tu servicio creas una clase StrategyFactory. Este factory te devolverá la estrategia correspondiente según el UserType.

Un patrón factory te permite crear objetos bajo alguna condición sin necesidad de conocer o preocuparte acerca de la implementación real, en definitiva solo conoces la interfaz.

@Component
public class StrategyFactory {

   private Map<UserType, Strategy> strategies = new EnumMap<>(UserType.class);

   public StrategyFactory() {
       initStrategies();
   }

   public Strategy getStrategy(UserType userType) {
       if (userType == null || !strategies.containsKey(userType)) {
           throw new IllegalArgumentException("Invalid " + userType);
       }
       return strategies.get(userType);
   }

   private void initStrategies() {
       strategies.put(UserType.NORMAL, new StrategyOperationNormal());
       strategies.put(UserType.FULL, new StrategyOperationFull());
       strategies.put(UserType.GOLD, new StrategyOperationGold());
   }
  
}


En tu servicio haces uso del factory para obtener la estrategia y delegarle la lógica de cambio de tipo de UserType en el metodo “changeType” .

@Service
public class UserService {

   private final StrategyFactory strategyFactory;
   private final UserRepository repository;

   @Autowired
   public UserService(UserRepository repository, StrategyFactory strategyFactory) {
       this.repository = repository;
       this.strategyFactory = strategyFactory;
   }

   // other methods of service..

   public User changeType(long id, UserType type) {
       Strategy strategy = strategyFactory.getStrategy(type);
       User user = repository.findOne(id);
       strategy.changeLimit(user);
       return repository.save(user);
   }

}

Probando tu Strategy

Usando el Controller de la app, que tenemos ya definido, creamos un User. Por defecto el User se crea con un tipo “NORMAL”.

El controller de nuestra app

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public ResponseEntity<User> list() {
        List<User> users = userService.list();
        return new ResponseEntity(users, HttpStatus.OK);
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<User> userById(@RequestParam(value = "id") long id) {
        User user = userService.get(id);
        return new ResponseEntity(user, HttpStatus.OK);
    }

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseEntity<User> create(@Valid @RequestBody User user) {
        User userCreated = userService.create(user);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

    @RequestMapping(value = "/change_type", method = RequestMethod.POST)
    public ResponseEntity<User> changeType(@RequestParam(value = "id") long id,
                                           @RequestParam(value = "type") UserType type) {
        User userCreated = userService.changeType(id, type);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

}

El Service completo de nuestra app

@Service
public class UserService {

    private final StrategyFactory strategyFactory;
    private final UserRepository repository;

    @Autowired
    public UserService(UserRepository repository, StrategyFactory strategyFactory) {
        this.repository = repository;
        this.strategyFactory = strategyFactory;
    }

    public User get(long userId) {
        return repository.findOne(userId);
    }

    public List<User> list() {
        Iterable<User> users = repository.findAll();
        List<User> list = new ArrayList<User>();
        users.forEach(list::add);
        return list;
    }

    public User create(User user) {
        Strategy strategy = strategyFactory.getStrategy(UserType.NORMAL);
        strategy.changeLimit(user);
        return repository.save(user);
    }


    public User changeType(long id, UserType type) {
        Strategy strategy = strategyFactory.getStrategy(type);
        User user = repository.findOne(id);
        strategy.changeLimit(user);
        return repository.save(user);
    }

}

Ejecutas el rest api “/create” para crear un User que luego vamos a modificar. Enviamos en el body lo mínimo que necesita la entidad User.
El user se crea con el type “NORMAL” por defecto.

Luego ejecutas el rest api “/change_type?id=1&type=FULL” para probar tu Servicio que usa el Factory y el Strategy. Aquí intentas cambiar el type de NORMAL a FULL para el user previamente creado.

Observa que el user ha cambiado su límite acorde a la Strategy.

Descarga este código completo desde GitHub

Compartir esto:

Spring Boot – como capturar las excepciones y retornar un json estandar

Cómo capturar y manejar las excepciones con Spring Boot para una Rest Api

En una aplicacion Spring en la cual exponemos endpoints Rest para que los clientes la consuman contamos con un manejador de excepciones de forma global para toda tu app.
Esto nos da la flexibilidad de decidir que devolver al cliente para cada tipo de exception.

En este ejemplo te muestro como capturar una excepción cuando el cliente consume tu api y el body que te ha enviado no es válido.

En el caso de recibir un body inválido Spring crea una excepción MethodArgumentNotValidException y retorna un json que seguramente sobra para el cliente o información que te gustaría estandarizar para todos los errores de forma más acorde para tu api.

El modelo que representa el body que vas a recibir

public class User {

    @JsonProperty
    @NotBlank
    private String userName;

    @JsonProperty
    @NotBlank
    private String lastName;

    @JsonProperty
    @NotNull
    private Gender gender;

    @JsonProperty
    @Min(value = 18)
    @Max(value = 150)
    @NotNull
    private Integer age;

    @JsonProperty
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    @Past
    @NotNull
    private Date dateOfBirth;

Resultado de una exception MethodArgumentNotValidException

Este es el resultado que recibes cuando Spring lanza una excepción al recibir un body inválido en un post. En este ejemplo se omite un tag requerido “dateOfBirth” en el body y por esta razón se lanza la excepción.

¿Cómo debes capturar esta excepción y devolver algo a tu gusto?

Creas la clase que representará la respuesta json

Defines tu clase con los json property que deseas mostrar como retorno del error. Suponemos que deseas devolver tres atributos simples, el mensaje, el status code, y la url.

import com.fasterxml.jackson.annotation.JsonProperty;

public class ErrorInfo {

   @JsonProperty("message")
   private String message;
   @JsonProperty("status_code")
   private int statusCode;
   @JsonProperty("uri")
   private String uriRequested;

   public ErrorInfo(ApiException exception, String uriRequested) {
       this.message = exception.getMessage();
       this.statusCode = exception.getStatusCode().value();
       this.uriRequested = uriRequested;
   }

   public ErrorInfo(int statusCode, String message, String uriRequested) {
       this.message = message;
       this.statusCode = statusCode;
       this.uriRequested = uriRequested;
   }

   public String getMessage() {
       return message;
   }

   public int getStatusCode() {
       return statusCode;
   }

   public String getUriRequested() {
       return uriRequested;
   }

}

Creas el manejador global de excepciones ControllerAdvice

Para capturar este error debes crear una clase anotada @ControllerAdvice allí definirás cada tipo de error que deseas manejar @ExceptionHandler. Este método recibe el request y la excepción en particular. Puedes manejar todas las excepciones que desees.

Para nuestro caso:

@ControllerAdvice
public class ErrorHandler {

   @ExceptionHandler(MethodArgumentNotValidException.class)
   public ResponseEntity<ErrorInfo> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
       ErrorInfo errorInfo = new ErrorInfo(HttpStatus.BAD_REQUEST.value(), e.getMessage(), request.getRequestURI());
       return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
   }
}

Si pruebas el resultado verás esta respuesta, algo mejor, pero todavia el mensaje está poco claro.

Mejorar el mensaje

Para hacer más legible el mensaje puedes leer cada uno de los FieldError que son devueltos por MethodArgumentNotValidException.getBindingResult() para armar un string más legible.

@ControllerAdvice
public class ErrorHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorInfo> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {

        // get spring errors
        BindingResult result = e.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();

        // convert errors to standard string
        StringBuilder errorMessage = new StringBuilder();
        fieldErrors.forEach(f -> errorMessage.append(f.getField() + " " + f.getDefaultMessage() +  " "));

        // return error info object with standard json
        ErrorInfo errorInfo = new ErrorInfo(HttpStatus.BAD_REQUEST.value(), errorMessage.toString(), request.getRequestURI());
        return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);

    }

}

Observa el resultado nuevamente al enviar un post que no válida:

Conclusión

Puedes hacer uso de ControllerAdvice para capturar todas las excepciones de tu api y devolver un formato json estandarizado para toda tu aplicación.

Te recomiendo la lectura de este libro Spring Boot in Action . Espero te sirva.

Descarga este código completo desde GitHub

Compartir esto:

Spring Boot + MySql + HikariCP

Configurar Spring Boot con MySql usando HikariCP

Para configurar tu proyecto Spring con MySql utilizando HikariCP debes agregar las dependencias y configurar tu archivo properties con estos simples pasos.

Dependencias

Detalle de dependencias que necesitas para tu proyecto Spring con MySql y Hikari

<dependencies>

   <!-- Spring boot -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jpa</artifactId>
   </dependency>

   <!-- Hikari -->
   <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <version>2.6.0</version>
   </dependency>

   <!-- MySQL Connector -->
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
</dependencies>

Definición del archivo properties

Debes especificar el dialect para MySql, y para el tipo de datasource indicar HikariDataSource.
Así queda tu archivo application.properties

spring.jpa.hibernate.ddl-auto: create-drop
spring.jpa.show-sql: true
spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect

spring.datasource.url: jdbc:mysql://localhost:3306/example_db
spring.datasource.username: root
spring.datasource.password:
spring.datasource.driverClassName: com.mysql.jdbc.Driver
spring.datasource.type: com.zaxxer.hikari.HikariDataSource

 ## HikariCP config - spring.datasource.hikari.*
spring.datasource.hikari.pool-name: pool-hikari-example
spring.datasource.hikari.maximum-pool-size: 10
spring.datasource.hikari.connection-timeout: 60000


Inicia tu aplicación con Spring Boot + MySlq + Hikari

Corres tu app mvn spring-boot:run

Observa en la consola esta salida en dónde puedes ver que ha levantado el datasource de Hikari con el pool que especificaste previamente en tu archivo de configuración.

    spring.datasource.hikari.pool-name: pool-hikari-example

Salida del log de Spring

INFO 59268 --- [           main] com.zaxxer.hikari.HikariDataSource       : pool-hikari-example - Start completed.

Con esto ya tienes tu configuración usando HikariCP.

Descarga este código completo

Compartir esto:

Spring Batch – Como eliminar las tablas de jobs

Spring Batch mantiene diferentes tablas con las instancias de los Jobs, las ejecuciones de los Jobs y las ejecuciones de los Steps, entre otras.

Cuando estas en etapa de desarrollo resulta muchas veces útil eliminar las tablas para iniciar la app en limpio.

Si necesitas eliminar todas las tablas de Jobs y Steps debes correr este script en tu BD en este mismo orden:

DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT ;
DROP TABLE IF EXISTS BATCH_STEP_EXECUTION ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION ;
DROP TABLE IF EXISTS BATCH_JOB_INSTANCE ;

DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ ;
DROP TABLE IF EXISTS BATCH_JOB_SEQ ;

Al reiniciar tu aplicación estas tablas deberían ser creadas nuevamente por Spring Batch.

Compartir esto:

Spring Boot Rest – Ejemplo RestController con RequestBody y Valid

Como crear con Spring Boot una aplicación Rest

Para definir un RestController en Spring Boot debes anotar tu controlador como un @RestController y anotar cada método que responderá con @RequestMapping.

Spring hace uso de la anotación @RequestBody para mapear el json y la anotación @Valid para aplicar los constraints definidos en la clase para el caso de los post.

Para este ejemplo desarrollarás una pequeña aplicación que recibirá un post con un body en json para crear un User mediante un rest api, también tendrás la opción de consultar los User guardados en la BD.

-El endpoint /create recibirá un json con el User, lo validará y lo guardará en la BD.
-Crearemos otro endopoint /users para consultar todos los User en la BD
-También un /user?id=userId para mostrar un User por el id
-Este endpoint para crear, recibirá el json y lo mapeará contra el pojo User

Configurar las dependencias

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>

Como configurar la Base de datos

El pom definiste una base de datos h2 que correrá embebida en la applicación, en nuestro caso persistirá solamente en memoria.

¿Cómo definir una base de datos H2 en memoria en Spring?

Para que la base de datos h2 persista solo en memoria debemos indicar el datasource en el application.properties de este modo:

#in memory database
spring.datasource.url = jdbc:h2:mem:example_db

#in file database
#spring.datasource.url = jdbc:h2:file:~example_db

spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

Configurar la app para Spring Boot

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Creas el modelo User

En el modelo que soporta nuestro json desde el post agrega los json property @JsonProperty indicando cómo deberá entenderse cada atributo del json. Además anotas las validaciones que deseas. Para este ejemplo, este mismo modelo lo usaremos para la persistencia por eso lo anotamos como @Entity.

Algunas de las validaciones que para este ejemplo son

@NotNull – verifica que el valor no quede nulo.
@NotBlank – verifica que el valor sea nulo o blanco para Strings
@Past – verifica que el valor este en el pasado
@Min – valor mínimo posible
@Max – valor máximo posible
@Email – verifica que el string tenga un formato de email valido

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @JsonProperty("user_name")
    @NotBlank
    private String userName;

    @JsonProperty("last_name")
    @NotBlank
    private String lastName;

    @JsonProperty("gender")
    @NotNull
    private Gender gender;

    @JsonProperty("age")
    @Min(value = 18)
    @Max(value = 150)
    @NotNull
    private Integer age;

    @JsonProperty("birth")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    @Past
    @NotNull
    private Date dateOfBirth;
    
   // get and set... 

}

package com.gp.model;
public enum Gender {
    MALE, FEMALE;
}

Creas el servicio y el repository

Tu servicio recibirá las consultas desde el controller y la resolverá lógica del negocio, para el caso del acceso a la base este servicio consultará al repositorio especifico.

@Service
public class UserService {

    @Autowired
    private UserRepository repository;

    public User get(long userId) {
        return repository.findOne(userId);
    }

    public List<User> list() {
        Iterable<User> users = repository.findAll();
        List<User> list = new ArrayList<User>();
        users.forEach(list::add);
        return list;
    }

    public User create(User user) {
        return repository.save(user);
    }
}

El repositorio será un CrudRespository que nos da lo básico para este ejemplo.

import com.gp.model.User;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {

}

Definir un RestController con Spring Boot

Para crear un endpoint a fin de consultar Users y crearlos debes definir un controller de este modo usando las anotaciones @RestController y @RequestMapping

package com.gp.controllers;

import com.gp.model.User;
import com.gp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public ResponseEntity<User> list() {
        List<User> users = userService.list();
        return new ResponseEntity(users, HttpStatus.OK);
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<User> userById(@RequestParam(value = "id") long id) {
        User user = userService.get(id);
        return new ResponseEntity(user, HttpStatus.OK);
    }

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseEntity<User> create(@Valid @RequestBody User user) {
        User userCreated = userService.create(user);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

}

Pruebas RestController con @Valid y @RequestBody

Prueba enviar tu json con un valor inválido:

Envía al rest /create vía post este body que contiene un valor invalido para el “age”.

Observa la respuesta con un status 400 por no haber pasado los constraint del modelo.

Prueba con valores válidos:

Ahora envía el json con todos lo valores correctos, el endpoint te devuelve el modelo que creado ya con un id asignado.

Consulta de todos los User guardados:

Mediante el el endpoint /users podemos obtener toda la lista de User que hemos ido creando.

Descarga este código completo

Compartir esto:

Android Retrofit con Proguard – Como evitar errores con Gson

Si estas usando Retrofit en tu app Android y a la vez estas ofuscando y minizando tu app con proguard será necesario que agregues estas lineas a tu archivo proguard.txt

# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions

Por otro lado, si estas mapeando tus archivos desde la response de Retrofit con Gson necesitarás además evitar que estas clases de tu modelo que mapeas sean modificadas por proguard. Para esto puedes agregar la anotación @Keep a tus clases

@Keep
public class User {

    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("username")
    @Expose
    private String username;
 ....
	
}

Observa en tu archivo proguard que por default este evita las clases anotadas de con @Keep

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

Puedes, en vez de usar @Keep, agregar el paquete de tu modelo en el archivo proguard, aunque yo prefiero usar la anotación.
Suponiendo que las clases de tu modelo estan en “com.model” puedes agregar este paquete para que proguard lo mantenga intacto.

-keep class com.model.** { *; }

Referencias:
http://square.github.io/retrofit/

Compartir esto: