Canigó - Servei de Configuració 2.3.x
SERVEI DE CONFIGURACIÓ
IntroduccióPropósitEl Servei de Configuració té com a propósit la configuració de les propietats de qualsevol component de l'aplicació. Aquestes propietats poden ser tant referències a altres objectes com propietats internes (atributs) que necessiten per al seu correcte funcionament. Aquesta configuració es pot realitzar de 2 formes diferenciades: 1. Forma tradicional Una de les formes tradicionals d'estructurar el nostre codi és que siguin els components qui realitzin la localització i instanciació dels serveis o components que han de cridar dins la seva lógica (així com l'obtenció de les propietats internes) En aquesta aproximació, encara utilitzada però poc pràctica, es pot fer ús del patró factory per amagar la complexitat de la instanciació i obtenció de les instàncies requerides. Tot i això, aquest mecanisme l'han de realitzar els clients, els quals han de conéixer quines dependències tenen els components entre sí i per tant conéixer quina seqüència han de seguir per la inicialització de cadascun d'ells. En altres casos, l'especificació J2EE fa ús de JNDI com a mecanisme d'obtenció de referències d'objectes. Aquesta implementació requereix molts canvis si es fa un canvi d'entorn i deixa d'usar-se JNDI. 2. Patró 'Dependency Injection' Mitjançant l'ús del patró Dependency Injection el nostre aplicatiu només ha de definir les dependències, i deixar que un codi extern (el framework) s'encarregui de la complexitat d'instanciació, inicialització i seqüència de les referència que requereixin els nostres components. Aquest patró elimina totes les problemàtiques de la forma tradicional. Canigo es basa en l'ús del patró 'Dependency Injection' mitjançant l'ús de Spring. El més important és que el seu ús no és intrusiu, ja que en qualsevol moment podríem canviar de mecanisme intern sense afectar el nostre codi. Context i Escenaris d'ÚsEl Servei de Configuració es troba dins dels serveis de Propósit General de Canigo. El seu ús és imprescindible en Canigo, ja que tots els serveis utilitzen la gestió de configuració definida per aquest servei. Versions i DependènciesLes dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui va dirigitEs recomana una lectura prèvia de http://static.springframework.org/spring/docs/1.2.x/reference/beans.html Documents i Fonts de ReferènciaPer a començar a fer ús del Servei de Configuració és imprescindible la lectura dels següents documents:
I es recomana la lectura de les següents referències:
GlossariDependency Injection Patró de disseny que estableix un nivell d'abstracció mitjançant la definició de interfícies i eliminant la dependència entre components mitjançant un codi extern que injecta aquestes dependències de forma transparent. Existeixen 3 formes del patró: 'setter', 'constructor' i 'interface based injection'. Descripció DetalladaArquitectura i ComponentsEls components podem classificar-los en:
Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls: JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-configuration/apidocs/index.html Instal.lacióLa instal.lació del servei requereix de la utilització de la llibreria 'canigo-services-configuration' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'. ConfiguracióLa configuració del Servei de Configuració es realitza principalment amb el mecanisme d'injecció utilitzat. En el cas de Spring consultar http://static.springframework.org/spring/docs/1.2.x/reference/beans.html. A més de la configuració ja existent mitjançant el mecanisme d'injecció base (Spring) s'ofereixen les següents facilitats:
En cap lloc s'especificarà quin és el servei de configuració que es fa servir per l'obtenció dels components i la seva injecció. Spring, per defecte cercarà totes les configuracions que s'hagin definit amb les classes configuradores (PropertyPlaceHolderConfigurer,...). Configuració Segons l'EntornA partir de la versió 2.3 de Canigó es substitueix la configuració en funció del nom de la màquina per una configuració en funció de l'Entorn. D'aquesta manera no cal coneixer a priori els noms de les màquines on s'instal.larà l'aplicació i es solucionen els problemes derivats de la instal.lació en clusters, on dues o més màquines han de compartir la mateixa configuració. Definició de l'Entorn El concepte d'Entorn es defineix mitjançant la propietat entorn, a la que s'assigna el valor corresponent a l'arrancar la màquina virtual Java java ... -Dentorn=pro Amb aquesta configuració en funció de l'entorn, el servei de configuració de Canigó cerca la configuració adient en els següents arxius de propietats:
el primer és depenent de l'Entorn ja que el primer element del path es defineix utilitzant la propietat definida a l'arrancar la màquina virtual. Si aquest arxiu no existeix, s'intenta recuperar la segona configuració "per defecte". Configuració del servei de Configuració Atributs:
HostPropertyResourceConfigurer Propietats:
HostPropertyPlaceholderConfigurer Propietats:
Exemple: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="configurationService"class="net.gencat.ctti.canigo.services. configuration.springframework.beans.factory.config.HostPropertyPlaceholderConfigurer"> <property name="basePropertyFiles"> <list> <value>classpath:jdbc/jdbc.properties</value> </list> </property> </bean> </beans> En aquest exemple, si el paràmetre entorn és "pro" es cercarà l'arxiu de configuració 'pro/jdbc/jdbc.properties'. En cas que no es trobi aquest arxiu es cercaria 'jdbc/jdbc.properties'. Configuració de variables del sistemaÚs de múltiples fitxers de configuracióSovint és útil dividir el conjunt de definicions en múltiples fitxers XML. Mitjançant alguns dels configuradors és possible referenciar més d'un fitxer. Tot i això és possible que volguem repartir els fitxers en diferents parts. En aquest cas podem fer ús de l'element 'import'. <beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> . . . En aquest exemple, les definicions externes de beans s'estan carregant des de 3 fitxers, services.xml, messageSource.xml i themeSource.xml. Tots els paths són considerats relatius al fitxer de definició que realitza la importació, per tant en aquest cas services.xml ha d'estar en el mateix directori o classpath location que el fitxer que realitza la importació, mentres que messageSource.xml i themeSource.xml han d'estar en el directori resources per sota del fitxer que realitza la importació. Es pot comprovar que una barra inicial és ignorada, però atés que aquests paths són relatius, probablement és millor no usar la barra. Els continguts dels fitxers importats han de ser definicions XML de beans completament vàlides d'acord amb el DTD, incloent l'element bean de més alt nivell. ![]() Utilització del ServeiDegut a que la configuració dels elements de l'aplicació o dels serveis es realitza de forma externa mitjançant el patró 'Dependency Injection' no és convenient l'obtenció de propietats des dels clients. Aquesta obtenció es realitza de forma transparent fora de les classes de l'aplicació. Obtenció de valors de configuració des de les classesSi en algun cas es requereix de l'obtenció de propietats externes des del nostre codi (opció no recomanada), podem fer ús de la interfície 'ConfigurationService'. package net.gencat.ctti.canigo.services.configuration; ... public interface ConfigurationService { public String getProperty(String key) throws ConfigurationServiceException; } Es proporciona una implementació basada en Spring, anomenada 'HostPropertyPlaceholderConfigurer' El mètode que s'exposa és un i es correspon a la següent signatura:
Si el valor de configuració no es troba, es llença una excepció de tipus 'ConfigurationServiceException' indicant que el valor que es busca no s'ha trobat. Per a utilitzar-lo, només cal que afegim al fitxer XML de definició de beans el nostre servei de configuració i injectem als nostres beans el servei de configuració, tal i com es mostra a continuació: ... <bean id="myBean" class="com.myapp.model.MyBean"> <property name="configService" ref="configurationService" /> </bean> ... <bean id="configurationService" class="net.gencat.ctti.canigo. services.configuration.springframework.beans. factory.config.HostPropertyResourceConfigurer."> <property name="basePropertyFiles"> <list> <value>classpath:config.properties</value> </list> </property> </bean> ...
I des de la classe que vol fer ús del servei: ... public class MyBean { private ConfigurationService configService = null; public String myMethod() throws Exception { String configValue = configService.getProperty("config.value");* ... } } Encriptació de propietatsDes de la versió 2.3.13 del Framework el mètode 'getProperty()' de 'HostPropertyPlaceholderConfigurer' es capaç de recuperar el valor de propietats encriptades en els fitxers de configuració. ... propietat.encriptada={AES:0123456789ABCDEF} ... On 'AES' és l'algoritme a utilitzar (veure javax.crypto.Cipher) i '0123456789ABCDEF' es el valor en hexadecimal de la propietat encriptada. Per a obtenir el valor en hexadecimal de qualsevol String encriptada es pot utilitzar una crida al mètode 'encrypt()' de la classe 'Crypto' tal com s'indica a continuació: import net.gencat.ctti.canigo.services.configuration.springframework.beans.factory.config.Crypto; ... String valorEncriptat = Crypto.encrypt("AES", "això_és_un_password"); ... Integració amb Altres ServeisEl Servei de Configuració és usat per tots els Serveis per tal de definir les seves propietats de forma externa. En comptats casos, algunes de les implementacions sobre el que es troben els serveis de Canigo no permeten la definició de les seves propietats mitjançant la injecció. Servei de Traces basat en Log4JEn el Servei de Traces Canigo ofereix la possibilitat de configurar diferents fitxers segons el host en el que es troba l'aplicació mitjançant la classe 'net.gencat.ctti.canigo.services.logging.log4j.xml.HostDOMConfigurator'. Per a més referència consultar el document 'Servei de Traces'. Preguntes FreqüentsExemplesExemple de ConfiguracióImaginem que tenim un DAO que fa ús d'una factoria de sessions per llençar els objectes de negoci contra una capa de persistència i persistir les dades. Aquesta relació es representa en la següent figura:
![]() Aquests elements i relacions es representen amb un fitxer XML amb una estructura definida. En el nostre exemple, tindríem un fitxer XML amb la següent informació:
A continuació es mostra aquest fitxer: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--Element estructural que representa la factoria de sessions--> <bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> </bean> <!--Element estructural que representa el DAO--> <bean id="myDao" class="test.dao.hibernate.impl.HibernateCategoryDAOImpl"> <!--Relació entre el nostre DAO i la factoria de sessions--> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans> Tal i com defineix Spring, cada 'bean' representa un element o actor del nostre aplicatiu i es representa al XML com un node <bean/>. Els valors més importants a configurar en aquest node són:
Per a una explicació en més detall dels diferents atributs del node <bean/> consultar la url: http://static.springframework.org/spring/docs/1.2.x/reference/beans.html. A part els atributs del node <bean/>, aquest pot tenir subnodes, que principalment representen altres 'beans' que necessita per a treballar, és a dir, els col.laboradors. En el nostre exemple hem definit un node <property/> amb el nom 'sessionFactory' que fa referència al col.laborador: el 'bean' amb nom 'sessionFactory' que representa la factoria de sessions.. El nom de les <property/> es correspon amb els noms de les variables d'instància del propi 'bean' La definició d'aquests fitxers de configuració XML servirà posteriorment per anar a buscar en l'aplicatiu, en el cas que sigui necessari, les instàncies dels beans corresponents. Hi haurà doncs un procès automàtic que llegirà aquests fitxers i farà de punt d'entrada per a qualsevol petició de 'bean'. Aquest procès l'executa una entitat que es coneix com a 'BeanFactory'. L'existència d'aquesta factory en una aplicació J2EE bé representada per una classe que es diu 'WebApplicationContext'. ??* Nota: L'existència d'aquesta factory és totalment transparent al desenvolupador, ja que no s'ha de preocuperar de conèixer, a no ser que sigui necessari, de l'existència de la mateixa, si no de la correcta configuració dels XML per a que aquesta resolgui les dependències entre col.laboradors correctament. Exemple de Configuració amb Definició segons l'EntornEn l'anterior exemple hem vist la forma de configurar la nostra aplicació en funció dels fitxers XML que representen els nostres 'beans' i les seves relacions. Però hi han aspectes que s'escapen en aquesta configuració:
Definició de Propietats segons EntornImaginem que en l'escenari que hem presentat a la Figura 1 i afegim un 'dataSourceBean' que fa de factoria de connexions i que té una relació de col.laboració amb la factoria de sessions:
![]() Al fitxer de configuració abans presentat s'afegeix el següent bean: ... <bean id="dataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="$\{jdbc.driverClassName\}" /> <property name="url" value="$\{jdbc.url\}" /> <property name="username" value="$\{jdbc.username\}" /> <property name="password" value="$\{jdbc.password\}" /> </bean> ... i es modifica la configuració del bean 'sessionFactory' per afegir la relació de col.laboració: <property name="dataSource" ref="dataSourceBean" /> Si ens fixem en les propietats del bean 'dataSourceBean' veurem que hi han quatre <property/> sensibles de canviar segons l'entorn:
Els valors d'aquests atributs s'extreuen d'un fitxer extern segons la definició '${jdbc.driverClassName},${jdbc.url},${jdbc.username} i ${jdbc.password}. Però, d'ón es treuen els valors d'aquestes variables? En aquest punt intervé el servei de configuració: mitjançant les classes 'HostPropertyPlaceholderConfigurer' i 'HostPropertyResourceConfigurer' que trobem en el package 'net.gencat.ctti.canigo.services.configuration.springframework.beans.factory.config' es configura quina és la localització del fitxer extern. ... <bean id="dataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="$\{jdbc.driverClassName\}" /> <property name="url" value="$\{jdbc.url\}" /> <property name="username" value="$\{jdbc.username\}" /> <property name="password" value="$\{jdbc.password\}" /> </bean>... <bean id="configurationService" class="net.gencat.ctti.canigo.services.configuration. springframework.beans.factory. config.HostPropertyPlaceholderConfigurer"> <list> <value>classpath:config.propertiesc</value> </list> </bean> ... Configuració del fitxer extern Aquest fitxer extern defineix els valors de les propietats: jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb: D:/path_to_db/testDB jdbc.username=sa jdbc.password= |