FundeWeb 2.0 vs FundeWeb 1.2.5

PEDRO DELGADO YARZA 2014/01/29 13:47

Con el cambio de versión de Fundeweb, se han actualizado un gran número de librerías lo que ha supuesto un cambio sustancial en rendimiento, manejo y visualización de las aplicaciones lo que aporta grandes ventajas pero implica adaptar ciertos aspectos que se usaban en Fundeweb 1.2.5 a cómo se utilizarán en esta nueva versión.

La estructura del proyecto ha cambiado sustancialmente de la versión 1.2.5 a la 2.0 de Fundeweb, es importante conocerla para poder trabajar con ella sin problemas. Los contenidos referentes a la estructura se encuentran en esta guía.

Persistir datos

En Fundeweb 2.0 la persitencia ha de hacerse siempre dentro de un EJB. Si intentamos persistir desde fuera de un EJB nos dará un error indicando que no existe transacción. En esta versión de fundeweb son los EJB quienes manejan la apertura y cierre de transacciones, liberando a Seam de esa tarea.

NamedQuery

Actualmente en la notación de las NamedQueries podíamos poner la variable del select con el mismo nombre que la tabla en la que buscamos

  SELECT anuncio FROM Anuncio anuncio

En la nueva versión de hiberante 4.2.7 el validador de las expresiones incluidas en las NamedQueries se ha vuelto más restrictivo y no permite que el nombre de la variable selectora sea igual al de la tabla que mapea (ignora mayúsculas/minúsculas) por lo que la Query anterior debería expresarse como:

  SELECT an FROM Anuncio an

En Fundeweb 2.0 se deja de utilizar totalmente Richfaces debido a la falta de evolución de sus componentes y el alto tiempo de respuesta a la hora de solventar bugs o añadir nuevas características.

En su lugar se utilizará Primefaces puesto que posee un conjunto de componentes muy amplio, junto con un gran servicio de mejora y desarrollo, lo que garantiza actualizaciones, mejoras, y resolución de bugs en un periodo corto de tiempo.

Otros detalles importantes a tener en cuenta en la vista:

Validaciones Tags: <s:validate> <s:validateAll> ya no son necesarios JSF es capaz de gestionar ahora la validación de componentes sin necesidad de las librerías de seam.

La actualización a EJB 3.1 conlleva un conjunto de mejoras importantes que facilitan el manejo, acceso y gestión de los EJB.

Acceso sin interfaz

En EJB 3.1 no es necesario que un EJB implemente un determinado interfaz para proveer acceso local a sus métodos. El cliente podrá acceder a cualquier método público de un EJB una vez lo recupere. El siguiente ejemplo ilustra esto:

   @Stateless
   public class PruebaEJB {
 
     public String holaMundo() {
        return "Hola Mundo";
     }   
   }
 
 
  public class HolaMundoClass{
   @EJB
   private PruebaEJB pruebaEJB ;
 
     public String diHola() {   
      return pruebaEJB.holaMundo();
    } 
  }

Como podemos ver el EJB no implementa ninguna interfaz y en cambio la clase HolaMundoClass puede acceder sin problemas a sus métodos públicos. No obstante existen ciertas limitaciones sobre esta nueva funcionalidad:

  • El cliente nunca puede usar al operador new para adquierir la referencia, debe obtener el EJB por una referencia existente.
  • Si se intenta invocar un método que no es público se producirá una EJBException.
  • Si se expone una interfaz local, sólo serán accesibles los métodos de esa interfaz aunque tenga otros públicos.

Anotaciones

En esta nueva versión tenemos un conjunto nuevo de anotaciones que nos mejoran y amplían las posibilidades existentes a la hora de diseñar nuestra aplicación.

@Asynchronous : Permite marcar un método como asíncrono. El método devolverá un objeto de tipo Future que inicialmente estará vacío y se rellenará cuando la ejecución del método se complete. Los métodos asíncronos sólo pueden devolver tipo de objeto Future o ser void.

   @Singleton
    public class B { 
    ...
    @Asynchronous
    public void flushBye() { ... }
    ...
    @Asynchronous
    public Future<String> sayBye() {
    ...
    return new AsyncResult<String>(bye);
    }
 
}

