miércoles, 29 de diciembre de 2021

Usando el dialogo de configuración de impresión

Tradicionalmente, los usuarios quieren ver la configuración de pagina y otras opciones importantes de impresión. Desde el dialogo de impresión tu puedes seleccionar una impresora, especificar las paginas a imprimir y configurar el número de copias.

Una aplicación muestra un dialogo de impresión cuando el usuario presiona el botón "Imprimir", o elige la opción "Imprimir" desde el menú de la aplicación. Para mostrar el dialogo, llamar al método printDialog de la clase PrinterJob.

Este método retorna true si el usuario presiona Aceptar en el dialogo, y false en caso contrario. Las opciones referentes al formato y numero de paginas están restringidas según lo que se estableció en la clase PrinterJob.

El método printDialog en la porción de código anterior abre un dialogo de impresión nativo.

Tu puedes cambiar la información de configuración de pagina que se encuentra en el objeto PageFormat mediante la configuración de pagina en el dialogo.
Para mostrar el dialogo de configuración de pagina, llamar al metodo pageDialog de la clase PrinterJob.

El dialogo de configuración de pagina es inicializado usando el parámetro que se le pasa al objeto pageDialog. Si el usuario hace click en el botón Aceptar del dialogo un objeto de la clase PageFormat se va a crear de acuerdo con las opciones seleccionadas por el usuario y luego este objeto será devuelto por el método. Si el usuario cancela el dialogo, el método pageDialog devuelve el objeto original PageFormat sin modificar.

Usualmente la API de impresión Java 2D requiere una aplicación para mostrar un dialogo de impresión, pero en ocasiones es posible imprimir sin mostrar ningún dialogo en absoluto. Este tipo de impresión es llamado impresión silenciosa. Esto puede ser practico en casos específicos, tales como cuando tu necesitas imprimir un reporte semanal particular de una base de datos. En otros casos siempre es recomendable informarle al usuario cuando un proceso de impresión está iniciando.

martes, 28 de diciembre de 2021

Un programa básico de impresión en Java

Esta sección explica como crear un programa básico de impresión que muestra un dialogo de impresión e imprime el texto "Hola Mundo" en la impresora seleccionada.

Imprimir un documento consiste en dos partes:

  • La clase Job para controlar la impresión - Crear una petición de impresión, asociar la petición con una impresora, especificar el número de copias, e interacción del usuario con el dialogo de impresión
  • Dibujando la página - Dibujar contenido en una página y administrar contenido que abarca páginas (paginación).

Primero crea una instancia de la clase Job. La clase que representa una petición de impresión y la mayoría de las otras clases relacionadas se encuentran en el paquete java.awt.print

A continuación proporciono el código que imprime en contenido en la pagina, esto se logra implementando la interfaz Printable.

Una aplicación por lo general mostrará un dialogo de impresión para que el usuario pueda ajustar varias opciones como son el numero de copias, la orientación de la pagina o seleccionar la impresora cuando tengo varias.

Este dialogo se muestra visible hasta que el usuario aprueba o cancela la impresión. La variable doPrint será true si el usuario confirmo que quiere continuar con la impresión.

Si la variable doPrint es falso, el usuario ha cancelado la impresión. Dado que mostrar el cuadro de dialogo es opcional, el valor devuelto es puramente informativo.

Si la variable doPrint es verdadero, entonces la aplicación solicitará que la impresión se realice, esto se concreta llamando al metodo print de clase PrinterJob.

La excepción PrinterException se lanzará si ocurre un problema al envíar nuestro trabajo a la impresora. Sin embargo, dado que el metodo PrinterJob.print retorna tan pronto como el trabajo ha sido enviado a la impresora, el usuario de la aplicación no puede identificar si no hay papel o si este se ha atascado. El control que proporciona la clase PrinterJob es suficiente para usos básicos de impresión.

La interfaz Printable tiene un solo metodo:

La clase PageFormat describe la orientación de la pagina (portrait o landscape), el tamaño del papel y el area donde se puede dibujar en unidades de 1/72 de una pulgada.

El área imprimible representa los límites de margen de la mayoría de las impresoras (margen del hardware). El area imprimible es el espacio dentro de estos margenes, y en la práctica, a menudo se limita aún más para dejar espacio para encabezados o pies de página.

El parámetro page en base zero representa el numero de pagina que será impreso.

El siguiente código representa una implementación completa de la clase Printable

Recuerda que la impresora tiene un margen definido y lo debes de considerar, por ello llamamos al método translate de la clase Graphics. Para conocer el tamaño del margen llamamos a los métodos getImageableX y getImageableY 

Enviar una instancia de la clase Graphics a la impresora es esencialmente lo mismo que renderizarla en la pantalla. En ambos casos, debe realizar los siguientes pasos:

  • Dibujar una cadena de texto de prueba es tan fácil como las otras operaciones que se describieron para dibujar usando la clase Graphics2D.
  • Los gráficos de la impresora tienen una resolución más alta, que debería ser transparente para la mayoría de los programas.
  • El método Printable.print() es llamado por el sistema de impresión al igual que se llama al método Component.paint() para pintar un componente en la pantalla. El sistema de impresión llamará al método Printable.print() para la pagina 0, 1, .. etc.
  • Hasta que el método print() devuelva NO_SUCH_PAGE.
  • Se puede llamar al método print() con el mismo índice de pagina varías veces hasta que se complete el documento. Esta función es aplicada cuando el usuario especifica atributos como múltiples copias con la opción collate.
  • El área imprimible de PageFormat determina el área del clip. El área imprimible también es importante para calcular la paginación o cómo distribuir el contenido en las páginas impresas, ya que los saltos de página están determinados por cuánto cabe en cada página.

Nota: Se puede omitir una llamada al método print () para ciertos índices de página si el usuario ha especificado un rango de página diferente que no involucra un índice de página en particular.

domingo, 19 de diciembre de 2021

Impresión en Java

La API Java 2D te permite dibujar en cualquier superficie, una extensión natural de eso es la habilidad de imprimir los gráficos Java 2D. Una impresora puede ser considerada un dispositivo grafico al igual que la pantalla.

La API de impresión Java 2D no está limitada a solo imprimir gráficos. Ya que también es posible imprimir el contenido de la interface de usuario de una aplicación. El contenido puede ser impreso enviando dato sin formato a la impresora delegando el formateo a la API de impresión Java 2D, o usando la API Graphics de Java 2D.

En esta lección vas a explorar las funciones de las clases printer y job que pertenecen a la API de impresión Java 2D y que son complementos de los elementos del renderizado. Aprenderás como buscar las impresoras configuradas en el sistema operativo o en la red y consultar información relevante sobre estas impresoras tales como el tamaño del papel soportado, seleccionar los parámetros de impresión y diálogos de usuarios.

Las principales clases e interfaces incluidas en la API de impresión se encuentran en los paquetes java.awt.print y javax.print (este último paquete te permite acceder a los servicios de impresión).

Las operaciones básicas de impresión son representados en las siguientes secciones:

  • Un programa básico de impresión - Esta sección describe la interface Printable y presenta un programa básico de impresión.
  • Usando el dialogo de parametros de impresión - Estás secciones explica como mostrar el dialogo Configuración de Impresión
  • Imprimiendo un documento con varías paginas - Esta sección explica como usar paginación para imprimir un documento con varias paginas.
  • Trabajando con servicios de impresión y atributos - Esta sección te enseña acerca de los servicios de impresión, como especificar el formato de impresión de los datos, y como crear una peticion de impresión usando el paquete javax.print.
  • Imprimiendo el contenido de una interfaz de usuario - Esta sección explica como imprimir el contenido de una ventana.
  • Soporte de impresión en componentes Swing - Esta sección proporciona una breve descripción de la funcionalidad de impresión en Swing y se refiere a clases e interfaces especificas de Swing


miércoles, 8 de diciembre de 2021

Genericos en Java

En cualquier proyecto de software no trivial, los errores son simplemente una realidad. Una planificación, programación y pruebas cuidadosas pueden ayudar a reducir su omnipresencia, pero de alguna manera, en algún lugar, siempre encontrarán una manera de introducirse en su código. Esto se vuelve especialmente evidente a medida que se introducen nuevas funciones y su código base crece en tamaño y complejidad.

Afortunadamente, algunos errores son más fáciles de detectar que otros. Los errores en tiempo de compilación, por ejemplo, se pueden detectar desde el principio; puede utilizar los mensajes de error del compilador para averiguar cuál es el problema y solucionarlo, en ese mismo momento. Los errores en tiempo de ejecución, sin embargo, pueden ser mucho más problemáticos; no siempre aparecen de inmediato, y cuando lo hacen, puede ser en un punto del programa que está muy alejado de la causa real del problema.

