Capítulo 1. Primeros pasos: Compilar y ejecutar Java
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
1.0 Introducción
Este capítulo abarca algunas tareas básicas que debes saber hacer antes de poder continuar. Se dice que hay que gatear antes de poder andar, y andar antes de poder montar en bicicleta. Antes de que puedas probar nada en este libro, tienes que ser capaz de compilar y ejecutar tu código Java, así que empiezo por ahí, mostrando varias formas de hacerlo: a la manera del JDK, a la manera del Entorno de Desarrollo Integrado (IDE) y a la manera de las herramientas de compilación (Ant, Maven, etc.). Otro problema con el que se encuentra la gente es la configuración correcta de CLASSPATH
, así que lo trataré a continuación. Después vienen las advertencias de desaprobación, porque es probable que te las encuentres al mantener código Java antiguo. El capítulo termina con información general sobre compilación condicional, pruebas unitarias, aserciones y depuración.
Si aún no tienes Java instalado, tendrás que descargarlo. Ten en cuenta que hay varias descargas diferentes. La JRE (Java Runtime Environment) era, hasta Java 8, una descarga más pequeña para los usuarios finales. Dado que hay mucho menos Java de escritorio que antes, la JRE se eliminó en favor de jlink
para hacer una descarga personalizada (ver Receta 15.8). La descarga del JDK o Java SDK es el entorno de desarrollo completo, que querrás si vas a desarrollar software Java.
Las descargas estándar de la versión actual de Java están disponibles en, en el sitio web de Oracle.
A veces puedes encontrar versiones preliminares de la siguiente versión principal de Java en http://jdk.java.net. Todo el JDK se mantiene como un proyecto de código abierto, y el árbol de código fuente de OpenJDK se utiliza (con cambios y añadidos) para crear los JDK comerciales y compatibles de Oracle.
Si ya estás contento con tu IDE, puede que quieras saltarte parte o todo este material. Está aquí para asegurarnos de que todo el mundo puede compilar y depurar sus programas antes de que avancemos.
1.1 Compilar y ejecutar Java: JDK estándar
Solución
Esta es una de las pocas áreas en las que el sistema operativo de tu ordenador influye en la portabilidad de Java, así que vamos a quitarnos estos problemas de encima primero.
JDK
Utilizar el Kit de Desarrollo de Java (JDK) desde la línea de comandos puede ser la mejor manera de estar al día de las últimas mejoras de Java. Suponiendo que tengas el JDK estándar instalado en la ubicación estándar y/o hayas establecido su ubicación en tu PATH
, deberías poder ejecutar las herramientas JDK de la línea de comandos. Utiliza los comandosjavac para compilar y java para ejecutar tu programa (y, sólo en Windows, javaw para ejecutar un programa sin una ventana de consola), de esta forma:
C:\javasrc>javac HelloWorld.java C:\javasrc>java HelloWorld Hello, World C:\javasrc>
Si el programa hace referencia a otras clases cuyo código fuente está disponible (en el mismo directorio) y no existe un archivo .class compilado, javac lo compilará automáticamente por ti. A partir de Java 11, para los programas sencillos que no necesiten esa co-compilación, puedes combinar las dos operaciones pasando simplemente el archivo fuente de Java al comando java:
$ java HelloWorld.java Hello, Java $
Como puedes ver por la (falta de) salida del compilador, tanto javac como la compilación java funcionan según la filosofía Unix de "ninguna noticia es una buena noticia": si un programa ha sido capaz de hacer lo que le has pedido, no debería molestarse en parlotearte para decirte que lo ha hecho.
Existe un ajuste opcional llamado CLASSPATH
, comentado en la Receta 1.5, que controla dónde busca Java las clases. CLASSPATH
, si se establece, es utilizado tanto por javac como por java. En versiones antiguas de Java, tenías que configurar tu CLASSPATH
para que incluyera "." incluso para ejecutar un programa sencillo desde el directorio actual; esto ya no es así en las implementaciones actuales de Java .
El compilador javac de Sun/Oracle es la implementación oficial de referencia. Hubo varios compiladores alternativos de línea de comandos de código abierto, como Jikes y Kaffe, pero, en su mayor parte, ya no se mantienen activamente.
También ha habido algunos clones del runtime de Java, como Apache Harmony, Japhar, el IBM Jikes Runtime (del mismo sitio que Jikes), e incluso JNode, un sistema operativo completo e independiente escrito en Java; pero desde que la JVM de Sun/Oracle es de código abierto (GPL), la mayoría de estos proyectos han dejado de mantenerse. Harmony fue retirado por Apache en noviembre de 2011.
macOS
El JDK es pura línea de comandos. En el otro extremo del espectro en términos de teclado versus visual, tenemos el Macintosh de Apple. Se han escrito libros sobre lo genial que es la interfaz de usuario del Mac, y no voy a entrar en ese debate. macOS (versión 10.x del SO) está construido sobre una base BSD Unix (y "Mach"). Como tal, tiene una línea de comandos normal (la aplicación Terminal, oculta en /Aplicaciones/Utilidades), así como las herramientas de línea de comandos tradicionales de Unix y las herramientas gráficas de Mac. Si utilizas macOS, puedes utilizar las herramientas JDK de línea de comandos o cualquiera de las herramientas de compilación modernas. Las clases compiladas pueden empaquetarse en aplicaciones clicables utilizando el Jar Packager del que se habla en la Receta 15.6. Los aficionados a Mac pueden utilizar una de las muchas herramientas IDE completas que se comentan en la Receta 1.3. Apple proporciona XCode como IDE, pero fuera de la caja no es muy compatible con Java.
1.2 Compilar y ejecutar Java: GraalVM para un mejor rendimiento
Solución
Descarga e instala GraalVM.
Debate
GraalVM se presenta como "una máquina virtual universal para ejecutar aplicaciones escritas en JavaScript, Python, Ruby, R, lenguajes basados en JVM como Java, Scala, Clojure, Kotlin, y lenguajes basados en LLVM como C y C++".
Ten en cuenta que Graal está experimentando rápidos cambios. Aunque esta receta refleja la información más reciente en el momento de su publicación (finales de 2019), es posible que haya versiones más recientes y funcionalidades modificadas para cuando estés listo para instalarlo.
Al cierre de esta edición, GraalVM se basa en OpenJDK 11, lo que significa que puedes utilizar Módulos y otras características de Java 9, 10 y 11, pero no tiene soporte para las características de Java 12, 13 o 14. Puedes construir tu propio Graal en versiones posteriores, ya que el código fuente completo está en GitHub.
Consulta el sitio web de GraalVM para obtener más información sobre GraalVM. Consulta tambiénesta presentaciónde Chris Thalinger, que ha trabajado en JVMs durante década y media.
Comienza en la página de descargas.Tendrás que elegir entre la Community Edition y la Enterprise Edition. Para evitar problemas de licencias, esta receta comienza con la Community Edition. Puedes descargar un tarball para Linux, macOS y Windows. En este momento no existe un instalador formal. Para instalarlo, abre una ventana de terminal e intenta lo siguiente (el directorio elegido es para macOS):
$ cd /Library/Java/JavaVirtualMachines $ tar xzvf ~/Downloads/graalvm-ce-NNN-VVV.tar.gz # replace with actual version $ cd $ /usr/libexec/java_home -V # macOS only 11.0.2, x86_64: "OpenJDK 11.0.2" /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home 1.8.0_221, x86_64: "GraalVM CE 19.2.0.1" /Library/Java/JavaVirtualMachines/graalvm-ce-19.2.0.1/Contents/Home $
En otros sistemas, haz la instalación en un lugar sensato. En la mayoría de las versiones de Linux, después de instalar un JDK, puedes utilizar elcomando estándar de Linuxalternatives para convertirlo en tu JVM por defecto. En MacOS, la salida del comando java_home confirma que has instalado GraalVM, pero que aún no es tu JVM por defecto. Para ello, tienes que configurar tu PATH
:
export JAVA_HOME=<where you installed GraalVM>/Contents/Home export PATH=$JAVA_HOME/bin:$PATH
Asegúrate de incluir el :$PATH
al final de la línea -sin espacios- o todas tus herramientas estándar de la línea de comandos parecerán desaparecer (si cometiste este error, simplemente cierra la sesión y vuelve a entrar para restaurar tu ruta). Te sugiero que no actualices tus scripts de inicio de sesión hasta que estés seguro de que la configuración que tienes es correcta.
Ahora deberías estar ejecutando la versión Graal de Java. Esto es lo que deberías ver:
$ java -version openjdk version "1.8.0_222" OpenJDK Runtime Environment (build 1.8.0_222-20190711112007.graal.jdk8u-src-tar-gz-b08) OpenJDK 64-Bit GraalVM CE 19.2.0.1 (build 25.222-b08-jvmci-19.2-b02, mixed mode)
El resultado puede ser distinto, pero si pone "GraalVM", todo irá bien.
Graal incluye una serie de herramientas útiles, como native-image, que en algunos casos puede traducir un archivo de clase en un ejecutable binario para la plataforma en la que se ejecuta, optimizando la velocidad de inicio y reduciendo también el tamaño de descarga necesario para ejecutar una sola aplicación. La herramienta native-image debe descargarse por separado mediantegu install native-image
.
Exploraremos la ejecución de algunos de los otros lenguajes que no son Java en la Receta 18.4.
1.3 Compilar, ejecutar y probar con un IDE
Solución
Utiliza un Entorno de Desarrollo Integrado (IDE), que combina edición, pruebas, compilación, ejecución, depuración y gestión de paquetes.
Debate
Muchos programadores consideran que utilizar un puñado de herramientas separadas -un editor de texto, un compilador y un programa de ejecución, por no hablar de un depurador- es demasiado. Un IDE integra todas ellas en un único conjunto de herramientas con una interfaz gráfica de usuario. Existen muchos IDE, y los mejores son herramientas totalmente integradas con sus propios compiladores y máquinas virtuales. Los navegadores de clases y otras funciones de los IDE completan el conjunto de características de facilidad de uso de estas herramientas. Hoy en día, la mayoría de los desarrolladores utilizan un IDE debido a las ganancias de productividad. Aunque yo empecé siendo un adicto a la línea de comandos, creo que las funciones de IDE como las siguientes me hacen más productivo:
Completar código:: La Regla de Ian aquí es que nunca escribo más de tres caracteres de ningún nombre conocido por el IDE; ¡deja que el ordenador escriba!Funciones de compilación incremental:: Anota e informa de los errores de compilación mientras escribes, en lugar de esperar a que hayas terminado de escribir.Refactorización:: La capacidad de hacer cambios de gran alcance, pero que preserven el comportamiento, en una base de código sin tener que editar manualmente docenas de archivos individuales.
Aparte de eso, no pretendo debatir los méritos del IDE frente al proceso de línea de comandos; yo utilizo ambos modos en momentos distintos y en proyectos diferentes. Sólo voy a mostrar algunos ejemplos de uso de un par de IDE basados en Java.
Los tres IDE de Java más populares, que funcionan en todas las plataformas informáticas principales y en bastantes de nicho, son Eclipse, NetBeans e IntelliJ IDEA. Eclipse es el más utilizado, pero cada uno de los otros ocupa un lugar especial en el corazón y la mente de algunos desarrolladores. Si desarrollas para Android, la ADT se ha desarrollado tradicionalmente para Eclipse, pero ahora ha pasado a IntelliJ como base para Android Studio, que es el IDE estándar para Android, y para la otra plataforma móvil de Google, Flutter. Los tres IDE se basan en plug-ins y ofrecen una amplia selección de plug-ins opcionales y de terceros para mejorar el IDE, como la compatibilidad con otros lenguajes de programación, marcos y tipos de archivos. Aunque el párrafo siguiente muestra la creación y ejecución de un programa con Eclipse, los IDE IntelliJ IDEA y NetBeans ofrecen capacidades similares.
Uno de los IDE multiplataforma y de código abierto más populares para Java es Eclipse, originario de IBM y ahora dirigido por la Fundación Eclipse, sede de muchos proyectos de software, como Jakarta, la continuación de Java Enterprise Edition. La plataforma Eclipse también se utiliza como base de otras herramientas, como SpringSource Tool Suite (STS) y Rational Application Developer (RAD) de IBM. Todos los IDE hacen básicamente lo mismo cuando empiezas. El ejemplo de la Figura 1-1 muestra el inicio de un nuevo proyecto.
El Asistente para nueva clase Java de Eclipse que se muestra enla Figura 1-2 muestra la creación de una nueva clase.
Eclipse, como todos los IDE modernos, incorpora una serie de funciones de refactorización, que se muestran en la Figura 1-3.
Y, por supuesto, todos los IDE te permiten ejecutar y/o depurar tu aplicación.La Figura 1-4 muestra la ejecución de una aplicación; por variedad y neutralidad, se muestra utilizando IntelliJ IDEA.
macOS incluye las herramientas para desarrolladores de Apple. El IDE principal es Xcode. Por desgracia, las versiones actuales de Xcode no son realmente compatibles con el desarrollo Java, por lo que no puedo recomendarlo para nuestros fines; es principalmente para quienes construyen aplicaciones no portables (sólo para iOS o sólo para OS X) en los lenguajes de programación Swift u Objective-C. Así que, aunque estés en OS X, para desarrollar en Java debes utilizar uno de los tres IDE de Java.
Microsoft VSCode (antes parte de Visual Studio) ha estado recibiendo cierta atención en los círculos Java últimamente, pero no es un IDE específico de Java. Pruébalo si quieres.
¿Cómo eliges un IDE? Quizá lo dicte tu organización o lo elijan por mayoría tus compañeros desarrolladores. Dado que los tres IDE principales (Eclipse, NetBeans e IntelliJ) pueden descargarse gratuitamente y son 100% de código abierto, ¿por qué no probarlos todos y ver cuál se adapta mejor al tipo de desarrollo que haces? Independientemente de la plataforma que utilices para desarrollar Java, si tienes un runtime Java, deberías tener un montón de IDE entre los que elegir.
Ver también
El sitio web de cada IDE mantiene una lista actualizada de recursos, incluidos libros. Consulta la Tabla 1-1 para conocer el sitio web de cada una.
Nombre del producto | URL del proyecto | Nota |
---|---|---|
Eclipse |
Base del STS, RAD |
|
Idea IntelliJ |
Bases de Android Studio |
|
Netbeans |
Ejecutar en cualquier lugar JavaSE |
Estos IDE principales son extensibles; consulta su documentación para obtener una lista de los muchísimos plug-ins disponibles. La mayoría de ellos te permiten buscar e instalar plug-ins desde el IDE. En el caso de Eclipse, utiliza Eclipse Marketplace, cerca de la parte inferior del menú Ayuda. Como último recurso, si necesitas/quieres escribir un plug-in que amplíe la funcionalidad de tu IDE, también puedes hacerlo, y en Java.
Para Eclipse, tengo información útil enhttps://darwinsys.com/java. El sitio incluye una lista de atajos para ayudar a la productividad de los desarrolladores.
1.4 Explorar Java con JShell
Solución
Utiliza JShell, el intérprete REPL (Read-Evaluate-Print-Loop) de Java.
Debate
A partir de Java 11, JShell
se incluye como parte estándar de Java. Te permite introducir expresiones Java y hacer que se evalúen sin la molestia de crear una clase y un programa principal. Puedes utilizarlo para cálculos rápidos, para probar una API para ver cómo funciona, o para casi cualquier propósito; si encuentras una expresión que te guste, puedes copiarla en un archivo fuente regular de Java y hacerla permanente. JShell también puede utilizarse como lenguaje de scripting sobre Java, pero la sobrecarga de iniciar la JVM significa que no será tan rápido como awk, Perl o Python para un scripting rápido.
Los programas REPL son muy prácticos, y apenas son una idea nueva (los lenguajes LISP de los años 50 los incluían). Puedes pensar en los intérpretes de línea de comandos (CLI), como los shells Bash o Ksh en UNIX/Linux, o Command.com y PowerShell en Microsoft Windows, como REPLs para el sistema en su conjunto. Muchos lenguajes interpretados, como Ruby y Python, también pueden utilizarse como REPL. Java tiene por fin su propio REPL, JShell. Aquí tienes un ejemplo de uso:
$ jshell | Welcome to JShell -- Version 11.0.2 | For an introduction type: /help intro jshell> "Hello" $1 ==> "Hello" jshell> System.out.println("Hello"); Hello jshell> System.out.println($1) Hello jshell> "Hello" + sqrt(57) | Error: | cannot find symbol | symbol: method sqrt(int) | "Hello" + sqrt(57) | ^--^ jshell> "Hello" + Math.sqrt(57) $2 ==> "Hello7.54983443527075" jshell> String.format("Hello %6.3f", Math.sqrt(57) ...> ) $3 ==> "Hello 7.550" jshell> String x = Math.sqrt(22/7) + " " + Math.PI + ...> " and the end." x ==> "1.7320508075688772 3.141592653589793 and the end." jshell>
Aquí puedes ver algunas características y ventajas evidentes:
-
El valor de una expresión se imprime sin necesidad de llamar cada vez a
System.out.println
, pero puedes llamarlo si quieres. -
A los valores que no se asignan a una variable se les asignan identificadores sintéticos, como
$1
, que pueden utilizarse en sentencias posteriores. -
El punto y coma al final de una declaración es opcional (a menos que escribas más de una declaración en una línea).
-
Si cometes un error, recibes inmediatamente un mensaje de ayuda.
-
Puedes obtener la finalización con una sola tabulación, como en la finalización del nombre de archivo del intérprete de comandos.
-
Puedes obtener la parte correspondiente de la documentación Javadoc sobre clases o métodos conocidos con sólo pulsar dos veces el tabulador.
-
Si omites un cierre de comillas, un paréntesis u otro signo de puntuación, JShell se limitará a esperarte, dando un aviso de continuación (
…
). -
Si cometes un error, puedes utilizar el "historial del intérprete de comandos" (es decir, la flecha hacia arriba) para recordar la declaración y poder repararla.
JShell también es útil para crear prototipos de código Java. Por ejemplo, quería uno de esos temporizadores con temas de salud que te recuerdan que debes levantarte y moverte un poco cada media hora:
$ jshell | Welcome to JShell -- Version 11.0.2 | For an introduction type: /help intro jshell> while (true) { sleep (30*60); JOptionPane.showMessageDialog(null, "Move it"); } | Error: | cannot find symbol | symbol: method sleep(int) | while (true) { sleep (30*60); JOptionPane.showMessageDialog(null, "Move it");} | ^---^ | Error: | cannot find symbol | symbol: variable JOptionPane | while (true) { sleep (30*60); JOptionPane.showMessageDialog(null, "Move it");} | ^---------^ jshell> import javax.swing.*; jshell> while (true) { Thread.sleep (30*60); JOptionPane.showMessageDialog(null, "Move it"); } jshell> while (true) { Thread.sleep (30*60 * 1000); JOptionPane.showMessageDialog(null, "Move it"); } jshell> ^D
Luego puse la versión final de trabajo en un archivo Java llamado MoveTimer.java, puse una declaración class
y un método main()
alrededor de la línea principal, le dije al IDE que reformateara todo y lo guardé en mi repositorio darwinsys-api.
Así que adelante, experimenta con JShell. Lee el tutorial introductorio incorporado para obtener más detalles. Cuando obtengas algo que te guste, utiliza /save
, o cópialo y pégalo en un programa Java y guárdalo.
Lee más sobre JShell en eltutorial de JShell deOpenJDK.
1.5 Utilizar CLASSPATH con eficacia
Problema
Necesitas mantener tus archivos de clase en un directorio común, o estarás luchando con CLASSPATH
.
Solución
Establece en CLASSPATH
la lista de directorios y/o archivos JAR que contienen las clases que deseas.
Debate
CLASSPATH
es una lista de archivos de clases en cualquiera de una serie de directorios, archivos JAR o archivos ZIP. Al igual que el que utiliza tu sistema para encontrar programas, el es utilizado por el tiempo de ejecución de Java para encontrar clases. Incluso cuando escribes algo tan simple como PATH
CLASSPATH
java HolaMundo, el intérprete de Java busca en cada uno de los lugares nombrados en tu hasta que encuentra una coincidencia. Veamos un ejemplo. CLASSPATH
La CLASSPATH
puede establecerse como una variable de entorno del mismo modo que estableces otras variables de entorno, como tu variable de entorno PATH
. Sin embargo, normalmente es preferible especificar el CLASSPATH
para un comando determinado en la línea de comandos:
C:\> java -classpath c:\ian\classes MyProg
Supón que tu CLASSPATH
está configurado como C:\classes;. en Windows o ~/classes:. en Unix o Mac. Supón que acabas de compilar un archivo fuente llamado HelloWorld.java (sin declaración de paquete) en HelloWorld.class en el directorio por defecto (que es tu directorio actual) e intentas ejecutarlo. En Unix, si ejecutas una de las herramientas de rastreo del núcleo (trace
, strace
, truss
, o ktrace
), probablemente verías que el programa Java open
o stat
o access
los siguientes archivos:
-
Algunos archivos del directorio JDK
-
Luego ~/classes/HelloWorld.class, que probablemente no encontraría
-
Por último, ./HelloWorld.class, que encontraría, abriría y leería en memoria
La imprecisión "algún(os) archivo(s) en el directorio JDK" depende de la versión. No deberías meterte con los archivos del JDK, pero si tienes curiosidad, puedes encontrarlos en las Propiedades del Sistema (véase la Receta 2.2). Antes había una variable llamada sun.boot.class.path
, pero ya no se encuentra. Busquemos cualquier propiedad cuyo nombre sea boot
:
jshell
>
System
.
getProperties
().
forEach
((
k
,
v
)
->
{
...
if
(((
String
)
k
).
contains
(
"boot"
))
System
.
out
.
println
(
k
+
"->"
+
v
);})
sun
.
boot
.
library
.
path
->/
usr
/
local
/
jdk
-
11
/
lib
La razón por la que yo y otros sugerimos no establecer CLASSPATH
como variable de entorno es que no nos gustan las sorpresas. Es fácil añadir un JAR a tu CLASSPATH
y luego olvidar que lo has hecho; entonces un programa podría funcionar para ti pero no para tus colegas, debido a que desconocen tu dependencia oculta. Y si añades una nueva versión a CLASSPATH
sin eliminar la antigua, puedes encontrarte con conflictos.
Ten en cuenta también que proporcionar el argumento -classpath
hace que se ignore la variable de entorno CLASSPATH
.
Si aún así quieres establecer CLASSPATH
como variable de entorno, puedes hacerlo. Supón que también has instalado el archivo JAR que contiene las clases de soporte para programas de este libro, darwinsys-api.jar (el nombre real del archivo si lo descargas puede tener un número deversión como parte del nombre del archivo). Podrías entonces establecer tu CLASSPATH
enC:\classes;C:\classes\darwinsys-api.jar;. en Windows o~/classes:~/classes/darwinsys-api.jar:. en Unix.
Ten en cuenta que tienes que indicar explícitamente el nombre completo del archivo JAR. A diferencia de un archivo de clase individual, colocar un archivo JAR en un directorio listado en tu CLASSPATH
no hace que esté disponible.
Algunos programas especializados (como un servidor web que ejecute un contenedor Servlet
) podrían no utilizar ni bootpath ni CLASSPATH
exactamente como se muestra; estos servidores de aplicaciones suelen proporcionar su propio ClassLoader
(consulta la Receta 17.5 para obtener información sobre los cargadores de clases). Los contenedores Web EE, por ejemplo, configuran tu aplicación web CLASSPATH
para que incluya el directorio WEB-INF/classes y todos los archivos JAR que se encuentran en WEB-INF/lib.
¿Cómo puedes generar fácilmente archivos de clase en un directorio de tu CLASSPATH
? El comando javac tiene una opción -d
dir, que especifica dónde debe ir la salida del compilador. Por ejemplo, si utilizo -d
para colocar el archivo de clase HolaMundo en mi directorio $HOME/clases, sólo tengo que escribir lo siguiente (ten en cuenta que a partir de ahora utilizaré el nombre del paquete además del nombre de la clase, como un buen chico):
javac -d $HOME/classes HelloWorld.java java -cp $HOME/classes starting.HelloWorld Hello, world!
Mientras este directorio permanezca en mi CLASSPATH
, puedo acceder al archivo de clase independientemente de mi directorio actual. Ésa es una de las principales ventajas de utilizar CLASSPATH
.
Aunque estos ejemplos muestran el uso explícito de java
con -classpath
, en general es más cómodo (y reproducible) utilizar una herramienta de compilación como Maven(Receta 1.7) o Gradle, que proporcionan automáticamente el CLASSPATH
tanto para la compilación como para la ejecución.
Ten en cuenta que Java 9 y posteriores también tienen una ruta de módulo (variable de entorno MODULEPATH
, argumento de línea de comandos --module-path entry[:,…]
) con la misma sintaxis que la ruta de clase. La ruta de módulo contiene código que se ha modularizado; el sistema de módulos de Java se trata en la Receta 2.5 y en la Receta 15.9.
1.6 Descargar y utilizar los ejemplos de código
Solución
Descarga el último archivo de los ficheros fuente del libro, descomprímelo y ejecuta Maven (ver Receta 1.7) para compilar los ficheros.
Debate
El código fuente utilizado como ejemplo en este libro está incluido en un par de repositorios de código fuente que han estado en continuo desarrollo desde 1995, y que se enumeran en la Tabla 1-2.
Nombre del repositorio | URL de GitHub | Descripción del paquete | Tamaño aprox. |
---|---|---|---|
javasrc |
Ejemplos/demos de código Java |
1.400 clases |
|
darwinsys-api |
Una API publicada |
200 clases |
Puedes descargar estos repositorios desde las URL de GitHub que se muestran en la Tabla 1-2. GitHub te permite descargar un archivo ZIP con el estado actual de todo el repositorio, así como ver archivos individuales en la interfaz web. Es preferible descargarlo con git clone en lugar de como archivo comprimido, porque así puedes actualizarlo en cualquier momento con un simple comando git pull. Y con la cantidad de actualizaciones que ha sufrido esta base de código para la versión actual de Java, seguro que encontrarás cambios después de que se publique el libro.
Si no estás familiarizado con Git, consulta "CVS, Subversion, Git, ¡vaya!".
javasrc
Este es el repositorio más grande y consiste principalmente en código escrito para mostrar una función o API concreta. Los archivos están organizados en subdirectorios por temas, muchos de los cuales corresponden más o menos a capítulos del libro; por ejemplo, un directorio para ejemplos de cadenas(Capítulo 3), regex para expresiones regulares(Capítulo 4), números(Capítulo 5), etc. El archivo también contiene el índice por nombre y el índice por capítulo del sitio de descargas, para que puedas encontrar fácilmente los archivos que necesitas.
La biblioteca javasrc se desglosa a su vez en una docena de módulos Maven (que se muestran en la Tabla 1-3) para que no necesites todas las dependencias de todo en tu CLASSPATH
todo el tiempo.
Nombre del directorio/módulo | Descripción |
---|---|
pom.xml |
Pom padre de Maven |
Rdemo-web |
Demostración de R utilizando un framework web |
escritorio |
Cosas de AWT y Swing (ya no se tratan en el Libro de recetas de Java) |
ee |
Cosas de empresa (ya no se tratan en el Libro de recetas de Java) |
graal |
Demostraciones de GraalVM |
jlink |
Demostraciones JLink |
json |
Procesamiento JSON |
principal |
Contiene la mayoría de los archivos, es decir, aquellos que no es necesario que estén en uno de los otros módulos debido a |
restdemo |
Demostración del servicio REST |
chispa |
Demostración de Apache Spark |
pruebas |
Código para pruebas |
inseguro |
Demostración de la clase |
xml |
Cosas de XML (ya no se tratan en el Libro de recetas de Java) |
darwinsys-api
He creado una colección de cosas útiles en parte trasladando algunas clases reutilizables de javasrc a mi propia API, que utilizo en mis propios proyectos Java. Utilizo código de ejemplo de ella en este libro, e importo clases de ella en muchos de los otros ejemplos. Por tanto, si vas a descargar y compilar los ejemplos individualmente, primero debes descargar el archivo darwinsys-api-1.x.jar (para el último valor de x) e incluirlo en tu CLASSPATH
. Ten en cuenta que si vas a compilar el código javasrc con Eclipse o Maven, puedes saltarte esta descarga porque el script de Maven de nivel superior comienza incluyendo el archivo JAR de esta API.
Hay un archivo JAR compilado de darwinsys-api disponible enMaven Central; encuéntralo buscando darwinsys. Éste es el artefacto Maven actual :
<dependency> <groupId>com.darwinsys</groupId> <artifactId>darwinsys-api</artifactId> <version>1.1.3</version> </dependency>
Esta API consta de unas dos docenas de paquetes com.darwinsys
, enumerados en la Tabla 1-4. La estructura es vagamente paralela a la API estándar de Java; esto es intencionado. Estos paquetes incluyen ahora unas 200 clases e interfaces. La mayoría de ellos tienen documentación javadoc que puede consultarse con la descarga del código fuente.
Nombre del paquete | Descripción del paquete |
---|---|
|
Clases para archivos de valores separados por comas |
|
Clases para tratar bases de datos de forma general |
|
Utilidades de comparación |
|
Cosas genéricas de GUI |
|
Clases relativas a códigos de país, provincias/estados, etc. |
|
Gráficos |
|
Clases (sólo una hasta ahora) para tratar con HTML |
|
Clases para operaciones de entrada y salida, utilizando las clases de E/S subyacentes de Java |
|
Etiquetas Java EE JSP |
|
Clases para tratar las características estándar de Java |
|
API de bloqueo pesimista |
|
Clases para tratar el correo electrónico, principalmente una clase de conveniencia para enviar correo |
|
Modelos de datos de muestra |
|
Red |
|
Presentaciones |
|
Reflexión |
|
Cosas de expresiones regulares: un programa REDemo, una variante de Grep |
|
Seguridad |
|
Ayudantes de la API Servlet |
|
Clases para tratar con bases de datos SQL |
|
Clases para ayudar a construir y utilizar GUIs Swing |
|
Algunas implementaciones interesantes de LayoutManager |
|
Generadores de datos de prueba |
|
Herramientas de prueba |
|
Ayudantes Unix |
|
Algunas clases de utilidades varias |
|
Utilidades XML |
Muchas de estas clases se utilizan como ejemplos en este libro; sólo tienes que buscar archivos cuya primera línea empiece por lo siguiente
package com.darwinsys;
También verás que muchos de los otros ejemplos tienen importaciones de los paquetes com.darwinsys
.
Notas generales
Tu mejor opción es utilizar git clone para descargar una copia de ambos proyectos Git y luego hacer un git pull cada pocos meses para obtener actualizaciones. Como alternativa, puedes descargar de lapágina de catálogo de este libro un único subconjunto de intersección de ambas bibliotecas que está formado casi exclusivamente por archivos utilizados realmente en el libro. Este archivo está hecho a partir de las fuentes que se incluyen dinámicamente en el libro en el momento de formatearlo, por lo que debería reflejar exactamente los ejemplos que ves en el libro. Pero no incluirá tantos ejemplos como los tres archivos individuales, ni se garantiza que todo vaya a compilar porque falten dependencias, ni se actualizará a menudo. Pero si lo único que quieres es copiar piezas en un proyecto en el que estás trabajando, éste puede ser el que debes conseguir. Puedes encontrar enlaces a todos estos archivos desde mi propio sitio web para este libro; sólo tienes que seguir el enlace Descargas.
Los dos repositorios separados contienen múltiples proyectos autocontenidos con soporte para la construcción tanto con Eclipse(Receta 1.3) como con Maven(Receta 1.7). Ten en cuenta que Maven buscará automáticamente un amplio conjunto de bibliotecas de requisitos previos cuando se invoque por primera vez a un proyecto determinado, así que asegúrate de que estás conectado a una conexión a Internet de alta velocidad. De este modo, Maven se asegurará de que todos los requisitos previos estén instalados antes de la construcción. Si decides construir las piezas individualmente, busca en el archivo pom.xmlla lista de dependencias. Lamentablemente, no podré ayudarte si utilizas herramientas distintas de Eclipse o Maven con los archivos de control incluidos en la descarga.
Si tienes una versión de Java anterior a Java 12, algunos archivos no compilarán. Puedes inventar elementos de exclusión para los archivos que se sabe que no compilan.
Todo mi código en los dos proyectos se libera bajo la licencia menos restrictiva de sólo crédito, la licencia BSD de dos cláusulas . Si te resulta útil, incorpóralo a tu propio software. No hace falta que me escribas para pedirme permiso; simplemente utilízalo, con crédito. Si te haces rico con ello, envíame algo de dinero.
Consejo
La mayoría de los ejemplos de la línea de comandos hacen referencia a archivos fuente, suponiendo que estás en src/main/java, y a clases ejecutables, suponiendo que estás en (o has añadido a tu CLASSPATH
) el directorio de compilación (por ejemplo, normalmente target/classes). Esto no se mencionará con cada ejemplo, ya que hacerlo desperdiciaría mucho papel.
Caveat lector
Los repos llevan en desarrollo desde 1995. Esto significa que encontrarás algún código que no esté actualizado o que ya no refleje las buenas prácticas, lo cual no es sorprendente: cualquier cuerpo de código envejecerá si alguna de sus partes no se mantiene activamente. (Por eso, en este punto, invoco la canción de Culture Club "Do You Really Want to Hurt Me": "Dame tiempo para darme cuenta de mi crimen"). Cuando los consejos del libro no coincidan con algún código que hayas encontrado en el repositorio, ten esto en cuenta. Una de las prácticas de la Programación Extrema es la Refactorización Continua, la capacidad de mejorar cualquier parte de la base de código en cualquier momento. No te sorprendas si el código del directorio de fuentes en línea difiere de lo que aparece en el libro; es raro el mes en que no hago alguna mejora en el código, y los resultados se envían y publican con bastante frecuencia. Así que si hay diferencias entre lo que aparece en el libro y lo que obtienes de GitHub, alégrate, no te entristezcas, porque habrás recibido el beneficio de la retrospectiva. Además, la gente puede contribuir fácilmente en GitHub mediante pull requests; eso es lo que lo hace interesante. Si encuentras un error o una mejora, ¡envíame un pull request! El archivo consolidado de la página de este libro no se actualizará con tanta frecuencia.
1.7 Automatización de dependencias, compilación, pruebas e implementación con Apache Maven
Solución
Utiliza Apache Maven.
Debate
Maven es una herramienta de creación centrada en Java que incluye un sofisticado sistema distribuido de gestión de dependencias que también le proporciona reglas para crear paquetes de aplicaciones como archivos JAR, WAR y EAR e implementarlos en una serie de objetivos diferentes. Mientras que las herramientas de creación más antiguas se centran en el cómo, los archivos Maven se centran en el qué, especificando lo que quieres que se haga.
Maven se controla mediante un archivo llamado pom. xml (de Modelo de Objetos del Proyecto). Un pom.xml de muestra podría tener este aspecto:
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.example</groupId>
<artifactId>
my-se-project</artifactId>
<version>
1.0-SNAPSHOT</version>
<packaging>
jar</packaging>
<name>
my-se-project</name>
<url>
http://com.example/</url>
<properties>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>
junit</groupId>
<artifactId>
junit</artifactId>
<version>
4.8.1</version>
<scope>
test</scope>
</dependency>
</dependencies>
</project>
Esto especifica un proyecto llamado mi-proyecto-se (mi proyecto de edición estándar) que se empaquetará en un archivo JAR; depende del framework JUnit 4.x para las pruebas unitarias (consulta la Receta 1.10), pero sólo lo necesita para compilar y ejecutar pruebas. Si escribo mvn install en el directorio con este POM, Maven se asegurará de que tiene una copia de la versión dada de JUnit (y de cualquier cosa de la que JUnit dependa). Luego compilará todo (estableciendo CLASSPATH y otras opciones para el compilador), ejecutará todas y cada una de las pruebas unitarias y, si todas pasan, generará un archivo JAR para el programa. A continuación, lo instalará en mi repositorio personal de Maven (en ~/.m2/repository) para que otros proyectos Maven puedan depender de mi nuevo archivo JAR del proyecto. Ten en cuenta que no he tenido que decirle a Maven dónde se encuentran los archivos fuente, ni cómo compilarlos: todo esto se gestiona mediante valores predeterminados sensatos, basados en una estructura de proyecto bien definida. Se espera que el código fuente del programa se encuentre en src/main/java, y las pruebas en src/test/java; si se trata de una aplicación web, se espera que la raíz web esté por defecto en src/main/webapp. Por supuesto, puedes anular esta configuración.
Ten en cuenta que ni siquiera el archivo de configuración anterior tiene que ser, ni fue, escrito a mano; las reglas de generación de arquetipos de Maven le permiten construir la versión inicial de cualquiera de varios cientos de tipos de proyectos. He aquí cómo se creó el archivo:
$ mvn archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DgroupId=com.example -DartifactId=my-se-project [INFO] Scanning for projects... Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/ maven-deploy-plugin/2.5/maven-deploy-plugin-2.5.pom [several dozen or hundred lines of downloading POM files and Jar files...] [INFO] Generating project in Interactive mode [INFO] Archetype [org.apache.maven.archetypes:maven-archetype-quickstart:1.1] found in catalog remote [INFO] Using property: groupId = com.example [INFO] Using property: artifactId = my-se-project Define value for property 'version': 1.0-SNAPSHOT: : [INFO] Using property: package = com.example Confirm properties configuration: groupId: com.example artifactId: my-se-project version: 1.0-SNAPSHOT package: com.example Y: : y [INFO] ------------------------------------------------------------------------ [INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1 [INFO] ------------------------------------------------------------------------ [INFO] Parameter: groupId, Value: com.example [INFO] Parameter: packageName, Value: com.example [INFO] Parameter: package, Value: com.example [INFO] Parameter: artifactId, Value: my-se-project [INFO] Parameter: basedir, Value: /private/tmp [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] project created from Old (1.x) Archetype in dir: /private/tmp/ my-se-project [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6:38.051s [INFO] Finished at: Sun Jan 06 19:19:18 EST 2013 [INFO] Final Memory: 7M/81M [INFO] ------------------------------------------------------------------------
Alternativamente, puedes hacer mvn archetype:generate y seleccionar el predeterminado de una lista bastante larga de opciones. El predeterminado es un arquetipo Java de inicio rápido, que facilita el comienzo.
Los IDE (ver Receta 1.3) tienen soporte para Maven. Por ejemplo, si utilizas Eclipse, M2Eclipse (m2e) es un complemento de Eclipse que creará las dependencias de tu proyecto Eclipse a partir de tu archivo POM; este complemento se incluye por defecto con las versiones actuales de Eclipse para desarrolladores de Java. También está disponible para algunas versiones anteriores; consulta el sitio web de Eclipsepara obtener más información sobre el plug-in.
Un archivo POM puede redefinir cualquiera de los objetivos estándar. Los objetivos comunes de Maven (predefinidos por defecto para hacer algo sensato) incluyen los siguientes:
- limpia
-
Elimina todos los artefactos generados
- compila
-
Compila todos los archivos fuente
- prueba
-
Compila y ejecuta todas las pruebas unitarias
- paquete
-
Construye el paquete
- instala
-
Instala el pom.xml y el paquete en tu repositorio local de Maven para que lo utilicen tus otros proyectos
- implementación
-
Intenta instalar el paquete (por ejemplo, en un servidor de aplicaciones).
La mayoría de los pasos invocan implícitamente a los anteriores. Por ejemplo, package
compilará los archivos .class que falten y ejecutará las pruebas si no se ha hecho ya en esta ejecución.
Existe un elemento opcional distributionManagement
en el archivo POM o un -DaltDeploymentRepository
en la línea de comandos para especificar una ubicación de implementación alternativa. Hay objetivos específicos de servidores de aplicaciones proporcionados por los proveedores de servidores de aplicaciones; como ejemplo único, con el servidor de aplicaciones WildFly (conocido como JBoss AS hace una década o más), instalarías algún(os) complemento(s) adicional(es) según su documentación y luego realizarías la implementación en el servidor de aplicaciones utilizando
mvn wildfly:deploy
en lugar de la implementación habitual. Como utilizo este encantamiento de Maven con frecuencia, tengo un alias de shell o un archivo por lotes mwd
para automatizar incluso eso.
Pros y contras de Maven
Maven puede manejar proyectos complejos y es muy configurable. Construyo los proyectos darwinsys-api y javasrc con Maven y dejo que se encargue de encontrar las dependencias, haciendo que la descarga del código fuente del proyecto sea menor (en realidad, trasladando la sobrecarga de descarga a los servidores de los propios proyectos). Los únicos inconvenientes reales de Maven son que se tarda un tiempo en familiarizarse con él, y que puede ser difícil diagnosticar cuando las cosas van mal. Un buen buscador web es tu amigo cuando las cosas fallan.
Un problema que temo es que un pirata informático pueda acceder al sitio de un proyecto y modificar o instalar una nueva versión de un POM. Maven obtiene automáticamente las versiones actualizadas del POM. Sin embargo, utiliza firmas hash para verificar que los archivos no han sido manipulados durante el proceso de descarga, y todos los archivos que se suban deben estar firmados con PGP/GPG, por lo que un atacante tendría que comprometer tanto la cuenta de subida como las claves de firma. No obstante, no tengo constancia de que esto haya ocurrido nunca.
Ver también
Comienza en http://maven.apache.org.
1.8 Automatización de dependencias, compilación, pruebas e implementación con Gradle
Solución
Utiliza el sencillo formato de archivo de construcción de Gradle con configuración por convención para obtener archivos de construcción más cortos y construcciones rápidas.
Debate
Gradle es la última de la sucesión de herramientas de construcción (Make, Ant y Maven). Gradle se autodenomina "la herramienta de automatización empresarial" y se integra con las demás herramientas de construcción e IDE.
A diferencia de otras herramientas basadas en Java, Gradle no utiliza XML como lenguaje de scripting, sino un Lenguaje Específico de Dominio (DSL) basado en el lenguaje de scripting Groovy, basado en JVM y en Java.
Puedes instalar Gradle descargándolo del sitio web Gradle, descomprimiendo el ZIP y añadiendo su subdirectorio bin a tu ruta.
Entonces podrás empezar a utilizar Gradle. Suponiendo que utilices el directorio fuente estándar(src/main/java, src/main/test) que comparten Maven y Gradle, entre otras herramientas, el archivo build.gradle de ejemplo del Ejemplo 1-1 construirá tu aplicación y ejecutará tus pruebas unitarias.
Ejemplo 1-1. Ejemplo de archivo build.gradle
#
Simple
Gradle
Build
for
the
Java
-
based
DataVis
project
apply
plugin:
'java'
#
Set
up
mappings
for
Eclipse
project
too
apply
plugin:
'eclipse'
#
The
version
of
Java
to
use
sourceCompatibility
=
11
#
The
version
of
my
project
version
=
'1.0.3'
#
Configure
JAR
file
packaging
jar
{
manifest
{
attributes
'Main-class'
:
'com.somedomainnamehere.data.DataVis'
,
'Implementation-Version'
:
version
}
}
#
optional
feature:
like
-
Dtesting
=
true
but
only
when
running
tests
(
"test task"
)
test
{
systemProperties
'testing'
:
'true'
}
Puedes arrancar la gran inversión de la industria en infraestructura Maven añadiendo líneas como éstas en tu build.gradle:
#
Tell
Gradle
to
look
in
Maven
Central
repositories
{
mavenCentral
()
}
#
We
need
darwinsys
-
api
for
compiling
as
well
as
JUnit
for
testing
dependencies
{
compile
group:
'com.darwinsys'
,
name:
'darwinsys-api'
,
version:
'1.0.3+'
testCompile
group:
'junit'
,
name:
'junit'
,
version:
'4.+'
}
Ver también
Hay mucha más funcionalidad en Gradle. Empieza en el sitio web de Gradle, y consulta la documentación.
1.9 Hacer frente a las advertencias de desaprobación
Solución
Habrás parpadeado. O vives-peligrosamente-con las advertencias o revisas tu código para eliminarlas.
Debate
Cada nueva versión de Java incluye un montón de nuevas y potentes funcionalidades, pero a un precio: durante la evolución de este nuevo material, los mantenedores de Java encuentran algunas cosas antiguas que no se hicieron bien y que ya no deberían utilizarse porque realmente no pueden arreglarlas. En la primera gran revisión, por ejemplo, se dieron cuenta de que la clase java.util.Date
tenía serias limitaciones en cuanto a la internacionalización. En consecuencia, muchos de los métodos y constructores de la clase Date
están marcados como "obsoletos". Según el American Heritage Dictionary, obviar algo significa "expresar desaprobación hacia; deplorar", por lo que los desarrolladores de Java desaprueban la antigua forma de hacer las cosas. Prueba a compilar este código:
import
java.util.Date
;
/** Demonstrate deprecation warning */
public
class
Deprec
{
public
static
void
main
(
String
[]
av
)
{
// Create a Date object for May 5, 1986
@SuppressWarnings
(
"deprecation"
)
// EXPECT DEPRECATION WARNING without @SuppressWarnings
Date
d
=
new
Date
(
86
,
04
,
05
);
System
.
out
.
println
(
"Date is "
+
d
);
}
}
¿Qué ha ocurrido? Cuando lo compilé (antes de añadir la anotación @SuppressWarnings()
), recibí esta advertencia:
C:\javasrc>javac Deprec.java Note: Deprec.java uses or overrides a deprecated API. Recompile with "-deprecation" for details. 1 warning C:\javasrc>
Por tanto, seguimos órdenes. Para más detalles, recompila con -deprecation
para ver los detalles adicionales:
C:\javasrc>javac -deprecation Deprec.java Deprec.java:10: warning: constructor Date(int,int,int) in class java.util.Date has been deprecated Date d = new Date(86, 04, 05); // May 5, 1986 ^ 1 warning C:\javasrc>
La advertencia es sencilla: el constructor Date
que toma tres argumentos enteros ha quedado obsoleto. ¿Cómo solucionarlo? La respuesta es, como en la mayoría de las cuestiones de uso, consultar la documentación javadoc de la clase. La introducción de la página Date
dice, en parte:
La clase
Date
representa un instante concreto en el tiempo, con precisión de milisegundos.Antes del JDK 1.1, la clase
Date
tenía dos funciones adicionales. Permitía interpretar las fechas como valores de año, mes, día, hora, minuto y segundo. También permitía formatear y analizar cadenas de fechas. Por desgracia, la API de estas funciones no era apta para la internacionalización. A partir del JDK 1.1, la claseCalendar
debe utilizarse para convertir entre fechas y campos de hora y la claseDateFormat
debe utilizarse para formatear y analizar cadenas de fecha. Los métodos correspondientes deDate
están obsoletos.
Y más concretamente, en la descripción del constructor de tres enteros, el javadoc de Date
dice:
Date(int year, int month, int date)
Obsoleto. A partir de la versión 1.1 del JDK, sustituido por
Calendar.set(year + 1900
,month
,date)
oGregorianCalendar(year + 1900
,month
,date)
.
Por supuesto, la antigua clase Date
ha sido sustituida por LocalDate
y LocalDateTime
(véase el Capítulo 6), por lo que sólo verías ese ejemplo concreto en código heredado, pero los principios para tratar las advertencias de depreciación son importantes, porque muchas nuevas versiones de Java añaden advertencias de depreciación a partes de la API que antes se podían utilizar "sin problemas".
Como norma general, cuando algo ha sido desaprobado, no debes utilizarlo en ningún código nuevo; y, al mantener el código, esfuérzate por eliminar las advertencias de desaprobación.
Además de Date
(Java 8 incluye una API de fecha/hora completamente nueva; véase el Capítulo 6), las principales áreas de advertencias de desaprobación en la API estándar son el manejo de eventos realmente antiguo y algunos métodos (algunos de ellos importantes) de la clase Thread
.
También puedes desaprobar tu propio código, cuando se te ocurra una forma mejor de hacer las cosas. Coloca una anotación @Deprecated
inmediatamente antes de la clase o método que deseas desaprobar y/o utiliza una etiqueta@deprecated
en un comentario javadoc (véase la Receta 15.2). El comentario javadoc te permite explicar la desaprobación, mientras que la anotación es más fácil de reconocer para algunas herramientas porque está presente en tiempo de ejecución (por lo que puedes utilizar Reflection; véase el Capítulo 17).
Ver también
Hay muchas otras herramientas que realizan comprobaciones adicionales en tu código Java. Consulta mi sitio web Comprobación de programas Java.
1.10 Mantener la corrección del código con pruebas unitarias: JUnit
Solución
Utiliza pruebas unitarias para validar cada clase a medida que la desarrollas.
Debate
Detenerse para utilizar un depurador lleva mucho tiempo, ¡y encontrar un error en el código liberado es mucho peor! Es mejor probar de antemano. La metodología de las pruebas unitarias existe desde hace mucho tiempo; es un medio probado para probar tu código en pequeños bloques. Normalmente, en un lenguaje OO como Java, las pruebas unitarias se aplican a clases individuales, en contraste con las pruebas del sistema o de integración, en las que se prueba un trozo completo o incluso toda la aplicación.
Llevo mucho tiempo defendiendo esta metodología de pruebas tan básica. De hecho, los desarrolladores de la metodología de software conocida como Extreme Programming (XP para abreviar) abogan por Test-Driven Development (TDD): escribir las pruebas unitarias antes de escribir el código. También abogan por ejecutar tus pruebas casi cada vez que construyas tu aplicación. Y hacen una buena pregunta Si no tienes pruebas, ¿cómo sabes que tu código (aún) funciona? Este grupo de defensores de las pruebas unitarias cuenta con algunos líderes conocidos, como Erich Gamma, famoso por su libro Design Patterns, y Kent Beck, famoso por su libro eXtreme Programming (ambos de Addison-Wesley). Estoy totalmente de acuerdo con su defensa de las pruebas unitarias.
De hecho, muchas de mis clases solían venir con una prueba unitaria "incorporada". Las clases que no son programas principales por derecho propio solían incluir un método main
que simplemente probaba o al menos ejercitaba la funcionalidad de la clase. Lo que me sorprendió es que, antes de conocer XP, solía pensar que hacía esto a menudo, pero una inspección real de dos proyectos indicó que sólo un tercio de mis clases tenían casos de prueba, ya fuera interna o externa. Está claro que lo que se necesita es una metodología uniforme. Eso lo proporciona JUnit.
JUnit es una metodología centrada en Java para proporcionar casos de prueba, y se puede descargar gratuitamente. Es una herramienta de pruebas muy sencilla pero útil. Es fácil de usar: sólo tienes que escribir una clase de prueba que tenga una serie de métodos y anotarlos con @Test
(la versión anterior de JUnit 3.8 exigía que los nombres de los métodos de prueba empezaran por test
). JUnit utiliza la introspección (ver Capítulo 17) para encontrar todos estos métodos y luego los ejecuta por ti. Las extensiones de JUnit se encargan de tareas tan diversas como las pruebas de carga y las pruebas de componentes empresariales; el sitio web de JUnit proporciona enlaces a estas extensiones. Todos los IDE modernos ofrecen soporte integrado para generar y ejecutar pruebas JUnit.
¿Cómo empezar a utilizar JUnit? Todo lo que necesitas es escribir una prueba. Aquí he escrito una prueba sencilla de mi clase Person
y la he colocado en una clase llamada PersonTest
(fíjate en el patrón de nombres obvio):
public
class
PersonTest
{
@Test
public
void
testNameConcat
()
{
Person
p
=
new
Person
(
"Ian"
,
"Darwin"
);
String
f
=
p
.
getFullName
();
assertEquals
(
"Name concatenation"
,
"Ian Darwin"
,
f
);
}
}
JUnit 4 existe desde hace años y funciona bien. JUnit 5 sólo tiene unos pocos años y presenta algunas mejoras. Una prueba sencilla como esta clase PersonTest
será la misma en JUnit 4 o 5 (pero con importaciones diferentes). Utilizar funciones adicionales, como métodos de configuración que se ejecuten antes de cada prueba, requiere anotaciones diferentes entre JUnit 4 y 5.
Para mostrarte cómo ejecutar PersonTest
manualmente, compilo la prueba e invoco el arnés de pruebas de línea de comandos TestRunner
:
$ javac PersonTest.java $ java -classpath .:junit4.x.x.jar junit.textui.TestRunner testing.PersonTest . Time: 0.188 OK (1 tests) $
En la práctica, ejecutar las pruebas de ese modo es increíblemente tedioso, así que yo simplemente pongo mis pruebas en la estructura de directorios estándar (es decir, src/test/java/) con el mismo paquete que el código que se está probando y ejecuto Maven (véasela Receta 1.7), que compilará y ejecutará automáticamente todas las pruebas unitarias y detendrá la compilación si falla alguna prueba, cada vez que intentes compilar, empaquetar o implementar tu aplicación. Gradle también lo hará.
Todos los IDE modernos ofrecen soporte integrado para ejecutar pruebas JUnit; en Eclipse, puedes hacer clic con el botón derecho en un proyecto en el Explorador de paquetes y seleccionar Ejecutar como→Prueba JUnit para que encuentre y ejecute todas las pruebas JUnit de todo el proyecto. El complemento MoreUnit
(gratuito en el Marketplace de Eclipse) pretende simplificar la creación y ejecución de pruebas.
Los emparejadores Hamcrest te permiten escribir pruebas más expresivas a costa de una descarga adicional. La compatibilidad con ellos está integrada en JUnit 4 con el método estático assertThat
, pero tienes que descargar los emparejadores de Hamcrest o mediante el artefacto de Maven.
Aquí tienes un ejemplo de utilización de los emparejadores Hamcrest:
public
class
HamcrestDemo
{
@Test
public
void
testNameConcat
()
{
Person
p
=
new
Person
(
"Ian"
,
"Darwin"
);
String
f
=
p
.
getFullName
();
assertThat
(
f
,
containsString
(
"Ian"
));
assertThat
(
f
,
equalTo
(
"Ian Darwin"
));
assertThat
(
f
,
not
(
containsString
(
"/"
)));
// contrived, to show syntax
}
}
Ver también
JUnit ofrece una documentación propia considerable; descárgala del sitio web indicado anteriormente.
Un marco alternativo de pruebas unitarias para Java es TestNG; consiguió cierta tracción temprana al adoptar características como las anotaciones de Java antes de que lo hiciera JUnit; pero desde que JUnit se puso con el programa de anotaciones, ha seguido siendo el paquete dominante para las pruebas unitarias de Java.
Otro paquete de interés esAssertJ, que parece ofrecer una potencia similar a la combinación de JUnit con Hamcrest.
Por último, a menudo es necesario crear objetos sustitutos para que los utilice la clase que se está probando (las dependencias de la clase que se está probando). Aunque puedes codificarlos a mano, en general animo a que se utilicen paquetes comoMockito, que puede generar objetos mock de forma dinámica, hacer que estos mocks proporcionen valores de retorno fijos, verificar que las dependencias se han llamado correctamente, etc.
1.11 Mantener tu código con integración continua
Solución
Utiliza un servidor de Integración Continua como Jenkins/Hudson.
Debate
Si no has utilizado antes la Integración Continua, te preguntarás cómo has podido vivir sin ella. La Integración Continua es simplemente la práctica de hacer que todos los desarrolladores de un proyectointegren periódicamente (por ejemplo, hagan commit) sus cambios en una única copia maestra del código fuente del proyecto y, a continuación, construyan y prueben el proyecto para asegurarse de que sigue funcionando y supera sus pruebas. Esto puede hacerse unas cuantas veces al día, o cada pocos días, pero no debería ser más que eso, o de lo contrario la integración probablemente se encontrará con obstáculos mayores cuando varios desarrolladores hayan modificado el mismo archivo.
Pero no sólo los grandes proyectos se benefician de la IC. Incluso en un proyecto de una sola persona, es estupendo tener un solo botón que puedas pulsar para comprobar la última versión de todo, compilarlo, enlazarlo o empaquetarlo, ejecutar todas las pruebas automatizadas y dar un indicador rojo o verde de pasa/no pasa. Y lo que es mejor, puede hacerlo automáticamente todos los días o incluso en cada confirmación de la rama maestra.
Si tienes varios sitios web pequeños, ponerlos todos bajo control de CI es uno de los pasos importantes para desarrollar una cultura DevOps automatizada en torno a la implementación y gestión de sitios web.
Si eres nuevo en la idea de la IC, no puedo hacer nada mejor que rogarte que leas el perspicaz (como siempre) artículo de de Martin Fowler sobre el tema. Uno de los puntos clave es automatizar tanto la gestión del código y todos los demás artefactos necesarios para construir tu proyecto, como automatizar el proceso real de construirlo, posiblemente utilizando una de las herramientas de construcción comentadas anteriormente en este capítulo.1
Existen muchos servidores CI, tanto gratuitos como comerciales. En el mundo del código abierto,CruiseControl y Jenkins/Hudson2 También existen soluciones alojadas, comoTravis CI,TeamCity oCircleCI. Estas soluciones alojadas eliminan la necesidad de configurar y ejecutar tu propio servidor CI. También suelen tener su configuración directamente en tu repositorio(travis.yml, etc.), por lo que su implementación se simplifica.
Jenkins se ejecuta como una aplicación web, ya sea dentro de un servidor Jakarta EE o en su propio servidor web independiente. Una vez iniciado, puedes utilizar cualquier navegador web estándar como interfaz de usuario. Instalar e iniciar Jenkins puede ser tan sencillo como desempaquetar una distribución e invocarla del siguiente modo:
java -jar jenkins.war
Si lo haces, ¡asegúrate de configurar la seguridad si tu máquina es accesible desde Internet!
A mucha gente le parece más seguro ejecutar Jenkins en un servidor web Java EE o Java con todas las funciones; cualquier cosa, desde Tomcat a JBoss, pasando por WebSphere o Weblogic, hará el trabajo y te permitirá imponer restricciones de seguridad adicionales.
Una vez que Jenkins esté en marcha y hayas activado la seguridad y estés conectado con una cuenta con suficientes privilegios, puedes crear trabajos en. Un trabajo suele corresponder a un proyecto, tanto en términos de origen (una comprobación del código fuente) como de resultados (un archivo .war, un ejecutable, una biblioteca, lo que sea). Configurar un proyecto es tan sencillo como hacer clic en el botón Nuevo trabajo, situado en la parte superior izquierda del panel de control, como se muestra en la Figura 1-6.
Puedes rellenar los primeros datos: el nombre del proyecto y una breve descripción. Ten en cuenta que todos y cada uno de los campos de entrada tienen al lado un icono con un signo de interrogación, que te dará pistas a medida que avances. ¡No tengas miedo de echar un vistazo a estas pistas! La Figura 1-7 muestra los primeros pasos para configurar un nuevo trabajo.
En las siguientes secciones del formulario, Jenkins utiliza HTML dinámico para hacer que aparezcan los campos de entrada en función de lo que hayas marcado. Mi proyecto de demostración "TooSmallToFail" comienza sin un repositorio de gestión del código fuente (SCM) en , pero tu proyecto real probablemente ya esté en Git, Subversion o algún otro SCM. No te preocupes si el tuyo no está en la lista; hay cientos de plug-ins para gestionar casi cualquier SCM. Una vez que hayas elegido tu SCM, introducirás los parámetros para obtener el código fuente del proyecto de ese repositorio SCM, utilizando campos de texto que piden los datos específicos necesarios para ese SCM: una URL para Git, un CVSROOT para CVS, etc.
También tienes que decirle a Jenkins cuándo y cómo construir (y empaquetar, probar, implementar...) tu proyecto. En cuanto al cuándo, tienes varias opciones, como construirlo después de otro proyecto Jenkins, construirlo cada cierto tiempo según una programación tipo cron, o según un sondeo del SCM para ver si ha cambiado algo (utilizando la misma programación tipo cron). Si tu proyecto está en GitHub (no sólo en un servidor Git local), o en algún otro SCM, puedes hacer que el proyecto se construya cada vez que alguien introduzca cambios en el repositorio. Todo es cuestión de encontrar los plug-ins adecuados y seguir su documentación.
Luego tenemos el cómo, o el proceso de construcción. De nuevo, Jenkins incluye algunos tipos de compilación, y hay muchos más disponibles como complementos: He utilizado Apache Maven, Gradle, la herramienta tradicional de Unix make
, e incluso líneas de comandos o shell. Como antes, los campos de texto específicos de la herramienta elegida aparecerán una vez que la selecciones. En el ejemplo de juguete, TooSmallToFail
, sólo utilizo el comando shell /bin/false(que debería estar presente en cualquier sistema Unix o Linux) para asegurarme de que el proyecto, de hecho, no se construye, sólo para que veas qué aspecto tiene.
Puedes tener cero o más pasos de construcción; sólo tienes que seguir pulsando el botón Añadir y añadir otros, como se muestra en la Figura 1-8.
Cuando creas que has introducido toda la información necesaria, haz clic en el botón Guardar situado en la parte inferior de la página, y volverás a la página principal del proyecto. Aquí puedes hacer clic en el pequeño y gracioso iconoConstruir ahora, situado en el extremo izquierdo, para iniciar la construcción inmediatamente. O, si has configurado activadores de compilación, puedes esperar a que se activen; pero, ¿no preferirías saber de inmediato si lo has hecho bien? La Figura 1-9 muestra el inicio de la construcción.
En realidad, una construcción correcta muestra una bola azul por defecto (la bombilla de los semáforos japoneses, donde vive Kohsuke, es azul en lugar de verde), pero la mayoría de la gente de fuera de Japón prefiere el verde para el éxito, por lo que el complemento opcional Bolas Verdes suele ser uno de los primeros que se añaden a una nueva instalación.
Junto a la bola roja o verde, verás un informe meteorológico que va desde soleado (las últimas construcciones han tenido éxito) a nublado, lluvioso o tormentoso (ninguna construcción reciente ha tenido éxito).
Haz clic en el enlace al proyecto que ha fallado y, a continuación, en el enlace a la salida de la consola, y averigua qué ha fallado. El flujo de trabajo habitual consiste en realizar cambios en el proyecto, enviarlos al repositorio de código fuente y volver a ejecutar la compilación Jenkins.
Hay cientos de complementos opcionales para Jenkins. Para facilitarte la vida, casi todos ellos pueden instalarse haciendo clic en el enlace Gestionar Jenkins y luego yendo a Gestionar complementos. La pestaña Disponible enumera todos los que están disponibles en Jenkins.org; sólo tienes que hacer clic en la casilla de verificación junto a los que quieras, y hacer clic en Aplicar. También puedes encontrar actualizaciones allí. Si la adición o actualización de tu plug-in requiere un reinicio, verás una bola amarilla y palabras al respecto; de lo contrario, deberías ver una bola verde (o azul) que indica que el plug-in se ha realizado correctamente. También puedes ver la lista de plug-ins directamente en la web.
He mencionado que Jenkins comenzó su andadura con el nombre de Hudson. El proyecto Hudson sigue existiendo y está alojado en el sitio web de Eclipse. La última vez que lo comprobé, ambos proyectos habían mantenido la compatibilidad de plug-ins, por lo que muchos o la mayoría de los plug-ins de uno pueden utilizarse con el otro. De hecho, los plug-ins más populares aparecen en la pestaña Disponible de ambos, y la mayor parte de lo que se dice en esta receta sobre Jenkins se aplica igualmente a Hudson. Si utilizas un sistema de IC diferente, tendrás que consultar la documentación de ese sistema, pero los conceptos y las ventajas serán similares.
1.12 Obtener trazas de pila legibles
Solución
Asegúrate de que has compilado con la depuración activada.
Debate
Cuando un programa Java lanza una excepción, ésta se propaga por la pila de llamadas hasta que haya una cláusula catch
que coincida con ella. Si no se encuentra ninguna, el programa intérprete de Java que invocó tu método main()
captura la excepción e imprime un seguimiento de la pila mostrando todas las llamadas a métodos que llegaron desde la parte superior del programa hasta el lugar donde se lanzó la excepción. Tú mismo puedes imprimir este rastreo en cualquier cláusula catch
: la clase Throwable
tiene varias sobrecargas del método llamado printStackTrace()
.
El rastreo incluye los números de línea sólo si se han compilado. Cuando se utiliza javac, éste es el valor por defecto. Si añades la opción -g
, javac también incluirá los nombres de las variables locales y otra información en el código compilado, lo que mejorará la información de depuración en caso de fallo.
1.13 Encontrar más código fuente Java
Solución
Utiliza la fuente, Luke. Hay miles de aplicaciones, marcos y bibliotecas Java disponibles en código abierto.
Debate
El código fuente de Java está en todas partes. Como ya se ha mencionado, todos los ejemplos de código de este libro pueden descargarse: consulta la Receta 1.6.
Otro recurso valioso es el código fuente de la API Java . Puede que no te hayas dado cuenta, pero el código fuente de todas las partes públicas de la API de Java se incluye con cada versión del Kit de Desarrollo de Java. ¿Quieres saber cómo funciona realmente java.util.ArrayList
? Tienes el código fuente. ¿Tienes algún problema para hacer que se comporte un JTable
? El JDK estándar incluye el código fuente de todas las clases públicas. Busca un archivo llamado src.zip o src.jar; algunas versiones lo descomprimen y otras no.
Por si fuera poco, puedes obtener el código fuente de todo el JDK gratuitamente a través de Internet, ya sea mediante la biblioteca de código fuente Mercurial en openjdk.java.net o desde la réplica Git en AdoptOpenJDK en github.com. Esto incluye el código fuente de las partes públicas y no públicas de la API, así como el compilador (escrito en Java) y una gran cantidad de código escrito en C/C++ (el propio tiempo de ejecución y las interfaces con la biblioteca nativa). Por ejemplo,java.io.Reader
tiene un método llamado read()
, que lee bytes de datos de un archivo o de una conexión de red. Hay una versión de esto escrita en C para cada sistema operativo, porque llama a la llamada del sistema read()
para Unix, Windows, macOS o lo que sea. El kit fuente del JDK incluye el código fuente de todo esto.
1.14 Encontrar bibliotecas Java ejecutables
Solución
Utiliza Internet para encontrar software reutilizable.
Debate
Aunque la mayor parte de este libro trata de escribir código Java, esta receta no trata de escribir código, sino de utilizar código escrito por otros. Hay cientos de buenos marcos para añadir a tu aplicación Java: ¿por qué reinventar la rueda pinchada cuando puedes comprar una perfectamente redonda? Muchos de estos marcos de trabajo existen desde hace años y se han perfeccionado gracias a los comentarios de los usuarios.
Pero, ¿cuál es la diferencia entre una biblioteca y un framework? A veces es un poco vago, pero en general, un framework es un programa con agujeros que tú rellenas, mientras que una biblioteca es código que tú llamas. Es más o menos la diferencia entre construir un coche comprando un coche casi completo pero sin motor y construir un coche comprando todas las piezas y atornillándolas tú mismo.
Cuando te planteas utilizar un marco de terceros , hay muchas opciones y cuestiones a tener en cuenta. Una es el coste, que entra en la cuestión del código abierto frente al código cerrado. La mayoría de las herramientas de código abierto pueden descargarse gratuitamente y utilizarse, ya sea sin condiciones o con condiciones que debes cumplir. No hay espacio aquí para tratar estas cuestiones de licencias, así que te remitiré a Understanding Open Source and Free Software Licensing (O'Reilly).
Gran parte del software de código abierto está disponible en forma de biblioteca compilada en Maven Central, como se explica en "Maven Central: Mapeando el mundo del software Java".
En la Tabla 1-5 se enumeran algunas colecciones conocidas de marcos y bibliotecas de código abierto para Java. La mayoría de los proyectos de estos sitios están comisariados -es decir, juzgados y considerados dignos- por algún tipo de proceso comunitario.
Organización | URL | Notas |
---|---|---|
Fundación para el Software Apache |
¡No sólo un servidor web! |
|
Fundación de Software Eclipse |
Sede del IDE y de Yakarta EE |
|
Marco de trabajo Spring |
Hogar de una docena de frameworks: Spring IOC (fábrica DI), Spring MVC (web), más |
|
Comunidad JBoss |
Enumera media docena de sus proyectos, además de una larga lista de proyectos actuales de código abierto que utilizan y/o apoyan. |
|
Codehaus |
- |
Ver notaa |
a propia Codehaus dejó de funcionar hace unos años. A partir de 2019, el dominio es propiedad de la Apache Software Foundation, pero no responde a las peticiones del navegador. También hay una cuenta de Codehaus en github que contiene algunos de los proyectos que antes estaban en Codehaus, algunos activos y otros no. Consulta este artículo para saber más sobre la historia de Codehaus. |
También hay una gran variedad de repositorios de código fuente abierto, que no están comisariados: cualquiera que se registre puede crear un proyecto allí, independientemente del tamaño de la comunidad existente (si la hay). Los sitios de este tipo que tienen éxito acumulan demasiados proyectos como para tener una sola página que los enumere: tienes que buscar. La mayoría no son específicos de Java. La Tabla 1-6 muestra algunos de los repos de código fuente abierto.
Nombre | URL | Notas |
---|---|---|
Sourceforge.net |
Una de las más antiguas |
|
GitHub |
"Codificación Social"; probablemente el más utilizado, ahora propiedad de Microsoft |
|
Bitbucket |
Repos públicos y privados; planes gratuitos y de pago |
|
GitLab |
Repos públicos y privados; planes gratuitos y de pago |
|
Central Maven |
Tiene jar compilado, jar fuente y jar javadoc para cada proyecto |
No pretendo menospreciar estos repositorios; de hecho, la colección de programas de demostración de este libro está alojada en GitHub. Sólo digo que tienes que saber lo que buscas y tener un poco de cuidado antes de decidirte por un framework. ¿Existe una comunidad en torno a él, o es un callejón sin salida?
Mantengo un pequeño sitio Java que puede ser de utilidad. Incluye una lista de recursos Java y material relacionado con este libro.
Para el nivel empresarial o web de Java, hay dos marcos principales que también proporcionan inyección de dependencias: el primero es JavaServer Faces (JSF) y CDI, y el segundo es el paquete Spring Framework SpringMVC. JSF y el CDI (Contextos e Inyección de Dependencias) incorporado proporcionan DI, así como algunos contextos adicionales, como un contexto de Conversación Web muy útil que mantiene objetos a través de múltiples interacciones de páginas web. Spring Framework proporciona inyección de dependencias y las clases de ayuda de la capa web SpringMVC. La Tabla 1-7 muestra algunos recursos del nivel web. Spring MVC y JSF no son ni mucho menos los únicos marcos web; la lista de la Tabla 1-7incluye muchos otros, que pueden ser más adecuados para tu aplicación ¡Tienes que decidir!
Nombre | URL | Notas |
---|---|---|
Lista de Ian de 100 Frameworks Web Java |
||
JSF |
Tecnología estándar Java EE para páginas web |
Dado que JSF es un framework basado en componentes, existen muchos componentes adicionales que harán que tu sitio web basado en JSF sea mucho más capaz (y tenga mejor aspecto) que los componentes JSF predeterminados. La Tabla 1-8 muestra algunas de las bibliotecas de complementos de JSF.
Nombre | URL | Notas |
---|---|---|
BotasCaras |
Combina BootStrap con JSF |
|
Caras de mantequilla |
Biblioteca de componentes enriquecida |
|
ICEfaces |
Biblioteca de componentes enriquecida |
|
OpenFaces |
Biblioteca de componentes enriquecida |
|
PrimeFaces |
Biblioteca de componentes enriquecida |
|
RichFaces |
Componentes ricos; ya no se mantiene |
|
Apache DeltaSpike |
Numerosos complementos de código para JSF |
|
JSFUnit |
Pruebas JUnit para JSF |
|
OmniFaces |
Complemento JSF Utilities |
Hoy en día existen frameworks y bibliotecas para casi todo. Si mis listas no te conducen a lo que necesitas, probablemente lo hará una búsqueda en la red. ¡Intenta no reinventar el pinchazo!
Como con todo el software libre, asegúrate de que comprendes las ramificaciones de los distintos esquemas de licencia. El código cubierto por la GPL, por ejemplo, transfiere automáticamente la GPL a cualquier código que utilice incluso una pequeña parte de él. Consulta a un abogado. Tu kilometraje puede variar. A pesar de estas advertencias, el código fuente es un recurso inestimable para quien quiera aprender más Java.
1 Si la implementación o compilación incluye un paso como "Haz que Smith procese el archivo X en su escritorio y lo copie en el servidor", probablemente no entiendas del todo la noción de prueba automatizada.
2 Jenkins y Hudson comenzaron como Hudson, escrito en gran parte por Kohsuke Kawaguchi mientras trabajaba para Sun Microsystems. Más tarde hubo una disputa cultural que hizo que Jenkins se separara de Hudson, creando una nueva bifurcación del proyecto. Kohsuke trabaja en la mitad que ahora se conoce como Jenkins. Me limitaré a utilizar el nombre Jenkins, porque es el que yo utilizo, y porque lleva demasiado tiempo decir "Jenkins/Hudson" todo el tiempo. Pero casi todo lo que hay aquí se aplica también a Hudson.
Get Libro de cocina de Java, 4ª edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.