La interfaz Future provee los siguientes métodos:

  • cancel (boolean mayInterruptIfRunning): Intenta cancelar la ejecución del método asíncrono. El contenedor intentará cancelar la invocación si todavía no fue iniciada. Si la cancelación resulta exitosa el método devuelve true, sino false. El parámetro mayInterruptIfRunning controla, en caso de que no pueda detenerse la invocación, si el bean destino tiene visibilidad del pedido de cancelación por parte del cliente.
  • get: Devuelve el resultado del método cuando termina. Este método tiene dos versiones sobrecargadas, una que se bloquea hasta que termine la ejecución, y la otra que recibe un timeout como parámetro.
  • isCancelled: Indica si el método fue cancelado.
  • isDone: Indica si el método terminó exitosamente.

@Singleton: Nos permite crear de manera automática el singleton de una bean determinado creando lo que se conoce como Singleton Session Bean. El comportamiento en este caso, es el de un bean que se crea una única vez por aplicación y que puede ser instanciado por cualquier cliente, permitiéndose un acceso concurrente al mismo.

     @Singleton
     public class A { (...) }

Timer Service

Este servidio incluido en la nueva versión de EJB nos permite realizar tareas guiadas por instantes de tiempo. Ahorrando así el uso de otras tecnologías que antes eran necesarias para esta tarea como Quartz o Flux.

La anotación @Schedule se utiliza para crear un timer de forma automática, que coge como parámetro el timeout correspondiente a la ejecución. Esta anotación se aplica al método que será utilizado como callback del timeout. En el siguiente ejemplo se definen dos timers, uno que expira cada lunes a la medianoche y el otro que expira el último día de cada mes.

    @Stateless 
    public class TimerEJB { 
 
    @Schedule(dayOfWeek="Mon")
    public void itIsMonday(Timer timer) { (...) }
 
    @Schedule(dayOfMonth="Last")
    public void itIsEndOfMonth(Timer timer) { (...) }
 
    }