Los genéricos (o Generics) agregan estabilidad a su código al hacer que más errores sean detectables en el momento de la compilación.

¿Por qué utilizar genéricos?

En pocas palabras, los genéricos permiten que los tipos (clases e interfaces) sean parámetros al definir clases, interfaces y métodos. Al igual que los parámetros formales más familiares utilizados en las declaraciones de métodos, los parámetros de tipo proporcionan una forma de reutilizar el mismo código con diferentes entradas. La diferencia es que las entradas a los parámetros formales son valores, mientras que las entradas a los parámetros de tipo son tipos.

El código que usa genéricos tiene muchas ventajas sobre el código no genérico:

Comprobaciones de tipo más estrictas en tiempo de compilación.

Un compilador de Java aplica una fuerte verificación de tipos al código genérico y emite errores si el código viola la seguridad de los tipos. Arreglar errores en tiempo de compilación es más fácil que arreglar errores en tiempo de ejecución, que pueden ser difíciles de encontrar.

Eliminación de conversión de tipo (Cast).

El siguiente fragmento de código sin genéricos requiere conversión:

Cuando se reescribe para usar genéricos, el código no requiere conversión:

Permitir a los programadores implementar algoritmos genéricos.

Mediante el uso de genéricos, los programadores pueden implementar algoritmos genéricos que funcionan en colecciones de diferentes tipos, se pueden personalizar y son seguros para los tipos y más fáciles de leer.

Tipos genéricos

Un tipo genérico es una clase o interfaz genérica que se parametriza sobre tipos. La siguiente clase Box se modificará para demostrar el concepto.

Box, una clase simple

Comience examinando la clase Box no genérica que opera sobre objetos de cualquier tipo. Solo necesita proporcionar dos métodos: set, que agrega un objeto al cuadro, y get, que lo recupera:


Dado que sus métodos aceptan o devuelven un objeto, puede pasar lo que quiera, siempre que no sea uno de los tipos primitivos. No hay forma de verificar, en tiempo de compilación, cómo se usa la clase. Una parte del código puede colocar un número entero en el cuadro y esperar obtener números enteros, mientras que otra parte del código puede pasar por error una cadena, lo que da como resultado un error de tiempo de ejecución.

Una versión Generic de la clase Box

Una clase genérica se define con el siguiente formato:

class name<T1, T2, ..., Tn> { /* ... */ }

La sección de parámetro de tipo, delimitada por corchetes angulares (<>), sigue al nombre de la clase. Especifica los parámetros de tipo (también llamados variables de tipo) T1, T2, ... y Tn.

Para actualizar la clase Box para usar genéricos, cree una declaración de tipo genérico cambiando el código "Box de clase pública" a "Box de clase pública <T>". Esto introduce la variable de tipo, T, que se puede usar en cualquier lugar dentro de la clase.

Con este cambio, la clase Box se convierte en:


Como puede ver, todas las apariciones de Object se reemplazan por T. Una variable de tipo puede ser cualquier tipo no primitivo que especifique: cualquier tipo de clase, cualquier tipo de interfaz, cualquier tipo de arreglo o incluso otro tipo de variable.

Esta misma técnica se puede aplicar para crear interfaces genéricas.

Convenciones de nomenclatura de parámetros de tipo

Por convención, los nombres de los parámetros de tipo son letras mayúsculas simples. Esto contrasta fuertemente con las convenciones de nomenclatura de variables que ya conoce, y con una buena razón: sin esta convención, sería difícil distinguir entre una variable de tipo y un nombre de clase o interfaz ordinario.

Los nombres de parámetros de tipo más utilizados son:
  • E - Elemento (utilizado ampliamente por Java Collections Framework)
  • K - Clave
  • N - Número
  • T - Tipo
  • V - Valor
  • S, U, V, etc. - 2. °, 3. °, 4. ° tipos
Verá estos nombres utilizados en la API de Java SE y en el resto de esta lección.

Invocación y creación de instancias de un tipo genérico

Para hacer referencia a la clase Box genérica desde su código, debe realizar una invocación de tipo genérico, que reemplaza T con algún valor concreto, como Integer:

Box <Integer> integerBox;
Puede pensar que una invocación de tipo genérico es similar a una invocación de método ordinario, pero en lugar de pasar un argumento a un método, está pasando un argumento de tipo (Integer en este caso) a la clase Box en sí.

Terminología de parámetro de tipo y argumento de tipo: muchos desarrolladores usan los términos "parámetro de tipo" y "argumento de tipo" indistintamente, pero estos términos no son lo mismo. Al codificar, se proporcionan argumentos de tipo para crear un tipo parametrizado. Por lo tanto, la T en Foo <T> es un parámetro de tipo y la Cadena en Foo <String> f es un argumento de tipo. Esta lección observa esta definición al usar estos términos.
Como cualquier otra declaración de variable, este código en realidad no crea un nuevo objeto Box. Simplemente declara que integerBox contendrá una referencia a una "Box de Integer", que es como se lee Box <Integer>.

Una invocación de un tipo genérico se conoce generalmente como tipo parametrizado.

Para crear una instancia de esta clase, use la nueva palabra clave, como de costumbre, pero coloque <Integer> entre el nombre de la clase y el paréntesis:

Box <Integer> integerBox = new Box <Integer> ();

El diamante

En Java SE 7 y versiones posteriores, puede reemplazar los argumentos de tipo necesarios para invocar el constructor de una clase genérica con un conjunto vacío de argumentos de tipo (<>) siempre que el compilador pueda determinar, o inferir, los argumentos de tipo del contexto . Este par de paréntesis angulares, <>, se llama informalmente diamante. Por ejemplo, puede crear una instancia de Box <Integer> con la siguiente declaración:

Box <Integer> integerBox = new Box <> ();
Para obtener más información sobre la notación de diamantes y la inferencia de tipos, consulte Inferencia de tipos.

Parámetros de tipo múltiple

Como se mencionó anteriormente, una clase genérica puede tener múltiples parámetros de tipo. Por ejemplo, la clase genérica OrderedPair, que implementa la interfaz de par genérica:


Las siguientes declaraciones crean dos instancias de la clase OrderedPair:

Pair <String, Integer> p1 = new OrderedPair <String, Integer> ("Par", 8);
Pair <String, String> p2 = new OrderedPair <String, String> ("hola", "mundo");
El código, new OrderedPair <String, Integer>, instancia K como String y V como Integer. Por lo tanto, los tipos de parámetros del constructor de OrderedPair son String e Integer, respectivamente. Debido al autoboxing, es válido pasar un String y un int a la clase.

Como se menciona en The Diamond, debido a que un compilador de Java puede inferir los tipos K y V de la declaración OrderedPair <String, Integer>, estas declaraciones se pueden abreviar usando la notación de diamante:

OrderedPair <String, Integer> p1 = new OrderedPair <> ("Par", 8);
OrderedPair <String, String> p2 = new OrderedPair <> ("hola", "mundo");
Para crear una interfaz genérica, siga las mismas convenciones que para crear una clase genérica.

Tipos parametrizados
También puede sustituir un parámetro de tipo (es decir, K o V) con un tipo parametrizado (es decir, List <String>). Por ejemplo, usando el ejemplo OrderedPair <K, V>:

OrderedPair <String, Box <Integer>> p = new OrderedPair <> ("primes", new Box <Integer> (...));

lunes, 29 de noviembre de 2021

Log4j ofrece control sobre el logging

La API log4j de código abierto para Java ofrece servicios de logs rápidos y eficientes

¡Advertencia! Log4j 1.x ha sido deprecada y reemplazada por Log4j 2.x. Sin embargo es buena practica iniciarnos con la versión predecesora.

Casi todas las aplicaciones grandes incluyen su propia API de logging. La experiencia indica que el logging representa un componente importante del ciclo de desarrollo. Como tal, el logging ofrece varias ventajas. Primero, puede proporcionar un contexto preciso sobre una ejecución de la aplicación. Una vez insertado en el código, la generación de la salida del log no requiere intervención humana. En segundo lugar, la salida del log se puede guardar en un medio persistente para estudiarla más adelante. Finalmente, además de su uso en el ciclo de desarrollo, también se puede emplear un paquete de logs suficientemente rico como herramienta de auditoría.

De conformidad con esa regla, a principios de 1996, el proyecto EU SEMPER (Mercado electrónico seguro para Europa) decidió escribir su propia API de logging. Después de innumerables mejoras, varias encarnaciones y mucho trabajo, esa API se ha convertido en log4j, un paquete de logging popular para Java. El paquete se distribuye bajo la Licencia Pública de IBM, certificada por la iniciativa de código abierto.

