Documente Academic
Documente Profesional
Documente Cultură
9-2
Estructura de src/main (1)
pojo-minibank
src/main
java
es.udc.pojo.minibank
model
web
components
pages
accounts
preferences
search
services
util
(Continúa en la siguiente transparencia)
9-3
Estructura de src/main (y 2)
pojo-minibank
src/main
resources
es/udc/pojo/minibank/web
components
pages
accounts
preferences
search
org/apache/tapestry5
corelib
webapp
pages
WEB-INF
9-4
Implementación del layout de una aplicación Web (1)
9-5
Implementación del layout de una aplicación Web (2)
Contenido
Pie de página
9-6
Implementación del layout de una aplicación Web (3)
n Además, en MiniBank
n El menú y el pie de página son iguales en todas las páginas
n El título y el contenido son específicos en cada página
n Con un enfoque básico, la plantilla de cada página
tendría que contener
n El markup que define el layout => ¡el mismo en todas las
páginas!
n El markup de las áreas comunes => ¡el mismo en todas
las páginas!
n El markup de las áreas específicas
n Este enfoque básico conduce a una solución difícil de
mantener
9-7
Implementación del layout de una aplicación Web (4)
9-8
Implementación del layout de una aplicación Web (5)
9-9
Implementación del layout de una aplicación Web (6)
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
t:type="Layout" t:title="title" showTitleInBody="false">
<p class="text-center">${message:welcome}</p>
</html>
n Como se verá en las siguientes transparencias, el parámetro title se ha
definido con prefijo por defecto message
n Ejemplo: Index.properties
9 - 10
Implementación del layout de una aplicación Web: Layout.java (7)
package es.udc.pojo.minibank.web.components;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Property;
@Import(library = {"tapestry5/bootstrap/js/collapse.js",
"tapestry5/bootstrap/js/dropdown.js"},
stylesheet="tapestry5/bootstrap/css/bootstrap-theme.css")
public class Layout {
@Property
@Parameter(required = true, defaultPrefix = "message")
private String title;
@Parameter(defaultPrefix = "literal")
private Boolean showTitleInBody;
9 - 11
Implementación del layout de una aplicación Web: Layout.java (8)
if (showTitleInBody == null) {
return true;
} else {
return showTitleInBody;
}
9 - 12
Implementación del layout de una aplicación Web: Layout.java (9)
métodos)
n required: indica si el parámetro es obligatorio u opcional (false
por defecto)
n defaultPrefix: indica el prefijo por defecto
n En el ejemplo, el parámetro showTitleInBody permite
especificar si debe imprimir el título de la página
(parámetro title) al principio del área de contenido
n @Import
n Permite importar JavaScript y CSS
n La inclusión de los correspondientes ficheros ocurre en la fase de
renderización
9 - 13
Implementación del layout de una aplicación Web: Layout.java (10)
n @Import (cont)
n En el ejemplo se está importando JavaScript y CSS de Bootstrap
n collapse.js: requerido para que el menú (.navbar-collapse) se
“encoja” cuando el tamaño del viewport se estrecha
9 - 14
Implementación del layout de una aplicación Web: Layout.tml (11)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
<head>
<meta charset="utf-8" />
<title>${title} - MiniBank</title>
...
</head>
<body>
9 - 15
Implementación del layout de una aplicación Web: Layout.tml (12)
<div class="container">
<t:if test="showTitleInBody">
<h3 class="text-center">${title}</h3>
<br/>
</t:if>
<t:body/>
<hr/>
<footer>
<p class="text-center">${message:footer}</p>
</footer>
</div>
</body>
</html>
9 - 16
Implementación del layout de una aplicación Web: Layout.tml (13)
9 - 17
Implementación del layout de una aplicación Web: Layout.properties (y
14)
9 - 18
Integración con Spring (1)
n Tapestry proporciona una librería de integración con
Spring que permite inyectar beans de Spring en la capa
Web
n web.xml
<?xml version="1.0" encoding="UTF-8"?>
<display-name>POJO-Examples MiniBank</display-name>
<context-param>
<param-name>tapestry.app-package</param-name>
<param-value>es.udc.pojo.minibank.web</param-value>
</context-param>
9 - 19
Integración con Spring (2)
n web.xml (cont)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/pojo-minibank-spring-config.xml
</param-value>
</context-param>
...
<filter>
<filter-name>app</filter-name>
<filter-class>
org.apache.tapestry5.spring.TapestrySpringFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>app</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
9 - 20
Integración con Spring (3)
n web.xml (cont)
n Con respecto a la configuración estudiada en el tema 8
n Se ha hecho un cambio específico a Tapestry: reemplazar el
filtro estándar (TapestryFilter) por el filtro
TapestrySpringFilter
n Extiende a TapestryFilter, añadiendo la posibilidad de
inyectar servicios Spring en la capa Web
n Se ha hecho un cambio relativo al uso de Spring con cualquier
framework Web: se ha definido el parámetro
contextConfigLocation
n Especifica la ubicación del fichero de configuración de Spring
(valor por defecto: /WEB-INF/applicationContext.xml)
9 - 21
Integración con Spring (y 4)
n @org.apache.tapestry5.ioc.annotations.Inject
n Permite inyectar un bean de Spring, servicios de Tapestry y
algunos objetos especiales
n Generalmente se aplica sobre un atributo de una clase
página/componente
n Por defecto, tiene una semántica similar a la anotación
@Autowired de Spring, es decir, se inyecta un bean que tenga el
mismo tipo que el atributo anotado
@Inject
private AccountService accountService;
@Inject
@Service("mockAccountService")
private AccountService accountService;
9 - 22
Componentes Select, Loop y PageLink (1)
UserAccounts
9 - 23
Componentes Select, Loop y PageLink: FindAccounts.tml (2)
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
t:type="Layout" t:title="title">
<t:errors/>
<div class="form-group">
<t:label for="id" class="col-md-offset-3 col-md-2"/>
<div class="col-md-3">
<input t:type="TextField" t:id="id"
t:validate="required, min=0" size="16" maxlength="16"/>
</div>
</div>
<div class="form-group">
<t:label for="accountSearchType" class="col-md-offset-3 col-md-2"/>
<div class="col-md-3">
<t:select t:id="accountSearchType" validate="required"/>
</div>
</div>
9 - 24
Componentes Select, Loop y PageLink: FindAccounts.tml (3)
<div class="form-group">
<div class="col-md-offset-5 col-md-1">
<button type="submit" class="btn btn-
primary">${message:button-find}</button>
</div>
</div>
</form>
</html>
9 - 25
Componentes Select, Loop y PageLink: FindAccounts.tml (4)
n Componente Select
n Genera el elemento select de HTML y sus opciones
(elementos option)
n Los parámetros t:id y validate son idénticos a los de
otros componentes correspondientes a campos de entrada
de un formulario (e.g. TextField)
n En este ejemplo, t:id está haciendo referencia a la propiedad
accountSearchType de la clase página (FindAccounts)
n La propiedad debe tener los métodos get y set para acceder
9 - 26
Componentes Select, Loop y PageLink: FindAccounts.tml (5)
9 - 27
Componentes Select, Loop y PageLink: FindAccounts.java (6)
@Property
private Long id;
@Property
private AccountSearchType accountSearchType;
@InjectPage
private AccountDetails accountDetails;
@InjectPage
private UserAccounts userAccounts;
9 - 28
Componentes Select, Loop y PageLink: FindAccounts.java (7)
Object onSuccess() {
if (accountSearchType==AccountSearchType.ACC_ID) {
accountDetails.setAccountId(id);
return accountDetails;
} else {
userAccounts.setUserId(id);
return userAccounts;
}
9 - 29
Componentes Select, Loop y PageLink: UserAccounts.tml (8)
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter"
t:type="Layout" t:title="title">
<t:if test="accounts">
<thead>
<tr>
<th>${message:accountId-label}</th>
<th>${message:balance-label}</th>
</tr>
</thead>
9 - 30
Componentes Select, Loop y PageLink: UserAccounts.tml (9)
<tbody>
<tr t:type="Loop" t:source="accounts" t:value="account">
<td>
<a href="#" t:type="PageLink"
t:page="search/accountdetails"
t:context="account.accountId">
${account.accountId}
</a>
</td>
<td><t:output value="account.balance" format="format"/></td>
</tr>
El componente Output se
</tbody>
explica más adelante como
parte del soporte de
</table> internacionalización que
ofrece Tapestry
9 - 31
Componentes Select, Loop y PageLink: UserAccounts.tml (10)
<!-- "Previous" and "Next" links. -->
<ul class="pager">
<t:if test="previousLinkContext">
<li>
<a href="#" t:type="PageLink" t:page="search/useraccounts"
t:context="previousLinkContext">←
${message:link-previous}</a>
</li>
</t:if>
<li> </li>
<t:if test="nextLinkContext">
<li>
<a href="#" t:type="PageLink" t:page="search/useraccounts"
t:context="nextLinkContext">${message:link-next}
→</a>
</li>
</t:if>
</ul>
9 - 32
Componentes Select, Loop y PageLink: UserAccounts.tml (11)
<p:else>
<h4 class="alert alert-danger text-center">${message:noAccounts}</h4>
</p:else>
</t:if>
</html>
9 - 33
Componentes Select, Loop y PageLink: UserAccounts.tml (12)
9 - 34
Componentes Select, Loop y PageLink: UserAccounts.tml (13)
9 - 35
Componentes Select, Loop y PageLink: UserAccounts.tml (14)
@Inject
private AccountService accountService;
@Inject
private Locale locale;
9 - 37
Componentes Select, Loop y PageLink: UserAccounts. java (16)
public List<Account> getAccounts() {
return accountBlock.getAccounts();
}
Object[] onPassivate() {
return new Object[] {userId, startIndex};
}
9 - 39
Componentes Select, Loop y PageLink: UserAccounts. java (18)
n onPassivate
n Cuando el contexto de activación está formado por más de
un dato, tiene que devolver un Object[]
n onActivate
n Cuando el contexto de activación está formado por más de
un dato, puede recibir un Object[], o alternativamente,
recibir tantos parámetros como datos hay en el contexto de
activación (Tapestry hace automáticamente el cast al tipo
del parámetro)
n Componente Grid
n Alternativamente, para mostrar las cuentas, se podría haber
usado el componente Grid
n Permite mostrar un conjunto de items en una tabla
n Soporta una versión avanzada del patrón Page-by-Page
iterator, que incluye enlaces a cada página
9 - 40
Componentes Select, Loop y PageLink (y 19)
,00
9 - 41
Evento validate en formularios (1)
Si existe
Si no existe
SuccessfulOperation
RemoveAccount
9 - 42
Evento validate en formularios: RemoveAccount.tml (2)
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
t:type="Layout" t:title="title">
</html>
9 - 43
Evento validate en formularios: RemoveAccount.tml (3)
9 - 44
Evento validate en formularios: RemoveAccount.java (4)
@Property
private Long accountId;
@Component
private Form removeAccountForm;
@Component(id="accountId")
private TextField accountIdTextField;
@Inject
private Messages messages;
@Inject
private AccountService accountService;
9 - 45
Evento validate en formularios: RemoveAccount.java (5)
void onValidateFromRemoveAccountForm () {
if (!removeAccountForm.isValid()) {
return;
}
try {
accountService.removeAccount(accountId);
} catch (InstanceNotFoundException e) {
removeAccountForm.recordError(accountIdTextField,
messages.format("error-accountNotFound", accountId));
}
Object onSuccess() {
return SuccessfulOperation.class;
}
9 - 46
Evento validate en formularios: RemoveAccount.java (6)
n @org.apache.tapestry5.annotations.Compo
nent
n Permite definir un componente dentro de otro o de una
página
n id: especifica el identificador del componente (por defecto,
su valor es el nombre del atributo anotado)
n En RemoveAccount, @Component se está utilizando para
definir un formulario (Form) y un campo de entrada
(TextField) dentro de la página
n En ejemplos anteriores esto no fue necesario: los
componentes que necesitaba una página se especificaban en
la propia plantilla
n Sin embargo, cuando desde el código de la clase página se
desea manipular un componente, es necesario definirlo en la
propia clase de la página
9 - 47
Evento validate en formularios: RemoveAccount.java (7)
9 - 48
Evento validate en formularios: RemoveAccount.java (8)
9 - 49
Evento validate en formularios (y 9)
9 - 50
Internacionalización, Application Module Builder y evento
prepareForRender (1)
9 - 51
Internacionalización, Application Module Builder y evento
prepareForRender (2)
9 - 53
Internacionalización, Application Module Builder y evento
prepareForRender (4)
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.MappedConfiguration;
configuration.add(SymbolConstants.SUPPORTED_LOCALES,
"en,es,gl");
configuration.add(
SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER,
"jquery");
}
9 - 54
Internacionalización, Application Module Builder y evento
prepareForRender (5)
9 - 55
Internacionalización, Application Module Builder y evento
prepareForRender (6)
9 - 56
Internacionalización, Application Module Builder y evento
prepareForRender (7)
9 - 57
Internacionalización, Application Module Builder y evento
prepareForRender (8)
9 - 58
Internacionalización, Application Module Builder y evento
prepareForRender (9)
9 - 59
Internacionalización, Application Module Builder y evento
prepareForRender (10)
locale es locale en
9 - 60
Internacionalización, Application Module Builder y evento
prepareForRender (11)
9 - 61
Internacionalización, Application Module Builder y evento
prepareForRender (12)
9 - 62
Internacionalización, Application Module Builder y evento
prepareForRender (13)
locale es locale en
9 - 63
Internacionalización, Application Module Builder y evento
prepareForRender (14)
@Property
private String balance;
@Component
private Form createAccountForm;
@Component(id="balance")
private TextField balanceTextField;
@Inject
private Locale locale;
@Inject
private Messages messages;
9 - 64
Internacionalización, Application Module Builder y evento
prepareForRender (15)
@InjectPage
private AccountCreated accountCreated;
void onValidateFromCreateAccountForm() {
if (!createAccountForm.isValid()) {
return;
}
if (balanceAsBigDecimal == null) {
createAccountForm.recordError(balanceTextField,
messages.format("error-incorrectNumberFormat",
balance));
return;
} 9 - 65
Internacionalización, Application Module Builder y evento prepareForRender (16)
Account account =
new Account(userId, balanceAsBigDecimal);
try {
accountService.createAccount(account);
accountCreated.setAccountId(account.getAccountId());
} catch (MaxBalanceExceededException e) {
createAccountForm.recordError(messages.format(
"error-maxBalanceExceededForNewAccount"));
} catch (InsufficientAmountException e) {
createAccountForm.recordError(messages.format(
"error-insufficientBalanceForNewAccount",
BigDecimalUtils.getFormat(locale,
2).format(e.getMinValue())));
}
}
9 - 66
Internacionalización, Application Module Builder y evento
prepareForRender (17)
9 - 67
Internacionalización, Application Module Builder y evento
prepareForRender (18)
9 - 68
Internacionalización, Application Module Builder y evento
prepareForRender (19)
9 - 69
Internacionalización, Application Module Builder y evento
prepareForRender (20)
9 - 70
Internacionalización, Application Module Builder y evento
prepareForRender (21)
core-default-error-banner
core-goto-page
,00
9 - 71
Internacionalización, Application Module Builder y evento
prepareForRender (22)
min-integer
9 - 72
Internacionalización, Application Module Builder y evento
prepareForRender (23)
9 - 73
Internacionalización, Application Module Builder y evento
prepareForRender (24)
9 - 74
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.tml (25)
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
t:type="Layout" t:title="title">
<div class="form-group">
<t:label for="language" class="col-md-offset-3 col-md-2"/>
<div class="col-md-3">
<t:select t:id="language" model="languages" validate="required"/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-5 col-md-1">
<button type="submit" class="btn btn-primary">${message:button-
apply}</button>
</div>
</div>
</form>
</html>
9 - 75
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.tml (26)
n Componente Select
n Anteriormente se describió un uso del componente Select basado
en especificar en t:id el nombre de una propiedad enumerada
n En principio, se podría haber definido un enumerado al estilo
n public enum LanguageType {en, es, gl}
n Y proporcionar los valores visuales en
SelectLanguage[{_es,_gl}].properties
n Aunque en el caso de MiniBank sería una opción perfectamente
asumible, se ha decidido complicar la solución para que la lista de
idiomas del desplegable se muestre ordenada según el idioma
actualmente seleccionado, tal y como ilustran las pantallas
anteriores
n Esta solución es imprescindible si la lista de elementos del desplegable
es extensa
n La opción de usar un enumerado tampoco es válida cuando la lista
de elementos a mostrar es dinámica (e.g. se leen de una BD)
9 - 76
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.tml (27)
9 - 77
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.java (28)
@Property
private String language;
@Inject
private Locale locale;
@Inject
private PersistentLocale persistentLocale;
void onPrepareForRender() {
language = locale.getLanguage();
}
9 - 78
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.java (29)
Object onSuccess() {
persistentLocale.set(new Locale(language));
return Index.class;
}
n Evento prepareForRender
n Un formulario antes de renderizarse, emite el evento
prepareForRender
n En SelectLanguage, se captura este evento
(onPrepareForRender) para dar valor automáticamente al
atributo language
n Se establece el código del idioma correspondiente al locale actual
n Este ejemplo ilustra una solución a un problema muy frecuente:
mostrar un formulario con los valores actualmente seleccionados
n En MiniPortal, ocurre lo mismo en la página UpdateProfile
n En esta página también se captura este evento: se invoca al caso de
uso que devuelve el perfil del usuario y se inicializan los atributos
correspondientes a los campos del formulario
9 - 79
Internacionalización, Application Module Builder y evento
prepareForRender: SelectLanguage.java (30)
n org.apache.tapestry5.services.PersistentLocale
n Servicio proporcionado por Tapestry
n Permite establecer el locale mediante el método set
n Una vez invocado el método set, Tapestry codifica el identificador
del locale seleccionado en las URLs
n Formato: http://.../<nombre-aplicación>/<locale>/...
n Ejemplo: http://localhost:9090/pojo-minibank/en/findaccounts
9 - 80
Internacionalización, Application Module Builder y evento
prepareForRender: SupportedLanguages (31)
n SupportedLanguages
n La clase SelectLanguage implementa el método
getLanguages apoyándose en la clase
SupportedLanguages
n El método getOptions(Locale) de
SupportedLanguages devuelve la lista de idiomas
(ordenada) en el locale pasado como parámetro y con el
formato esperado por el parámetro model del componente
Select
9 - 81
Internacionalización, Application Module Builder y evento
prepareForRender: SupportedLanguages (32)
private SupportedLanguages() {}
codes = "en,es,gl";
9 - 82
Internacionalización, Application Module Builder y evento
prepareForRender: SupportedLanguages (33)
if (languages != null) {
return languages;
} else {
return options.get("en");
}
9 - 83
Internacionalización, Application Module Builder y evento
prepareForRender: SupportedLanguages (34)
n SupportedLanguages (cont)
n Y en AppModule (versión 2)
n Versión actual en el código de MiniBank
SupportedLanguages.initialize();
configuration.add(SymbolConstants.SUPPORTED_LOCALES,
SupportedLanguages.getCodes());
configuration.add(
SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER,
"jquery");
9 - 84
Internacionalización, Application Module Builder y evento
prepareForRender: SupportedLanguages (35)
n SupportedLanguages (cont)
n Actualmente, la clase SupportedLanguages contiene la
lista de idiomas para cada locale soportado
n En un escenario más realista, los códigos de los idiomas y
sus nombres respectivos seguramente se leerían de la BD
n En este caso, el método
SupportedLanguages::initialize crearía
dinámicamente el mapa options en función de los valores
leídos de la BD
n options actuaría como caché de sólo lectura
n Para leer los valores de la BD, sería preciso invocar un servicio
del modelo (e.g. LanguageService)
n Es posible inyectar servicios como parámetros en el método
contributeApplicationDefaults de la clase Application
Module Builder (AppModule en el ejemplo)
9 - 85
Internacionalización, Application Module Builder y evento
prepareForRender: AppModule (y 36)
SupportedLanguages.initialize(languageService);
configuration.add(SymbolConstants.SUPPORTED_LOCALES,
SupportedLanguages.getCodes());
configuration.add(
SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER,
"jquery");
9 - 86
Tratamiento de excepciones “unchecked” no esperadas (1)
9 - 87
Tratamiento de excepciones “unchecked” no esperadas (2)
9 - 88
Tratamiento de excepciones “unchecked” no esperadas (3)
configuration.add(SymbolConstants.PRODUCTION_MODE, "false");
<context-param>
<param-name>tapestry.production-mode</param-name>
<param-value>false</param-value>
</context-param>
9 - 89
Tratamiento de excepciones “unchecked” no esperadas (4)
Modo desarrollo
Modo
producción
9 - 90
Tratamiento de excepciones “unchecked” no esperadas (5)
9 - 91
Tratamiento de excepciones “unchecked” no esperadas (y 6)
Modo desarrollo
Modo
producción
9 - 92
[Recordatorio] Uso del DataSource proporcionado por el servidor de aplicaciones
...
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
p:jndiName="${dataSource.jndiName}"
p:resourceRef="true" />
<bean id="sessionFactory"
class=
"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="classpath:/pojo-minibank-hibernate-config.xml"/>
...
9 - 93
Habilitación del patrón Open Session in View (1)
9 - 94
Habilitación del patrón Open Session in View: web.xml (2)
...
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>
org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter>
<filter-name>app</filter-name>
<filter-class>
org.apache.tapestry5.spring.TapestrySpringFilter
</filter-class>
</filter>
9 - 95
Habilitación del patrón Open Session in View: web.xml (3)
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>app</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
9 - 96
Habilitación del patrón Open Session in View: web.xml (4)
n OpenSessionInViewFilter
n Los filtros pueden tener parámetros de configuración (tags
init-param)
n Entre otros, dispone del parámetro
sessionFactoryBeanName
n Indica el nombre del bean SessionFactory declarado en el
fichero de configuración de Spring
n Por defecto asume el nombre sessionFactory (por tanto, en
este caso no sería necesario especificarlo, dado que coincide
con el nombre que se le dio en el fichero de configuración de
Spring)
n Encadenamiento de filtros
n El orden en el que aparecen los tags filter-mapping en
web.xml determina el orden en el que se aplicarán los
filtros
9 - 97
Habilitación del patrón Open Session in View: web.xml (5)
9 - 98
Habilitación del patrón Open Session in View: web.xml (6)
9 - 99
Habilitación del patrón Open Session in View: web.xml (7)
n Solución 2
n Emplear un proxy (LazyConnectionDataSourceProxy) del
DataSource real que retrase la petición de una conexión al
DataSource real hasta que se intente lanzar una consulta
n En src/main/resources/pojo-minibank-spring-config.xml
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
p:jndiName="jdbc/pojo-examples-ds"
p:resourceRef="true" />
<bean id="dataSourceProxy"
class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"
p:targetDataSource-ref="dataSource"/>
9 - 100
Habilitación del patrón Open Session in View: web.xml (y 8)
n Solución 2 (cont)
n De esta forma, aunque cada petición HTTP abre una sesión
de Hibernate, sólo aquellas que acceden a la capa modelo
terminan consumiendo una conexión del pool
n pojo-minibank y pojo-miniportal
n El código actual no requiere que el patrón Open Session in
View esté habilitado
n pojo-mini{bank,portal}-spring-config.xml y
web.xml incluyen secciones comentadas para facilitar la
habilitación de este patrón en caso necesario
n pojo-advhibernatetut
n pojo-advhibernatetut-spring-config.xml y
web.xml habilitan el patrón Open Session in View
9 - 101
Minimización, compresión y caché
n Minimización
n MiniBank y MiniPortal incluyen la librería tapestry-webresources entre
sus dependencias
n Cuando se incluye esta librería y el valor de tapestry.production-
mode es true, Tapestry minimiza los recursos CSS y JavaScript
n Compresión
n Tapestry comprime las respuestas automáticamente mediante GZIP si el
cliente lo solicita (que es lo habitual) y tiene sentido
n No realiza compresión si la respuesta es muy pequeña o si el recurso solicitado ya
estaba comprimido (e.g. una imagen PNG)
n Caché de recursos estáticos
n Los recursos estáticos (CSS, JavaScript, imágenes, etc.) se deben
referenciar en las plantillas con el prefijo asset o context
n <img src="${context:images/tapestry_banner.gif}"
alt="Banner"/>
n En las respuestas, Tapestry indicará que son cachebles
n Más información
n http://tapestry.apache.org/assets.html
n http://tapestry.apache.org/response-compression.html
9 - 102