netbeans - javaserver - cursos jsf
Mac: JSF: ¿por qué las aplicaciones web de fase de desarrollo JSF no siempre captan los cambios de componentes compuestos? (1)
Parece que no puedo depurar correctamente todo el trazado de pila a través de com.sun.faces.facelets.impl.DefaultFaceletFactory.createFacelet(URL)
, el código fuente no está alineado con las clases compiladas para jsf-impl-2.2.12-jbossorg-2.jar
.
Para abreviar, reescribí el caché.
Con este nuevo caché, createFacelet(URL)
ahora se llama una vez para facelet en cada solicitud, recargando de manera efectiva los cambios de facelets de componentes compuestos.
Esta implementación de caché no está completamente probada y absolutamente no está lista para producción, pero es un comienzo.
Sin embargo, debe ser seguro para subprocesos, porque el semicaché interno se solicita con un alcance.
Tenga en cuenta que solo he usado importaciones de API ( javax.faces.*
) Y no com.sun.faces.*
, Así que esto debería funcionar con cualquier implementación de Mojarra / MyFaces 2.2.x.
public class DebugFaceletCacheFactory extends FaceletCacheFactory
{
protected final FaceletCacheFactory wrapped;
public DebugFaceletCacheFactory(FaceletCacheFactory wrapped)
{
this.wrapped = wrapped;
}
@Override
public FaceletCacheFactory getWrapped()
{
return wrapped;
}
@Override
public FaceletCache<?> getFaceletCache()
{
return new DebugFaceletCache();
}
public static class DebugFaceletCache extends FaceletCache<Facelet>
{
protected static final String MEMBER_CACHE_KEY = DebugFaceletCache.class.getName() + "#MEMBER_CACHE";
protected static final String METADATA_CACHE_KEY = DebugFaceletCache.class.getName() + "#METADATA_CACHE";
protected Map<URL, Facelet> getCache(String key)
{
Map<String, Object> requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
Map<URL, Facelet> cache = (Map<URL, Facelet>) requestMap.get(key);
if(cache == null)
{
cache = new HashMap<>();
requestMap.put(key, cache);
}
return cache;
}
protected MemberFactory<Facelet> getFactory(String key)
{
if(MEMBER_CACHE_KEY.equals(key))
{
return getMemberFactory();
}
if(METADATA_CACHE_KEY.equals(key))
{
return getMetadataMemberFactory();
}
throw new IllegalArgumentException();
}
protected Facelet getFacelet(String key, URL url) throws IOException
{
Map<URL, Facelet> cache = getCache(key);
Facelet facelet = cache.get(url);
if(facelet == null)
{
MemberFactory<Facelet> factory = getFactory(key);
facelet = factory.newInstance(url);
cache.put(url, facelet);
}
return facelet;
}
@Override
public Facelet getFacelet(URL url) throws IOException
{
return getFacelet(MEMBER_CACHE_KEY, url);
}
@Override
public boolean isFaceletCached(URL url)
{
return getCache(MEMBER_CACHE_KEY).containsKey(url);
}
@Override
public Facelet getViewMetadataFacelet(URL url) throws IOException
{
return getFacelet(METADATA_CACHE_KEY, url);
}
@Override
public boolean isViewMetadataFaceletCached(URL url)
{
return getCache(METADATA_CACHE_KEY).containsKey(url);
}
}
}
y se activa a través de faces-config.xml
:
<?xml version="1.0" encoding="utf-8"?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
...
<factory>
<facelet-cache-factory>it.shape.core.jsf.factory.DebugFaceletCacheFactory</facelet-cache-factory>
</factory>
</faces-config>
Codificación compuesta feliz;)
ACTUALIZAR
Encontré a JRebel interfiriendo con el eclipse depurador, así que lo deshabilité y reinicié.
Y encontré algunas cosas interesantes nuevas:
- La implementación de caché con JRebel habilitada se lee como
com.sun.faces.facelets.impl.DefaultFaceletCache.NoCache
pero en su lugar escom.sun.faces.util.ExpiringConcurrentCache
. Es por eso que había codificado las líneas de código fuente durante la depuración. - JSF (y específicamente Mojarra) necesita una refactorización profunda, en serio: hay al menos 5 fábricas diferentes y 2 cachés diferentes involucrados en la creación / almacenamiento en caché de facelets y metadatos, la mayoría haciendo trabajo de delegación simple y repetitivo.
-
com.sun.faces.facelets.impl.DefaultFaceletCache._metadataFaceletCache
ycom.sun.faces.application.view.FaceletViewHandlingStrategy.metadataCache
están mal emparejados : contienen los mismos datos y tienen unidireccional de sincronización dependiente. Conceptualmente incorrecto y consumir memoria. - El período de actualización de facelet predeterminado es diferente de lo que pensaba: es 2000 en lugar de 0.
Entonces otra solución es establecer:
<context-param>
<param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
<param-value>0</param-value>
</context-param>
en web.xml, pero sinceramente esto es mucho menos eficiente que la implementación de mi simple caché, ya que crea facelets y metadatos dos veces por instancia de componente compuesto ...
Finalmente, en esta sesión de depuración, nunca he visto un caso en el que la ventana modificada no se actualice y, aunque la implementación sea monstruosamente ineficaz y esquizofrénica, esta versión (2.2.12) parece funcionar.
En mi caso, creo que es un problema de JRebel.
Sin embargo, ahora finalmente puedo desarrollar con JRebel habilitado y recarga de facelets.
Si toco un caso oculto (como eclipse no copiando / actualizando facelets a la carpeta de destino y / o no establezco la fecha del último archivo modificado, al guardarlo del editor) actualizaré esta respuesta.
PD
Usan clases abstractas en algún caso porque las interfaces son sin estado y no son adecuadas para todos los patrones conceptuales. La herencia de clase única es IMO, el problema Java más grave. Sin embargo, con Java 8, tenemos métodos predeterminados / defensores, que ayudan a mitigar el problema. Sin embargo, no pueden ser llamados por JSF ExpressionLanguage 3.0 :(
CONCLUSIÓN
Ok, encontré el problema. No es simple de explicar, y requiere condiciones especiales (aunque comunes) para ser reproducidas.
Supongamos que tiene:
- FACELET_REFRESH_PERIOD = 2
- un componente compuesto llamado
x:myComp
- una página donde
x:myComp
se usa 100 veces
Ahora, esto es lo que sucede bajo el capó.
- la primera vez que se encuentra
x:myComp
durante la evaluación de la página, se crea unRecord
caché con_creation=System.currentTimeMillis()
- para cada otro momento
x:myComp
se encuentrax:myComp
durante la evaluación de la página, elRecord
recuperado del caché yDefaultFaceletCache.Record.getNextRefreshTime()
se llama dos veces (enget()
ycontainsKey()
) para verificar la caducidad. - los componentes compuestos se evalúan 2 veces
- suponiendo que la evaluación de página completa se completa en menos de 2 segundos, al final
DefaultFaceletCache.Record.getNextRefreshTime()
se ha llamado ((100 * 2) - 1) * 2 = 398 veces - cuando se
DefaultFaceletCache.Record.getNextRefreshTime()
, se incrementa una variable local atómica_nextRefreshTime
porFACELET_REFRESH_PERIOD * 1000
= 2000 - entonces, al final,
_nextRefreshTime = initial System.currentTimeMillis() + (398 * 2000 = 796 s)
ahora este elemento expirará en 796 segundos desde que se creó. ¡Cada acceso a esta página antes de su vencimiento agrega otros 796 segundos!
el problema es que la comprobación de la caché está acoplada (¡2 ^ 2 veces!) con la extensión de la vida.
Ver JAVASERVERFACES-4107 y JAVASERVERFACES-4176 (y ahora principalmente JAVASERVERFACES-4178 ) para más detalles.
Esperando la resolución del problema, estoy usando mi propia impl de caché (se requiere Java 8 ), tal vez también sea útil para usar / adaptar (condensada manualmente en una sola clase grande, tal vez haya algún error de copiado):
/**
* A factory for creating ShapeFaceletCache objects.
*
* @author Michele Mariotti
*/
public class ShapeFaceletCacheFactory extends FaceletCacheFactory
{
protected FaceletCacheFactory wrapped;
public ShapeFaceletCacheFactory(FaceletCacheFactory wrapped)
{
this.wrapped = wrapped;
}
@Override
public FaceletCacheFactory getWrapped()
{
return wrapped;
}
@Override
public ShapeFaceletCache getFaceletCache()
{
String param = FacesContext.getCurrentInstance()
.getExternalContext()
.getInitParameter(ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME);
long period = NumberUtils.toLong(param, 2) * 1000;
if(period < 0)
{
return new UnlimitedFaceletCache();
}
if(period == 0)
{
return new DevelopmentFaceletCache();
}
return new ExpiringFaceletCache(period);
}
public static abstract class ShapeFaceletCache extends FaceletCache<Facelet>
{
protected static volatile ShapeFaceletCache INSTANCE;
protected Map<URL, FaceletRecord> memberCache = new ConcurrentHashMap<>();
protected Map<URL, FaceletRecord> metadataCache = new ConcurrentHashMap<>();
protected ShapeFaceletCache()
{
INSTANCE = this;
}
public static ShapeFaceletCache getInstance()
{
return INSTANCE;
}
protected Facelet getFacelet(FaceletCacheKey key, URL url)
{
Map<URL, FaceletRecord> cache = getLocalCache(key);
FaceletRecord record = cache.compute(url, (u, r) -> computeFaceletRecord(key, u, r));
Facelet facelet = record.getFacelet();
return facelet;
}
protected boolean isCached(FaceletCacheKey key, URL url)
{
Map<URL, FaceletRecord> cache = getLocalCache(key);
FaceletRecord record = cache.computeIfPresent(url, (u, r) -> checkFaceletRecord(key, u, r));
return record != null;
}
protected FaceletRecord computeFaceletRecord(FaceletCacheKey key, URL url, FaceletRecord record)
{
if(record == null || checkFaceletRecord(key, url, record) == null)
{
return buildFaceletRecord(key, url);
}
return record;
}
protected FaceletRecord buildFaceletRecord(FaceletCacheKey key, URL url)
{
try
{
MemberFactory<Facelet> factory = getFactory(key);
Facelet facelet = factory.newInstance(url);
long lastModified = URLUtils.getLastModified(url);
FaceletRecord record = new FaceletRecord(facelet, lastModified);
return record;
}
catch(IOException e)
{
throw new FacesException(e.getMessage(), e);
}
}
protected FaceletRecord checkFaceletRecord(FaceletCacheKey key, URL url, FaceletRecord record)
{
return record;
}
protected Map<URL, FaceletRecord> getLocalCache(FaceletCacheKey key)
{
if(key == FaceletCacheKey.MEMBER)
{
return memberCache;
}
if(key == FaceletCacheKey.METADATA)
{
return metadataCache;
}
throw new IllegalArgumentException();
}
protected MemberFactory<Facelet> getFactory(FaceletCacheKey key)
{
if(key == FaceletCacheKey.MEMBER)
{
return getMemberFactory();
}
if(key == FaceletCacheKey.METADATA)
{
return getMetadataMemberFactory();
}
throw new IllegalArgumentException();
}
@Override
public Facelet getFacelet(URL url) throws IOException
{
return getFacelet(FaceletCacheKey.MEMBER, url);
}
@Override
public Facelet getViewMetadataFacelet(URL url) throws IOException
{
return getFacelet(FaceletCacheKey.METADATA, url);
}
@Override
public boolean isFaceletCached(URL url)
{
return isCached(FaceletCacheKey.MEMBER, url);
}
@Override
public boolean isViewMetadataFaceletCached(URL url)
{
return isCached(FaceletCacheKey.METADATA, url);
}
public void clearFacelets()
{
getLocalCache(FaceletCacheKey.MEMBER).clear();
}
public void clearViewMetadataFacelets()
{
getLocalCache(FaceletCacheKey.METADATA).clear();
}
public void clearAll()
{
clearViewMetadataFacelets();
clearFacelets();
}
}
public static class UnlimitedFaceletCache extends ShapeFaceletCache
{
public UnlimitedFaceletCache()
{
super();
}
}
public static class DevelopmentFaceletCache extends ShapeFaceletCache
{
public DevelopmentFaceletCache()
{
super();
}
@Override
protected FaceletRecord checkFaceletRecord(FaceletCacheKey key, URL url, FaceletRecord record)
{
try
{
Set<URL> urls = (Set<URL>) FacesContext.getCurrentInstance()
.getAttributes()
.computeIfAbsent(key, x -> new HashSet<>());
if(urls.add(url))
{
long lastModified = URLUtils.getLastModified(url);
if(lastModified != record.getLastModified())
{
return null;
}
}
return record;
}
catch(IOException e)
{
throw new FacesException(e.getMessage(), e);
}
}
}
public static class ExpiringFaceletCache extends ShapeFaceletCache
{
protected final long period;
public ExpiringFaceletCache(long period)
{
super();
this.period = period;
}
@Override
protected FaceletRecord checkFaceletRecord(FaceletCacheKey key, URL url, FaceletRecord record)
{
try
{
long now = System.currentTimeMillis();
if(now > record.getLastChecked() + period)
{
long lastModified = URLUtils.getLastModified(url);
if(lastModified != record.getLastModified())
{
return null;
}
record.setLastChecked(now);
}
return record;
}
catch(IOException e)
{
throw new FacesException(e.getMessage(), e);
}
}
}
public static class FaceletRecord
{
protected final Facelet facelet;
protected final long lastModified;
protected long lastChecked;
public FaceletRecord(Facelet facelet, long lastModified)
{
this.facelet = facelet;
this.lastModified = lastModified;
lastChecked = System.currentTimeMillis();
}
public long getLastModified()
{
return lastModified;
}
public Facelet getFacelet()
{
return facelet;
}
public long getLastChecked()
{
return lastChecked;
}
public void setLastChecked(long lastChecked)
{
this.lastChecked = lastChecked;
}
}
public static enum FaceletCacheKey
{
MEMBER,
METADATA;
@Override
public String toString()
{
return getClass().getName() + "." + name();
}
}
public static class URLUtils
{
public static long getLastModified(URL url) throws IOException
{
URLConnection urlConnection = url.openConnection();
if(urlConnection instanceof JarURLConnection)
{
JarURLConnection jarUrlConnection = (JarURLConnection) urlConnection;
URL jarFileUrl = jarUrlConnection.getJarFileURL();
return getLastModified(jarFileUrl);
}
try(InputStream input = urlConnection.getInputStream())
{
return urlConnection.getLastModified();
}
}
}
}
Mac OS X: Yosemite 10.10.5
NetBeans8.1beta or NetBeans8.1
Glassfish4.1 or Glassfish4.1.1
Mojarra 2.2.7 or 2.2.12 [2016-08-14 EDIT: or 2.2.8-17]
[EDIT: Primefaces 5.3]
Soy un desarrollador experimentado de NetBeans + JSF, lo que quiere decir que sé cómo se supone que debe funcionar, y por lo general funciona, pero esto es por alguna razón que ya no funciona correctamente, en uno (y solo uno por lo que puedo decir) Máquina MacBook Pro [EDIT: 2016-08-14 y también en MacMini con la misma versión de OS X].
Breve descripción del problema: Hace unos días, mientras desarrollaba felizmente una gran aplicación web JSF / Primefaces, descubrí que después de un par de recargas de páginas JSF / Primefaces complejas en las que estaba trabajando, dejaba de actualizar / reflejar los cambios que hice (y guardado) en componentes compuestos. Sin embargo, encontré que si esperaba algunos minutos, podía realizar la recarga de nuevo, de acuerdo, algunas veces, reflejando los cambios CC, hasta que "se atascó" nuevamente.
Sucede, hasta donde puedo decir, solo en mi máquina principal de desarrollo que es una MacBook Pro 15 "(macbookpro11,3 Mid2014.).
[EDIT: 2016-08-14 Ahora reproducido también en un Mac201 Mid2010 que ejecuta la misma versión de OS X y ejecuta una versión * copiada (ligeramente) adaptada de la misma configuración de NetBeans / GlassFish NB8.1Beta / GF4.1, y con JSF 2.2.8-17]
No parece importar si:
Yo uso NetBeans-8.1beta / Glassfish4.1 o NetBeans8.1 / Glassfish4.1.1 [ASEO: la razón por la que estoy usando principalmente NB8.1beta / GF4.1 no NB8.1 / GF4.1.1 se explica en: https: // stackoverflow.com/questions/35681181/jsfobjectdb-why-might-deployment-of-a-large-web-app-to-glassfish-4-1-1-take-5]
Utilizo una instalación completamente nueva de NetBeans + Glassfish o una existente.
Uso JDK1.7 (jdk1.7.0_51.jdk) o JDK1.8 (jdk1.8.0_60.jdk) (incluso para NetBeans / Glassfish y / o para la compilación y ejecución de código fuente).
Utilizo un proyecto que involucra a Git (el problema ocurrió por primera vez en un proyecto grande, pero lo he reproducido en el más simple de los proyectos sin Git, es decir, tiene algo que ver con lo que está sucediendo detectando cambios de facelets en / build / web / )
Uso Primefaces o no (puedo conseguir que suceda en una aplicación JSF muy básica).
Uso una recarga de GET limpia o una recarga de comandos del navegador.
Pero NO ocurre, hasta donde puedo decir, con una configuración casi idéntica en una MacMini anterior (macmini4,1 Mid2010).
[EDIT: 2016-08-14 Sí, sí ocurre en MacMini también si recargo las páginas JSF con la suficiente frecuencia en la aplicación web completa y grande que estoy desarrollando, no solo una mini aplicación de prueba]
Algunas otras cosas que creo que sé sobre esto:
Esto es con la función Implementar al guardar desactivada en todos los casos.
No parece afligir a las plantillas JSF o incluye, solo parece afectar a los componentes compuestos.
No es un problema con javax.faces.FACELETS_REFRESH_PERIOD (que de forma predeterminada para mojarra es 2). Si lo cambio a 0, el problema desaparece (no hay almacenamiento en caché) pero los tiempos de carga / recarga de páginas JSF grandes y complejas se vuelven dolorosos, en algunos casos, minutos en lugar de segundos.
Pasar de una página JSF a otra no ayuda.
No importa qué ámbito de JSF utilizo.
Sucede con una aplicación implementada en / build / web.
Las marcas de tiempo de los archivos XHTML modificados para los componentes compuestos están cambiando definitivamente a medida que los guardo en NetBeans (se están copiando correctamente en / build / web / resources / ...).
No he realizado ninguna actualización o instalación de software del sistema operativo durante muchos días.
Hice screencasts (no disponible aquí) de todo el problema como se informa a continuación.
Experiencia con la aplicación web muy grande original
La primera vez que me encontré con el problema fue en una aplicación web muy grande. Lo noté con un pequeño componente compuesto que genera texto con una clase de estilo (para un icono), que se utilizó CC dentro de ap: acorordionPanel y p: tab. Descubrí que después de volver a cargar los cambios un par de veces, dejaba de detectar los cambios. Fue solo por accidente que descubrí que si esperaba muchos minutos, a veces hasta 10 minutos, entonces "captaba" el cambio.
Luego volví en mis compromisos unos días, a un tiempo en el que claramente pude desarrollarme sin ningún problema, ¡y el problema volvió a ocurrir! Lo he probado muchas veces, sea cual sea el problema, no está en la confirmación .git (que incluye / nbproject / private pero no todas las subcarpetas de / nbproject / private).
Experiencia con una aplicación web de prueba Primefaces más pequeña
Luego probé con una aplicación web de prueba mucho más pequeña con algunas páginas de prueba Primefaces. Pude reproducir el problema si volví a cargar la página index.xhtml varias veces, mientras cambiaba un pequeño componente compuesto de una línea de implementación utilizado en la página index.html. Luego descubrí que tenía que esperar unos 10 segundos o, a veces, todo un minuto, y luego el cambio volvería a "atrapar".
Experiencia con una pequeña aplicación web básica JSF
Con un index.xhtml y un solo componente compuesto con una sola palabra h: outputText, podría obtener el problema si guardara el CC y luego volviera a cargar index.xhtml muy rápidamente. No estoy hablando de que no parezca cambiar (porque uno "venció" a javax.faces.FACELETS_REFRESH_PERIOD) Estoy hablando de "cerrar" para que no capte el cambio en el CC después de eso, no importa con qué frecuencia uno recarga la página, hasta que el fantasma en la máquina decide "desbloquear" a sí mismo.
Normalmente, proporcionaría un ejemplo o ''Pasos para reproducir el problema'', pero tiene poco sentido hacerlo; cuando muevo el proyecto de prueba de una máquina (mi MacBook Pro) a otra (la MacMini que ejecuta la misma versión de SO), el problema desaparece. Y puedo lograrlo (en mi máquina de desarrollo MacBook Pro principal) con la aplicación web NetBeans JSF más simple posible con un index.xhtml que incluye un solo CC.
[EDITAR: 2016-08-14 Puedo reproducirlo en MacMini que ejecuta la misma versión del sistema operativo, pero solo pude reproducirlo hasta ahora con la gran aplicación web que estoy desarrollando, que no se puede proporcionar fácilmente a otros para prueba (y necesitaría, por ejemplo, quitar la dependencia de la base de datos ObjectDB y proporcionar datos ficticios)]
Me doy cuenta de que normalmente uno hace una sola pregunta sobre Stackoverflow, pero las respuestas a cualquiera de ellas, que podrían ayudarme a seguir adelante, serían apreciadas:
Q0: ¿Alguien ha experimentado algo similar (en una Mac)?
P1: ¿Qué más puedo tratar de diagnosticar? Se me acabaron las ideas.
Q2: ¿Alguien sabe de algo específico para un MacBook Pro que pueda afectar la votación / detección de cambios en las carpetas de compilación / web que podrían explicarlo?
P3: ¿Hay algo sobre cómo funcionan Facelets y / o Glassfish junto con una aplicación implementada en / build / web que podría explicarlo?