El logging tiene sus inconvenientes. Puede ralentizar una aplicación. Si es demasiado detallado, puede causar ceguera de desplazamiento. Para aliviar esas preocupaciones, log4j está diseñado para ser rápido y flexible. Dado que el log rara vez es el enfoque principal de una aplicación, la API de log4j se esfuerza por ser simple de entender y usar.

Este artículo comienza describiendo los componentes principales de la arquitectura log4j. Continúa con un ejemplo simple que describe el uso y la configuración básica. Concluye tocando los problemas de rendimiento y la próxima API de logging de Sun.

Categorías, Appenders y Layouts

Log4j tiene tres componentes principales:

  • Categorías
  • Appenders
  • Layouts

Los tres componentes trabajan juntos para permitir que los desarrolladores registren los eventos según el tipo de evento y la prioridad, y para controlar en tiempo de ejecución cómo se formatean estos mensajes y dónde se informan. Echemos un vistazo a cada uno de ellos.

Jerarquía de categorías

La primera y principal ventaja de cualquier API de logs sobre System.out.println simple reside en su capacidad para deshabilitar ciertas declaraciones de registro mientras permite que otras impriman sin obstáculos. Esa capacidad asume que el espacio del log, es decir, el espacio de todas las declaraciones de logs posibles, se categoriza de acuerdo con algunos criterios elegidos por el desarrollador.

De conformidad con esa observación, la clase org.log4j.Category figura en el núcleo del paquete. Las categorías son entidades con nombre. En un esquema de nomenclatura familiar para los desarrolladores de Java, se dice que una categoría es padre de otra categoría si su nombre, seguido de un punto, es un prefijo del nombre de la categoría secundaria. Por ejemplo, la categoría denominada com.foo es un padre de la categoría denominada com.foo.Bar. De manera similar, java es un padre de java.util y un antepasado de java.util.Vector.

La categoría raíz, que reside en la parte superior de la jerarquía de categorías, es excepcional de dos maneras:

  1. Siempre existe
  2. No se puede recuperar por nombre

En la clase Category, al invocar el método getRoot () estático se recupera la categoría raíz. El método estático getInstance () crea una instancia de todas las demás categorías. getInstance () toma el nombre de la categoría deseada como parámetro. Algunos de los métodos básicos de la clase Categoría se enumeran a continuación:

A las categorías se les pueden asignar prioridades del conjunto definido por la clase org.log4j.Priority. Aunque el conjunto de prioridades coincide con el del sistema Unix Syslog, log4j fomenta el uso de solo cuatro prioridades: ERROR, WARN, INFO y DEBUG, enumeradas en orden decreciente de prioridad. La lógica detrás de ese conjunto aparentemente restringido es promover una jerarquía de categorías más flexible en lugar de un conjunto de prioridades estático (aunque sea grande). Sin embargo, puede definir sus propias prioridades subclasificando la clase Prioridad. Si una categoría determinada no tiene una prioridad asignada, hereda una de su antepasado más cercano con una prioridad asignada. Como tal, para garantizar que todas las categorías puedan eventualmente heredar una prioridad, la categoría raíz siempre tiene una prioridad asignada.

Si una categoría no tiene una prioridad asignada va a heredar la prioridad de su padre.

Para registrar un evento de la aplicación, invoque uno de los métodos de impresión de una instancia de categoría. Esos métodos de impresión son:

  • error()
  • warn()
  • info()
  • debug()
  • log()

Por definición, el método de impresión determina la prioridad del registro del evento. Por ejemplo, si c es una instancia de categoría, entonces la instrucción c.info ("..") es una solicitud de logging de prioridad INFO.

Se dice que una solicitud de registro está habilitada si su prioridad es mayor o igual que la prioridad de su categoría. De lo contrario, se dice que la solicitud está deshabilitada. Una categoría sin una prioridad asignada heredará una de la jerarquía.

A continuación, encontrará un ejemplo de esa regla:

Llamar al método getInstance () con el mismo nombre siempre devolverá una referencia al mismo objeto de categoría. Por lo tanto, es posible configurar una categoría y luego recuperar la misma instancia en algún otro lugar del código sin pasar referencias. Las categorías se pueden crear y configurar en cualquier orden. En particular, una categoría padre buscará y vinculará a sus hijos incluso si se crea una instancia después de ellos. El entorno log4j generalmente se configura en la inicialización de la aplicación, preferiblemente leyendo un archivo de configuración, un enfoque que discutiremos en breve.

Log4j facilita la tarea de nombrar categorías por componente de software. Eso se puede lograr instanciando estáticamente una categoría en cada clase, con el nombre de la categoría igual al nombre completo de la clase, un método útil y sencillo para definir categorías. Como la salida del registro lleva el nombre de la categoría generadora, dicha estrategia de denominación facilita la identificación del origen de un mensaje del registro. Sin embargo, esa es solo una estrategia posible, aunque común, para nombrar categorías. Log4j no restringe el posible conjunto de categorías. De hecho, el desarrollador es libre de nombrar las categorías como desee.

Appenders y Layouts

La capacidad de habilitar o deshabilitar selectivamente las solicitudes de registro según su categoría es solo una parte de la imagen. Log4j también permite que las solicitudes de registro se impriman en múltiples destinos de salida llamados appenders en log4j. Actualmente, existen appenders para la consola, archivos, componentes GUI, servidores de socket remotos, NT Event Loggers y demonios remotos de UNIX Syslog.

Una categoría puede referirse a múltiples appenders. Cada solicitud de registro habilitada para una categoría determinada se reenviará a todos los appenders de esa categoría, así como a los que se agregan más arriba en la jerarquía. En otras palabras, los appenders se heredan de forma aditiva de la jerarquía de categorías. Por ejemplo, si agrega un appender de consola a la categoría raíz, todas las solicitudes de registro habilitadas se imprimirán al menos en la consola. Si, además, se agrega un appender de archivos a una categoría, digamos C, las solicitudes de registro habilitadas para C y los hijos de C se imprimirán en un archivo y en la consola. Tenga en cuenta que puede anular ese comportamiento predeterminado para que la acumulación de appenders ya no sea aditiva.

Las reglas que gobiernan la aditividad del appender se resumen a continuación.

Aditividad del appender

La salida de una declaración de registro del registrador C irá a todos los appenders en C y sus ancestros. Este es el significado del término "aditividad del appender".

Sin embargo, si un ancestro del registrador C, digamos P, tiene el indicador de aditividad establecido en falso, entonces la salida de C se dirigirá a todos los anexos en C y sus ancestros hasta e incluyendo P, pero no a los agregadores en ninguno de los ancestros de P .

Los registradores tienen su indicador de aditividad establecido en verdadero de forma predeterminada.

La siguiente tabla muestra un ejemplo:

Logger
Name
Added
Appenders
Additivity
Flag
Output TargetsComment
rootA1not applicableA1The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root.
xA-x1, A-x2trueA1, A-x1, A-x2Appenders of "x" and root.
x.ynonetrueA1, A-x1, A-x2Appenders of "x" and root.
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1Appenders in "x.y.z", "x" and root.
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false.
security.accessnonetrueA-secOnly appenders of "security" because the additivity flag in "security" is set to false.

Para configurar el flag agregar la siguiente linea al archivo de configuración.

log4j.additivity.nombre.logger=false

La mayoría de las veces, los usuarios desean personalizar no solo el destino de salida, sino también el formato de salida, una hazaña que se logra al asociar un layout con un appender. El layout formatea el mensaje del registro de acuerdo con los deseos del usuario, mientras que un appender se encarga de enviar la salida formateada a su destino. PatternLayout, parte de la distribución estándar de log4j, permite al usuario especificar el formato de salida de acuerdo con patrones de conversión similares a la función printf del lenguaje C.

Por ejemplo, PatternLayout con el patrón de conversión % r [% t]% - 5p% c -% m% n generará algo similar a:

En la salida de arriba:

  • El primer campo es igual al número de milisegundos transcurridos desde el inicio del programa.
  • El segundo campo indica el hilo que realiza la solicitud de registro
  • El tercer campo representa la prioridad de la declaración de registro.
  • El cuarto campo es igual al nombre de la categoría asociada con la solicitud de registro.
  • El texto después de - indica el mensaje de la declaración.
Mas sobre los Layouts
https://logging.apache.org/log4j/2.x/manual/layouts.html

Mas sobre los Appenders
https://logging.apache.org/log4j/2.x/manual/appenders.html

Mas sobre Filtros (disponible en la versión 2.x)
https://logging.apache.org/log4j/2.x/manual/filters.html

Configuración

Insertar solicitudes de registro en el código de la aplicación requiere una gran cantidad de planificación y esfuerzo. La observación muestra que el código dedicado al registro representa aproximadamente el cuatro por ciento del total de la aplicación. En consecuencia, incluso las aplicaciones de tamaño moderado tendrán miles de declaraciones de registro incrustadas en su código. Dado su número, es imperativo administrar esas declaraciones de registro sin la necesidad de modificarlas manualmente.

