Apuntes DAM
    Tema 7 de 9DI · 2º DAM

    Desarrollo de Interfaces

    Distribución de Aplicaciones

    Empaquetado e instalación: JAR ejecutables, jlink y jpackage, instaladores (Inno Setup, WiX), firma de código, licencias, canales de distribución, tiendas y actualizaciones.

    50 min lecturaIntermedio

    1. De «funciona en mi equipo» a «funciona en el de todos»

    Terminar el código no es terminar la aplicación. Hay que empaquetarla con todo lo que necesita, crear un instalador que el usuario sepa usar, firmarla para que el sistema confíe en ella, publicarla por el canal adecuado y prever cómo se actualizará.

    Distribuir es como enviar un mueble 📦

    🧰
    Empaquetar
    Meter todas las piezas y tornillos (dependencias, recursos, runtime) en la caja.
    📖
    Instalar
    Incluir unas instrucciones de montaje sencillas: el instalador.
    🏷️
    Firmar y publicar
    El sello del fabricante (firma digital) y la tienda donde se vende (canal de distribución).

    Componentes de un paquete de distribución

    • Los binarios de la aplicación (clases compiladas, ejecutable).
    • Las dependencias (librerías) y los recursos (imágenes, estilos, FXML, traducciones, plantillas de informes).
    • El entorno de ejecución si hace falta (una JRE recortada con jlink).
    • Configuración por defecto, documentación y licencia (EULA).
    • Accesos directos, asociaciones de ficheros, entradas del menú y un desinstalador.

    2. Empaquetar una aplicación Java

    FormatoContieneEl usuario necesita
    JAR normalSolo tus clasesJava instalado y las dependencias aparte
    JAR ejecutable «fat» (uber-jar)Tus clases + todas las dependenciasJava instalado; se abre con java -jar o doble clic
    Imagen con jlinkTu aplicación + una JRE recortada con los módulos necesariosNada
    Instalador nativo (jpackage)Imagen jlink dentro de un .msi/.exe, .dmg/.pkg o .deb/.rpmNada: instalación como cualquier programa
    pom.xml (fragmento)
    1<!-- JAR ejecutable con todas las dependencias (maven-shade-plugin) -->
    2<plugin>
    3  <groupId>org.apache.maven.plugins</groupId>
    4  <artifactId>maven-shade-plugin</artifactId>
    5  <version>3.6.0</version>
    6  <executions>
    7    <execution>
    8      <phase>package</phase>
    9      <goals><goal>shade</goal></goals>
    10      <configuration>
    11        <transformers>
    12          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    13            <mainClass>com.empresa.gestion.Lanzador</mainClass>
    14          </transformer>
    15        </transformers>
    16      </configuration>
    17    </execution>
    18  </executions>
    19</plugin>
    terminal
    1mvn clean package                         # target/gestion-2.1.0.jar (con dependencias)
    2java -jar target/gestion-2.1.0.jar
    3
    4# Runtime a medida: solo los módulos que usa la aplicación
    5jdeps --print-module-deps target/gestion-2.1.0.jar      # p. ej. java.base,java.sql,javafx.controls
    6jlink --add-modules java.base,java.sql --output runtime --strip-debug --no-header-files --no-man-pages
    7
    8# Instalador nativo con jpackage (hay que generarlo en cada sistema operativo)
    9jpackage --name GestionClientes --app-version 2.1.0 --vendor "Empresa S.L." \
    10  --input target --main-jar gestion-2.1.0.jar --main-class com.empresa.gestion.Lanzador \
    11  --type msi --icon icono.ico --win-menu --win-shortcut --win-dir-chooser
    12# macOS: --type dmg --icon icono.icns     Linux: --type deb --linux-shortcut

    JavaFX

    Desde Java 11, JavaFX no viene con el JDK: se añade como dependencia (OpenJFX) y el plugin javafx-maven-plugin permite ejecutar (mvn javafx:run) y crear la imagen con jlink (mvn javafx:jlink). Como el main de una aplicación JavaFX extiende Application, para el JAR ejecutable se usa una clase Lanzador que llama a App.main().
    How to CREATE an EXECUTABLE JAR With Dependencies Using Maven — Begin Secure

    3. Instaladores

    Un instalador copia los ficheros, crea accesos directos y entradas en el menú, registra la aplicación en el sistema (para poder desinstalarla), comprueba requisitos y permite elegir opciones (carpeta, idioma, componentes).

    HerramientaGeneraNotas
    jpackageMSI/EXE, DMG/PKG, DEB/RPMIncluido en el JDK desde Java 16
    Inno SetupEXE para WindowsGratuito, con un lenguaje de script sencillo
    WiX ToolsetMSI para WindowsInstaladores profesionales; lo usa jpackage por debajo
    NSISEXE para WindowsLibre, muy configurable
    Install4j, Advanced InstallerMultiplataforma / WindowsComerciales
    dpkg-deb, rpmbuildPaquetes .deb / .rpmPara repositorios de Linux
    instalador.iss (Inno Setup)
    1[Setup]
    2AppName=Gestión de Clientes
    3AppVersion=2.1.0
    4AppPublisher=Empresa S.L.
    5DefaultDirName={autopf}\GestionClientes
    6DefaultGroupName=Gestión de Clientes
    7OutputBaseFilename=GestionClientes-2.1.0-setup
    8LicenseFile=LICENSE.txt
    9Compression=lzma2
    10SetupIconFile=icono.ico
    11
    12[Languages]
    13Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl"
    14
    15[Files]
    16Source: "dist\GestionClientes\*"; DestDir: "{app}"; Flags: recursesubdirs
    17
    18[Icons]
    19Name: "{group}\Gestión de Clientes"; Filename: "{app}\GestionClientes.exe"
    20Name: "{autodesktop}\Gestión de Clientes"; Filename: "{app}\GestionClientes.exe"; Tasks: escritorio
    21
    22[Tasks]
    23Name: escritorio; Description: "Crear un acceso directo en el escritorio"
    • Instalación desatendida: parámetros para instalar sin preguntas en muchos equipos (msiexec /i app.msi /qn, setup.exe /VERYSILENT), útil para desplegar por GPO o Intune.
    • Versión portable: un ZIP que se ejecuta sin instalar (no toca el registro).
    • Guardar los datos del usuario fuera de la carpeta del programa (%APPDATA%, ~/.config) para que sobrevivan a una actualización.
    ¿CÓMO CREAR UN INSTALADOR DE TU APLICACIÓN JAVA? | FÁCIL Y SENCILLO — Sin Rueda Tecnológica
    Crear instalador Inno Setup — Andres Hernandez

    4. Firma digital y licencias

    Una aplicación sin firmar muestra avisos como «Windows protegió su PC» o «desarrollador no identificado». La firma de código garantiza quién la ha publicado y que nadie la ha modificado: se calcula un hash del fichero y se cifra con la clave privada de un certificado de firma de código emitido por una autoridad de certificación.

    firmar
    1# Windows: signtool (Windows SDK) con un certificado de firma de código y sello de tiempo
    2signtool sign /f certificado.pfx /p **** /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 GestionClientes-2.1.0.msi
    3
    4# JAR firmado
    5jarsigner -keystore empresa.jks -tsa http://timestamp.digicert.com gestion-2.1.0.jar empresa
    6jarsigner -verify gestion-2.1.0.jar
    7
    8# macOS: codesign + notarización obligatoria de Apple
    9codesign --deep --sign "Developer ID Application: Empresa" GestionClientes.app
    10
    11# Publicar también el hash para que el usuario verifique la descarga
    12sha256sum GestionClientes-2.1.0.msi > SHA256SUMS
    LicenciaTipoPermite
    Propietaria (EULA)PrivativaSolo lo que diga el contrato; normalmente usar, no modificar ni redistribuir
    MIT, BSD, Apache 2.0Libre permisivaUsar, modificar y redistribuir, incluso en software cerrado, manteniendo el aviso
    GPLLibre copyleftUsar y modificar; si se distribuye, el derivado debe ser GPL con su código
    LGPLCopyleft débilEnlazarla desde software propietario (es la de OpenJFX con excepción de enlace)

    Revisa las licencias de tus dependencias

    Al distribuir una aplicación distribuyes también sus librerías: comprueba que sus licencias son compatibles con la tuya e incluye sus avisos (fichero THIRD-PARTY o NOTICE). Plugins como license-maven-plugin generan el listado automáticamente.

    5. Canales de distribución y actualizaciones

    CanalParaNotas
    Web propia / GitHub ReleasesEscritorioDescarga directa; publicar el hash y las notas de la versión
    Microsoft Store, Mac App StoreEscritorioRevisión de la tienda, actualizaciones automáticas, formatos MSIX / .app
    winget, Chocolatey, HomebrewEscritorio (usuarios técnicos)Instalación con un comando
    Snap, Flatpak, AppImageLinuxPaquetes universales con sus dependencias
    Google Play, App StoreMóvilAAB firmado (Android), revisión de Apple; fichas con capturas y política de privacidad
    Web / PWANavegadorSin instalación; la PWA se puede «instalar» desde el navegador
    Contenedores (Docker)ServidoresImagen con la aplicación y su entorno
    Despliegue corporativoEmpresaIntune, SCCM, GPO, repositorios internos

    Actualizaciones

    • La aplicación comprueba al arrancar si hay una versión nueva (un fichero JSON en el servidor con la última versión y la URL) y ofrece descargarla.
    • Herramientas: update4j o JavaFX con un lanzador propio en Java; Squirrel/electron-updater en Electron; las tiendas lo hacen solas.
    • Las actualizaciones deben estar firmadas, conservar los datos del usuario y migrar la base de datos si cambia el esquema (Flyway, Liquibase).
    ComprobadorVersion.java
    1// Comprueba si hay una versión nueva publicada en https://empresa.es/app/ultima.json
    2// {"version": "2.2.0", "url": "https://empresa.es/app/GestionClientes-2.2.0.msi"}
    3public static Optional<String> nuevaVersion(String actual) throws Exception {
    4    HttpClient cliente = HttpClient.newHttpClient();
    5    HttpResponse<String> r = cliente.send(
    6        HttpRequest.newBuilder(URI.create("https://empresa.es/app/ultima.json")).build(),
    7        HttpResponse.BodyHandlers.ofString());
    8    JsonObject json = JsonParser.parseString(r.body()).getAsJsonObject();   // Gson
    9    String ultima = json.get("version").getAsString();
    10    return Version.compare(ultima, actual) > 0 ? Optional.of(json.get("url").getAsString()) : Optional.empty();
    11}

    6. Ejercicios

    Ejercicio Práctico
    Fácil

    Elige el formato de distribución

    Indica cómo distribuirías cada aplicación y por qué: 1. Una herramienta interna de JavaFX para 40 ordenadores de una empresa con Windows gestionados por el departamento de informática. 2. Un editor de código abierto para Windows, macOS y Linux. 3. Una app Android para los clientes de un gimnasio. 4. Una utilidad de línea de comandos en Java para otros programadores. 5. Una aplicación de gestión para un único cliente que no tiene conocimientos técnicos.

    Ejercicio Práctico
    Difícil

    Instalador de una aplicación JavaFX

    Partiendo de un proyecto Maven JavaFX (por ejemplo, el de los temas anteriores): 1. Crea una clase Lanzador y genera un JAR ejecutable con todas las dependencias. 2. Con jdeps averigua los módulos necesarios y crea una imagen con jlink. 3. Genera un instalador con jpackage con nombre, versión, icono, acceso directo en el menú y en el escritorio y posibilidad de elegir la carpeta. 4. Comprueba la instalación y la desinstalación en una máquina virtual sin Java. 5. Documenta el proceso en el README (sección «Crear el instalador»).

    Ejercicio Práctico
    Medio

    Publicación en Google Play

    Enumera los pasos para publicar una app Android en Google Play por primera vez, desde el código hasta que está disponible, incluyendo la firma, el formato del paquete, la ficha de la tienda, las pruebas y los requisitos de privacidad.

    ¿Has terminado este tema?

    Crea una cuenta gratis para guardar qué temas has terminado, subir de nivel y ganar medallas.

    Guardar mi progreso