Un método puede estar anotado con más de un timer, en el siguiente ejemplo definiremos dos timers para el método mealTime, uno de los cuales expira todos los días a la 1pm y el otro expira a las 8pm.

    @Stateless
    public class MealEJB { 
 
    @Schedules(
    {    @Schedule(hour="13"),
         @Schedule(hour="20")
    }  
    public void mealTime(Timer timer) { (...) }
 
   }

Los timersser persistentes (por defecto) o no-persistentes. Los timers no-persistentes no sobreviven a un apagado de la aplicación o una caída del contenedor. La persistencia puede definirse usando el atributo persistente de la anotación, o pasando la clase TimerConfig como parámetro del método createTimer en la interfaz TimerService.

Algunos ejemplos más completos de cómo definir un timer:

  Todos los martes a las 7.30am 
  @Schedule(hour = "7", minute = "30",  dayOfWeek = "Tue")
 
  De lunes a viernes, a las 7, 15 y 20 horas
  @Schedule(hour = "7, 15, 20", dayOfWeek = "Mon-Fri")
 
  Cada hora de los domingos
  @Schedule(hour = "*", dayOfWeek = "0")
 
  Último viernes de diciembre, a las 12
  @Schedule(hour = "12", dayOfMonth = "Last Fri", month="Dec")
 
  Tres días antes del fin de mes, para cada mes del 2009, a las 8pm
  @Schedule(hour = "20", dayOfMonth = "-3", year="2009")
 
  Cada 5 minutos de todas las horas, comenzando a las 3pm
  @Schedule(minute = "*/5", hour = "15/1")

Servidor

El nuevo servidor en Fundeweb 2.0 es Weblogic 12c, este servidor nos proporciona facilidades en cuanto al despliegue y administración de aplicaciones. Estas facilidades nos permiten abstraernos de las tareas Ant utilizadas en Fundeweb 1.2.5 para el despliegue facilitándonos una opción única para desplegar un proyecto.

OC4J 10G Release 3 Carpeta APPLIB

La carpeta /applib del contenedor OC4J se corresponde con la carpeta /lib dentro de un dominio de Weblogic. Es importante destacar, que si se actualiza un JAR en la carpeta /lib, se tiene que reiniciar todos los servidores del dominio para que se coja el cambio. Solo se deben metar librerías que no cambien frecuentemente y que sean compartidas por todas las aplicaciones.

Classloader

Los classloaders en Weblogic funcionan de la siguiente manera:

  • Tendremos un system classloader padre del resto de classloader.
  • Como hijo tenemos un /lib classloader para las JAR compartidos para todas las aplicaciones del dominio.
  • Como hijo tenemos un classloader con las carpetas /lib y /classes de la raíz del EAR.
  • Como hijo tenemos un classloader con los módulos EJBs.
  • Como hijo tenemos un classloader por cada modulo web.

Esta configuración puede cambiar ya que se pueden crear classloaders que contengan ejb y web, etc.

Para indicar que se quieren cargar las clases de los classloader que vienen en la aplicación en lugar de el system classloader o el /lib classloader del dominio, se utilizan los ficheros weblogic-application.xml en la carpeta META-INF de la raíz del EAR o el weblogic.xml en la carpeta META-INF de la raíz del WEB.

Estos ficheros admiten prefer-application-packages y prefer-application-resources:

<prefer-application-packages>
  <package-name>org.apache.log4j.*</package-name>
  <package-name>antlr.*</package-name>
</prefer-application-packages>
 
<prefer-application-resources>
   <resource-name>org.apache.log4j.*</resource-name>
   <resource-name>antlr.*</resource-name>
</prefer-application-resources>

Además en el fichero weblogic.xml podemos añadir

<prefer-web-inf-classes>true</prefer-web-inf-classes>

para indicar que se prefieren las clases que están en la carpeta WEB-INF/lib en lugar de las que vienen por los classloaders padres.

Shared Library y Optional Packages

En Weblogic podemos instalar shared libraries y optional packages.

Optional Packages

En Java los optional packages se suelen instalar en las carpetas:

  • lib/ext [in the JRE]
  • jre/lib/ext [in the JDK]

Optional packages proporcionan un funcionalidad parecida a las librerías JAVA EE, permitiendo compartir un JAR entre varias aplicaciones. Primero hay que registrarlos en el servidor webLogic desplegando un JAR como optional package. Después se pueden desplegar módulos Java EE que hacen referencia a estos paquetes en sus ficheros manifest.

La diferencia entre los optional packages y las librerías Java EE radica en que pueden ser referenciados por cualquier modulo JEE (EAR, JAR, WAR, o archivo RAR) o por directorios desempaquetados. Las librerías Java EE solo pueden ser referenciadas por aplicaciones JEE validas.

Por ejemplo, las clases de los framework para crear aplicaciones, necesitadas por varias aplicaciones web, pueden ser empaquetadas y desplegadas en un simple JAR, y referenciadas por varios módulos de aplicaciones web del dominio. En este caso, se deben utilizar los optional packages en lugar de librerías JEE, porque un modulo individual de una aplicación web, puede referencias a un JAR compartido, con las librerías JEE, solo la aplicación completa puede referenciar a la librería.

Utiliza los optional packages cuando necesites compartir una o más clases (empaquetadas en un fichero JAR) entre diferentes módulos JEE.

Shared Library

Las librerías JEE compartidas, deben utilizarse cuando necesita que se compartir uno o más módulos EJB, Web o JEE entre diferentes aplicaciones JEE. Utiliza las librerías JEE compartidas, para compartir un JAR, si solo necesitas referenciarlo en una o más aplicaciones JEE, y no necesitas cumplir estrictamente la especificación JEE.

Para crear una librería compartida JEE, y compartirla entre múltiples aplicaciones tienes:

1. Ensamblar la librería dentro de un modulo o aplicación JEE valido y desplegable. La librería necesita que tener un descriptor de despliegue JEE para el módulo o la aplicación. Es recomendable que se empaquete como una aplicación JEE (EAR).

2. Ensamblar las clases de los optional package en un directorio de trabajo.

3. Crear y editar el fichero META-INF/MANIFEST.MF especificando el nombre (Extension-Name) y la información de la versión (Specification-Version y Implementation-Version). Ejemplo:

Extension-Name: myLibrary
Specification-Version: 2.0
Implementation-Version: 9.0.0

4. Empaquetar para la distribución y despliegue (Deploying Applications Using wldeploy.). Ejemplo:

java weblogic.Deployer -adminurl http://localhost:7001 -username weblogic
     -password weblogic -deploy -targets myserver1,myserver2
     -library /deployments/myLibraryApplication/

Para referenciar a una librería JEE compartida en una aplicación JEE, se utilizan el fichero weblogic-application.xml o weblogic.xml. Ejemplo:

<library-ref>
   <library-name>myLibrary</library-name>
   <specification-version>2.0</specification-version>
   <implementation-version>9.0.0</implementation-version>
   <exact-match>true</exact-match>
</library-ref>

Con <exact-match>true</exact-match> estamos indicando que se coja la versión exacta, si es false (valor por defecto), entonces weblogic establece la librería con mayor versión.

Cambiar la versión de JSF 2.1

Como actualizar la version de JSF 2.1 cuando pasamos la librería en la aplicación.

<container-descriptor>
    <prefer-web-inf-classes>false</prefer-web-inf-classes>
    <prefer-application-packages>
        <package-name>javax.faces.*</package-name>
        <package-name>com.sun.faces.*</package-name>
        <package-name>com.bea.faces.*</package-name>
    </prefer-application-packages>
 
    <prefer-application-resources>
        <resource-name>javax.faces.*</resource-name>
        <resource-name>com.sun.faces.*</resource-name>
        <resource-name>com.bea.faces.*</resource-name>
        <resource-name>META-INF/services/javax.servlet.ServletContainerInitializer</resource-name>
        <resource-name>META-INF/services/com.sun.faces.spi.FacesConfigResourceProvider</resource-name>
    </prefer-application-resources>
</container-descriptor>

Diferencias de Componentes

JSF vs Seam JSF

<param> → <f:viewParam>

Los elementos <param> de JBoss Seam definidos en las etiquetas <page> de los ficheros page.xml o el fichero pages.xml global se sustituye por la etiqueta <f:viewParam> que se define dentro de una etiqueta <f:metadata> en la propia página XHTML. También se le pueden añadir conversores y validadores.

<f:viewParam name="productId" value="#{bean.productId}" required="true"
	requiredMessage="Product id is required"
	validatorMessage="Product id must be between 0 and 1000"
	converter="javax.faces.Integer"
	converterMessage="Product id must be an integer">
     <f:validateLongRange minimum="0" maximum="1000"/>
</f:viewParam>

<action> → <f:event> (JSF 2.1) o <f:viewAction> (JSF 2.2)

Los elementos <action> de JBoss Seam definidos en las etiquetas <page> de los ficheros page.xml o el fichero pages.xml global se sustituye por la etiqueta <f:event> (JSF 2.1) o por <f:viewAction> (JSF 2.2), que se define dentro de una etiqueta <f:metadata> en la propia página XHTML.

    <f:event type="preRenderView" listener="#{bean.init}" />
 
 
    <f:viewAction action="#{catalog.checkItem}"/>

@RequestParameter → @ManagedProperty

La anotación de JBoss Seam @RequestParameter se puede sustituir por la anotación que proporciona JSF 2 @ManagedProperty. Realmente la anotación @ManagedProperty es más potente y permite injectar cualquier bean manejado por JSF. Pero para obtener los parámetros de una llamada GET, tenemos que utilizar en una expresión el el objeto param y después el nombre del parámetro de URL a obtener.

@ManagedProperty(value = "#{param.id}")
private Integer id;

JSF 2 y Problemas con Componentes @ViewScoped

Why does @PostConstruct callback fire every time even though bean is @ViewScoped?

No podemos utilizar la propiedad binding de componentes JSF en beans de respaldo con ámbito view. Parece que si este bean, tiene un metodo anotado con @PostConstruct este se ejecuta en cada acceso, Una solución es hacer el binding con un bean de respaldo de tipo request, e inyectar este bean en el bean con ámbito view.

NOTA: Parece que a partir de la versión de JSF Mojarra 2.1.18 esta solucionado.

JSTL c:forEach causes @ViewScoped bean to invoke @PostConstruct on every request

Los beans de ámbito view, se almacenan en el estado de la vista JSF, que solo esta disponible después de la fase restore view. Las etiquestas JSTL se ejecutan durante la fase restore view, donde los beans de ámbito view no estan disponibles. Lo que provoca la creación de una nueva instancia del bean con ámbito view, que es reemplazada por la instancia real que esta almacenada en el estado de la vista JSF.

NOTA: Como la anterior, puede que a partir de la versión JSF Mojarra 2.1.18 este solucionado.

Diferencia entre <f:viewParam> y @ManagedProperty

Para obtener parámetros en peticiones GET (estan en la URL), podemos utilizar la etiqueta <f:viewParam> o la anotación @ManagedProperty. Las ventajas o diferencias son las siguientes.

<f:viewParam>

<f:metadata>
  <f:viewParam name="id" value="#{someBean.id}"/>
</f:metadata>
  • Establece el valor solo durante la fase update model values (desde que extiende UIInput).
  • El valor establecido, no esta disponible durante la ejecución de un método anotado con @PostConstruct. Pero se puede utilizar la etiqueta <f:event type=“preRenderView” listener=“#{bean.init}” /> dentro de la etiqueta <f:metadata> para realizar una inicialización/precarga basada en este valor (en JSF 2.2 se podra utilizar la etiqueta <f:viewAction> en su lugar).
  • Permite anidar <f:converter> y <f:validator> y asociar un <h:message>.
  • Puede incluirse como parámetro de URL en llamadas GET, utilizando includeViewParams=true con <h:link> o <h:button>.
  • Puede utilizarse en beans @RequestScoped, pero es necesario un bean @ViewScoped si se quiere que el parametro sobreviva a los errores de validación de los formularios que contiene la vista o utilizar la etiqueta <f:param> en componentes que extienden de UICommand.

@ManagedProperty

@ManagedProperty(value = "#{param.id}")
private Integer id;
  • Establece el valor inmediatamente después de que el bean sea creado.
  • El valor establecido, esta disponible durante la ejecución de un método anotado con @PostConstruct. Facilitando la inicialización o precarga basada en ese valor.
  • No esta disponible para conversion/validation declarativa en la vista.
  • La propiedad #{param} no esta disponible para beans con ámbito mayor de el ámbito de request. Es obligatorio un bean con ámbito@RequestScoped.
  • Si necesitas tener la propiedad #{param} en posteriores solicitudes POST, tienes que utilizar la etiqueta <f:param> en componentes que extienden de UICommand.

Logging con log4j2

Con la nueva API, podemos utilizar placeholders (evitan la concatenación de Strings y ahorran memoria), como con el log de JBoss Seam pero en lugar de utilizar #0..#n o {0}..{n} se utiliza {}..{}.

logger.debug("Hi, {} {}", u.getA(), u.getB());

La nueva API también nos trae marcadores (Markers) y trazas de flujo (flow tracing).

private Logger logger = LogManager.getLogger(MyApp.class.getName());
private static final Marker QUERY_MARKER = MarkerManager.getMarker("SQL"); 
...
 
public String doQuery(String table) {
   logger.entry(param);
   logger.debug(QUERY_MARKER, "SELECT * FROM {}", table); 
   return logger.exit();
}

Los marcadores (Markers) permiten identificar entradas de log rapidamente. Los trazas de flujo Flow Traces son métodos que se pueden llamar al inicio y fin de un método. En tu fichero de log, podrás ver una gran cantidad de entradas nuevas con nivel de traza: tu flujo de programa es logeado. Ejemplo de trazas de flujo:

19:08:07.056 TRACE com.test.TestService 19 retrieveMessage - entry
19:08:07.060 TRACE com.test.TestService 46 getKey - entry

Arquitectura de Plugins

Log4j2 soporta arquitectura de plugins, que permiten extender log4j de una manera sencilla. Puedes construir extensiones con el espacio de nombres de tu elección, solo tienes que indicar al framework donde buscar (puedes añadir varios paquetes, separados por comas).

<configurationpackages="de.grobmeier.examples.log4j2.plugins">
...
</configuration>

Ejemplo de plugin:

@Plugin(name = "Sandbox", type = "Core", elementType = "appender")
public class SandboxAppender extends AppenderBase {
 
    private SandboxAppender(String name, Filter filter) {
        super(name, filter, null);
    }
 
    public void append(LogEvent event) {
        System.out.println(event.getMessage().getFormattedMessage());
    }
 
    @PluginFactory
    public static SandboxAppender createAppender(
         @PluginAttr("name") String name,
         @PluginElement("filters") Filter filter) {
        return new SandboxAppender(name, filter);
    }
}

El método con la anotación @PluginFactory sirve como factoría. Los dos argumentos de la factoría, son leídos del fichero de configuración. Se logra mediante las anotaciones @PluginAttr y @PluginElement utilizadas en los argumentos.

Además de appenders se pueden crear Loggers o Filters. Mira la documentación de plugins.

Configuración

La nueva configuración de log4j2 es más sencilla:

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
  <appenders>
    <Console name="STDOUT" target="SYSTEM_OUT"> 
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </appenders>
  <loggers>
    <logger name="com.foo.Bar" level="trace" additivity="false">
      <appender-ref ref="Console"/>
    </logger>
    <root level="error">
      <appender-ref ref="Console"/>
    </root>
  </loggers>
</configuration>

Mira la sección de appenders. Puedes utilizar etiquetas parlantes, por ejemplo, que coinciden con el nombre de un appender. No más nombres de clases. Bien, este fichero de configuración es un documento XML que no se puede validar. Si necesitan hacer validación de XML, también se puede utilizar el modo estricto, con lo que se parecerá al antiguo formato:

<appenders>
   <appender type="type" name="name">
     <filter type="type" ... />
   </appender>
   ...
</appenders>

Pero, no es todo. Se puede recargar la configuración automáticamente mediante la propiedad monitorInterval:

<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="30">
...
</configuration>

El valor del intervalo de monitorización es en segundos, con un valor mínimo de 5. Esto significa, que log4j2 puede reconfigurar el logging en caso de que la configuración haya cambiado. Si se utiliza el valor de cero o no se configura, no se detectaran cambios. Lo mejor, es que no se perderán eventos durante la reconfiguración.

Integración con otros sistemas de log

Apache log4j 2.0 tiene varias integraciones: Commons Logging, Slf4j. También puedes hacer que las aplicaciones log4j 1.x utilicen log4j2 en segundo plano.

Loggers y Appenders Asíncronos

El logging asíncrono, puede proporcionar una mejora en el rendimiento de la aplicación, ejecutando las operaciones de I/O en un hilo separado. Log4j2 ha mejorado en este área:

  • Loggers Asíncronos. Se han creado con la intención de volver de una llamada a Logger.log a la aplicación, lo más rápido posible. Puedes escoger entre configurar todos los logger como asíncronos (mayor rendimiento) o una mezcla entre síncronos y asíncronos (mayor flexibilidad).
  • Appenders Asíncronos. ya existían en Log4j 1.x, pero se han mejorado para volcar a disco al final del lote (cuando la cola esta vacía). Es más eficiente, ya que todos los eventos de log estas siempre disponibles en disco, pero no se necesita tocar el disco en cada evento de log. (Appenders Asíncronos utilizan internamente la clase ArrayBlockingQueue y no necesitan de la libreria disruptor.)
  • Fast File Appenders. Se pueden utilizar tanto en síncrona como asíncronamente. Son una alternativa a los Buffered File Appenders. Estos appenders utilizan un ByteBuffer + RandomAccessFile en lugar de BufferedOutputStream.

Los beneficios son el aumento del rendimiento de la aplicación y la bajada de la latencia en las llamadas de log. El perjuicio es la gestión de errores. Si se produce un error durante el proceso y se lanza una excepción, es menos sencillo para un logger o appender asíncrono señalar el error a la aplicación. Se puede aliviar en parte utilizando un ExceptionHandler, pero no cubre todos los casos. Por esta razón, si el logging es utilizado como framework de auditoria, es recomendable utilizar los mecanismos síncronos, y dejar los asíncronos para debug/trace.

Ejemplo de configuración mixta de loggers síncronos y asíncronos:

<?xml version="1.0" encoding="UTF-8"?>
 
<!-- No need to set system property "Log4jContextSelector" to any value 
     when using <asyncLogger> or <asyncRoot>. -->
 
<configuration status="WARN">
  <appenders>
    <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
    <FastFile name="FastFile" fileName="asyncWithLocation.log" 
              immediateFlush="false" append="false">
      <PatternLayout>
        <pattern>%d %p %class{1.} [%t] %location %m %ex%n</pattern>
      </PatternLayout>
    </FastFile>
  </appenders>
  <loggers>
    <!-- pattern layout actually uses location, so we need to include it -->
    <asyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
      <appender-ref ref="FastFile"/>
    </asyncLogger>
    <root level="info" includeLocation="true">
      <appender-ref ref="FastFile"/>
    </root>
  </loggers>
</configuration>
  • fdw2.0/fundeweb2.0/gt/fundeweb_2.0_vs_fundeweb_1.2.5.txt
  • Última modificación: 07/11/2017 10:46
  • (editor externo)