El entorno log4j se puede configurar completamente mediante programación. Sin embargo, es mucho más flexible configurar log4j utilizando archivos de configuración. Actualmente, los archivos de configuración se pueden escribir en XML o en formato de propiedades Java (clave = valor).

Démosle una idea de cómo se hace con la ayuda de una aplicación imaginaria, MyApp, que usa log4j:

Como se ve en el código anterior, MyApp comienza importando clases relacionadas con log4j. Luego define una variable de categoría estática con el nombre MyApp, que resulta ser el nombre completo de la clase.

MyApp usa la clase Bar definida en el paquete com.foo:

En MyApp, la invocación del método BasicConfigurator.configure () crea una configuración log4j bastante simple. Ese método está programado para agregar a la categoría raíz una impresión FileAppender en la consola. La salida se formateará utilizando un PatternLayout establecido en el patrón% -4r [% t]% -5p% c% x -% m% n.

Tenga en cuenta que, de forma predeterminada, la categoría raíz se asigna a Priority.DEBUG.

La salida de MyApp es:

La Figura 1 muestra el diagrama de objetos de MyApp inmediatamente después de que llama al método BasicConfigurator.configure ().

//agregar figura

La Figura 1 muestra el diagrama de objetos de MyApp inmediatamente después de que llama al método BasicConfigurator.configure ().

La clase MyApp configura log4j invocando el método BasicConfigurator.configure (). Otras clases solo necesitan importar la clase org.log4j.Category, recuperar las categorías que desean usar y cerrar sesión.

El ejemplo anterior siempre genera la misma información de registro. Afortunadamente, es fácil modificar MyApp para que la salida del registro se pueda controlar en tiempo de ejecución. A continuación, verá una versión ligeramente modificada:

Esta versión de MyApp indica a PropertyConfigurator que analice un archivo de configuración y configure el registro en consecuencia.

Veamos un archivo de configuración de muestra que da como resultado exactamente el mismo resultado que el ejemplo anterior basado en BasicConfigurator:

Suponga que ya no deseamos ver la salida de ningún componente que pertenezca al paquete com.foo. El siguiente archivo de configuración muestra una posible forma de lograrlo:

La salida de MyApp configurada con este archivo se muestra a continuación:

Como la categoría com.foo.Bar no tiene una prioridad asignada, hereda su prioridad de com.foo, que se estableció en WARN en el archivo de configuración. La declaración de registro del método Bar.doIt () tiene la prioridad DEBUG, menor que la prioridad de categoría WARN. En consecuencia, se suprime la solicitud de registro de doIt ().

A continuación, vemos otro archivo de configuración que usa múltiples appenders:

Llamar a la MyApp mejorada con ese archivo de configuración generará lo siguiente en la consola:

Además, como a la categoría raíz se le ha asignado un segundo appender, la salida también se dirigirá al archivo example.log. Ese archivo se renovará cuando alcance los 100 KB. Cuando se produce una renovación, la versión anterior de example.log se mueve automáticamente a example.log.1.

Tenga en cuenta que para obtener esos diferentes comportamientos de registro, no es necesario volver a compilar el código. Con la misma facilidad podríamos haber iniciado sesión en un demonio de Unix Syslog y redirigir toda la salida de com.foo a un registrador de eventos de NT. De manera similar, podríamos haber reenviado los eventos de registro a un servidor log4j remoto, que registraría de acuerdo con la política del servidor local, por ejemplo, iniciando sesión en un archivo local y reenviando el evento de registro a un segundo servidor log4j.

Contextos de diagnóstico anidados

La mayoría de los sistemas del mundo real deben tratar con varios clientes simultáneamente. En una implementación multiproceso típica de un sistema de este tipo, diferentes subprocesos manejarán diferentes clientes. Teniendo en cuenta eso, el registro es especialmente adecuado para rastrear y depurar aplicaciones distribuidas complejas. Un enfoque común para diferenciar la salida de registro de un cliente de otro es crear una instancia de una nueva categoría separada para cada cliente. Sin embargo, ese enfoque promueve la proliferación de categorías y aumenta la sobrecarga administrativa del registro.

En una técnica más ligera, puede sellar de forma única cada solicitud de registro iniciada desde la misma interacción con el cliente, un enfoque descrito por Neil Harrison (ver Recursos). Para sellar de forma única cada solicitud, el usuario inserta información contextual en el contexto de diagnóstico anidado (NDC). La clase NDC se muestra a continuación:

El NDC se gestiona por hilo como una pila de información contextual. Tenga en cuenta que todos los métodos de org.log4j.NDC son estáticos. Suponiendo que la impresión NDC está activada, cada vez que se realiza una solicitud de registro, el componente log4j apropiado incluirá la pila NDC completa para el hilo actual en la salida del registro. Eso se hace sin la intervención del usuario, que es responsable únicamente de colocar la información correcta en el NDC mediante el uso de los métodos push () y pop () en algunos puntos bien definidos del código. En contraste, el enfoque de categoría por cliente exige cambios extensos en el código.

Para ilustrar ese punto, tomemos el ejemplo de un servlet que entrega contenido a numerosos clientes. El servlet puede construir el NDC al comienzo de la solicitud antes de ejecutar otro código. La información contextual puede ser el nombre de host del cliente y otra información inherente a la solicitud, generalmente información contenida en cookies. Por lo tanto, incluso si el servlet está sirviendo a varios clientes simultáneamente, los registros iniciados por el mismo código (es decir, pertenecientes a la misma categoría) aún se pueden distinguir porque cada solicitud de cliente tendrá una pila de NDC diferente. Compare eso con la complejidad de pasar una categoría recién instanciada a todo el código ejercido durante la solicitud del cliente.

No obstante, algunas aplicaciones sofisticadas, como los servidores web de alojamiento virtual, deben registrarse de manera diferente, según el contexto del host virtual y el componente de software que emite la solicitud. La versión más reciente de log4j admite múltiples árboles de jerarquía, una mejora que permite que cada host virtual posea su propia copia de la jerarquía de categorías.

Rendimiento

Uno de los argumentos más citados en contra del logging proviene de su costo computacional. Esa es una preocupación legítima, ya que incluso las aplicaciones de tamaño moderado pueden generar miles de solicitudes de registro. Se dedicó mucho esfuerzo a medir y ajustar el rendimiento del registro. Log4j afirma ser rápido y flexible: primero la velocidad, luego la flexibilidad.

No obstante, el usuario debe conocer los siguientes problemas de rendimiento:

1.- Rendimiento del registro cuando el registro está desactivado. Cuando el registro está desactivado por completo o para un conjunto de prioridades, el costo de una solicitud de registro es una invocación de método más una comparación de enteros. La invocación del método implica el costo oculto de la construcción de parámetros. Para algunas categorías cat, escribir

incurre en el costo de construir el parámetro de mensaje que convierte tanto el entero i como la entrada [i] en una cadena y concatena cadenas intermedias, independientemente de si el mensaje se registrará o no. Si a uno le preocupa la velocidad, escriba:

Eso no incurrirá en el costo de la construcción de parámetros si la depuración está deshabilitada. Por otro lado, si la categoría está habilitada para la depuración, incurrirá dos veces en el costo de evaluar si la categoría está habilitada o no: una vez en debugEnabled () y una vez en debug (). Eso representa una sobrecarga insignificante porque la evaluación de una categoría lleva aproximadamente el 1 por ciento del tiempo que lleva registrar realmente. En log4j, las solicitudes de registro se realizan a instancias de la clase Categoría. Debido a que Categoría es una clase y no una interfaz, el costo de la invocación del método es posiblemente menor, pero a costa de cierta flexibilidad. En una máquina Pentium II de 233 MHz, el costo típico de registro cuando el registro está desactivado está en el rango de 5 a 50 nanosegundos.

2.- El rendimiento de decidir si registrar o no registrar un registro cuando el logging está activado. En ese escenario, la pregunta se centra en el costo de rendimiento de recorrer la jerarquía de categorías. Cuando el registro está activado, log4j aún necesita comparar la prioridad de la solicitud de registro con la prioridad de la categoría de solicitud. Sin embargo, es posible que las categorías no tengan una prioridad asignada; pueden heredarlos de la jerarquía de categorías. Por lo tanto, para descubrir su prioridad, la categoría puede necesitar buscar a sus antepasados. Se ha hecho un esfuerzo serio para que esa jerarquía camine lo más rápido posible. Por ejemplo, las categorías secundarias se vinculan solo a sus antepasados ​​existentes. En el ejemplo de BasicConfigurator mostrado anteriormente, la categoría denominada com.foo.Bar enlaza directamente con la categoría raíz, evitando así las categorías com o com.foo inexistentes, mejorando significativamente la velocidad de la caminata, especialmente en jerarquías dispersas. caminar por la jerarquía está en el rango de 5 a 15 microsegundos, nuevamente en una máquina Pentium II de 233 MHz.

