· Web viewAERMC Manuel du développeur. Framework de l’agence de l’eau. Auteur : Rémi...
Transcript of · Web viewAERMC Manuel du développeur. Framework de l’agence de l’eau. Auteur : Rémi...
Date : 11/09/2023
Manuel du développeur
AERMC
Manuel du développeurFramework de l’agence de l’eau
Auteur : Rémi BERTHET Vérificateur : Jérôme PAIRE
Mise à jourVersion Date Nature de la modification
1.0 Novembre 2014 Evolutions pour industrialisation1.1 Aout 2015 Evolutions pour éditions BDOC1.2 Octobre 2016 Evolutions liées au développement de l’application QEE [BAO
V3.1] Navigation en Mode « mono-onglet » File d’Ariane Conservations des critères Import de fichier CSV (librairie OpenCSV) Export de fichier CSV Déclaration des beans Spring en annotation Masquage des colonnes dans les tableaux de résultats Zone de texte extensible
1.3 Mars 2017 Précisions sur les évolutions de la BAO liées à QEE [BAO V3.1]
Date : 11/09/2023
Manuel du développeur
DiffusionPour Agence de l’eau
Réf. : AERMC- Guide développeur V1.2
Date : 11/09/2023
Manuel du développeur
SOMMAIRE1. Architecture globale du framework................................................................................................52. Evolutions apportées dans la version 3.1 / Généralisation.............................................................53. Configuration Spring.......................................................................................................................5
3.1. Préambule..............................................................................................................................53.2. XML.........................................................................................................................................5
3.2.1. Beans Spring...................................................................................................................63.2.2. Tiles / Ressources statiques............................................................................................63.2.3. Données..........................................................................................................................73.2.4. Intercepteurs..................................................................................................................83.2.5. Batchs.............................................................................................................................8
3.3. Annotations JAVA...................................................................................................................83.3.1. Classe de configuration référente...................................................................................93.3.2. Tiles...............................................................................................................................103.3.3. Données........................................................................................................................113.3.4. Batchs...........................................................................................................................14
4. Controller Spring : AbstractListFicheController.............................................................................144.1. Scénario courant...................................................................................................................144.2. Exemples de méthodes génériques......................................................................................154.3. Héritages..............................................................................................................................16
4.3.1. IListeController.............................................................................................................164.3.2. IFicheController............................................................................................................17
4.4. Interfaçage des Controllers...................................................................................................184.4.1. IActionForm : pour les validations de formulaires........................................................184.4.2. IModelAttributes : pour les attributs Spring..................................................................184.4.3. IModelForm : pour les modèles associés aux formulaires............................................18
5. Mapping Hibernate......................................................................................................................195.1. Table sans « PK »..................................................................................................................195.2. Utilisation de séquence........................................................................................................195.3. Astuces « OneToMany ».......................................................................................................19
6. Repository JPA..............................................................................................................................206.1. Querydsl...............................................................................................................................20
7. Services métier : IModeleService<M>...........................................................................................207.1. Besoins Hibernate.................................................................................................................207.2. Exécution de procédure stockée..........................................................................................21
7.2.1. A l’aide d’un SimpleJdbcCall.........................................................................................227.2.2. A l’aide d’un JdbcTemplate...........................................................................................22
7.3. Exécution de requête SQL.....................................................................................................237.3.1. En SQL direct.................................................................................................................237.3.2. En HQL..........................................................................................................................24
8. Intégration de nouveaux tags.......................................................................................................248.1. FwkLabelTag.........................................................................................................................258.2. FwkOngletsTag.....................................................................................................................258.3. FwkErreurChampOngletTag..................................................................................................25
Date : 11/09/2023
Manuel du développeur
8.4. FwkPlusCriteresTag...............................................................................................................258.5. FwkUploadFileTag.................................................................................................................26
9. Introduction de nouvelles annotations.........................................................................................269.1. Contraintes de validation......................................................................................................26
9.1.1. Définition......................................................................................................................269.1.2. Utilisation......................................................................................................................27
9.2. Formatage de données.........................................................................................................279.2.1. Définition......................................................................................................................279.2.2. Déclaration dans la configuration Spring......................................................................299.2.3. Utilisation......................................................................................................................29
10. Externalisation de la connexion à la base de données..............................................................2910.1. Mode de connexion..........................................................................................................3010.2. Déclaration dans Tomcat..................................................................................................3010.3. Déclaration dans Spring....................................................................................................30
11. Définition des profils dans le LDAP...........................................................................................3111.1. Structure du LDAP.............................................................................................................3111.2. Requêtage dans le LDAP (service LdapDao)......................................................................3211.2.1. getDetailsFromNomUser(username)............................................................................3211.2.2. getDetailsFromNumint(numInt)....................................................................................3211.2.3. getProfilsUserApplication(username, codApp).............................................................3311.2.4. getProfilsApplication(codApp)......................................................................................3311.2.5. getApplications()...........................................................................................................33
12. Besoins spécifiques de l’Agence de l’Eau..................................................................................3312.1. Onglets applicatifs (jQuery)..............................................................................................33
12.1.1. Fonctionnement ‘léger’................................................................................................3312.1.2. Fonctionnement ‘complexe’.........................................................................................33
12.2. Onglets utilisateur (navigateur)........................................................................................3412.3. Mode de navigation..........................................................................................................34
12.3.1. Description....................................................................................................................3412.3.2. Fonctionnement du fil d’Ariane....................................................................................3512.3.3. Conservation des critères.............................................................................................3712.3.4. Paramétrage.................................................................................................................37
12.4. Export générique des tableaux de résultats.....................................................................3712.4.1. Problématiques.............................................................................................................3712.4.2. Principe de fonctionnement.........................................................................................3712.4.3. Prérequis Java...............................................................................................................3712.4.4. Application dans la JSP..................................................................................................38
12.5. Ecrans de suivi des Batchs................................................................................................3812.6. CSV....................................................................................................................................38
12.6.1. Imports.........................................................................................................................3912.6.2. Exports..........................................................................................................................40
12.7. Auto authentification avec Kerberos................................................................................4012.7.1. Paramétrage du serveur...............................................................................................4012.7.2. Paramétrage client.......................................................................................................4012.7.3. Création du fichier ‘keytab’...........................................................................................41
Date : 11/09/2023
Manuel du développeur
12.8. Utilisation courante..........................................................................................................4112.8.1. DataTables....................................................................................................................4112.8.2. Auto complétion...........................................................................................................4212.8.3. Popups..........................................................................................................................4212.8.4. Dialog jQuery................................................................................................................4312.8.5. Actions XHR..................................................................................................................4612.8.6. Menus...........................................................................................................................4612.8.7. Multi-select...................................................................................................................4712.8.8. Upload..........................................................................................................................48
13. Gestion des éditions avec BDOC...............................................................................................4813.1. JAXB..................................................................................................................................4813.2. Traitement asynchrone.....................................................................................................49
13.2.1. Exemple........................................................................................................................4913.2.2. Paramétrage du batch..................................................................................................50
13.3. Traitement synchrone......................................................................................................5013.3.1. Génération directe........................................................................................................5013.3.2. Génération via Web Service..........................................................................................51
14. Système de batchs....................................................................................................................5314.1. Fonctionnement...............................................................................................................53
14.1.1. Utilisation d’un lanceur.................................................................................................5414.2. Configuration XML............................................................................................................5414.3. Exemples de cas fréquents :.............................................................................................56
14.3.1. Export (génération) de fichier XML...............................................................................5614.3.2. Import de fichier XML...................................................................................................56
15. Bibliothèque logicielle JAVA.....................................................................................................5716. Bibliothèque JavaScript............................................................................................................58
16.1. Plugins jQuery utilisés.......................................................................................................5816.2. Descriptif du contenu des fichiers JS spécifiques..............................................................58
16.2.1. ajaxForm.js....................................................................................................................5816.2.2. cookies.js......................................................................................................................5916.2.3. datatable.js...................................................................................................................5916.2.4. dialogRecherche.js........................................................................................................6016.2.5. framework.js.................................................................................................................6016.2.6. jq-styled-input.js...........................................................................................................6116.2.7. jqueryWidgetFramework.js..........................................................................................6116.2.8. xhrUtils.js......................................................................................................................62
Date : 11/09/2023
Manuel du développeur
1. Architecture globale du framework
Voir le document « AERMC - Guide développeur V0 4.docx ».
2. Evolutions apportées dans la version 3.1 / Généralisation
Dans les paragraphes suivants, nous allons étudier quelles améliorations ont été ajoutées à la version initiale pour satisfaire aux besoins d’une industrialisation de cette BAO.
3. Configuration Spring
3.1. Préambule
Depuis la version 3 de Spring, il est possible de configurer une application basée sur ce framework à partir de classes JAVA (annotations) et non seulement des fichiers XML.
Dans le cadre de la BAO, la version initiale se basait sur une configuration XML. Au fur et à mesure des différentes évolutions de celle-ci, la configuration par annotation a été introduite afin de bénéficier un certain nombre de ses avantages :
Permet de généraliser une configuration sur plusieurs applications (sans dupliquer les fichiers XML)
Syntaxe vérifiée à la compilation plutôt qu'à l'exécution Complétion dans Eclipse sans plugin Import Java mettant en évidence les dépendances non présentes Refactoring facilité de la configuration Ajout de code possible lors de la déclaration de beans (ex : logs) Accélère le chargement du contexte applicatif Traite la configuration comme du code Navigation dans la configuration facilitée Mixité possible avec la configuration XML Plus besoin de connaitre les namespaces XML Syntaxe plus "moderne"
3.2. XML
Le fichier applicationContext.xml utilisé par défaut permet de déclarer un certain nombre de composants (Beans, vues, …) auprès de Spring.
Date : 11/09/2023
Manuel du développeur
Ce fichier de configuration XML doit être déclaré au niveau du web.xml de l’application, là où est déclarée la Servlet Spring.
<!-- Définition de la Servlet --><servlet>
<servlet-name>Framework Spring</servlet-name><servlet-class>
fr.eau.rmc.framework.web.servlet.FrameworkSpringDispatcherServlet</servlet-class><init-param>
<param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param><load-on-startup>1</load-on-startup>
</servlet>
3.2.1. Beans Spring
Ces beans sont des singletons qui peuvent être injectés dans n’importe quel autre bean Spring grâce à l’annotation @Autowired. Ces beans peuvent être catégorisés en 3 familles :
@Service : permet d’identifier le bean en tant que service @Controller : permet d’identifier le bean en tant contrôleur Spring MVC @Component : annotation générique pouvant s’appliquer sur n’importe quel bean.
Ces beans sont déclarés à l’aide de la balise <context:component-scan base-package=’…’ » qui permet de scanner les différents packages identifiés comme parent des beans Spring et ainsi les partager avec les autres beans.
<!-- Déclaration du package à scanner pour les Controller --><context:component-scan base-package="fr.eau.rmc.framework.web" />
<!-- Déclaration du package à scanner pour les Service Métier --><context:component-scan base-package="fr.eau.rmc.framework.utils" /><context:component-scan base-package="fr.eau.rmc.framework.service" />
<!-- Déclaration du package à scanner pour les DAO --><context:component-scan base-package="fr.eau.rmc.framework.dao" />
<!-- Déclaration du package à scanner pour les utilitaires divers --><context:component-scan base-package="fr.eau.rmc.framework.utils" />
3.2.2. Tiles / Ressources statiques
Pour la configuration de Tiles, au moins 2 éléments doivent être déclarés dans le fichier XML : TilesViewResolver TilesConfigurer
<!-- Déclaration des ressources statiques --><mvc:resources location="/resources/" mapping="/resources/**" />
Date : 11/09/2023
Manuel du développeur
<mvc:resources location="/js/" mapping="/js/**" /><mvc:resources location="/css/" mapping="/css/**" /><mvc:resources location="/images/" mapping="/images/**" /><mvc:resources location="/swf/" mapping="/swf/**" />
<!-- TILES resolver --><bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" /></bean>
<!-- Definition Tiles --><bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"><property name="definitions">
<list><value>/WEB-INF/tiles.xml</value>
</list></property>
</bean>
3.2.3. Données
La déclaration de la DataSource, de la configuration Hibernate ainsi que la gestion des transactions doivent aussi se faire dans le fichier XML. <!-- Data Source Declaration (JNDI) --><jee:jndi-lookup id="dataSource" jndi-name="${database.gfr.jndi.name}" cache="false" lookup-on-startup="true" proxy-interface="javax.sql.DataSource" />
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="entityManagerFactory"/></bean> <!-- Déclaration EntityManager --><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" /><property name="packagesToScan">
<array> <value>fr.eau.rmc.framework.model</value> </array> </property>
<property name="jpaVendorAdapter"><bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"><property name="showSql" value="${dev}" /><property name="generateDdl" value="false" /><property name="databasePlatform"
value="org.hibernate.dialect.Oracle10gDialect" /></bean>
</property></bean>
Date : 11/09/2023
Manuel du développeur
<!-- Publishing session factory to be able access HibernateSessionFactory --><bean id="sessionFactory" factory-bean="entityManagerFactory" factory-method="getSessionFactory" />
<!-- Declaration TransactionManager JPA --><bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" /><property name="jpaDialect" ref="hibernateJpaDialect" />
</bean>
<bean id="hibernateJpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<tx:annotation-driven transaction-manager="transactionManager" /><!-- Declaration package des repositories JPA --><jpa:repositories base-package="fr.eau.rmc.framework.dao.repositories" factoryclass="fr.eau.rmc.framework.dao.repositories.FrameworkRepositoryFactoryBean" />
3.2.4. Intercepteurs
Les intercepteurs (avant et après l’exécution de la requête HTTP) doivent également être déclarés dans le fichier XML.
<!-- Configures Handler Interceptors --><mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<bean class="fr.eau.rmc.framework.web.interceptors.TempsExecutionInterceptor" />
<bean class="fr.eau.rmc.framework.web.interceptors.HistoriqueNavigationInterceptor" />
<bean class="fr.eau.rmc.framework.web.interceptors.CrudableInterceptor" /><bean class="fr.eau.rmc.framework.web.interceptors.RechercheInterceptor" />
</mvc:interceptors>
3.2.5. Batchs
Les batchs sont définis dans un fichier XML séparé mais qui doit cependant être déclaré dans le fichier applicationContext.xml.
<!-- ######################### Batchs ######################### --><import resource="appBatchJobs.xml" />
3.3. Annotations JAVA
Comme pour la configuration XML, celle par annotations est définie au niveau de la servlet Spring dans le web.xml où est indiquée la classe référence de la configuration par annotation.
Date : 11/09/2023
Manuel du développeur
<!-- Définition de la Servlet --><servlet>
<servlet-name>Framework Spring</servlet-name><servlet-class>
fr.eau.rmc.framework.web.servlet.FrameworkSpringDispatcherServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
<!-- Paramètres Spring --><context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value></context-param><context-param> <param-name>contextConfigLocation</param-name> <param-value> fr.eau.rmc.gda.configuration.GdaConfiguration </param-value></context-param>
3.3.1. Classe de configuration référente
Chaque classe de configuration, s’identifie par l’annotation @Configuration.
A partir de cette classe, il est notamment possible de définir : l’import d’une configuration basée sur un fichier XML (@ImportResource) des classes sœurs dont la configuration sera chargée au démarrage (@Import) les packages à scanner pour trouver les Beans Spring à instancier (@ComponentScan) le fichier de propriétés de l’application (@PropertySource)
@Configuration@ImportResource({ "/WEB-INF/gdaBatchJobs.xml", })@Import(value = { //
FrameworkWebConfig.class, //FrameworkViewResolverConfig.class, //FrameworkMailConfig.class, //GdaJmxConfig.class, //GdaDataSourceConfig.class, //GdaInfrastructureConfig.class, //FrameworkSecurityInfrastructureConfig.class, //FrameworkSecurityConfig.class, //FrameworkSecurityKerberosConfig.class, //
})@ComponentScan(basePackages = { //
"fr.eau.rmc.gda.web", "fr.eau.rmc.gda.service", "fr.eau.rmc.gda.webservice", //
"fr.eau.rmc.gda.interfaces.editique", "fr.eau.rmc.gda.dao", "fr.eau.rmc.gda.aop" //
Date : 11/09/2023
Manuel du développeur
})@EnableJpaRepositories(basePackages = { "fr.eau.rmc.gda.dao.repositories" }, repositoryFactoryBeanClass = FrameworkRepositoryFactoryBean.class)@PropertySource({ "file:/appli/propappli/gda.properties" })@EnableWebMvc@EnableAspectJAutoProxypublic class GdaConfiguration extends FrameworkConfiguration {
} Classe mère : FrameworkConfiguration
@Configuration@PropertySource({ "file:/appli/propappli/activeDirectory.properties" })@ComponentScan(basePackages = { //
"fr.eau.rmc.framework.web", "fr.eau.rmc.framework.service", "fr.eau.rmc.framework.dao", "fr.eau.rmc.framework.aop", "fr.eau.rmc.framework.utils", //})@EnableJpaRepositories(basePackages = { "fr.eau.rmc.framework.dao.repositories" }, repositoryFactoryBeanClass = FrameworkRepositoryFactoryBean.class)@EnableSchedulingpublic abstract class FrameworkConfiguration extends WebMvcConfigurerAdapter {
@Beanpublic CsUserService csUserService() {
return new CsUserServiceImpl();}
@Beanpublic LdapDao ldapDao(Environment environment) {
LdapDaoImpl ldapDao = new LdapDaoImpl(); […]
return ldapDao;}
[…]
@Overridepublic void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());registry.addInterceptor(crudableInterceptor());registry.addInterceptor(rechercheInterceptor());registry.addInterceptor(historiqueNavigationInterceptor());
}
@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry); registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");registry.addResourceHandler("/swf/**").addResourceLocations("/swf/");
}
Date : 11/09/2023
Manuel du développeur
}
Dans cette classe, sont définis les Beans Spring du Framework qui sont amenés à être utilisés dans les différentes applications (services LDAP, utilisateurs, …). On retrouve également les intercepteurs et les ressources statiques.
3.3.2. Tiles
Une classe dédiée à la configuration de Tiles (or déclaration des vues qui reste paramétrée dans le fichier tiles.xml) : FrameworkViewResolverConfig (qui pourra être surchargée au besoin dans chaque application).
@Configurationpublic class FrameworkViewResolverConfig {
private interface VIEW {String FRAMEWORK_EXCEPTION_ONGLET = "frameworkExceptionOngletView";String FRAMEWORK_EXCEPTION = "frameworkExceptionView";
}
@Autowiredprivate ServletContext servletContext;
@Beanpublic UrlBasedViewResolver tilesViewResolver() {
UrlBasedViewResolver urlBasedViewResolver = new UrlBasedViewResolver();
urlBasedViewResolver.setViewClass(FwkTilesView.class);return urlBasedViewResolver;
}
@Beanpublic TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();tilesConfigurer.setDefinitions("/WEB-INF/tiles.xml");return tilesConfigurer;
}
[…]
@Beanpublic SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver mappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();mappings.put(FrameworkException.class.getName(),
VIEW.FRAMEWORK_EXCEPTION);mappings.put(FrameworkExceptionInOnglet.class.getName(),
VIEW.FRAMEWORK_EXCEPTION_ONGLET);mappings.put(IllegalArgumentException.class.getName(),
VIEW.FRAMEWORK_EXCEPTION);mappings.put(HttpRequestMethodNotSupportedException.class.getName(),
VIEW.FRAMEWORK_EXCEPTION);
Date : 11/09/2023
Manuel du développeur
mappingExceptionResolver.setExceptionMappings(mappings);return mappingExceptionResolver;
}}
3.3.3. Données
3.3.3.1. Accès BD
Pour accéder à la base de données, une classe abstraite (FrameworkDataSourceConfig) a été créée au niveau du Framework où l’implémentation doit uniquement fournir le nom de la connexion JNDI correspondante à l’application (en fait, il y a 2 connexions JNDI : 1 pour la partie sécurité et 1 pour l’application en elle-même).
@Configurationpublic abstract class FrameworkDataSourceConfig {
protected abstract String getJndiName(Environment env);
protected abstract String getJndiConnexionName(Environment env);
@Beanpublic JndiObjectFactoryBean dataSource(Environment env) throws
IllegalArgumentException {JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();dataSource.setJndiName(this.getJndiName(env));dataSource.setCache(false);dataSource.setLookupOnStartup(true);dataSource.setProxyInterface(DataSource.class);return dataSource;
}
@Beanpublic JndiObjectFactoryBean dataSourceConnexion(Environment env) throws
IllegalArgumentException {JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();dataSource.setJndiName(this.getJndiConnexionName(env));dataSource.setCache(false);dataSource.setLookupOnStartup(true);dataSource.setProxyInterface(DataSource.class);return dataSource;
}
}
Avec un exemple de son implémentation :
@Configurationpublic class GdaDataSourceConfig extends FrameworkDataSourceConfig {
Date : 11/09/2023
Manuel du développeur
@Overrideprotected String getJndiName(Environment env) {
return env.getProperty("database.gda.jndi.name");}
@Overrideprotected String getJndiConnexionName(Environment env) {
return env.getProperty("database.gda.cnx.jndi.name");}
}
3.3.3.2. Hibernate
Pour le paramétrage Hibernate, une classe abstraite (FrameworkInfrastructureConfig) a été créée au niveau du Framework où l’implémentation doit uniquement fournir le nom des packages à scanner (pour effectuer le mapping Hibernate).
@Configurationpublic abstract class FrameworkInfrastructureConfig {
@Autowiredprivate DataSource dataSource;
protected abstract String[] getPackagesToScan();
@Beanpublic SharedEntityManagerBean entityManager(Environment env) {
SharedEntityManagerBean entityManagerBean = new SharedEntityManagerBean();
entityManagerBean.setEntityManagerFactory(entityManagerFactory(env));return entityManagerBean;
}
@Beanpublic EntityManagerFactory entityManagerFactory(Environment env) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(this.dataSource);em.setPackagesToScan(this.getPackagesToScan());em.setJpaVendorAdapter(jpaVendorAdaper(env));em.afterPropertiesSet();return em.getObject();
}
@Beanpublic JpaVendorAdapter jpaVendorAdaper(Environment env) {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(Boolean.valueOf(env.getProperty("dev")));hibernateJpaVendorAdapter.setGenerateDdl(false);
Date : 11/09/2023
Manuel du développeur
hibernateJpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
return hibernateJpaVendorAdapter;}
@Beanpublic JpaTransactionManager transactionManager(Environment env) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory(env));jpaTransactionManager.setJpaDialect(hibernateJpaDialect());return jpaTransactionManager;
}
@Beanpublic JpaDialect hibernateJpaDialect() {
return new HibernateJpaDialect();}
}Avec un exemple de son implémentation :
@Configuration@EnableTransactionManagementpublic class GdaInfrastructureConfig extends FrameworkInfrastructureConfig {
@Overrideprotected String[] getPackagesToScan() {
return new String[] { "fr.eau.rmc.gda.model", "fr.eau.rmc.framework.model" };
}}
3.3.4. Batchs
Le paramétrage des batchs se fait toujours à l’aide d’un fichier XML qu’il faut importer dans la configuration Spring de base.
@Configuration@ImportResource({ "/WEB-INF/gdaBatchJobs.xml", })@Import(value = { //
FrameworkWebConfig.class, //FrameworkViewResolverConfig.class, //FrameworkMailConfig.class, //GdaJmxConfig.class, //GdaDataSourceConfig.class, //GdaInfrastructureConfig.class, //FrameworkSecurityInfrastructureConfig.class, //FrameworkSecurityConfig.class, //FrameworkSecurityKerberosConfig.class, //
})@ComponentScan(basePackages = { //
"fr.eau.rmc.gda.web", "fr.eau.rmc.gda.service", "fr.eau.rmc.gda.webservice",
Date : 11/09/2023
Manuel du développeur
//"fr.eau.rmc.gda.interfaces.editique", "fr.eau.rmc.gda.dao",
"fr.eau.rmc.gda.aop" //})@EnableJpaRepositories(basePackages = { "fr.eau.rmc.gda.dao.repositories" }, repositoryFactoryBeanClass = FrameworkRepositoryFactoryBean.class)@PropertySource({ "file:/appli/propappli/gda.properties" })@EnableWebMvc@EnableAspectJAutoProxypublic class GdaConfiguration extends FrameworkConfiguration {
}
Il est possible d’activer (ou désactiver) le système de batchs, en modifiant la valeur (booléen) de la propriété execBatch au niveau du fichier de propriétés. Par défaut, la valeur sera true.
4. Controller Spring : AbstractListFicheController
4.1. Scénario courant Saisie de critères
Recherche correspondante
Modification de l’élément via une fiche
Ce scénario s’appuyant sur un type d’élément déterminé, il semble logique de typer le Controller associé à cet ensemble d’actions. Ainsi, et de manière générique, il sera possible d’accéder à des méthodes qui seront prédéfinies dans le Controller abstrait et qui devront être implémentées dans le Controller final.Ce Controller permet également de factoriser des algorithmes génériques : recherche par critères, création/modification de l’objet de référence (fiche)
4.2. Exemples de méthodes génériques
/** * Initialise les données suite à l'exécution de la recherche * * @param criteres * @return * @throws FrameworkException */protected void executerRecherche(final FormCriteres<T> criteres) throws FrameworkException {
// lancement de la rechercheList<T> listFromBD;if (this.getRecherchePageable() != null) {
// restriction de la recherche à l'aide d'un Pageable si celui-ci// est définiPage<T> pageRechercheElements =
Date : 11/09/2023
Manuel du développeur
getModeleServiceRecherche().pageRechercheElements(criteres, this.getRecherchePageable());
listFromBD = pageRechercheElements.getContent();} else {
// recherche complètelistFromBD =
getModeleServiceRecherche().rechercherElements(criteres);}this.getLog().info(listFromBD.size() + " éléments correspondant à la
recherche !");// mise à jour de la liste de résultats sur l'objet CriteresFormcriteres.setList(listFromBD);// mise à jour de la liste de l'objet CriteresFormthis.setFormCriteres(criteres);// libération de la liste d'objets mappéslistFromBD = null;
// sauvegarde des critèresthis.setCriteresRechercheSauvegardes(criteres);
}
/** * Persiste la modification de l'objet de référence de la fiche * * @throws FrameworkException */protected void modifier() throws FrameworkException {
this.getModeleServiceFiche().modifier(this.getModeleFiche());}
/** * Persiste la création de l'objet de référence de la fiche * * @throws FrameworkException */protected void creer() throws FrameworkException {
this.getModeleServiceFiche().creer(this.getModeleFiche());}
4.3. Héritages
Ce Controller gérant à la fois la recherche et le cycle de vie d’un élément donné, 2 interfaces ont été introduites afin d’orienter le développeur dans ses choix de développement.
4.3.1. IListeController
Cette interface fournit toutes les méthodes nécessaires à l’exécution d’une recherche :
public interface IListeController<T extends MappedModel> extends IActionForm {
/** * Renvoie le nom de la vue associée à la recherche
Date : 11/09/2023
Manuel du développeur
* * @return */String getViewRecherche();
/** * Renvoie l'action portée par le formulaire de recherche * * @return */@ModelAttribute(ACTION_FORM_RECHERCHE)String getRechercheActionForm();
/** * Initialise les données suite à l'exécution de la recherche * * @param criteres * @return * @throws FrameworkException */void executerRecherche(FormCriteres<T> criteres) throws FrameworkException;
/** * Service utilisé pour la recherche * * @return */IModeleService<T> getModeleServiceRecherche();
/** * Renvoie un Pageable (qui permet de limiter le nombre d'élément de la
liste de résultats) * * @return */Pageable getRecherchePageable();
}
4.3.2. IFicheControllerCette interface fournit les méthodes nécessaires au cycle de vie de l’élément (création, modification, suppression) :
public interface IFicheController<T extends MappedModel> extends IActionForm {
/** * Renvoie le nom de la vue associée à la fiche * * @return */String getViewFiche();
/** * Renvoie l'action portée par le formulaire de la fiche
Date : 11/09/2023
Manuel du développeur
* * @return */@ModelAttribute(ACTION_FORM_FICHE)String getFicheActionForm();
/** * Renvoie l'objet Form correspondant à la fiche * * @param obj * @return */IFicheForm<T> getFormFicheFromModel(T obj);
/** * Persiste la création de l'objet de référence de la fiche * * @throws FrameworkException */void creer() throws FrameworkException;
/** * Persiste la modification de l'objet de référence de la fiche * * @throws FrameworkException */void modifier() throws FrameworkException;
/** * Persiste la création de l'objet de référence de la fiche * * @throws FrameworkException */void supprimer() throws FrameworkException;
/** * Service utilisé dans la fiche * * @return */IModeleService<T> getModeleServiceFiche();
}
4.4. Interfaçage des Controllers
Ces interfaces permettent de généraliser l’injection de variables dans le contexte Spring : noms d’action de validation, noms d’objet de base …Ces interfaces sont implémentées par le Controller abstrait (bien qu’elles ne contiennent uniquement des constantes)
4.4.1. IActionForm : pour les validations de formulaires
public interface IActionForm {
Date : 11/09/2023
Manuel du développeur
String ACTION_FORM_RECHERCHE = "actionFormRecherche";String ACTION_FORM_FICHE = "actionFormFiche";
}
4.4.2. IModelAttributes : pour les attributs Spring
public interface IModelAttributes {String RESULTATS_LIST = "resultats";String LIST_OPERATORS = "listOperators";String ERREUR = "erreur";String INFOS = "infos";
}
4.4.3. IModelForm : pour les modèles associés aux formulaires
public interface IModelForm {String CRITERES_FORM_OBJ = "criteresForm";String FICHE_FORM_OBJ = "ficheForm";
}
5. Mapping Hibernate
5.1. Table sans « PK »
Il est possible de rencontrer certaines tables ne possédant pas de « Primary Key ». Or, Hibernate a besoin d’une PK pour valider son mapping. Pour contourner le problème, il suffit de mapper le « rowid » en tant qu’identifiant de la table :
@Entity@Table(name = "cs_batch_erreur")public class BatchErreur implements MappedModel<Serializable>, Serializable { […]
@Id@Column(name = "ROWID", updatable = false, insertable = false, nullable =
false)private String rowID;
}
NB : ce mapping permet uniquement des ordres SQL de type ‘SELECT’, pour les mises à jour (‘INSERT’, ‘UPDATE’, ‘DELETE’) il faudra passer manuellement des ordres SQL.
5.2. Utilisation de séquence
Date : 11/09/2023
Manuel du développeur
Exemple standard d’utilisation de déclaration de séquence :
@Entity@Table(name = "cs_batch_lance")public class BatchLance implements MappedModel<Long>, Serializable {
private static final long serialVersionUID = -3149271265373705542L;
@Id@Column(name = "NUMJOB", unique = true, nullable = false)@SequenceGenerator(name = "NUMJOB_GENERATOR", sequenceName =
"DBSECUR.SQ_NUMJOB_CS")@GeneratedValue(strategy = GenerationType.AUTO, generator =
"NUMJOB_GENERATOR")private Long numjob;
}
Ici, « NUM_JOB_GENERATOR » désigne le nom du générateur de séquence à utiliser et « DBSECUR.SQ_NUMJOB_CS » le nom de la séquence à laquelle le générateur est associé.
5.3. Astuces « OneToMany »
Pour la suppression en cascade d’une liste d’objets liés par un « OneToMany », il faut bien penser à ajouter l’option ‘orphanRemoval’.
@OneToMany(fetch = FetchType.LAZY, mappedBy = "demandeAide", cascade = { CascadeType.ALL }, orphanRemoval = true)private List<DemandeAidePieces> listDemandeAidePieces = new ArrayList();
6. Repository JPA
6.1. Querydsl
Avec Spring Data JPA, il est possible de générer des requêtes simplement à l’aide de méthodes dont le nom s’appuie sur les critères de la requête. Ces méthodes sont à définir dans le Repository ET ne nécessite pas d’implémentation.
public interface CommuneRepository extends FrameworkRepository<Commune, String> {List<Commune> findByNumcomStartsWithOrderByNumcomAsc(String numcom);
List<Commune> findByNomcomIgnoreCaseStartsWithOrderByNomcomAsc(String nomcom);}
Date : 11/09/2023
Manuel du développeur
Cf. doc Spring Data JPA : http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
7. Services métier : IModeleService<M>
Avec cette interface, les méthodes standard devront être explicitement implémentées !
public interface IModeleService<M extends MappedModel> extends IHibernateService<M> {
List<M> rechercherElements(FormCriteres<M> criteres) throws FrameworkException;
M rechercherElementById(Serializable id) throws FrameworkException;
Page<M> pageRechercheElements(FormCriteres<M> criteres, Pageable pageable) throws FrameworkException;
void modifier(M modele) throws FrameworkException;
void creer(M modele) throws FrameworkException;
void supprimer(M modele) throws FrameworkException;}
7.1. Besoins Hibernate
Initialisation de listes ‘Lazy’ : Afin de traiter de manière générique les initialisations d’objets Hibernate, un niveau d’abstraction a été ajouté pour traiter cette initialisation de façon générique. Pour cela, il faudra utiliser la classe abstraite AbstractModeleService :
public abstract class AbstractModeleService<M extends MappedModel<I>, I extends Serializable> implements IHibernateService<M> {
@Autowiredprivate JpaTransactionManager transactionManager;
/* (non-Javadoc) * @see
fr.eau.rmc.framework.service.IHibernateService#refresh(java.lang.Object) */@Overridepublic void refresh(M mappedModel) {
Session currentSession = createSession();currentSession.refresh(mappedModel);closeSession(currentSession);
}
private Session createSession() {
Date : 11/09/2023
Manuel du développeur
EntityManagerFactory entityManagerFactory = transactionManager.getEntityManagerFactory();
EntityManager createEntityManager = entityManagerFactory.createEntityManager();
Session currentSession = createEntityManager.unwrap(Session.class);return currentSession;
}
private void closeSession(Session currentSession) {currentSession.close();
}
}
L’implémentation finale du service devra étendre cette classe :
@Servicepublic class CsBatchLanceServiceImpl extends AbstractModeleService<CsBatchLance, Long> implements CsBatchLanceService {
@Autowiredprivate CsBatchLanceRepository repository;
[…]
}
7.2. Exécution de procédure stockée
Ici, l’objectif est d’éviter au développeur de définir une nouvelle implémentation de l’appel à une procédure stockée à chaque fois qu’il en a besoin. De plus, la factorisation de ce code évite l’injection du ‘transactionManager’ (nécessaire à l’appel de la procédure stockée) dans chacun des services mais une fois et une seule via la classe abstraite.
7.2.1. A l’aide d’un SimpleJdbcCall
Pour utiliser cette solution, une méthode a été implémentée dans la classe ‘AbstractModeleService’ : « callProcStockWithSimpleJdbcCall(mapParametres, nomPackage, nomProcedure) » .Les paramètres sont les suivants :
mapParametres : définition des paramètres à passer en entrée de la procédure nomPackage : nom du package contenant la procédure nomProcedure : nom de la procédure
NB : pour pouvoir utiliser cette méthode, il faut que le service sur lequel on est en train de travailler étende la classe ‘AbstractModeleService’
Exemple : […]Map<String, Object> mapParameters = new HashMap<String, Object>();mapParameters.put("p_numint", numint);
Date : 11/09/2023
Manuel du développeur
mapParameters.put("p_typeEmis", typeEmission);mapParameters.put("p_typeInterro", typeInterrogation);mapParameters.put("p_anexe", anexe);mapParameters.put("p_numtit", numtit);Map<String, Object> mapResult = this.callProcStockWithSimpleJdbcCall(mapParameters, "PARECHPIECE", "PRRECHPIECE");BigDecimal p_numrechor = (BigDecimal) mapResult.get("P_NUMRECHOR");[…]
ATTENTION : cette méthode ne fonctionne pas si le propriétaire (Oracle) n’est pas le même que celui utilisé pour l’application ! Si on est dans ce cas, il faudra utiliser la méthode définie ci-dessous.
7.2.2. A l’aide d’un JdbcTemplate
Cette méthode se rapproche de ce qui était fait dans la version 2.x de la BAO.Ici, il suffit de passer l’ordre SQL correspondant à l’appel de la procédure stockée désirée « callProcStockWithJdbcTemplate(sqlCallProcStock, listSqlParameter, mapParameters) ». Les paramètres sont les suivants :
sqlCallProcStock : ordre SQL d’appel de la procédure listSqlParameter : déclaration des paramètres (IN et OUT) de la procédure maParameters : définition des paramètres à passer en entrée de la procédure
Exemple :[…]List<SqlParameter> listSqlParameter = new ArrayList<SqlParameter>();// déclaration des paramètres INlistSqlParameter.add(new SqlParameter("pcodappli", Types.VARCHAR));listSqlParameter.add(new SqlParameter("pnumint", Types.VARCHAR));[…]// déclaration des paramètres OUTlistSqlParameter.add(new SqlOutParameter("padr0", Types.VARCHAR));listSqlParameter.add(new SqlOutParameter("padr1", Types.VARCHAR));[…]listSqlParameter.add(new SqlOutParameter("retour", Types.NUMERIC));
Map<String, Object> mapParameters = new HashMap<String, Object>();mapParameters.put("pcodappli", numint);mapParameters.put("pnumint", numint);[…]String sql = "{CALL PRADRCPL (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}";Map<String, Object> mapResult = this.callProcStockWithJdbcTemplate(sql, listSqlParameter, mapParameters);BigDecimal retour = (BigDecimal) mapResult.get("retour");[…]
7.3. Exécution de requête SQL
Date : 11/09/2023
Manuel du développeur
Pour exécuter directement une requête SQL, il est nécessaire de récupérer la Session Hibernate. Afin d’éviter la création de Session dans chaque service qui en aura besoin, celui-ci sera déporté dans la classe ‘AbstractModeleService’.
NB : une Session Hibernate ne doit pas être utilisée directement dans le Service créé par le développeur. Chaque besoin devra être déporté dans la classe abstraite.
7.3.1. En SQL direct
Il est possible d’exécuter directement une commande SQL (cf. org.hibernate.SQLQuery) à l’aide de la méthode « createSqlQuery(sqlQuery)» où sqlQuery est la commande SQL à exécuter.
7.3.1.1. Objet FwkHibernateSQLQuery
L’appel de la méthode « createSqlQuery(sqlQuery)» renvoie un objet de type ‘FwkHibernateSQLQuery’. Cet objet s’appuie sur l’utilisation de la classe ‘SQLQuery’ de Hibernate ainsi que la Session Hibernate (invisible à l’extérieur).
Les méthodes visibles sont les suivantes : setParameter(nomParam, valParam) : valParam peut être aussi bien un String, un Double, un
Long, une Date, une List, … addEntity(classEntity) getList : exécute la méthode ‘list()’ de SQLQuery et ferme la session getUniqueResult : exécute la méthode ‘uniqueResult()’ de SQLQuery et ferme la session exeUpdate : exécute la méthode ‘executeUpdate()’ de SQLQuery et ferme la session
7.3.1.2. Exemple
[…]StringBuffer sb = new StringBuffer();sb.append("select vw.* ");sb.append(" from VW_SSBVPERIM@dbl_ig vw");sb.append(" where vw.codssbvperim in ( ");sb.append(" select c.codssbv ");sb.append(" from IG_NUMCOM@dbl_ig n, FC_INT i,IG_COMSSBV@dbl_ig c ");sb.append(" where n.lettre_corse = c.num_com ");sb.append(" and c.pourcent>=5 ");sb.append(" and n.texte = i.numcom ");if (!obj.isEPCI()) {
sb.append(" and i.numint = :numint ");} else {
sb.append(" and i.numcom in (:listNumcom )");}sb.append(" ) ");
FwkHibernateSQLQuery q = createSqlQuery(sb.toString());q.addEntity(VwSsbvperim.class);
Date : 11/09/2023
Manuel du développeur
if (!obj.isEPCI()) {q.setParameter("numint", obj.getNumint());
} else {q.setParameter("listNumcom", obj.getListNumcom());
}
obj.setListVwSsbvperim((List<VwSsbvperim>) q.getList());[…]
7.3.2. En HQL
Il est possible d’exécuter directement une commande SQL (cf. org.hibernate.Query) à l’aide de la méthode « createHqlQuery(hqlQuery)» où hqlQuery est la commande HQL à exécuter.
7.3.2.1. Objet FwkHibernateQuery
L’appel de la méthode « createHqlQuery(hqlQuery)» renvoie un objet de type ‘FwkHibernateHQLQuery’. Cet objet s’appuie sur l’utilisation de la classe ‘Query’ de Hibernate ainsi que la Session Hibernate (invisible à l’extérieur).
Les méthodes visibles sont les suivantes : setParameter(nomParam, valParam) : valParam peut être aussi bien un String, un Double, un
Long, une Date, une List, … getList : exécute la méthode ‘list()’ de Query et ferme la session getUniqueResult : exécute la méthode ‘uniqueResult()’ de Query et ferme la session exeUpdate : exécute la méthode ‘executeUpdate()’ de Query et ferme la session
8. Intégration de nouveaux tags
Les tags fournis par Spring peuvent ne pas être suffisant pour les besoins exprimés. Pour répondre à cette problématique, de nouveaux tags ont été introduit (notamment pour gérer les cas spécifiques liés aux onglets jQuery, cf. paragraphe sur les besoins spécifiques).
8.1. FwkLabelTag
Comme son nom l’indique, ce tag (<aermc:label>) se rapproche du LabelTag de Spring avec une petite évolution permettant d’intégrer un astérisque lorsque le champ associé est obligatoire et les ‘:’ faisant suite au libellé.
<aermc:label path="critereNumjob" cssClass="intituleChamp"><spring:message code="criteresBatch.numJob" />
</aermc:label>
Date : 11/09/2023
Manuel du développeur
8.2. FwkOngletsTag
Ce tag (<aermc:onglets>) permet de générer des onglets jQuery à l’aide d’une seule balise. Celle-ci devra embarquer une map associant chaque libellé d’onglets à l’URL correspondante, cette map sera injectée dans la vue Spring.
<aermc:onglets mapOnglets="${mapOnglets}" id="ongletsLotsRapports" ongletInFiche="true"/>
8.3. FwkErreurChampOngletTag
Afin de gérer les messages lors de la validation du formulaire d’un onglet, il a fallu introduire un nouvel élément (la validation Spring n’étant pas effective en XHR) permettant de gérer les retours du serveur.Ce tag est semblable au tag ErrorsTag de Spring.
<aermc:erreurChampOnglet champAssocie="selection" />
8.4. FwkPlusCriteresTag
Ce tag (<aermc:plusCriteres>) permet de gérer la réduction de l’espace dédié aux critères (par extension, n’importe quel élément d’affichage) afin de gagner de la place. Par défaut, cette solution est implémentée sur tous les écrans de critères (elle contiendra l’ensemble des éléments de critères ainsi que le bouton « Rechercher »).
Les DIV qui seront « pliables » devront posséder la class CSS « optional » afin d’être correctement pris en compte lors des différentes actions.
Il existe 2 formes d’affichage du « Afficher / Masquer », soit sous la forme , soit sous la forme . Par défaut, l’affichage choisi est le second (triangle orienté vers le bas) mais il est possible d’afficher le premier (le « moins ») en affectant l’attribut « plusMoins » à « true ».
<form:form method="POST" action='${actionFormRecherche}' commandName="criteresForm">
<div class="fondBleu ui-corner-all" id="divCriteresForm"><aermc:plusCriteres path="displayCriteres" plusMoins="true">
<div id="criteresPage" class="optional"><tiles:insertAttribute name="page" />
</div><div id="criteresButtons" class='criteresButtons optional'>
<button type="submit" class="submit jq-searchbutton">Rechercher</button>
</div></aermc:plusCriteres>
</div></form:form>
Date : 11/09/2023
Manuel du développeur
NB : penser à utiliser la fonction JavaScript apply_togglebutton() qui est appelée par défaut dans la page footerSimple.jsp.
8.5. FwkUploadFileTag
Ce tag (<aermc:uploadFile>) permet de personnaliser l’affichage d’un champ de chargement de fichier (surtout pour le bouton « Parcourir »).
<form:form method="POST" action="${actionOnglet2}" commandName="formOnglet2" enctype="multipart/form-data">[…]
<aermc:label path="multipartFile" class="intituleChamp">Fichier</aermc:label>
<aermc:uploadFile path="multipartFile"/>[…]</form:form>
NB 1 : ne pas oublier la propriété « enctype » dans le formulaire englobant ce champ.NB 2 : penser à utiliser la fonction JavaScript apply_upload() qui est appelée par défaut dans la page footerSimple.jsp.
9. Introduction de nouvelles annotations
9.1. Contraintes de validation
9.1.1. Définition
Sur le modèle de l’interface @Pattern, une annotation spécifique a été créée - afin de prendre en compte un libellé ‘intelligible’ lors de l’affichage du message d’erreur pour le pattern associé - dans le package « fr.eau.rmc.framework.web.validations.annotations » :
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })@Retention(RUNTIME)@Documented@Constraint(validatedBy = FwkPatternValidator.class)public @interface FwkPattern {
String nomChamp() default "";
String regexpExplicite() default "";[…]
}
Comme défini ci-dessous, cette annotation doit être associée à un Validator :
Date : 11/09/2023
Manuel du développeur
public class FwkPatternValidator implements ConstraintValidator<FwkPattern, String> {
private java.util.regex.Pattern pattern;
public void initialize(FwkPattern parameters) { […]
}
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) {return true;
}Matcher m = pattern.matcher(value);return m.matches();
}}
9.1.2. Utilisation
public class CriteresListInterlocuteurs extends FormCriteres<Interlocuteur> implements Serializable {
@FwkPattern(regexp = "[\\d]{5}", message = "{error.champ.regexp}", nomChamp = "Numéro", regexpExplicite = "5 caractères numériques")
private String critereNumint;[…]
}
9.2. Formatage de données
9.2.1. Définition
De la même manière que pour les contraintes de validation, il est possible de créer des annotations permettant de gérer le formatage des données (dans le package « fr.eau.rmc.framework.formatters.annotations ») :
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })@Retention(RetentionPolicy.RUNTIME)public @interface NumIntFormat {}
Ici, il sera nécessaire de définir un Formatter qui lui sera associé à l’aide d’une factory (AnnotationFormatterFactory) :
public class NumIntFormatter implements Formatter<String> {private final int numintLongueur;
Date : 11/09/2023
Manuel du développeur
public NumIntFormatter(Integer numIntLongueur) {this.numintLongueur = numIntLongueur;
}
@Overridepublic String print(String object, Locale locale) {
return object;}
@Overridepublic String parse(String text, Locale locale) throws ParseException {
if (text.length() == numintLongueur || text.length() == 0) {return text;
}return StringUtils.leftPad(text, numintLongueur, "0");
}}
public class NumIntFormatterFactory implements AnnotationFormatterFactory<NumIntFormat> {
private Integer longueurNumInt;
public Integer getLongueurNumInt() {return longueurNumInt;
}
public void setLongueurNumInt(Integer longueurNumInt) {this.longueurNumInt = longueurNumInt;
}
@Overridepublic Set<Class<?>> getFieldTypes() {
Set<Class<?>> set = new HashSet<Class<?>>();set.add(String.class);return set;
}
@Overridepublic Printer<?> getPrinter(NumIntFormat annotation, Class<?> fieldType) {
return new NumIntFormatter(this.getLongueurNumInt());}
@Overridepublic Parser<?> getParser(NumIntFormat annotation, Class<?> fieldType) {
return new NumIntFormatter(this.getLongueurNumInt());}
}
9.2.2. Déclaration dans la configuration Spring
Pour déclarer la(les) Factory correspondant aux différents Formatter, il est nécessaire de les définir dans le fichier XML de configuration de Spring à l’aide du bean FormattingConversionServiceFactoryBean :
Date : 11/09/2023
Manuel du développeur
<beans xmlns=http://www.springframework.org/schema/beans […] ><!-- Déclaration des annotations MVC --><mvc:annotation-driven conversion-service="fwkConversionService" />
<bean id="fwkConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters"><list>
<bean class=" fr.eau.rmc.framework.formatters.NumIntFormatterFactory">
<property name="longueurNumInt" value="5" /></bean>
</list></property>
</bean>[…]
</beans>
9.2.3. Utilisation
public class CriteresListInterlocuteurs extends FormCriteres<Interlocuteur> implements Serializable {
@NumIntFormatprivate String critereNumint;
[…]
}
10.Externalisation de la connexion à la base de données
La solution choisie pour répondre à cette problématique est de créer une ressource JNDI au sein du serveur applicatif (Tomcat dans ce cas).
10.1. Mode de connexion
Le mode de connexion qui a été choisi est la ressource JNDI (cf § 9.2).
10.2. Déclaration dans Tomcat
Pour définir la ressource JNDI dans Tomcat il suffit de modifier le fichier context.xml du serveur (sur les serveurs d’intégration ce fichier doit se trouver dans /appli/tomc-<appli>/tomcat/conf, s’il n’existe pas il faudra le créer à l’identique de celui utilisé en local).
Date : 11/09/2023
Manuel du développeur
<?xml version="1.0" encoding="UTF-8"?><Context>
<!-- Default set of monitored resources --> <WatchedResource>WEB-INF/web.xml</WatchedResource>
<!-- Datasource Jedi --><Resource name="jdbc/jedi" auth="Container"
driverClassName="oracle.jdbc.OracleDriver" maxActive="20" maxIdle="10" maxWait="10000" password="test" type="javax.sql.DataSource" url="jdbc:oracle:thin:@DEVSRV:1526:ETSIA" username="test" testOnBorrow="true" validationQuery="SELECT 1 FROM DUAL" validationQueryTimeout="10"/>
<!-- Datasource Security Jedi --><Resource name="jdbc/jedi-security" auth="Container"
driverClassName="oracle.jdbc.OracleDriver" maxActive="20" maxIdle="10" maxWait="10000" password="test" type="javax.sql.DataSource" url="jdbc:oracle:thin:@DEVSRV:1526:ETSIA" username="test" testOnBorrow="true" validationQuery="SELECT 1 FROM DUAL" validationQueryTimeout="10"/>
</Context>
Ici, deux ressources sont définies : Celle utilisée par l’application elle-même (jdbc/jedi) Celle utilisée pour l’authentification (jdbc/jedi-security) [celle-ci sera utilisée pour récupérer
en base des infos lors de la connexion]
10.3. Déclaration dans Spring
La déclaration de la ressource JNDI dans Spring se fait dans le fichier de configuration « <appli>-context.xml » à l'aide de la balise <jee:jndi-lookup>.
Par exemple :
<beans xmlns="http://www.springframework.org/schema/beans" [...]
xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/mvc [...] http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
[...]<!-- Data Source Declaration -->
<jee:jndi-lookup id="dataSource" jndi-name="${database.jedi.jndi.name}" cache="false" lookup-on-startup="true" proxy-interface="javax.sql.DataSource" />
[...]
</beans>
Date : 11/09/2023
Manuel du développeur
Dans cet exemple, la variable « database.jedi.jndi.name » sera externalisée dans le fichier de propriétés de l’application :
[…]database.jedi.jndi.name=java:comp/env/jdbc/jedi[…]
Date : 11/09/2023
Manuel du développeur
11.Définition des profils dans le LDAP
11.1. Structure du LDAP
Désormais les profils ne seront plus gérés dans une table ORACLE mais dans le LDAP. Le découpage s’effectue par Environnements / Applications / Profils / Utilisateurs selon le schéma ci-dessous établi par l’Agence de l’Eau.
Date : 11/09/2023
Manuel du développeur
11.2. Requêtage dans le LDAP (service LdapDao)
11.2.1. getDetailsFromNomUser(username)Cette méthode permet de récupérer dans le LDAP les informations de l'utilisateur en fonction de son nom d'utilisateur.
11.2.2. getDetailsFromNumint(numInt)
Cette méthode permet de récupérer dans le LDAP les informations de l'utilisateur en fonction de son numéro (numInt).
11.2.3. getProfilsUserApplication(username, codApp)
Cette méthode permet de récupérer dans le LDAP la liste des profils d'un utilisateur pour une application donnée.
11.2.4. getProfilsApplication(codApp)
Cette méthode permet de récupérer la liste des profils d'une application.
11.2.5. getApplications()
Cette méthode permet de récupérer la liste des applications.
12.Besoins spécifiques de l’Agence de l’Eau
12.1. Onglets applicatifs (jQuery)
12.1.1. Fonctionnement ‘léger’
Les onglets ‘applicatifs’ pourront être gérés de façon simple à l’aide de différents div déclarés dans la JSP.
<div class="onglets" style="height: 120px;" id="ongletsCriteresLotsRapports"><ul>
<li><a href="#onglet1">Onglet 1</a></li><li><a href="#onglet2">Onglet 2</a></li>
Date : 11/09/2023
Manuel du développeur
</ul><div id="onglet1">
[…]</div><div id="onglet2">
[…]</div>
</div>Ce type de solution pourra être utilisée par exemple dans le cas de présentation de critères sous la forme d’onglets
12.1.2. Fonctionnement ‘complexe’
Pour les onglets plus évolués – faisant appel à une page dédiée, la balise ‘<aermc:onglets>’ définie plus haut (cf. utilisation §6.2) devra être utilisée (par exemple dans une fiche).
Le principe de fonctionnement d’une fiche avec onglets est le suivant : Un seul bouton ‘Enregistrer’ pour toute la fiche Validation du format des données saisies lors du changement d’onglet (implicite grâce à
l’utilisation de la balise ‘<aermc:onglets>’) Un objet ‘formulaire’ pour la fiche possédant en attribut chacun des objets associés à chacun
des onglets. Cet objet sera stocké dans le contexte Spring à chaque changement d’onglet afin de ne pas perdre les informations correctement saisies.
La validation de la cohérence des données ne sera effectuée uniquement lors de l’action ‘Enregistrer’
12.2. Onglets utilisateur (navigateur)
Afin de conserver l’intégrité des données au moment où l’utilisateur ouvre un nouvel onglet (ou une nouvelle fenêtre) de son navigateur, il est impératif que le ‘scope’ du Controller Spring n’ait pas la valeur ‘session’ !
12.3. Mode de navigation
Suivant les besoins des utilisateurs, plusieurs modes de navigation ont été envisagés : Chaque lien s’ouvre dans un nouvel onglet (navigateur) [solution retenue pour l’application
GDA] Chaque lien s’ouvre dans un unique onglet [solution retenue pour l’application QEE]
12.3.1. Description
12.3.1.1. Mode multi-ongletsLe mode multi-onglets se définit de la manière suivante :
Tous les items du menu s’ouvrent dans un nouvel onglet
Date : 11/09/2023
Manuel du développeur
Tous les liens référencés dans une liste de résultats s’ouvrent dans un nouvel onglet
Fil d’Ariane :
Avec ce mode de navigation, un fil d’Ariane n’a pas de sens, il a donc été supprimé.
Bouton « Retour » :
La navigation à l’aide du bouton « Retour », situé en bas de page, se traduit par la fermeture de l’onglet courant (après validation de l’utilisateur si des modifications sont en cours) dans la plupart des cas ou par un équivalent du « Précédent » du navigateur s’il n’y a pas eu d’ouverture d’onglet préalable.
NB : ce mode de navigation se rapproche de ce qui était fait dans les anciennes applications développées en PowerBuilder.
12.3.1.2. Mode mono-ongletLe mode mono-onglet se définit par une navigation au sein d’un seul et unique onglet (rien n’empêche l’utilisateur de faire un clic droit > « Ouvrir dans un nouvel onglet » mais ce n’est pas le mode de navigation recommandé dans ce cas).
Fil d’Ariane :
Avec ce mode de navigation, la présence d’un fil d’Ariane est nécessaire (son fonctionnement sera défini dans le § 11.3.2).
Bouton « Retour » :
La navigation à l’aide du bouton « Retour », situé en bas de page, se traduit par un retour vers la page précédente (rechargement de la page s’il s’agit d’un écran « fiche », relance de la recherche s’il s’agit d’un écran « recherche »).Si des modifications sont en cours dans l’écran courant, alors une confirmation sera demandée à l’utilisateur s’il souhaite conserver ses modifications ou non avant le retour à la page précédente.
NB : ce mode de navigation se rapproche de ce qui était fait dans les anciennes applications développées avec les versions précédentes de la BAO (2.7 et antérieures).
12.3.2. Fonctionnement du fil d’Ariane
Le fil d’Ariane (ou BreadCrumb) est généré à partir de la navigation de l’utilisateur : à chaque appel de lien depuis le navigateur, celui-ci sera intercepté afin d’établir une Map d’actions pour chacun des utilisateurs de l’application concernée.
Date : 11/09/2023
Manuel du développeur
12.3.2.1. HistoriqueNavigationInterceptor
HistoriqueNavigationInterceptor est un Interceptor Spring qui permet : Avant l’exécution côté serveur :
De gérer la pile d’actions de chaque utilisateur (ajout, ràz) : celle-ci exclut les ressources statiques de l’application (images, css, js, resources, …) mais également toutes les requêtes POST ou Ajax pour conserver uniquement les liens qui seront nécessaires à la navigation.
Après l’exécution côté serveur :D’enrichir le Model Spring afin de mettre à jour le fil d’Ariane et d’ajouter d’éventuelles informations supplémentaires dans la page (cas d’un retour vers une recherche avec rechargement des résultats correspondant aux critères).
12.3.2.2. FwkBreadCrumbManager
FwkBreadCrumbManager est un singleton permettant de gérer les piles d’actions de chaque utilisateur – identifiées par leur numéro de session HTTP.Celui-ci permet de :
Ajouter un élément à la pile d’action de la session courante Supprimer le dernier élément de la pile Supprimer les éléments postérieurs à l’élément courant Réinitialiser la pile d’une session utilisateur (notamment lors du retour sur la page d’Accueil) Retrouver le Controller responsable d’une recherche à partir de son URL (et ré-exécuter la
recherche)
12.3.2.3. FwkBreadCrumb
FwkBreadCrumb est l’objet JAVA qui permet d’afficher le contenu du fil d’Ariane à l’écran. Il possède 2 attributs : un libellé et une url (lien vers la page correspondante).
12.3.2.4. FwkViewPreparer (Tiles)
FwkViewPreparer permet d’enrichir le contexte Tiles de la page courante après l’exécution côté serveur mais avant la restitution côté client.Dans ce cas, il sert à restituer le même titre au niveau de la page HTML que dans le fil d’Ariane (titre récupéré dans le fichier properties de messages, à partir d’un code suivant le format « ecran.<codeEcran> »)
Par défaut, cette classe est déjà paramétrée au niveau du layout de base dans le fichier tiles.xml généré par l’archetype :
<!-- Définition des Layouts --><definition name="base.definition" template="/WEB-INF/jsp/base/layout.jsp"
Date : 11/09/2023
Manuel du développeur
preparer="fr.eau.rmc.framework.web.servlet.tiles.FwkViewPreparer"><put-attribute name="title" value="" /><put-attribute name="titrePage" value="" /><put-attribute name="ecranID" value="" type="string" cascade="true" /><put-attribute name="header" value="/WEB-INF/jsp/base/header.jsp" /><put-attribute name="nav" value="/WEB-INF/jsp/base/nav.jsp" /><put-attribute name="breadcrumbs" value="/WEB-INF/jsp/base/breadcrumbs.jsp"
/><put-attribute name="criteres" value="" /><put-attribute name="resultats" value="" /><put-attribute name="fiche" value="" /><put-attribute name="section" value="" /><put-attribute name="footer" value="/WEB-INF/jsp/base/footerSimple.jsp" />
<!-- currentMenu correspond à l'ID de la balise <li> du menu que l'on souhaite mettre en évidence -->
<put-attribute name="currentMenu" value="" type="string" cascade="true" /> </definition>
12.3.2.5. HistoriqueController
HistoriqueController est un Controller Spring permettant de gérer le clic sur le bouton « Retour » (correspondant à l’url /appli/retour).Celui-ci récupère l’URL appelant le retour et dépile cet élément de la liste des actions de l’utilisateur puis retourne l’élément précédent de la pile (dont l’URL sera appelée côté client). Si nécessaire, recharge la liste de résultats avec les critères conservés en mémoire.
12.3.3. Conservation des critères
En fonction du mode de navigation choisi, il peut être possible de conserver les critères saisis lors d’une recherche.Ceux-ci seront sauvegardés par session HTTP auprès du contexte de Spring qui les restituera à chaque constitution de la page.
12.3.4. Paramétrage
Ces différentes solutions sont paramétrables au niveau du fichier properties de l’application : displayBreadCrumb (booléen avec comme valeur par défaut, false) : affichage du fil d’Ariane ouvertureNouvelOnglet (booléen avec comme valeur par défaut, false) : navigation multi-
onglets conserveCriteres (booléen avec comme valeur par défaut, false) : conservation des critères
12.4. Export générique des tableaux de résultats
Date : 11/09/2023
Manuel du développeur
12.4.1. Problématiques
Générer un document XLSX (pas de limite sur le nombre de lignes) Possibilité d’afficher des données différentes du tableau de résultat (cas de colonnes où le
contenu est tronqué, pour gagner de la place)
12.4.2. Principe de fonctionnement
Les données récupérées sont stockées dans le contexte de Spring après l’exécution de la recherche.Une vue (au sens Spring) spécifique est définie afin de récupérer ces données et les générer sous la forme d’un fichier XLSX à l’aide de l’API POI.
12.4.3. Prérequis Java
Les données sauvegardées dans le contexte Spring doivent se présente sous la forme d’une List d’objets IExportable (NB : l’interface IModelListView étend cette classe).
L’implémentation de cet objet IExportable nécessite la déclaration des colonnes à afficher dans le fichier Excel ainsi que le mode de récupération des données via une Map<NomColonne, MethodeAAppeler>). Cette Map permettra de récupérer par introspection les données correspondantes pour chacun des objets appartenant à la liste.
Exemple d’implémentation des méthodes de IExportable :
@Overridepublic String[] getHeadersForExport() {
return new String[] { "numjob", "codbatch"};}
@Overridepublic Map<String, String> getMapContentMethodsForExport() {
return new HashMap<String, String>() {{
put("numjob", "getNumJob");put("codbatch", "getCodBatch");
}};
}
12.4.4. Application dans la JSP
Dans le DataTable, une classe CSS est nécessaire pour permettre le bon fonctionnement de l’export. Elle doit se présenter sous la forme « exportClassNomClasseObjetReference » où NomClasseObjetReference représente l’objet mappé de référence du Controller. Cette référence servira d’identifiant pour la liste de données à exporter ainsi que pour le nom du fichier Excel qui sera généré.
Date : 11/09/2023
Manuel du développeur
Exemple de déclaration de tableau exportable :
<table id="executionsBatchs" class="dataTableStandard avecTri avecFiltre exportClassCsBatchLance"> […]</table>
12.5. Ecrans de suivi des Batchs
Des écrans de suivi des batchs (criteresExecutions.jsp, listExecutions.jsp, detailExecution.jsp) seront présents dans chaque application utilisant la BAO v3.1. Ces écrans s’appuient sur les objets (CsBatchLance, CsBatchAppli, CsBatchErreur) et le Controller (SuiviBatchController) qui sont situés dans le framework-core.
Il est tout à fait possible de déroger à ces écrans et définir un nouveau Controller de suivi.
12.6. CSV
Dans le cadre de l’application QEE, des besoins d’imports et d’exports de fichiers CSV ont été identifiés. La librairie ‘OpenCsv’ a été choisie afin de répondre à ces besoins. Bien que la librairie soit tout à fait intégrable en l’état, un certain nombre de classes utilitaires ont été développées dans le Framework afin de faciliter les développements.
12.6.1. Imports
L’import des données à l’aide de fichier CSV s’appuie sur un objet de référence dont les propriétés à renseigner seront annotées (@CsvBindByName).
Ces annotations permettent de faire le lien entre l’entête de colonne (quel que soit son positionnement ; le nom de la colonne doit impérativement être en MAJUSCULES) et les données à récupérer et ainsi constituer un jeu de données (parser.getList()).Si une erreur se produit lors de la récupération d’une donnée (format incorrect, donnée non renseignée alors qu’elle est obligatoire) alors cette ligne n’est pas prise en compte mais l’erreur est tout de même stockée dans le parser (parser.getCapturedExceptions()).
Les données ne doivent pas nécessairement être des String, la librairie ‘OpenCsv’ permet des conversions automatiques vers des objets de types Integer, Long, Boolean, Short, Date, …
public void readCsv() throws IOException {FwkCsvParser<YourOrderBean> parser = new FwkCsvParser<YourOrderBean>();parser.parse(TestJCSV.csvFile, YourOrderBean.class);List<YourOrderBean> list = parser.getList();List<CsvException> capturedExceptions = parser.getCapturedExceptions();
}
Date : 11/09/2023
Manuel du développeur
public class YourOrderBean implements FwkCsvExport {@CsvBindByName(column = "SIRET")private Long siret;
@CsvBindByName(column = "CODE PLVT EDI")private String codePlvtEdi;
@CsvBindByName(column = "CODE PRIX")private String codePrix;
@CsvBindByName(column = "LIEU PREL")private String lieuPlvt;
@CsvBindByName(column = "DATE PRÉLÈVEMENT")@CsvDate("dd/MM/yy")private Date datePlvt;
@CsvBindByName(column = "HEURE PRÉLÈVEMENT")@CsvDate("HH:mm:ss")private Date heurePlvt;
}
12.6.2. Exports
Pour générer un fichier CSV, le framework s’appuie sur un ensemble d’objets héritant de la classe FwkCsvExport. Celle-ci possède deux méthodes :
getHeaders() : retourne un tableau (statique) de String représentant l’entête du fichier CSV getData() : retourne une représentation sous forme de tableau de String de la donnée à
exporter
NB : afin de conserver une cohérence des données, l’ordre des colonnes devra être identique dans les 2 méthodes !
Par défaut, le fichier généré sera un fichier temporaire mais il est tout à fait possible de paramétrer un fichier précis comme résultat de l’export.
public void writeCsv() throws IOException {List<FwkCsvExport> dataIn = getData();FwkCsvWriteFile fwkCsvWriter = new FwkCsvWriteFile();File writeCsv = fwkCsvWriter.writeCsv(dataIn);
}
public interface FwkCsvExport {String[] getHeaders();String[] getData();
}
Date : 11/09/2023
Manuel du développeur
12.7. Auto authentification avec Kerberos
ATTENTION : l’authentification Kerberos n’est pas possible si le serveur et le client correspondent à la même machine.
12.7.1. Paramétrage du serveur
Afin que le serveur applicatif puisse dialoguer avec le serveur nécessaire, il est nécessaire de créer un fichier ‘keytab’ à paramétrer dans le fichier de configuration de Spring Security.
Il est également possible de désactiver l’auto authentification Kerberos simplement à l’aide de la propriété ‘dev’ incluse dans le fichier de propriétés de l’application (aura pour valeur ‘false’ si l’on souhaite conserver l’authentification Kerberos et ‘true’ pour se connecter à l’aide d’un écran de login – sans mot de passe).
12.7.2. Paramétrage client
Afin de tester l’authentification Kerberos à partir de la session Windows courante, il faut effectuer les actions suivantes :
Dans Intenet Explorer : s’assurer que la case ‘Options Internet > Sécurité > Personnaliser le niveau > Authentification utilisateur > Connexion automatique …’ soit cochée ainsi que celle correspondant à ‘Options Internet > Avancé > Sécurité > Activer l’authentification intégrée’
Dans Firefox : aller à la page ‘about:config’ et ajouter le serveur applicatif à la liste des uri identifiées à la ligne ‘network.negotiate-auth.trusted-uris’
12.7.3. Création du fichier ‘keytab’
Pré-requis sous Windows : il est nécessaire d’avoir un fichier ‘krb5.ini’ dans ‘C:\Windows’ si l’on veut créer un fichier ‘keytab’ (le fichier ‘krb5.ini’ est en réalité identique au fichier ‘krb5.conf’ utilisé dans la BAO v2).
Ce fichier doit contenir les utilisateurs (et mots de passe) nécessaires à la communication avec le serveur Kerberos. En dév, l’utilisateur est ‘APP-KRB-DEV’, en intégration ‘APP-KRB-INT’ et en production ‘APP-KRB-PRO’. Chaque utilisateur devra être ajouté à l’aide de la commande suivante, à exécuter à partir du répertoire ‘bin’ de la ‘JRE’ utilisée.
C:\java\jdk1.6.0_27\bin> ktab -a <user> "<password>"
Par défaut, le fichier sera créé dans le dossier ‘C:\Users\<userWindows>’ et il faudra ensuite le transférer dans le répertoire ‘/appli/propappli’.
Date : 11/09/2023
Manuel du développeur
12.8. Utilisation courante
12.8.1. DataTables
Dans la version initiale, les DataTables s’affichaient suite à l’appel d’une méthode JavaScript bien précise.Ceci a été simplifié et l’appel aux DataTables – ainsi que ses options – se fait à partir de ses différentes classes CSS et à son identifiant (ces classes ne doivent pas être déclarées dans un des fichiers CSS) qu’il faudra positionner sur la balise ‘table’ de référence.
Pour le moment, les classes suivantes ont été définies (dans le fichier ‘datatable.js’) : dataTableStandard : déclaration d’un DataTable de base exportClass<ObjetRef> : déclaration d’un bouton ‘Excel’ permettant l’export du résultat de la
recherche (Attention : l’export s’effectue sur les données fournies par Hibernate et non pas sur ce qui est affiché, c’est-à-dire que les tris ne sont pas pris en compte par exemple) cf. § 7.6
Le bouton sera rajouté dans l’entête du tableau, permettant l’export des données.
avecTri : ce DataTable sera triableL’icône sera rajouté dans l’entête de chacune des colonnes du tableau.
avecFiltre : ce DataTable sera filtrable tableauRecherchePopup : ce DataTable sera positionné dans une popup, en conséquence les
valeurs possibles du nombre de lignes par page seront réduites treetable : ce DataTable sera un TreeTable masqueColmn : ce DataTable permettra de rendre invisible certaine colonne du tableau
Une nouvelle ligne apparaitra alors dans l’entête du tableau permettant de rendre visible ou invisible chacune des colonnes du tableau
12.8.2. Auto complétion
Dans la solution initiale, la méthode utilisée pour récupérer les informations nécessaires à l’auto complétion était basée sur un document XML. Cependant il serait plus simple de mettre en place un document JSON à retransmettre à la page HTML (la signature de la méthode Javascript n’évoluant pas par rapport à la version initiale présentée plus haut).
En effet, du point de vue du Controller il suffit d’effectuer la recherche en fonction des paramètres puis de sérialiser le résultat en JSON.NB : ne pas oublier l’annotation @ResponseBody !!!
Date : 11/09/2023
Manuel du développeur
Par exemple :
@RequestMapping(value = "/ac/numInt", method = RequestMethod.GET)@ResponseBodypublic Serializable getListAutocompleteNumint(@RequestParam(defaultValue = "") String saisie, @RequestParam(defaultValue = "false") Boolean sansRestrictionSaisie) {
List<Interlocuteur> commenceParNumInt = this.interlocuteurService.commenceParNumInt(saisie);
List<LovValues> listNumInt = new ArrayList<LovValues>();for (final Interlocuteur interlocuteur : commenceParNumInt) {
listNumInt.add(new LovValues() {@Overridepublic String getLibelle() {
return interlocuteur.getNomint1() + " - " + interlocuteur.getNomint2();
}
@Overridepublic String getCode() {
return interlocuteur.getNumint();}
});}return listNumInt.toArray(new LovValues[listNumInt.size()]);
}
12.8.3. Popups
Afin de réduire le nombre de fenêtres ouvertes, les popups seront gérées en tant que ‘dialog’ jQuery. L’ouverture de la ‘dialog’ se base sur le clic d’un bouton (ex : « Recherche interlocuteur ») ou un lien faisant référence à une nouvelle URL (« href ») – identifiée par la classe CSS ‘openRecherche’.
<form:input id="nomPrenomInterlocuteur" cssClass="valeurChamp" path="nomPrenomInterlocuteur"/><a class="valeurChamp openRecherche jq-personbutton-notext" href="${editUserUrl}">...</a>
Cependant, ici, cette URL ne sera pas appelée dans une nouvelle fenêtre mais redirigée vers le contenu de la div ‘dialog’.
// Renvoie le contenu de la page dans la Dialog$(document).on("click", ".openRecherche", function(event) { event.preventDefault();
// get dialog content $.get(this.href).done(function(data, textStatus, jqXHR) { $("#dialogRecherche").empty().append(data); $("#dialogRecherche").dialog("open"); });});
Date : 11/09/2023
Manuel du développeur
Dans notre exemple de recherche, le document renvoyé par la recherche devra également être redirigé vers la dialog. Pour cela, il suffit d’ajouter la classe ‘submitRecherche’ au bouton correspondant et ainsi le clic sur celui-ci permettra de poster le formulaire de recherche avant le retour du contenu dans la ‘dialog’.
// Renvoie le contenu du post de la recherche dans la Dialog$(document).on("click", ".submitRecherche", function(event) { event.preventDefault(); var form = $(this).closest("form"); // get dialog content $.post($(form).attr('action'), $(form).serialize()).done(function(data, textStatus, jqXHR) { $("#dialogRecherche").empty().append(data); $("#dialogRecherche").dialog("open"); });});
En conclusion, pour l’appel à une méthode de type ‘GET’, il faudra utiliser un bouton possédant la classe ‘openRecherche’ ; pour une méthode ‘POST’, il faudra utiliser un bouton ‘submitRecherche’.
NB : la ‘dialog’ s’ouvrira en fenêtre modale, ce qui veut qu’aucune action ne sera possible dans la page appelante.
12.8.4. Dialog jQuery
NB : les méthodes JavaScript permettant de gérer l’ouverture et la fermeture des ‘dialog’ se trouvent dans le fichier ‘framework.js’
12.8.4.1. Fenêtre d’attente
Lors de l’appel d’une méthode pouvant prendre un certain temps, il est nécessaire de bloquer toute action utilisateur supplémentaire. Pour cela, une fenêtre modale sera affichée tant que le serveur n’aura pas rendu la main.
Par exemple, lors du clic sur un bouton ‘Rechercher’, affichage de la fenêtre suivante :
L’appel sera fait en jQuery de manière implicite à partir du moment où le bouton ‘Rechercher’ possède la classe CSS ‘jq-searchbutton’.
12.8.4.2. Fenêtre d’alerte
Date : 11/09/2023
Manuel du développeur
Pour ouvrir une alerte dans une fenêtre modale, il faut utiliser la méthode « openAlertDialog(message) » où le message attendu devra être passé en paramètre (récupéré à partir du fichier properties).
/** * Ouvre la dialog d'affichage des alertes * * @param message */function openAlertDialog(message) {
$('#alertDialog').find('.messageDialog').html(message);$('#alertDialog').dialog('open');
}
12.8.4.3. Fenêtre de confirmation
Pour ouvrir une fenêtre modale de confirmation, il faut utiliser la méthode « openConfirmDialog(msg, textOui, textNon, actionOui, actionNon) ». Celle-ci attend en paramètre :
msg : le message principal à afficher dans la fenêtre (libellé récupéré depuis le fichier properties)
textOui : le texte correspondant au libellé du bouton « Oui » textNon : le texte correspondant au libellé du bouton « Non » actionOui : l’action JavaScript à appeler lors du clic sur le bouton « Oui » (redirection, appel
XHR, etc.) – la fermeture de la dialog étant déjà gérée, il n’est pas utile de la redéfinir ici actionNon : l’action JavaScript à appeler lors du clic sur le bouton « Non » (redirection, appel
XHR, etc.) – la fermeture de la dialog étant déjà gérée, il n’est pas utile de la redéfinir ici
/** * Ouverture d'une dialog de confirmation * * @param msg : message à afficher dans la dialog * @param textOui : libelle du bouton 'Oui' * @param textNon : libelle du bouton 'Non' * @param actionOui : action JavaScript à évaluer lors du clic sur le bouton 'Oui' * @param actionNon : action JavaScript à évaluer lors du clic sur le bouton 'Non' */function openConfirmDialog(msg, textOui, textNon, actionOui, actionNon) {
// initialisation des paramètres si non définisif (!msg) {
msg = "Etes vous sur de vouloir effectuer cette action ?";}if (!textOui) {
textOui = 'Oui';}if (!textNon) {
textNon = 'Non';}if (!actionOui) {
actionOui = function() {
Date : 11/09/2023
Manuel du développeur
$(this).dialog("close");return true ;
};} else {
var origActionOui = actionOui;actionOui = function() {
eval(origActionOui);$(this).dialog("close");return true ;
};}if (!actionNon) {
actionNon = function() {$(this).dialog("close");return false ;
};} else {
var origActionNon = actionNon;actionNon = function() {
eval(origActionNon);$(this).dialog("close");return false;
};}
$('#confirmDialog').find('.messageDialog').html(msg);var tabBtns = $('#confirmDialog').dialog( "option", "buttons" );tabBtns['Oui'] = {
text: textOui,click: eval(actionOui),
};tabBtns['Non'] = {
text: textNon,click: eval(actionNon),
};$('#confirmDialog').dialog( "option", "buttons", tabBtns);$('#confirmDialog').dialog('open');
}
12.8.5. Actions XHR
Une action XHR permet d’interroger le serveur en Ajax et ainsi rafraichir certaines parties de l’écran sans rafraichir l’entièreté de la page.
Afin d’être le plus générique possible, une méthode JavaScript a été introduite. Celle-ci prend en paramètre :
L’élément source de l’événement L’élément cible qui sera rafraichi (champ texte, LOV, page complète, div, …) L’action serveur qu’il faudra appeler (postage du formulaire auquel le champ est rattaché) La fonction JavaScript qui sera appelée au retour de l’appel serveur
Date : 11/09/2023
Manuel du développeur
function xhr(element, targetElement, actionServer, jsFunctionToCall) {// affichage de la fenêtre d'attente$('#waitingAreaForAjax').dialog('open');if (actionServer) {
// récupération du formulaire à postervar form = $(element).closest("form");// postage du formulaire auprès du serveur$.post(actionServer, form.serialize()).done(function(data) {
// appel de la méthode javascript en fonction des données renvoyées par le serveur (valeur d'un champ, page complète, ...)
if (jsFunctionToCall) {eval(jsFunctionToCall)(targetElement, data);
}// fermeture de la fenêtre d'attente$('#waitingAreaForAjax').dialog('close');
}).fail(function (readyState) {alert('Erreur lors de l\'execution du POST !!!\nErreur '
+readyState.status+ ' : '+readyState.statusText);$('#waitingAreaForAjax').dialog('close');
});} else {
console.log('Pas d\'action définie pour ce xhr !!!');$('#waitingAreaForAjax').dialog('close');
}}
12.8.6. Menus
Les menus sont gérés dans la JSP ‘nav.jsp’ sous forme de balises ‘<ul><li>’. Ces balises seront mises en ordre à l’aide du plugin jMenu.
$("#jNavFwk").jMenu({openClick : false,ulWidth :'150px',TimeBeforeOpening : 100,TimeBeforeClosing : 11,animatedText : false,paddingLeft: 1,effects : {
effectSpeedOpen : 150,effectSpeedClose : 150,effectTypeOpen : 'slide',effectTypeClose : 'slide',effectOpen : 'swing',effectClose : 'swing'
}});
// Mise en evidence du menu sélectionné$("#jNavFwk").find("#"+currentMenu).addClass('ui-widget-menu-accueil');
Par exemple:
Date : 11/09/2023
Manuel du développeur
Ces menus pourront évidemment être personnalisés à l’aide des feuilles CSS correspondantes.
12.8.7. Multi-select
Par exemple :
Pour gérer ce genre d’exemple, il suffit de définir un élément HTML <select> sur lequel on va appliquer le plugin multi-select.
// JSP
<form:form method="POST" action="${actionOnglet2}" commandName="formOnglet2" enctype="multipart/form-data">[…]
<form:select path="multipleSelect" multiple="multiple" items="${listDelegationsMultiple }" itemLabel="libelle" itemValue="code"/>[…]</form:form>// JavaScript
<script>$(document).ready(function() {
$('#multipleSelect').multiSelect();apply_widgets();
});</script>
Date : 11/09/2023
Manuel du développeur
12.8.8. Upload
Date : 11/09/2023
Manuel du développeur
12.8.9. Textarea élastique
Il est possible de rendre les zones de saisie de type textarea auto-extensible en fonction de leur contenu. Une limitation de la hauteur peut être définie, au-delà de laquelle la zone de texte ne s’agrandira plus.Pour ce faire, la BAO intègre le composant JQUERY « elastic »
13.Gestion des éditions avec BDOC
Le besoin d’éditions BDOC s’exprime de 2 façons : Besoin immédiat (asynchrone) avec un affichage de l’édition demandée dans le navigateur Besoin différé (asynchrone) : l’édition sera générée par un traitement régulier avec
possibilité d’envoyer l’impression sur une imprimante choisie
13.1. JAXB
La librairie JAXB permet de générer un ensemble de classes correspondant au besoin exprimé dans le fichier XSD. Les classes ainsi générées devront être utilisées afin de générer le document XML valide et l’envoyer par la suite vers BDOC.
Pour générer les classes :
Résultat :
Date : 11/09/2023
Manuel du développeur
13.2. Traitement asynchrone
Le traitement asynchrone d’édition se traduit par le postage d’un batch dans la table « CS_BATCH_LANCE » sur une action utilisateur. Ce batch sera ensuite traité par le système de batchs dédié suivant un ordonnancement défini.Suivant le principe de traitement des batchs de façon asynchrone, il faut définir un lanceur pour chacun des « CODBATCH » à traiter. Dans le cas de la génération de document BDOC, il faut utiliser un lanceur de type BdocXmlGenerator qui permettra de générer un fichier XML dans un répertoire local avant de le transférer vers le serveur BDOC via FTP pour qu’il soit traité par le service BDOC Online.
13.2.1. Exemple
public class GdaBdocXmlGenerator extends BdocXmlGenerator {@Autowiredprivate BdocService bdocService;
/** * Retourne un objet (JAXB) sérialisable à partir des données du batch * * @return */
@Overrideprotected Object getData() {
Date : 11/09/2023
Manuel du développeur
return this.bdocService.populateData(this.getBatchEnCours());}
}
13.2.2. Paramétrage du batch
# paramètres nécessaires à la génération du fichier XMLbatchTableCSTaskKey.RBE.replocal=/appli/pdfappli/chemin/vers/repertoire/local/creationXmlbatchTableCSTaskKey.RBE.racineFichierXML=BDOCbatchTableCSTaskKey.RBE.nomFichierXSD=file:/appli/pdfappli/chemin/vers/repertoire/XSD/monValidateur.xsdbatchTableCSTaskKey.RBE.namespace=http://aramis.primevere.agence.eau.fr/model/in/v1u0
# paramètres nécessaires au transfert du fichier XML vers BDOC via FTPbatchTableCSTaskKey.RBE.nameRemoteConnexion=SRVFTP_BDOCbatchTableCSTaskKey.RBE.repLog=/appli/tomc-gda/log/tomcat/batchTableCSTaskKey.RBE.b=truebatchTableCSTaskKey.RBE.repremote=/appli/data/chemin/vers/repertoire/distant
NB : les informations relatives à la connexion FTP doivent être définies dans le fichier « remote.properties ».
13.3. Traitement synchrone
Le traitement synchrone se traduit par la création du document dans un répertoire prédéfini (répertoire temporaire pour le moment) et éventuellement son ouverture dans le navigateur. La génération du document se fera à l’aide des API fournies par BDOC, soit par Web Service soit en se connectant directement au serveur BDOC.Quelque soit le type de génération choisie, il existe 2 possibilité de transmettre les données :
A l’aide d’un fichier XML généré (et valide) A l’aide d’objets JAVA résultant du fichier XSD correspondant (générés par JAXB)
L’ensemble des méthodes nécessaires à un traitement synchrone sont regroupées dans une même classe : BdocApiUtils qui sera injectée par Spring. Dans les différents cas, plusieurs paramètres sont nécessaires :
Le nom du domaine BDOC à laquelle appartient l’édition désirée Le nom du template BDOC associée à l’édition Les données à fournir : soit sous forme de fichier XML, soit sous forme d’objet JAVA (générés
par JAXB)
13.3.1. Génération directe
/** * Génération directe du PDF à partir d'un document XML * * @param domaine
Date : 11/09/2023
Manuel du développeur
* @param bdocTemplate * @param xmlSourceFilename * @return */public String generatePdfDirectWithXml(final String domaine, final String bdocTemplate, final String xmlSourceFilename) {
LOG.info("Appel direct à BDOC --- template " + bdocTemplate + " du domaine " + domaine + " avec le fichier " + xmlSourceFilename);
return composeDocument(connect(), domaine, bdocTemplate, xmlSourceFilename,null);
}
/** * Génération directe du PDF à partir d'un objet JAXB * * @param domaine * @param bdocTemplate * @param objectJaxb * @return */public String generatePdfDirectWithObject(final String domaine, final String bdocTemplate, final Object objectJaxb) {
LOG.info("Appel direct à BDOC --- template " + bdocTemplate + " du domaine " + domaine + " avec un objet de type " + objectJaxb.getClass().getSimpleName());
return composeDocument(connect(), domaine, bdocTemplate, null, objectJaxb);}
/** * Connects to Bdoc-Web */private JBApplication connect() {
try {JBConnection connection = new JBConnection(this.getHost(),
this.getPort());return new JBApplication(connection);
} catch (BDocException e) {e.printStackTrace();return null;
}}
/** * Composes document * * @param application * @param domaine * @param templateName * @param xmlSourceFile * @param objectJaxb * @return */private String composeDocument(final JBApplication application, final String domaine,
final String templateName, final String xmlSourceFile, final Object objectJaxb) { […]}
Date : 11/09/2023
Manuel du développeur
13.3.2. Génération via Web Service
/** * Génération via le WS Bdoc du PDF à partir d'un document XML * * @param templateName * @param domaine * @param xmlFileName * @return */public String generatePdfWsWithXml(final String templateName, final String domaine, final String xmlFileName) {
LOG.info("Appel au WS de BDOC --- template " + templateName + " du domaine " + domaine + " avec le fichier " + xmlFileName);
BdocWsClient client = getBdocWsClient(domaine, templateName);String documentPath =
client.getDocumentFromFile(SpringSecurityUtils.getSecuritySessionId(), new File(xmlFileName));
LOG.info("Le fichier " + documentPath + " a été généré avec succès !");return documentPath;
}
/** * Génération via le WS Bdoc du PDF à partir d'un objet JAXB * * @param templateName * @param domaine * @param objectJaxb * @return */public String generatePdfWsWithObject(final String templateName, final String domaine, final Object objectJaxb) {
LOG.info("Appel au WS de BDOC --- template " + templateName + " du domaine " + domaine + " avec un objet de type " + objectJaxb.getClass().getSimpleName());
try {BdocWsClient client = getBdocWsClient(domaine, templateName);ByteArrayOutputStream out = new ByteArrayOutputStream();SerializatorObject.serialize(objectJaxb, out);String documentPath =
client.getDocumentFromOut(SpringSecurityUtils.getSecuritySessionId(), out);LOG.info("Le fichier " + documentPath + " a été généré avec
succès !");return documentPath;
} catch (JAXBException e) {LOG.error("Erreur lors de la sérialisation de l'objet de type " +
objectJaxb.getClass().getSimpleName(), e);return null;
}}
private BdocWsClient getBdocWsClient(final String domaine, final String templateName) {
return new BdocWsClient(this.getUrlWebService(), domaine, templateName);}
Date : 11/09/2023
Manuel du développeur
14.Système de batchs
14.1. Fonctionnement
Le système de batchs de la BAO v3.1 s’appuie sur ce qui avait été déjà fait dans la version 2.7 de la BAO. Celui-ci s’articule autour de Spring avec des déclenchements périodiques gérés par Quartz (QuartzJobBean).
Chaque Job Quartz défini sera associé à une tâche (IFwkScheduledTask) qui elle-même se verra rattacher 2 éléments : un déclencheur de tâche (IFwkDeclencheur) et un exécuteur de tâche.
Les déclenchements périodiques de Job sont généralement élaborés à partir d’expression Cron.
Date : 11/09/2023
Manuel du développeur
A ce moment-là, le déclencheur associé vérifie si le traitement doit s’effectuer ou non (méthode isEnclenche()). Si oui, alors l’exécuteur entre en action (méthode executeTraiement()) avec éventuellement des traitements pré et/ou post traitement.
14.1.1. Utilisation d’un lanceur
Dans le cas de batchs postés dans la table ‘CS_BATCH_LANCE’, seul une tâche est définie (avec un déclencheur, l’enregistrement est présent en base de données, et un exécuteur, exécution d’un traitement batch) alors que les traitements diffèreront si des batchs différents ont été postés au même moment.Pour différencier les différents traitements, la notion de ‘Lanceur’ a été introduite au niveau de l’exécuteur de batch (FwkBatchExecutor). Ce lanceur correspond au traitement spécifique d’un batch (clé <CODAPP, COBATCH> dans la table ‘CS_BATCH_LANCE’) et il faudra donc instancier un ‘lanceur’ par batch (dans le sens ‘CS_BATCH_LANCE’).
14.2. Configuration XML
Déclaration du Job :<bean name="job.purgeBatchLance" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="fr.eau.rmc.framework.tacheAsynchrone.FwkBatchJob" />
<property name="jobDataAsMap"><map>
<entry key="taskFactory" value ref="scheduledTaskFactory.purgeBatchLance" />
</map></property>
</bean>
Déclaration de la Task :<bean id="scheduledTaskFactory.purgeBatchLance" class="fr.eau.rmc.framework.tacheAsynchrone.IScheduledTaskFactory">
<lookup-method name="getNewInstance" bean="task.purgeBatchLance"/>
</bean>
<bean id="task.purgeBatchLance" class="fr.eau.rmc.gda.tachesAsynchrones.GdaScheduledTask" parent="baoTask" scope="prototype">
<property name="declencheur" ref="batchDeclencheur.true" /><property name="executor" ref="batchExecutorPurgeBatchLance" /><property name="taskKey" value="dureeRetention" />
</bean>
Date : 11/09/2023
Manuel du développeur
Déclaration du Declencheur :<bean id="batchDeclencheur.true" class="fr.eau.rmc.gda.tachesAsynchrones.GdaDeclencheurTrue" scope="prototype" />
Déclaration de l’Executor<bean id="batchExecutorPurgeAppli" class="fr.eau.rmc.gda.tachesAsynchrones.executors.GdaExecutorPurgeRepAppli" scope="prototype" />
Cas du FwkBatchExecutor :<!-- Executor pour les batch lancés --><bean id="batchExecutor" class="fr.eau.rmc.framework.tacheAsynchrone.FwkBatchExecutor" scope="prototype">
<!-- Déclaration d'une map de Lanceurs associés au BatchExecutor
Liste des batchs déclenchés par la table CS_BATCH_LANCE--><property name="lanceurs">
<map><entry key="BASLOT" value-ref="lanceur.batchProckStock"></entry><entry key="GARATT" value-ref="lanceur.batchProckStock"></entry><entry key="SLDJUR" value-ref="lanceur.batchProckStock"></entry><entry key="RAZTAB" value-ref="lanceur.batchProckStock"></entry><entry key="SLDCVT" value-ref="lanceur.batchProckStock"></entry><entry key="SLDOPR" value-ref="lanceur.batchProckStock"></entry><entry key="GLINFO" value-
ref="lanceur.batchInterface.GLINFO"></entry><entry key="LOIPA" value-ref="lanceur.batchInterface.LOIPA"></entry><entry key="RELANC" value-
ref="lanceur.batchInterface.RELANCE"></entry><entry key="TOIPA" value-ref="lanceur.batchInterface.TOIPA"></entry><entry key="OPELOT" value-
ref="lanceur.batchInterface.OPELOT"></entry><entry key="NODASI" value-
ref="lanceur.batchInterface.NODASI"></entry><entry key="NODASF" value-
ref="lanceur.batchInterface.NODASF"></entry> <entry key="INFOMB" value-ref="lanceur.batchInterface.INFOMB"></entry>
<entry key="CPP" value-ref="lanceur.batchInterface.CPP"></entry><entry key="EDPLIQ" value-
ref="lanceur.batchInterface.EDPLIQ"></entry><entry key="EMIMDT" value-
ref="lanceur.batchInterface.EMIMDT"></entry><entry key="EMIOR" value-ref="lanceur.batchInterface.EMIOR"></entry><entry key="RETSIR" value-
ref="lanceur.batchInterface.RETSIR"></entry><entry key="RTSIOR" value-
ref="lanceur.batchInterface.RTSIOR"></entry><entry key="RCPFIC" value-
ref="lanceur.recupFichiersConvention"></entry></map>
</property>
</bean>
Date : 11/09/2023
Manuel du développeur
Déclaration du déclenchement (avec une expression Cron) :<bean id="cronTrigger.purgeBatchLance" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="job.purgeBatchLance" /><property name="cronExpression" value="<cronExpression>" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="triggers">
<list><!-- liste des Triggers à exécuter --><ref bean="cronTrigger.purgeRepAppli" />
[…] </list>
</property></bean>
Dans un souci de lisibilité, l’ensemble des batchs définis (ainsi que tous leurs composants) seront déclarés dans un fichier XML distinct du fichier de configuration Spring de l’application.Il suffira de l’importer au besoin dans celui-ci :
<!-- ######################### Batchs ######################### --><import resource="appBatchJobs.xml" />
14.3. Exemples de cas fréquents :
Certains traitements batchs peuvent être génériques dans leur fonctionnement et ainsi être partagés par plusieurs applications.Cela est notamment le cas pour les traitements de fichiers XML (import et export). C’est pour cette raison que des briques ont été mises en place au niveau de la BAO afin de minimiser les développements spécifiques par application.
14.3.1. Export (génération) de fichier XML
La génération d’un fichier XML est surtout utilisée dans deux cas pour les besoins de l’Agence de l’Eau :
à destination de BDOC à des fins d’édition à destination d’une autre application en tant qu’interface
Le lanceur (cf § 13.1.1) dédié (FwkXmlGeneratorLanceur) permet de : récupérer les données (méthode getData()) en fonction du batch courant (et de ses
paramètres) générer un fichier XML à partir de celle-ci (méthode constructExportXmlFile()) valider le fichier généré avec la grammaire XSD associée (méthode getValidyOfXmlToXsd())
Date : 11/09/2023
Manuel du développeur
éventuellement le transférer en FTP sur un autre serveur (méthode putFileOnServer()) tout en gérant l’état d’avancement du traitement du batch
14.3.2. Import de fichier XML
L’import de fichier XML est principalement utilisé dans le cadre d’interface avec d’autres applications.
Le lanceur dédié (FwkXmlImportLanceur) permet de : Lister les fichiers à importer à partir d’un répertoire donné (local ou distant via FTP) et d’un
nom de fichier (ou un pattern) (méthode getListFichierATraiter()) Valider chaque fichier à l’aide de la grammaire XSD associée (méthode
getValidyOfXmlToXsd()) Convertit le fichier en données JAVA (avec JAXB) pour la suite du traitement (méthode
getDataFromFile()) Traitement des données récupérées (méthode traitementSucess()) Déplacement du fichier dans le répertoire correspondant à la réussite du traitement
(méthode deplacementFichier()) Tout en gérant l’état d’avancement du traitement du batch
15.Bibliothèque logicielle JAVA
Le tableau ci-dessous énumère les principales librairies JAVA utilisées par la BAO v3.1.
Librairie Version FonctionnalitéSpring 4.1.4 ConteneurSpring MVC 4.1.4 PrésentationSpring Security 3.2.5 SécuritéSpring Security Kerberos 1.0.0.RC
1Intégration Spring Security avec Kerberos
Apache Tiles 3.0.1 PrésentationJstl 1.2 Présentation
Spring Data JPA 1.4.1 Accès base de donnéesHibernate 4.2.5 ORMCommons DBCP 1.4 Pool connexions JDBCLdap SDK 4.1 Accès LDAP
Commons Lang 3.4 UtilitairesJoda Time 2.7 Utilitaires de manipulation de dateApache Http Client 4.5.2 Utilitaire appels HTTP
Date : 11/09/2023
Manuel du développeur
Librairie Version FonctionnalitéCommons Net 3.3 Client HTTPQuartz 2.2.1 Planification de tâchesJava Mail 1.4 Envoi de mail
Jaxb 2.2.11 Manipulation de XMLJackson 2.1.4 Manipulation de JSON, CSV, …POI 3.10.1 Manipulation de XLSX, DOCX, …jWordConvert V2015R Conversion Word vers PDFOpen CSV 3.8 Manipulation de CSVAxis 2 1.6.2 Support WebService
SLF4J 1.7.5 Gestion des logs
Jasper Reports 5.6.1 Editions JASPERBdoc interactive 5.3 Editions BDOC (partie interactive)
Librairie intégrée, mais non utilisée.Bdoc Web 5.3 Editions BDOC (partie Bdoc Web)
Librairie utilisée pour les éditions directes depuis une application métier)
16.Bibliothèque JavaScript
16.1. Plugins jQuery utilisés
Plusieurs plugins jQuery sont utilisés dans la BAO v3.1 : Jquery UI pour le rendu global à l’écran (version 1.11.2) Jquery Datatables pour l’affichage des tableaux de résultats (version 1.9.4) Jquery multi-select pour « transvaser » les éléments d’une liste à une autre (version 0.9.11) Jquery chosen pour l’affichage des listes de valeurs (version 0.9.11) Jmenu pour l’affichage du menu (version 2.0) Jquery UI timepicker pour la saisie d’une date horodatée (version 1.2) Jquery mask pour créer des masques de saisie sur les champs (version 1.7.7) Jquery numberMask pour créer des masques de saisie sur des champs numériques
(version ???) Jquery treetable pour afficher des tableaux de résultats sous forme d’arbre (version 3.0.1) Jquery uploadfile pour permettre l’upload de fichier vers le serveur (version 3.1.10) TableTools (version 2.0.0) Jquery Elastic pour rendre les zones de saisies de type textearea auto extensible en fonction
du contenu (version 1.6.3)
Date : 11/09/2023
Manuel du développeur
En dehors des plugins UI, DataTables, multi-select (qui ont dû être modifiés pour des besoins spécifiques), tous les fichiers JS correspondant ont été rassemblés dans un seul : aermc-jquery-plugins.min.js.
NB : afin de simplifier l’inclusion des différents JS dans les pages JSP, toutes les déclarations ont été regroupés dans un même JSP : importJs.jsp.
16.2. Descriptif du contenu des fichiers JS spécifiques
16.2.1. ajaxForm.js
Ce script contient l’ensemble des méthodes nécessaires à la validation de formulaire de manière asynchrone. Celles-ci seront principalement utilisées dans les écrans de type ‘fiche’ avec des onglets ; en effet, le contenu de chaque onglet doit être validé avant l’affichage du prochain, afin de ne pas avoir des données incohérentes inter-onglets.
Les méthodes définies dans ce script sont les suivantes :
ajaxSubmitForm : valide le formulaire passé en paramètre à l’aide d’une requête AJAX et renvoie les erreurs fournies par le serveur
clearErrors : efface les erreurs (liées aux champs) dues à une validation précédente displayErrorsInOnglet : affiche l’erreur renvoyée par le serveur à côté du champ concerné getAjaxGetContents : fait appel à une URL en AJAX et renvoie ce contenu au sein d’une balise
<div> getDivOngletFormLi : renvoie le <div> (jquery tab) correspondant à la balise <li> donnée getDivOnglets : renvoie le <div> correspondant à l’ID passé en paramètre ou par défaut celui
possédant la classe CSS ‘onglets’ getDivOngletsAffiche : renvoie le <div> correspondant à l’onglet affiché (courant) getDoc : renvoie le document (au sens CSS) contenu dans la frame passé en paramètre isDivDisplay : vérifie si le <div> passé en paramètre est bien celui qui est affiché (le courant) valideOngletsEtSubmitForm : valide l’ensemble des formulaires de chacun des onglets avant
de valider le formulaire global (ne comprenant pas les onglets !)
16.2.2. cookies.js
Ce script permet de gérer les différents cookies de l’application en JavaScript.
Les méthodes définies dans ce script sont les suivantes :
ExistSession : vérifie l’existence du cookie correspondant à la session en fonction de son ID passé en paramètre
GetCookie : renvoie la valeur du cookie correspondant au nom passé en paramètre
Date : 11/09/2023
Manuel du développeur
GetCurrentSession : renvoie la valeur correspondant à l’ID de la session stockée dans un cookie
RemoveSession : supprime l’ID de session passé en paramètre de la liste des cookies SetCookie : permet de renseigner un cookie suivant son nom et sa valeur SetSessionId : permet de renseigner dans un cookie l’ID de la session passé en paramètre
16.2.3. datatable.js
Ce script fournit le nécessaire pour afficher un tableau de résultats sous la forme d’un DataTable.
Dans la première partie du fichier sont déclarés un ensemble de types qui peuvent être utilisés dans le DataTable. En effet, le tri s’effectue sur le contenu de chaque cellule qui est considéré en tant que ‘String’ et qui induit des problèmes de tri dans les cas où on affiche des nombres ou des dates.
Par la suite, on retrouve la définition des paramètres de base servant à la création d’un DataTable.
Les méthodes définies dans ce script sont les suivantes :
applyParamsDataTable : créer un DataTable correspondant à l’identifiant passé en paramètre à partir des paramètres de configuration donnés (c’est également dans cette méthode que l’on définit l’URL à appeler pour les exports Excel génériques)
calculTotalPourFooter : effectue la somme des données de la colonne donnée pour la page courante (NB : ne pas oublier de définir le <tfoot> dans le <table>)
customParamsById : ajuste les paramètres du DataTable suivant son identifiant (définition des types pour les colonnes qui ne correspondent pas à des ‘String’, …)
16.2.4. dialogRecherche.js
Ce script contient l’ensemble des méthodes nécessaire au bon fonctionnement d’une fenêtre modale (jQuery dialog) de recherche.
Les méthodes définies dans ce script sont les suivantes :
closeRechercheDialog : ferme la dialog de recherche openRechercheDialogEvent : définit l’événement qui déclenchera l’ouverture de la dialog et
son alimentation par une URL de type « GET » postRechercheDialogEvent : définit l’événement qui déclenchera l’alimentation de la dialog
par une URL de type « POST »
16.2.5. framework.js
Ce script contient diverses méthodes qui peuvent être utiles dans les JSP.
Date : 11/09/2023
Manuel du développeur
Les méthodes définies dans ce script sont les suivantes :
afficheErreur : affiche les erreurs globales (en haut au milieu de la page, en rouge) afficheIdEcran : affiche l’ID de l’écran sous la forme d’une infobulle à côté du bouton de
déconnexion afficheInfos : affiche les messages d’information (en haut au milieu de la page, en noir) applyDisabledInput : applique des modifications sur les champs en lecture seule
(désactivation du clic sur les ‘radio’ ou les ‘checkbox’ par exemple) clone : clone l’objet JavaScript passé en paramètre closeWaitingDialog : ferme la fenêtre modale de mise en attente consoleLog : log les informations dans la console du navigateur (si celle-ci est active) dateValide : vérifie si la date passée en paramètre existe bien doOpen : ouvre l’URL passée en paramètre, dans un nouvel onglet s’il le faut doRetour : exécute une action lors du clic sur le bouton ‘Retour’ : si on se trouve dans un
nouvel onglet (bouton « » du navigateur grisé), on ferme l’onglet sinon on retourne à la page précédente (équivalent au bouton « » du navigateur)
fireChangedEvent : propage un événement indiquant au formulaire qu’un champ a été modifié (utile pour indiquer à l’utilisateur qu’il a modifié un champ sans avoir enregistré)
formatDDmmYYYY : formate une date en JavaScript formatDoubleWith0JS : formate un nombre en tant que ‘String’ en précisant le nombre de
décimales attendues getDocHeight : récupère la hauteur du ‘document’ de la page isDivInFicheForm : vérifie que le <div> recherché est bien positionné dans un écran de type
fiche isMsie : vérifie que le navigateur utilisé est Internet Explorer mainmenu : applique l’animation du jMenu openAlertDialog : ouvre une fenêtre modale pour afficher le message passé en paramètre openCancelDialog : ouvre une fenêtre modale de confirmation faisant suite à un clic sur le
bouton retour après avoir modifié la valeur d’au moins un champ openCloseTabDialog : ouvre une fenêtre modale de confirmation de fermeture d’onglet openConfirmDialog : ouvre une fenêtre modale de confirmation (avec le message passé en
paramètre ainsi que les actions à effectuer lors du clic sur les boutons ‘Oui’ ou ‘Non’) openWaitingDialog : ouvre la fenêtre modale de mise en attente parseISO : crée une date au format ISO à partir d’une ‘String’ removeFromArray : supprime la valeur recherchée dans le tableau donné resizeDiv : redimensionne les différents composants de la page (suite à un
redimensionnement de fenêtre par exemple) et ajoute des ‘scroll’ si nécessaire resizeDivSection : redimensionne le composant ‘section’ resizeDivsFiche : redimensionne les composants sur un écran de type ‘fiche’ resizeDivsList : redimensionne les composants sur un écran de type ‘liste’ resizePanelButtonDialog : réajuste les classes CSS dans le cadre de l’affichage d’une fenêtre
modale avec un unique bouton (centrage de celui-ci)
16.2.6. jq-styled-input.js
Date : 11/09/2023
Manuel du développeur
Ce script permet de mettre en forme les boutons avec des icônes.Attention, les styles ci-dessous ne définissent pas le libellé du bouton, ceux-ci sont définis en tant que message Spring.
Exemples de styles CSS à utiliser :
jq-savebutton :
jq-cancelbutton :
jq-icon-seek-prev :
jq-icon-seek-next :
jq-searchbutton :
jq-searchbutton-notext :
jq-icon-triangle-s :
16.2.7. jqueryWidgetFramework.js
Ce script contient l’ensemble des méthodes nécessaires à la mise en forme des composants jQuery.
Les méthodes définies dans ce script sont les suivantes :
apply_multiselect : permet la mise en forme des éléments de type ‘multiselect’ (basculement des éléments d’une liste à une autre)
apply_togglebutton : permet la mise en forme des éléments pliables/dépliables (par exemple les critères de recherche)
apply_upload : permet la mise en forme d’un composant de type ‘upload’ apply_widgets: permet la mise en forme de divers composants jQuery (‘datepicker’, ‘tabs’, …)
et l’initialisation des comportements par défaut de ceux-ci applyChosen : permet la mise en forme des composants ‘lovChosen’ (effectif sur les listes de
valeurs, multiple ou non) applyMask : positionne des masques sur les champs de saisie (date, nombre) autocompletion : positionne un comportement d’auto-complétion sur un champ donné fireChangeCritereDate : propage le changement de date dans un écran de critères
(nécessaire pour la recopie de valeurs dans des champs de saisie de type ‘du’ / ‘au’)
16.2.8. xhrUtils.js
Ce script contient l’ensemble des méthodes nécessaires à l’exécution de requêtes en XHR.
Date : 11/09/2023
Manuel du développeur
Les méthodes définies dans ce script sont les suivantes :
afficheCacheChampEtLabel : permet d’afficher ou de masquer un champ ainsi que le libellé qui lui est associé
changeSelectValues : modifie les valeurs d’une liste de valeurs à partir des éléments retournés par le serveur
changeValAndColor : modifie la valeur du champ ainsi que la couleur de fond (méthode créée pour générer des exemples, peut être modifiée pour répondre au besoin)
getElement : renvoie l’objet jQuery correspondant au composant cible recherché gotoPage : redirige la page vers l’URL renvoyée par le serveur modifValeurInput : modifie la valeur d’un champ à partir de la donnée renvoyée par le
serveur refreshElement : rafraichit l’élément avec le contenu HTML renvoyé par le serveur refreshElementAndHidden : rafraichit l’élément cible avec la donnée renvoyée par le serveur
ainsi que le champ ‘hidden’ associé refreshElementOnglet : rafraichit l’onglet courant avec le contenu HTML renvoyé par le
serveur refreshElements : à l’aide d’une Map postée par le serveur, rafraichit les champs indiqués
avec les données correspondantes refreshDataTable : remplace un DataTable existant par un autre fourni par le serveur (à l’aide
d’une vue Spring par exemple) xhr : exécute l’action XHR (POST) puis la commande JavaScript donnée avec les données
renvoyées par le serveur en paramètres de celle-ci