La razón de obtener la mayor información del entorno de ejecución es más para el entorno de pruebas o producción que para el entorno en el que desarrollamos, ya que estos primeros pueden llevar alguna configuración especial que no solemos revisar o están fuera de nuestro control aunque en los últimos también puede ser interesante si las hay varias personas que colaboran con posiblemente diferentes sistemas operativos instalados y configuraciones. Tener a nuestra disposición esta información nos puede permitir darnos cuenta de la causa de algun comportamiento extraño. Algún ejemplo de información que nos puede ser de utilidad es la hora del sistema, si trabajamos con fechas la zona horaria en la que está configurado o la codificación de caracteres por defecto.
Vamos a verlo en el caso de una aplicación web. En las aplicaciones web Java tenemos a nuestra disposición dos interfaces que podemos implementar y ser notificados cuando ocurran ciertos eventos en la aplicación como son cuando se inicia, cuando se va a terminar, cuando se crea una sesión y cuando se destruye una sesión. Ahora que estamos interesandos en obtener información del sistema vamos a ver unos ejemplos de clases ServletContextListener y HttpSessionListener que podríamos utilizar para obtener información.
Mediante la interfaz ServletContextListener y sus métodos contextInitialized y contextDestroyed podremos obtener una referencia a un objeto ServletContext y podremos acceder a los métodos que proporciona para por ejemplo conocer los parámetros de inicialización de la aplicación si esta la parametrizamos de esta manera. En el siguiente ejemplo se saca al log la siguiente información: unas trazas de cabecera para saber donde empieza en la salida del log cada inicio de la aplicación, el nombre de contexto de la aplicación, las propiedades del sistema (incluyendo la zona horaria y la codificación de caracteres por defecto), la información del servidor sobre el que se ejecuta la aplicación y la versión de la Java Servlet API, los nodos del arbol JNDI y un enumerado que identificará el servidor de aplicaciones sobre el que se ejecuta la aplicación.
package es.com.blogspot.elblogdepicodev.misc; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameClassPair; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import es.com.blogspot.elblogdepicodev.Entorno.Servidor; /** * Clase que recibe los eventos de inicio y fin de la aplicación web. */ public class ContextListener implements ServletContextListener { private static Logger logger; public static ServletContext SERVLET_CONTEXT; @Override public void contextInitialized(ServletContextEvent sce) { SERVLET_CONTEXT = sce.getServletContext(); // Configurar el entorno try { Utilidades.setupEntorno(); } catch (Exception e) { e.printStackTrace(); } logger = LoggerFactory.getLogger(ContextListener.class); logger.info(""); logger.info("--- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. ---"); logger.info("--- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. ---"); logger.info("--- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. ---"); { try { logger.info("Iniciando aplicación web [" + sce.getServletContext().getServletContextName() + "]..."); logger.info("Propiedades del sistema:"); SecurityManager sm = System.getSecurityManager(); Enumeration e = System.getProperties().keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); try { if (sm != null) { sm.checkPropertyAccess(key); } logger.info(key + ": " + System.getProperties().get(key)); } catch (SecurityException se) { logger.info(key + ": [No hay permisos]"); } } } catch (Exception e) { logger.warn(e.getMessage(), e); } } { logger.info("Propiedades del entorno:"); logger.info("Server info: " + sce.getServletContext().getServerInfo()); logger.info("Java Servlet API version: " + sce.getServletContext().getMajorVersion() + "." + sce.getServletContext().getMinorVersion()); } { // Obtener los nodos del árbol JNDI Map enviroment = null; try { enviroment = new LinkedHashMap(); buildEnv(new InitialContext(), Constantes.JNDI_EVANDTI, enviroment); } catch (Exception e) { logger.error(e.getMessage(), e); } logger.info("Propiedades de entorno para la aplicacion web:"); Iterator it = enviroment.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); logger.info("{}: {}", new Object[] { key, enviroment.get(key) }); } } { logger.info("Propiedades dinámicas:"); Servidor servidor = getServidor(SERVLET_CONTEXT); Entorno.setPropiedadDinamica(Entorno.PROP_DINAMICA_SERVIDOR, servidor); Iterator it = Entorno.getPropiedadesDinamicas().iterator(); while (it.hasNext()) { String propiedad = (String) it.next(); Object valor = Entorno.getPropiedadDinamica(propiedad); logger.info("{}: {}", propiedad, valor); } } logger.info("Iniciando aplicacion con la configuracion del entorno de «{}»", Entorno.getEntorno().toString().toLowerCase()); } @Override public void contextDestroyed(ServletContextEvent sce) { logger.info("Finalizando aplicación..."); } /** * Añade en un Map los nodos de árbol JNDI configurados en el servidor. */ private void buildEnv(Context c, String name, Map env) throws NamingException { NamingEnumeration ne = c.list(name); while (ne.hasMoreElements()) { NameClassPair n = (NameClassPair) ne.next(); String x = name + "/" + n.getName(); Object o = c.lookup(x); env.put(x, o); if (o instanceof Context) { buildEnv(c, x, env); } } } /** * Devuelve un enum del servidor sobre el que se está ejecutando la aplicación. */ private static Servidor getServidor(ServletContext sc) { String si = sc.getServerInfo(); if (si.contains(Constantes.SERVIDOR_TOMCAT)) { return Servidor.TOMCAT; } else if (si.contains(Constantes.SERVIDOR_JBOSS)) { return Servidor.JBOSS; } else { return Servidor.DESCONOCIDO; } } }
Esto sería un ejemplo de salida de esta clase ContextListener:
2012-05-04 21:03:33,031 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener 2012-05-04 21:03:33,037 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- -.-. . -. ... ..- .-. .- -.. --- 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Iniciando aplicación web [TapestryCensurado]... 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Propiedades del sistema: 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.runtime.name: OpenJDK Runtime Environment 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.boot.library.path: /usr/lib/jvm/java-6-openjdk/jre/lib/amd64 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.version: 20.0-b12 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener shared.loader: 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.vendor: Sun Microsystems Inc. 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vendor.url: http://java.sun.com/ 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener path.separator: : 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener tomcat.util.buf.StringCache.byte.enabled: true 2012-05-04 21:03:33,038 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.util.logging.config.file: /home/picodotdev/archivos/apache-tomcat-7.0.27/conf/logging.properties 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.name: OpenJDK 64-Bit Server VM 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener file.encoding.pkg: sun.io 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.java.launcher: SUN_STANDARD 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.country: ES 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.os.patch.level: unknown 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.specification.name: Java Virtual Machine Specification 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.dir: /home/picodotdev/archivos/apache-tomcat-7.0.27/bin 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.runtime.version: 1.6.0_24-b24 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.awt.graphicsenv: sun.awt.X11GraphicsEnvironment 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.endorsed.dirs: /home/picodotdev/archivos/apache-tomcat-7.0.27/endorsed 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener os.arch: amd64 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.io.tmpdir: /home/picodotdev/archivos/apache-tomcat-7.0.27/temp 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener line.separator: 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.specification.vendor: Sun Microsystems Inc. 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.naming.factory.url.pkgs: org.apache.naming 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.util.logging.manager: org.apache.juli.ClassLoaderLogManager 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener os.name: Linux 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.jnu.encoding: UTF-8 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener tomcat.util.scan.DefaultJarScanner.jarsToSkip: bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-tribes.jar,jasper.jar,jasper-el.jar,ecj-*.jar,tomcat-api.jar,tomcat-util.jar,tomcat-coyote.jar,tomcat-dbcp.jar,tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,tomcat-juli-adapters.jar,catalina-jmx-remote.jar,catalina-ws.jar,tomcat-jdbc.jar,commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,commons-math*.jar,commons-pool*.jar,jstl.jar,geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,dnsns.jar,ldapsec.jar,localedata.jar,sunjce_provider.jar,sunmscapi.jar,sunpkcs11.jar,jhall.jar,tools.jar,sunec.jar,zipfs.jar,apple_provider.jar,AppleScriptEngine.jar,CoreAudio.jar,dns_sd.jar,j3daudio.jar,j3dcore.jar,j3dutils.jar,jai_core.jar,jai_codec.jar,mlibwrapper_jai.jar,MRJToolkit.jar,vecmath.jar,junit.jar,junit-*.jar,ant-launcher.jar 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.library.path: /usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-6-openjdk/jre/lib/amd64:/usr/lib/jvm/java-6-openjdk/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib 2012-05-04 21:03:33,039 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.specification.name: Java Platform API Specification 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.class.version: 50.0 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.management.compiler: HotSpot 64-Bit Tiered Compilers 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener os.version: 3.3.4-2-ARCH 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.home: /home/picodotdev 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener catalina.useNaming: true 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.timezone: Europe/Madrid 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.awt.printerjob: sun.print.PSPrinterJob 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener file.encoding: UTF-8 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.specification.version: 1.6 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener catalina.home: /home/picodotdev/archivos/apache-tomcat-7.0.27 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.class.path: /home/picodotdev/archivos/apache-tomcat-7.0.27/bin/bootstrap.jar:/home/picodotdev/archivos/apache-tomcat-7.0.27/bin/tomcat-juli.jar 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.name: picodotdev 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.naming.factory.initial: org.apache.naming.java.javaURLContextFactory 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener package.definition: sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.specification.version: 1.0 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.java.command: org.apache.catalina.startup.Bootstrap start 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.home: /usr/lib/jvm/java-6-openjdk/jre 2012-05-04 21:03:33,040 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.arch.data.model: 64 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener user.language: es 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.specification.vendor: Sun Microsystems Inc. 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vm.info: mixed mode 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.version: 1.6.0_24 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.ext.dirs: /usr/lib/jvm/java-6-openjdk/jre/lib/ext:/usr/java/packages/lib/ext 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.boot.class.path: /usr/lib/jvm/java-6-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/netx.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/plugin.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/rhino.jar:/usr/lib/jvm/java-6-openjdk/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-6-openjdk/jre/classes 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener server.loader: 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vendor: Sun Microsystems Inc. 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener catalina.base: /home/picodotdev/archivos/apache-tomcat-7.0.27 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener file.separator: / 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java.vendor.url.bug: http://java.sun.com/cgi-bin/bugreport.cgi 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener common.loader: ${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.io.unicode.encoding: UnicodeLittle 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.cpu.endian: little 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener package.access: sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.desktop: gnome 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener sun.cpu.isalist: 2012-05-04 21:03:33,041 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Propiedades del entorno: 2012-05-04 21:03:33,042 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Server info: Apache Tomcat/7.0.27 2012-05-04 21:03:33,042 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Java Servlet API version: 3.0 2012-05-04 21:03:33,107 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Propiedades de entorno para la aplicacion web: 2012-05-04 21:03:33,107 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com: org.apache.naming.NamingContext@49b9ef36 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com/censurado: org.apache.naming.NamingContext@4e07e80a 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com/censurado/mail: org.apache.naming.NamingContext@6913108b 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com/censurado/mail/censurado: javax.mail.Session@62a34b91 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com/censurado/jdbc: org.apache.naming.NamingContext@4eb64f2e 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener java:/comp/env/com/censurado/jdbc/censurado: org.apache.tomcat.dbcp.dbcp.BasicDataSource@23087d61 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Propiedades dinámicas: 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener dinamica.servidor: TOMCAT 2012-05-04 21:03:33,108 INFO es.com.blogspot.elblogdepicodev.misc.ContextListener Iniciando aplicacion con la configuracion del entorno de «desarrollo»
La siguiente clase HttpSessionListener es muy básica pero nos puede permitir conocer si en algún momento se están creando muchas sesiones de usuario y podríamos acceder a más información a través de la interfaz HttpSession que podemos obtener del objeto HttpSessionEvent que recibimos en el listener.
package es.com.blogspot.elblogdepicodev.misc; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Clase que recibe los eventos de creación y destrucción de las sesiones de los * usuarios. */ public class SessionListener implements HttpSessionListener { private static Logger logger = LoggerFactory.getLogger(SessionListener.class); @Override public void sessionCreated(HttpSessionEvent se) { logger.info("Sesion creada"); } @Override public void sessionDestroyed(HttpSessionEvent se) { logger.info("Sesion destruida"); } }
Referencia:
[1] Librerías de logging para Java (slf4j, log4j, java.util.logging, logback, MentaLog)-slf4j.html
Configuración de entorno en Java con ayuda de Groovy