3.- El rendimiento real del Loggin. Otro problema de rendimiento se deriva del costo de formatear la salida del registro y enviarla a su destino. Aquí nuevamente, se hizo un gran esfuerzo para que los Layouts (formateadores) funcionen lo más rápido posible. Lo mismo ocurre con los Appenders. El costo típico de registrar realmente varía entre 100 y 300 microsegundos.

Aunque log4j tiene muchas características, su primer objetivo de diseño fue la velocidad. Algunos componentes de log4j se han reescrito muchas veces para mejorar el rendimiento. Sin embargo, los contribuyentes suelen presentar nuevas optimizaciones.

La empresa SUN está construyendo su API de Logging

**Actualización: La API ya se encuentra disponible, se le conoce como JUL Java Util Logging**

Sun ha iniciado la petición JSR47 en Java Community Process (JCP) para definir una API de Logging para Java. La API resultante requerirá la versión 1.4 de JDK y pronto estará lista para su revisión pública.

La API JSR47 y Log4j son bastante similares a nivel arquitectónico. La API JSR47 admitirá un espacio de nombres jerárquico, una de las características centrales de log4j. Por otro lado, log4j posee muchas características útiles que faltan en JSR47. Dado el impulso detrás de log4j, en mi opinión partidista, es probable que JSR47 esté obsoleto para cuando se lance. Log4j está escrito por las personas que lo utilizan, no por un comité cerrado.

Por cierto, log4j ya proporciona soporte para la conexión en la API JSR47 cuando esta última esté disponible.

Permítanme también mencionar JLog, otra API de registro disponible en alphaWorks de IBM. JLog se llamaba anteriormente RAS Toolkit hasta que IBM lo renombró. En el sitio alphaWorks, JLog tiene la etiqueta "Logging Toolkit for Java", casi la misma etiqueta que tenía log4j en alphaWorks antes de migrar a http://log4j.org. JLog, aunque es un buen paquete de registro, no debe confundirse con log4j.

Conclusión

Log4j es un paquete de registro popular escrito en Java. Uno de sus rasgos distintivos incluye la noción de herencia en categorías. Usando una jerarquía de categorías, es posible controlar qué declaraciones de registro se generan con granularidad arbitraria, reduciendo así el volumen de salida registrada y minimizando el costo de registro.


Una de las ventajas de la API de log4j es su capacidad de administración. Una vez que las declaraciones de registro se han insertado en el código, se pueden controlar con archivos de configuración. Además, pueden habilitarse o deshabilitarse selectivamente y enviarse a diferentes y múltiples destinos de salida en formatos elegidos por el usuario. Además, el paquete log4j está diseñado para que las declaraciones de registro puedan permanecer en el código enviado sin incurrir en un alto costo de rendimiento.


Log4j es el resultado de un esfuerzo colectivo. Mi especial agradecimiento a todos los autores que han contribuido al proyecto. Sin excepción, las mejores características del paquete se han originado en la comunidad de usuarios.

Referencia: https://logging.apache.org/log4j/1.2/manual.html

Más información sobre este tema

"Patterns for Logging Diagnostic Messages," Neil Harrison in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997)
http://www.amazon.com/exec/obidos/ASIN/0201310112/qid%3D973215403/002-8420944-7444837/javaworld

Log4j project homepage
http://jakarta.apache.org/log4j/docs/

Sun's Java Logging API Specification
http://java.sun.com/aboutJava/communityprocess/jsr/jsr_047_log.html

Igor Poteryaev, an independent author, has ported log4j to the Python language, called log4p
http://log4p.sourceforge.net

IBM's JLog API
http://www.alphaworks.ibm.com/tech/loggingtoolkit4j
The EU's Secure Electronic Marketplace for Europe (SEMPER) project homepage

http://www.semper.org

OpenSource.org
http://www.opensource.org

Level
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html

Category
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html

PatternLayout
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

miércoles, 24 de noviembre de 2021

Logging de una aplicación en Java

El logging de una aplicación consiste en registrar (logs) información relevante del comportamiento de la ejecución de nuestra aplicación. Contiene datos de variables, entidades, cambios de estado,  componentes de software involucradas en dicha ejecución y la llamada de los métodos. Su principal funcionalidad es facilitar el seguimiento o análisis de la ejecución de la aplicación:

  • Analizar el comportamiento de la aplicación durante la fase de desarrollo y depuración (pruebas de caja blanca)
  • Analizar los bugs o errores de ejecución detectados, sus causas y consecuencias
  • Servir de registro de auditoría cuando la información contenida y el modo en que se ha procesado cumpla los criterios requeridos
  • Medir el rendimiento o carga de los sistemas o aplicaciones
  • Revertir el estado del aplicativo siguiendo en orden inverso el log

Aunque depende de las circunstancias en las que nos encontremos, el segundo de los usos suele ser el más relevante. Un buen log desarrollado correctamente en el código y mantenido o configurado en explotación es una garantía de respuesta rápida para análisis de errores; que incluso podrían hacerse sin necesidad de parar el aplicativo, reconfigurarlo o aplicarle ningún cambio.

El log o registro de la aplicación suele formarse por un conjunto de eventos que se almacenan secuencialmente, por lo general en el orden en que suceden, de manera persistente o recuperable. Se pueden almacenar en ficheros, en BBDD, en componentes distribuidos a tal efecto. Se pueden habilitar mecanismos de rotación o históricos de estos logs, se pueden utilizar por monitores para lanzar alertas, se pueden integrar y fusionar para hacer análisis más exhaustivos. Lo relevante es que la información registrada y la forma en que se gestiona sea útil.

Existen numerosas soluciones y propuestas de software, tanto libres como propietarias, más o menos estandarizadas, más sencillas o más completas, de mil tipos y formas. Lo importante es buscar aquella se ajusta a nuestras necesidades y entornos, para olvidarnos de la implementación del mecanismo por completo; y ceñirnos a los dos aspectos más importantes:

  • El contenido de cada registro o evento, principal preocupación del desarrollador
  • El modo en que se procesa, persiste y gestiona, principal preocupación de la explotación del sistema o aplicativo

El coste de implementación del logging se encuentra en el ir, mientras se desarrolla, dejando registros (logs) en los diferentes puntos del código. Esta actividad debe hacerse durante el desarrollo, siguiendo patrones, criterios y procedimientos preestablecidos. De esta manera los desarrolladores tendrán criterios comunes y los logs serán coherentes entre las diferentes partes del código.

La información registrada en el log debe ser relevante y completa. Se pueden considerar los siguientes aspectos a la hora de decidir qué información incluir:

Qué:

Qué evento o acción ha ocurrido.

Qué entidades han estado involucradas.

Si hay un cambio de estado, ¿Cuál era el anterior? ¿Cuál es el nuevo estado?.

Dónde:

En qué punto del código ha ocurrido: componente, clase, fichero de código, método o bloque de ejecución, línea de código… Cuanto más detallada sea esta información mejor para localizar el lugar del posible error o por donde ha pasado la ejecución, por un lado; pero más puede afectar el logging al rendimiento.

Cuándo:

Registrando el momento temporal, bien absoluto o bien relativo al comienzo de ejecución o cualquier otro evento.

Generando los logs secuencial o causal, en la que los eventos que ocurren antes en el tiempo o que ocasionan otros, aparezcan antes.

En qué contexto:

Registrando estados o variables: propios de la ejecución (parámetros), de personalización o específicos de usuario, referentes a la sesión o transacción en ejecución…

Indicando hilos, transacciones o peticiones relacionadas cuando estemos en entornos concurrentes.

Para que la información de los logs sea más detallada en momento de análisis y más manejable durante la explotación de la misma, se establecen niveles de filtrado. De tal manera que solo se muestra o almacenan aquellos eventos con un nivel mayor o igual al del nivel de log establecido. Las librerías de logging permiten filtrar los eventos por otros criterios como son la clase o contexto del evento, también.

Los niveles más comunes son DEBUG, INFO, WARNING y ERROR. La clasificación de los diferentes eventos en cada nivel es parte del ejercicio de análisis, y deben orientarse a que la traza sea legible y útil en los diferentes contextos del aplicativo, desde el desarrollo hasta la explotación.

Este puede ser un ejemplo de semántica de niveles de logging:

DEBUG: para información de muy bajo nivel solo útil para el debug de la aplicación, tanto en el desarrollo como en el análisis de incidencias

Llamadas a funciones y procedimientos y otros componentes, con parámetros y respuestas

Flujos de ejecución

Desarrollo de algoritmos y procedimientos que permitan identificar y seguir su ejecución en desarrollo

INFO: información de más alto nivel que permita hacer un seguimiento de la ejecución normal

Paradas y arranques de servicios y sistemas

Parámetros críticos o relevantes de configuración

Comienzo y fin de transacciones y operaciones completas

Cambios de estado de operaciones

WARN: información de situaciones, que aún sin ser de error, si son anómalas o no previstas, aunque el aplicativo tiene alternativas para solventarlas

Parámetros no definidos, y cuyo valor se toma por defecto

Situaciones anómalas, pero que son resueltas por el aplicativo, dejando la operación en un estado correcto

Funcionalidades no primordiales o imprescindibles, que no pueden resolverse, pero que dejan la operación en un estado correcto

ERROR: información de situaciones que son de error y que impiden la ejecución correcta de una operación o transacción, pero sin afectar a otras operaciones o transacciones (error aislado o contenido)

No se pudo realizar una operación o transacción, pero no afecta a otras

Peticiones o consultas erróneas (almacenando los parámetros de entrada)

Funcionalidades generales del aplicativo, que aún afectando al funcionamiento general del aplicativo, no se consideran primordiales o imprescindibles

FATAL: información de situaciones de error que afectan al funcionamiento general del aplicativo (errores no aislados o contenidos en alcance)

Parámetros no definidos o configuraciones erróneas

Falta de conexión o comunicación con otros componentes

Errores de ejecución que pueden afectar a operaciones o transacciones independientes, o que afectan al funcionamiento general de la aplicación

Tanto el contenido y forma de cada evento de log, como la semántica de los niveles son parte del diseño del aplicativo. Estos criterios deben definirse y ser comunicados al equipo de desarrollo para que se apliquen de manera homogénea y coherente en todo el desarrollo. Si estos criterios son consensuados con el equipo se puede sacar mucho provecho de la experiencia de los desarrolladores.

Otras recomendaciones a tener en cuenta son:

Registrar los eventos de manera atómica, que toda la información referente a un evento se almacene en un registro o línea.

Orientar el formato de la información mostrada a poder ser usado de manera informatizada o automática con herramientas específicas.

En lo referente a excepciones:

Mostrar siempre la traza (trace o llamada secuencial de los métodos) completa de la excepción con su mensaje y todo el stacktrace.

Si la excepción es capturada, tratada y luego lanzada de nuevo (bien la misma u otra) no dejar traza de la excepción hasta que se capture y se trate en última instancia. De esta manera cada excepción solo se mostrará una vez. Si no lo hacemos así y registramos cada captura y relanzamiento aparecerá en la traza varias veces y no sabremos si es la misma o son varias. (Antipatrón catch-log-throw).

Hacer uso de la propiedad de excepción causante o interna cuando capturemos y relancemos la excepción. En Java es el método getCause(), en .NET la propiedad InnerException.

Implementar el método toString() de todas las clases de negocio o POJOs para facilitar su traceado.

Hay que tener en cuenta las diferencias y zonas horarias a la hora de trabajar con logs de varias máquinas o componentes.

El logging no puede tener efectos laterales, no puede modificar el estado de ningún parámetro, variable o procedimiento. Solo muestra información. Debe ser ligero, el propio logging no puede requerir de procesamientos largos o costosos.

Las llamadas a métodos, componentes y componentes distribuidos externos, deberían registrarse tras la llamada incluyendo los parámetros de entrada y la respuesta, así como clase/componente y método, a nivel de DEBUG. Y a nivel de ERROR y FATAL cuando ocurra un error en la llamada (si fuese una excepción se debe tracear parámetros de entrada y excepción)

En Java tenemos varias API's o librerías para llevar a cabo el logging de nuestra aplicación, siendo las mas comunes:

  • Log4j2 antes Log4j
  • Loogback
  • JUL (Java Util Logging)
Tenemos dos fachadas las cuales permitirán que nuestra aplicación funcione con cualquiera las tres API's mencionadas, estas fachadas son:
  • Commons Logging
  • SLF4J
Continua con una guía practica para la API Log4j  Log4j ofrece control sobre el logging

martes, 26 de octubre de 2021

Aprendiendo el lenguaje Java

Ruta: Aprendiendo el lenguaje Java

Este ruta cubre los fundamentos de la programación en el lenguaje de programación Java.

Conceptos de la programación Orientada a Objetos

Conceptos básicos del lenguaje

Clases y Objetos

Anotaciones

Interfaces y Herencias

Números y Cadenas de caracteres (Strings)

Tipos Genéricos son una característica poderosa del lenguaje de programación Java. Mejoran la seguridad de tipos de su código, haciendo que más errores sean detectables en tiempo de compilación.

Paquetes (Packages)


Referencia: https://docs.oracle.com/javase/tutorial/java/index.html

domingo, 19 de septiembre de 2021

6 Herramientas para mejorar la productividad del desarrollador Java

Los desarrolladores de Java tienen perspectivas laborales muy altas en la industria del desarrollo de software. Con razón, Java impulsa muchas de las aplicaciones y servidores de escritorio, web y móviles del mundo. La mayoría de las empresas que reclutan desarrolladores de Java pertenecen a las industrias más maduras, como la atención médica y el Retail.

En consecuencia, es importante que los desarrolladores de Java sean eficaces y eficientes en sus trabajos.

A continuación se muestran seis de las herramientas más útiles que los desarrolladores de Java deberían considerar usar.

Netbeans

Son pocos los desarrolladores de Java que no conocen Netbeans. Es un entorno de desarrollo integrado (IDE) de código abierto en varios idiomas.

Lejos de ser un simple editor de texto, proporciona algunas características interesantes que ayudan a los desarrolladores a ser productivos.

Las características clave incluyen su resaltado de sintaxis que facilita la refactorización del código, ya que no solo le brinda información de sintaxis sino también de semántica. También proporciona excelentes plantillas y asistentes para una amplia gama de lenguajes de programación.

También es una plataforma de aprendizaje: tiene un tutorial completo de Java (y también PHP). Otra cosa que se destaca de Netbeans es que facilita a los desarrolladores la gestión de sus proyectos mediante el uso de favoritos y el control de versiones. También se sincroniza bien con Github.

La comunidad crea continuamente una gran cantidad de herramientas poderosas para hacer que el uso de Netbeans sea más placentero.

¿Cuál es tu nivel de conocimiento en NetBeans?, ¿Básico, medio o experto?. 

Rookout

Como desarrollador de Java, al igual que con todos los desarrolladores, encontrar y solucionar errores son dos de las actividades que requieren más tiempo. Estos no solo consumen mucho tiempo, sino que causan mucha angustia innecesaria cada vez que parece que un error da origen a otros errores en el futuro.

El problema se agrava cuando el error se experimenta en producción y el equipo de operaciones querría que corrigiera los errores en el código de producción.

Si bien es importante contar con una buena estrategia de depuración, es aún más esencial contar con las herramientas adecuadas para ayudarlo a hacerlo. Después de todo, nuestros ojos humanos están limitados en lo que pueden sentir.

Aquí es donde una herramienta de depuración como Rookout puede ayudar. Te permite depurar en cualquier entorno. El equipo de operaciones quiere que vayas y averigües qué está mal con el código enviado a producción. Puede realizar una depuración remota en vivo allí. También puede implementar la depuración de producción, lo que permite una experiencia perfecta.

También mejora la colaboración al proporcionar excelentes datos e información sobre los errores encontrados y también comparte métricas comerciales a pedido.

Y tu, ¿ya has usado Rookout?  ¿Te atreverías a probarlo?

JUnit

Corregir errores es una gran cosa. Pero, a menudo, el código que no presenta errores no se comporta como se esperaba durante la ejecución. Por ejemplo, declarar una calculo matemático incorrecto, tu programa puede no presentar errores de compilación, pero como garantizas que el resultado es el correcto?. O cuando realizas modificación en una clase, ¿Cómo garantizas que el cambio no impacta en toda la funcionalidad?

Este error se puede detectar mediante pruebas automatizadas.

JUnit es una herramienta de prueba elegida por los desarrolladores de Java. Es un Framework de código abierto. Por lo general, usa anotaciones para que las pruebas tengan accesorios antes de ejecutarlas. Algunas de sus ventajas incluyen la simplicidad de uso, proporciona informes de inmediato, no solo te permite escribir pruebas sino también todo un conjunto de pruebas.

La mayoría de los IDE son compatibles con JUnit, por lo que se ha convertido en uno de los Framework de prueba más populares, si no el más popular, para Java.

Y tu, ¿Haces pruebas unitarias de tus funcionalidades?

Apache Maven

Apache Maven es simplemente un software de gestión de proyectos de software. Si bien probablemente pueda usar cualquier otra herramienta para este propósito, Maven también administra compilaciones para sus proyectos Java.

Su principio de funcionamiento se basa en el concepto de modelo de objetos de proyecto (POM). La creación de un proyecto con Maven le facilitará automáticamente la creación de los siguientes. Una de las cosas que ayuda a aumentar la productividad de los desarrolladores de Java es su actualización automática de dependencias.

Dado que es de código abierto, la comunidad siempre agrega nuevas herramientas a su ya enorme biblioteca. No necesitará muchas configuraciones cuando lleguen nuevas herramientas útiles o se necesiten actualizaciones.

En este blog puedes encontrar muchos artículos útiles sobre Maven.

Git

Git es un software de control de versiones diseñado por Linus Torvalds, pensando en la eficiencia, la confiabilidad y compatibilidad del mantenimiento de versiones de aplicaciones cuando estas tienen un gran número de archivos de código fuente. Contar con un sistema de versionamiento te permitirá gestionar tu proyecto de forma flexible, revertir cambios, comparar versiones, aprobar cambios y restaurar cambios.

En este blog puedes encontrar una guía básica pero muy útil de GitHub

Apache Spark

Apache Spark permite a los desarrolladores de Java crear aplicaciones de procesamiento de datos intensivo. Permite procesar grandes cantidades de datos de forma rápida y escalable.

Es un marco basado en el modelo de programación MapReduce de Hadoop. Incluso amplía este modelo para incluir cálculos en una amplia gama de tipos de datos y tipos de consultas (incluida la transmisión).

A los desarrolladores de Java interesados en la ciencia de datos les encantará.

Conclusión

La productividad es más que usar herramientas. Es importante que un desarrollador también sepa jugar bien con todo el equipo. Sin embargo, estas herramientas, ya sea que se utilicen individualmente o juntas, de hecho pueden aumentar la cantidad de trabajo de calidad realizado por los desarrolladores.

jueves, 22 de julio de 2021

La Aplicacion Hola Mundo en Java

La siguiente lista de secciones proporciona instrucciones detalladas para compilar y ejecutar una aplicación simple. La aplicación "Hola Mundo". La primera sección proporciona información para iniciarte en el uso del IDE  NetBeans, un ambiente de desarrollo integrado que simplifica enormemente el proceso de desarrollo de software. El IDE NetBeans se ejecuta en todas las plataformas que soporten la JVM de Java. Las secciones restantes proporcionan instrucciones para ejecutar tu aplicación sin una herramienta IDE, estás instrucciones son especificas para cada plataforma. Si te encuentras con problemas al ejecutar tu programa, vuelve a revisar las instrucciones y videos. Por ultimo, no dudes de preguntar tus dudas.

"Hola Mundo" para el IDE NetBeans - Estás instrucciones son para usuarios del IDE NetBeans. El IDE NetBeans se ejecuta en la plataforma de Java, esto significa que puedes usarlo en cualquier sistema operativo donde tengas instalado el JDK de Java. Entre estos sistemas operativos se incluyen a Windows, Solaris OS, Linux, y Mac OS X. En el curso vamos a usar el IDE NetBeans en lugar de la linea de comandos (Símbolo de Sistemas o la Shell).

"Hola Mundo" para Microsoft Windows - Estás instrucciones de linea de comandos son para los usuarios de Windows.

lunes, 5 de julio de 2021

Instalación de Java JDK y Netbeans

El JDK de Java

Open JDK y Oracle JDK

Las versiones de Java

Pasos para instalar Open JDK en Windows

El IDE NetBeans

Pasos para instalar Netbeans


Para poder continuar con nuestra ruta de aprendizaje de la tecnología Java necesitamos instalar el JDK (Java Development Kit) de Java. El JDK de Java es un entorno o ambiente de desarrollo para construir aplicaciones y componentes usando el lenguaje de programación Java.

El JDK de Java incluye herramientas útiles para desarrollar, probar, y monitorear programas escritos en el lenguaje de programación Java y que se ejecutan en la plataforma Java. Algunas de estás herramientas son los comandos javac, jar y programas como el jdkconsole

La tecnología de Java ha evolucionado con el tiempo, siempre buscando incorporar nuevas funcionalidades, simplificar el proceso de desarrollo para responder a los cambios de los requerimientos de una forma más ágil. La ultima versión de Java es la versión 16.

Oracle en los últimos años ha liberado las siguientes versiones

Java SE 8 (LTSL) Marzo 2014.

Java SE 9 Septiembre de 2017

Java SE 10 Marzo de 2018.

Java SE 11 (LTS) Septiembre de 2018.

Java SE 12 Marzo de 2019

Note que las versiones 9 y 10 no tienen ya soporte, Desde Java 10 Oracle estará liberando releases cada 6 meses, sin embargo no todos los releases serán LTS (Long Term Support, Soporte extendido), para este tipo de productos será tener un release LTS cada 3 años.

Existen dos implementaciones principales del JDK de Java, Open JDK y Oracle JDK, a mediados del 2019 Oracle realizó algunos cambios importantes en la licencia de uso de Java, es por esta razón que preferimos trabajar con Open JDK y es la implementación que vamos a instalar. La diferencia principal radica en el soporte y actualizaciones, siendo Oracle JDK la primera en obtener actualizaciones y soporte.

Crea tu directorio de desarrollo

Soy un pro de los estándares, para este curso te recomiendo crear un directorio de desarrollo, puedes crearlo donde más gustes, incluso en Mis Documentos. En mi caso he decido crearlo en una partición D.

D:\devel\java

   + src

   + tools

   + dist


Si decides crear tu directorio de desarrollo en Mis Documentos, entonces quedaría algo así:

C:\Users\RLopez\Documents\devel\java

   + src

   + tools

   + dist

                                                        

Pasos para instalar OpenJDK en Windows

Esta guía esta probada para Windows 10, pero debería funcionar bien para otras versiones anteriores de Windows.

1.- Descargar OpenJDK 16

2.- Descomprimir en un directorio, en mi caso C:\devel\java\tools

3.- Crear la variable de entorno JAVA_HOME con la ruta C:\devel\java\tools\ Muchas aplicaciones Java requieren esta variable.

4.- Agregar a la variable de entorno Path la ruta %JAVA_HOME%\bin Si no lo haces tendrás que pasar la ruta completa cada que quieras ejecutar un programa Java.

5.- Comprobar

Te aconsejo reiniciar tu equipo, Windows debe dejar disponibles las variables de entorno a las aplicaciones.

Estructura de los directorios de Open JDK

Como conocimiento general daremos un repaso breve de los directorios dentro de Open JDK

/jdk-16.0.1/bin Los ejecutables y herramientas de linea de comandos.

/jdk-16.0.1/conf Las configuraciones .properties, .policy y otras configuraciones que pueden ser editados por los desarrolladores, y usuarios finales.

/jdk-16.0.1/lib Librerías necesarias para soportar la ejecución de nuestra maquina virtual.

/jdk-16.0.1/jmods La definición de los módulos incluidos en la versión

/jdk-16.0.1/legal Los archivos de las licencias para cada uno de los modulos.

/jdk-16.0.1/lib/src.zip Este directorio contiene el código fuente de la Plataforma Java.

/jdk-16.0.1/include Los archivos Header del lenguaje C, necesarios para soportar programación nativa mediante JNI (java native interface) y las interfaces para el Debugger de la JVM (Java Virtual Machine).

El IDE Netbeans

Un IDE es una herramienta de Desarrollo Integrado, tiene integrado las siguientes herramientas: editor de código, herramientas built como Ant o Maven, herramientas de versionamiento como SVN o Git.

Netbeans es más que un editor de código, tiene un potente asistente de código que te permite auto acompletar el código fuente y destacar la sintaxis y semántica del código fuente.

Escribí una serie de entradas sobre NetBeans que puedes encontrar en este blog con la etiqueta Netbeans.

Pasos para instalar Netbeans

1.- Descargar NetBeans, existen dos versiones para Windows, el binario y el instalador. Si descargas el instalador este viene con un asistente paso a paso. Para está guía he decidido descargar el binario de la versión 12.4 de NetBeans. Descargar NetBeans 12.4

2.- Descomprimir en un directorio, en mi caso C:\devel\java\tools

3.- Crear el acceso directo, entras al directorio de bin NetBeans y creas el acceso directo del ejecutable.

4.- Agregar al ejecutable la opción --jdkhome %JAVA_HOME%

5.- Ejecuta el acceso directo para asegurarte que funciona correctamente

Te dejo el video para que te sea más fácil. No olvides de suscribirte al canal y darle me gusta.


Referencias

https://blogs.oracle.com/java-platform-group/oracle-java-se-releases-faq

https://docs.oracle.com/en/java/javase/16/install/

https://www.oracle.com/cl/java/technologies/javase-subscription/documentation.html


martes, 29 de junio de 2021

El Gas de Ethereum

Este artículo es una versión traducida del original “A Guide to Gas”. Revisaremos la teoría y terminaremos con un ejemplo practico de como calcular el costo de una transacción en USD.

Entre el 1 y el 3 de enero de 2018, el costo promedio de ejecutar una transacción en el blockchain de Ethereum creció 187%. Esto se debe al aumento en el costo del “gas”, la unidad de medida utilizada para representar el costo de correr operaciones en Ethereum.

El gas puede compararse con las comisiones de transacción que cobran las empresas de tarjetas de crédito. Pero los consumidores nunca interactúan directamente con estos costos de transacción. Estos están ocultos dentro del precio del bien o servicio.

En un blockchain no hay una entidad central que cobre comisiones, determine penalidades o que pueda bloquear transacciones de manera arbitraria, dejando a personas aisladas de la economía. Sin embargo, esperar que cada usuario de Ethereum tenga que comprender el funcionamiento técnico del gas no es una experiencia de uso escalable.

En el largo plazo, el ecosistema encontrará formas de simplificar el uso del gas para mejorar la experiencia del usuario y reducir el riesgo de transacciones anuladas. Mientras tanto, sin embargo, es importante comprender la utilidad del gas y la razón de su existencia.

La Máquina Virtual de Ethereum y el Gas

La Máquina Virtual de Ethereum (MVE) es el ambiente en el cual se ejecutan los smart contracts en el blockchain. Cada nodo de la red participa del blockchain de Ethereum. Entre todos, forman la MVE.

Una transacción dentro de Ethereum se compone de una serie de operaciones secuenciales que ocurren dentro de la MVE. Cada operación requiere de cierta cantidad de energía de cómputo para ejecutarse. Como los mineros deben utilizar energía para completar esas operaciones, se creó una unidad de medida para monitorear este esfuerzo y compensarlos. Esta unidad se llama gas.

El gas es la unidad que mide el trabajo computacional requerido para correr transacciones o smart contracts en la MVE. Mientras más energía se necesite para correr una operación (por ejemplo, porque su código es más complejo) más gas se requiere.

En sí mismo, el gas no tiene existencia. No existe algo como un “token de gas”. El valor de cada unidad de gas se expresa en ETH. Por ejemplo, una operación podría costar 3 gas, lo que sería equivalente a 0.00004 ETH.

Ahora bien: si el gas se mide en ETH, ¿por qué no deshacerse de él y expresar el costo de cada operación directamente en ETH?

El precio del ETH es altamente volátil (entre el 1 y 3 de enero subió 25%). Pero la energía computacional requerida para realizar una transacción es constante. El costo de gas para realizar una “operación de suma” en Ethereum era el mismo el 1 o 3 de enero, sin importar el valor del ETH.

El concepto de gas existe para distinguir entre el costo computacional de correr una operación y el precio de mercado del ether.

El costo de gas (la energía requerida de una operación) se mantiene constante más allá de la volatilidad en el precio. Sin embargo, el precio de cada unidad de gas en ether cambia continuamente. Si el precio del ether sube significativamente, la red puede reducir cuánto ether representa cada unidad de gas para mantener constante el costo para el usuario que paga por el gas.

Los Componentes del Gas

El gas tiene tres componentes fundamentales: costo del gas, precio del gas y límite de gas.

El costo de gas son las unidades de gas necesarias para correr cada operación. Este costo está definido en los documentos técnicos de Ethereum. Por ejemplo, el costo de correr una “operación de suma” es de 3 gas, y seguirá siendo 3 gas sin importar cuál sea el valor en dólares del ether. La red no puede cambiar fácilmente la cantidad de gas necesaria para correr una operación. Pero el precio del gas expresado en ether sí puede ajustarse fácilmente para responder a la volatilidad en el precio del ether o al tráfico de la red.

El precio del gas es el valor de una unidad de gas expresado en ether. El precio del gas se mide en Gwei. El Wei es la menor unidad de ether. Un Gwei es 1000 millones de Wei. En sitios como ethgasstation.info podemos ver el precio de gas promedio de la red. Los usuarios dispuestos a pagar un mayor precio de gas por su transacción serán priorizados por los mineros y la transacción se procesará más rápidamente. Los mineros son quienes se quedan con el gas (o, más exactamente, con el ether que representa ese gas) que el usuario utiliza al enviar su transacción. Por lo tanto, los mineros priorizan las transacciones que tienen mayor precio de gas sobre las transacciones que tienen menor precio de gas.

El límite de gas es la máxima cantidad de gas que uno está dispuesto a pagar en una transacción específica. El límite de gas típicamente es mayor que la cantidad real de gas que requiere la transacción. Si un usuario específica un límite de gas demasiado bajo (las operaciones dentro de la transacción requieren más gas que el que el usuario asigna a la transacción), entonces un minero completará la transacción hasta que el gas se acabe. Cuando esto ocurra, la operación fallará y el minero conservará las comisiones (ya que gastó tiempo y energía en ejecutar tantas operaciones como pudo). El blockchain guardará un registro de la operación como “fallida”.

Por Qué Existe el Gas

El gas existe dentro del blockchain de Ethereum por tres motivos: financiero, teórico y computacional.

El motivo financiero es para incentivar a los mineros a invertir tiempo y energía en ejecutar transacciones. Operaciones más complejas requieren más poder de cómputo y, por lo tanto, más gas. Si un usuario desea que su transacción tenga prioridad, debe ofrecer un mayor precio de gas, para convencer a los mineros de procesarla antes.

El motivo teórico indica que el gas sirve para alinear los incentivos de los participantes de la red. Gran parte de la teoría del blockchain se ocupa de cómo combatir a los agentes maliciosos en un ambiente donde no existe una autoridad central en la que confiar. El gas resuelve parcialmente este problema alineando los incentivos económicos entre usuarios. Los mineros tienen un incentivo para trabajar en la red. Los usuarios tienen un desincentivo a escribir código pobre o malicioso porque están poniendo su ether (bajo la forma de gas) en juego.

El motivo computacional detrás del gas responde a un viejo problema de la teoría de computación: el halting problem. El halting problem trata de determinar si un programa arbitrario parará de correr en algún momento o correrá por siempre, con sólo observar su descripción y los valores de input. En 1936, Alan Turing determinó que es imposible para una máquina resolver el halting problem. En la MVE, esto significa que un minero nunca puede iniciar el procesamiento de una transacción teniendo una certeza total de que la transacción se detendrá en algún momento. Gracias al límite de gas, cada transacción está asociada con una cantidad finita de gas. Incluso si un minero empieza a procesar una transacción que fue programada para seguir indefinidamente (debido a un bug o un ataque a la red), el gas eventualmente se acabará, la transacción terminará y el minero será compensado.

Mirando el futuro

Para los usuarios que recién empiezan a interactuar con el blockchain de Ethereum, el gas suele ser un concepto difícil de entender. Si ponemos un precio de gas demasiado bajo, nuestra transacción no será procesada. Si lo ponemos demasiado alto, estaremos pagando por demás. Si ponemos un límite demasiado bajo, nuestra transacción fallará antes de finalizar (pero igualmente tendremos que pagar por ella).

Colocar el peso de este conocimiento en el usuario no es un modelo escalable. En el futuro, el ecosistema encontrará formas más amigables en las que los usuarios no tendrán que interactuar con el gas, del mismo modo en que los consumidores actualmente no interactúan directamente con las comisiones de transacción. Un mejor manejo del gas será importante para que el blockchain se acerque un paso más a la adopción masiva.

¿Cual es el costo de enviar Ethers?

La cantidad de gas que se necesita para realizar una transacción en Ethereum depende la cantidad de las instrucciones de la transacción, enviar Ethers es una transacción relativamente simple. En promedio se requieren 21000 unidades de gas, supongamos que en este momento cada unidad de gas valen 29 Gwei, necesitarías 609000 Gwei. 1 Gwei equivale a 0.000000001 Ethers, por lo tanto Ethers = 609000 Gwei  * 0.000000001 = 0.000609 Ethers. En estos momentos cada Ether vale 2198 USD, entonces 0.000609 * 2198 = 1,33 USD.

El costo de enviar Ethers en estos momentos nos saldría 1,33 USD. Claro, suponiendo que tienes tu propia Billetera.
Metamask es una billetera que me parece que no te cobra comisión adicional, de hecho en Metamask tu puedes ver el costo estimado de la transacción.

Oh casi lo olvido, para saber el precio del Gas puedes visitar la pagina https://ethgasstation.info/

Referencias
https://media.consensys.net/a-guide-to-gas-12b40d03605d
https://defiprime.com/gas