07 desarrollando con spring

38
Capítulo 7: Desarrollando con Spring Creando nuestra primera aplicación En este último capítulo utilizaremos todo lo que hemos estado viendo hasta ahora en los anteriores capítulos. Javier Sevilla Sánchez Trabajo de fin de carrera de la Ingeniería Técnica en Informática de Gestión (I.T.I.G)

Upload: yopp-lov

Post on 22-Oct-2015

173 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 07 Desarrollando Con Spring

Capítulo 7: Desarrollando con Spring

Creando nuestra primera aplicación

En este último capítulo utilizaremos todo lo que hemos estado viendo hasta ahora en los anteriores capítulos.

Trabajo de fin de carrera de la Ingeniería Técnica en Informática de Gestión (I.T.I.G)

Page 2: 07 Desarrollando Con Spring
Page 3: 07 Desarrollando Con Spring

Contenido¿Qué vamos a hacer?...................................................................................................................4

¿Por dónde empiezo?..................................................................................................................4

Maven..........................................................................................................................................4

Web.xml.......................................................................................................................................5

Ficheros de definición de Beans...................................................................................................7

Campus-servlet.xml......................................................................................................................7

Controllers.xml...........................................................................................................................10

Campus-hibernate.xml...............................................................................................................10

Campus-security.xml..................................................................................................................14

Objetos de acceso a datos (DAO) y el modelo a persistir...........................................................17

Spring MVC.................................................................................................................................21

Controllers..............................................................................................................................21

Ajax con Spring MVC..............................................................................................................22

Vista...........................................................................................................................................23

Resolutores............................................................................................................................23

Tags JSP..................................................................................................................................24

Form tag.............................................................................................................................24

Spring tag...........................................................................................................................25

Security tag.........................................................................................................................25

Binding con WebDataBinder......................................................................................................26

Uso y creación de CustomEditor............................................................................................26

Creando CustomEditors con PropertyEditorSupport..............................................................27

Uso de WebBindingInitializer: La clase CampusBindingInitializer..........................................29

Uso de la anotación @InitBinder............................................................................................30

Seguridad...................................................................................................................................31

Page 4: 07 Desarrollando Con Spring

¿Qué vamos a hacer?

Este estudio sobre el framework de Spring ha sido el objetivo de un trabajo de fin de carrera. A lo largo de él, hemos hecho referencias académicas como estudiantes, profesores o la universidad de Alcalá. De modo que es bastante oportuno hacer una aplicación de campus virtual que implemente cada una de las funcionalidades explicadas.

Así, esta aplicación tendrá que dar uso de las funcionalidades de Spring en materia de conexión de beans (gracias al contenedor), aspectos, persistencia de datos, web y seguridad.

¿Por dónde empiezo?

Elegir un IDE para desarrollar puede ser el primer de los pasos que demos, cada desarrollador tiene entornos preferidos. A pesar de que particularmente prefiero eclipse facilitaremos lo posible el que el IDE sea independiente de la aplicación.

MavenVamos a utilizar maven, una herramienta de software para la gestión y construcción de proyectos Java. Es similar en funcionalidad a Apache Ant, pero tiene un modelo de configuración de construcción más simple, basado en un formato XML. Maven utiliza un Project Object Model (POM) para describir el proyecto de software a construir, sus dependencias de otros módulos y componentes externos, y el orden de construcción de los elementos. Viene con objetivos predefinidos para realizar ciertas tareas claramente definidas, como la compilación del código y su empaquetado.

En el fichero pom.xml definiremos, entre otras cosas las dependencias de Spring, el siguiente código es un extracto del fichero /pom.xml:

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${org.springframework-version}</version><exclusions>

<!-- Exclude Commons Logging in favor of SLF4j --><exclusion>

<groupId>commons-logging</groupId><artifactId>commons-logging</artifactId>

</exclusion></exclusions>

</dependency><dependency>

<groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${org.springframework-version}</version>

</dependency><dependency>

Page 5: 07 Desarrollando Con Spring

<groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${org.springframework-version}</version>

</dependency><dependency>

<groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${org.springframework-version}</version>

</dependency><dependency>

<groupId>org.springframework.webflow</groupId><artifactId>spring-webflow</artifactId><version>${org.springwebflow-version}</version>

</dependency><dependency>

<groupId>org.springframework.webflow</groupId><artifactId>spring-js</artifactId><version>${org.springwebflow-version}</version>

</dependency><dependency>

<groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>3.0.5.RELEASE</version><exclusions>

<!-- Exclude Commons Logging in favor of SLF4j --><exclusion>

<groupId>commons-logging</groupId><artifactId>commons-logging</artifactId>

</exclusion></exclusions><type>jar</type>

</dependency><dependency>

<groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>${org.springsecurity-version}</version>

</dependency><dependency>

<groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>${org.springsecurity-version}</version><exclusions>

<!-- Exclude Commons Logging in favor of SLF4j --><exclusion>

<groupId>commons-logging</groupId><artifactId>commons-logging</artifactId>

</exclusion></exclusions>

</dependency>

Con este código tendremos todas las librerías de Spring que utilizaremos más adelante.

Web.xml

El fichero web.xml configura nuestra aplicación, y es el punto en el que Spring arrancará. Para ello tendremos que incluir tanto el DispatcherServlet del framework MVC, el Listener de contexto, el filtro de la seguridad así como la configuración del contexto.

Page 6: 07 Desarrollando Con Spring

El siguiente código muestra cómo configurar el contexto:

<context-param><param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/spring/root-context.xml</param-value>

</context-param>

<listener><listener-class>

org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

El siguiente código muestra cómo configurar el DispatcherServlet:

<servlet> <servlet-name>campus</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class></servlet><servlet-mapping> <servlet-name>campus</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>

Este Servlet será el punto de partida para la carga de todos los componentes de Spring. Buscará en la raíz de la aplicación un fichero llamado campus-servlet.xml que contendrá la definición del contenedor de Spring.

Para poder implementar la seguridad tendremos que incluir los siguientes filtros:

<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern></filter-mapping>

Uno de los problemas más habituales que existe en las aplicaciones web es la codificación, los servidores de aplicaciones han de ser configurados explícitamente para que transmitan y reciban en una determinada codificación. De este modo, Spring hace posible que cada aplicación tenga su configuración o incluso que se tengan varias configuraciones para una misma aplicación. El siguiente filtro servirá para forzar la codificación, en este caso UTF-8.

<filter> <filter-name>CharacterEncodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

Page 7: 07 Desarrollando Con Spring

<param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param></filter>

Con esta definición creamos las herramientas necesarias para poder empezar nuestra aplicación. El siguiente paso será crear y configurar nuestro fichero campus-servlet.xml.

Ficheros de definición de Beans

La forma más comúnmente utilizada por la comunidad para definir beans en Spring es a través de los ficheros XML, ya sea de forma explícita con cada uno de los beans, o definiendo etiquetas que buscaran anotaciones en nuestras clases. Existen múltiples combinaciones para realizar esto, pero en la mayoría de los expertos dividen según su finalidad cada uno de los ficheros. Acorde a nuestras necesidades y basándonos en estas normas de estilo dividiremos nuestros beans basándonos en si el bean en cuestión es un objeto de persistencia, de seguridad, de servicio o web. Así, los beans que definan el modelo así como el motor de persistencia (en nuestro caso hibernate) irán en el fichero campus-hibernate.xml, los objetos de servicio, irán en service.xml. Usaremos Spring Security y las definiciones de los beans irán en el fichero campus-security.xml. Por último, El fichero campus-servlet.xml será el fichero en el que se definan todos los objetos relativos a la parte web, en nuestro caso lo hemos dividido en un segundo llamado Controllers.xml para tener un código más mantenible y más limpio, en él definiremos los controladores.

Campus-servlet.xml

Vamos a explicar cuáles son y para qué se utilizan los componentes definidos en este fichero, que como ya comentamos contendrá los componentes relacionados con la parte web.

El primer elemento que nos encontramos es el siguiente:

<mvc:resources mapping="/resources/**" location="/resources/" />

Con este tag lo que estamos diciendo es donde está la ruta que contendrá ficheros estáticos y cuál será su localización web. En este caso estamos diciendo que estará en la carpeta llamada resources que está en la raíz de la aplicación y que tendrá la misma localización web.

El siguiente elemento que nos encontramos proporcionará capacidades de anotaciones:

<mvc:annotation-driven/>

Los siguientes elemento son indispensables ya que los utilizaremos para resolver las vistas, son los resolutores de vistas. Vamos a definir dos, ResourceBundleViewResolver e

Page 8: 07 Desarrollando Con Spring

InternalResourceViewResolver. Utilizaremos dos porque vamos a mezclar dos maneras de resolver las vistas y las ordenaremos. La que va en primer orden utiliza un fichero properties que define dónde están y qué clase se hace cargo de resolver cada uno de los nombres de las vistas, lo vamos a hacer así porque es una manera elegante de poder utilizar plantillas de Apache Tiles. El segundo es más sencillo, simplemente buscará el nombre de la vista añadiendo el prefijo /WEB-INF/jsp/ y el sufijo .jsp.

Como vemos definimos dos propiedades, basename que será el fichero .properties donde se encontrará la correspondencia entre vista y clase resolutora y order, que indica el orden.

<bean id="viewResolver"class="org.springframework.web.servlet.view.ResourceBundleViewResolver" >

<property name="basename" value="views"/> <property name="order" value="0"/>

</bean>

El siguiente resolutor, como ya hemos comentado hará una correspondencia entre cada nombre de vista y la clase resolutora encargada. En este caso el orden es 1, posterior al anterior resolutor definido.

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1"/>

Gracias a la clase TilesCOnfigurer de Spring podremos integrar tiles como si de otro bean se tratase, beneficiándonos de la inyección de dependencia. El siguiente código muestra como hacerlo:

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" p:definitions="/WEB-INF/tiles/templates.xml"/> Como vemos hemos de asignarle la propiedad del fichero .xml donde estarán las plantillas definidas.

El siguiente tag sirve para importar los beans de otro fichero como si estuviesen declarados en este. Como ya dijimos anteriormente, hemos dividiremos los componentes web y los controladores en dos ficheros, es precisamente este fichero Controllers.xml el que contendrá los controladores.

<import resource="spring/controllers.xml" />

En muchas aplicaciones estamos acostumbrados a ver códigos de excepciones ocurridas en la ejecución de nuestra petición, y por defecto, los servidores de aplicaciones muestran esa excepción al usuario, que bien podría tener poco que ver con el equipo de desarrollo y poco le importan las trazas expuestas. De esta forma, la clase SimpleMappingExceptionResolver lo que

Page 9: 07 Desarrollando Con Spring

hace es redireccionar a distintas vistas cuando ocurra determinada excepción y así mostrar una vista más amigable al usuario.

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings">

<props> <prop key="org.springframework.web.servlet.PageNotFound">

pageNotFound</prop><prop key="org.springframework.dao.DataAccessException">

dataAccessFailure</prop><prop key="org.springframework.transaction.TransactionException">

dataAccessFailure</prop>

</props></property></bean>

La internacionalización es un aspecto crucial en toda aplicación, y más para entornos web. Gracias a la clase ReloadableResourceBundlemessageSource podemos configurar ficheros de propiedades que contengan nuestros mensajes internacionalizados. El siguiente código muestra cómo definir este bean:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="10" /> <property name="fallbackToSystemLocale" value="false" /> <property name="basenames"> <list> <value>/WEB-INF/properties/messages</value> </list> </property> </bean>

Como vemos existen diversos parámetros como defaultEncoding, para la codificación, cacheSeconds, que determina cada cuanto se recarga el fichero etc.

Otros beans necesarios en la internacionalización son el LocaleChangeInterceptor y el SessionLocaleResolver definidos con el siguiente código:

<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="locale" /> </mvc:interceptors>

Page 10: 07 Desarrollando Con Spring

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

Una de las funcionalidades de Spring MVC indispensables es el enlace de datos, gracias a InitBinder podremos enlazar parámetros de petición con objetos completos. La clase AnnotationMethodHandlerAdapter se sirve de una clase que habremos de implementar ( en nuestro caso CampusDindingInitializer) en la que podremos definir nuestros enlaces personalizados, como por ejemplo recibir un identificador y devolver un objeto persistido.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="webBindingInitializer"> <bean class="com.campus.web.binding.campusBindingInitializer"/> </property> </bean></beans>

Controllers.xml

Como ya hemos comentado, este fichero estará contenido en el campus-servlet y en él definiremos los controladores. En nuestro caso habrá dos tipos de definición, la que otorga el tag <mvc:view-controller> que simplemente redirecciona una deterinada ruta a un fichero jsp o <context:component-scan> que define un paquete base en el que se escanearán los controladores, en nuestro caso com.campus.web.controllers

El siguiente código es una muestra de cómo estos están definidos:

<mvc:view-controller path="/login" />

<context:component-scan base-package="com.campus.web.controllers" />

Campus-hibernate.xml

En este fichero definiremos todos los beans que tengan relación con la persistencia. Como bien indica el nombre del fichero el motor de persistencia será Hibernate, sin embargo y gracias a la modularización de Spring, múltiples motores de persistencia podrán ser configurados o podremos tener varios ficheros, uno para JPA, iBatis, etc e intercambiarlos según nuestras necesidades. Esto es posible gracias a que los demás beans que esperen los DAO definidos aquí recibirán la implementación definida, pero ellos operarán con la interfaz, desconociendo cómo estos DAO operan.

Page 11: 07 Desarrollando Con Spring

El primer bean definido en este fichero será un property placeholder, cuyo fichero será jdbc.properties. El contenido de este fichero serán los distintos parámetros de configuración para la fuente de datos así como para el sessionFactory de hibernate.

El contenido de este fichero es el siguiente:

hibernate.generate_statistics=truehibernate.show_sql=truejpa.showSql=true

jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc\:mysql\://localhost\:3306/campusjdbc.username=campusjdbc.password=campus

hibernate.dialect=org.hibernate.dialect.MySQLDialectjpa.databasePlatform=oracle.toplink.essentials.platform.database.MySQL4Platformjpa.database=MYSQL

El siguiente bean definido es el dataSource. En esta aplicación de ejemplo hemos creado un dataSource básico, si dispusiésemos de un dataSource en nuestro servidor podríamos acceder a él también definiendo su ruta. Como no es nuestro caso definiremos un dataSource de tipo BasicDataSource de apache commons.

<context:property-placeholder location="classpath:jdbc.properties"/>

El siguiente bean a definir será el data source, en nuestro caso utilizaremos BasicDataSource de Apache.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"p:username="${jdbc.username}" p:password="${jdbc.password}"/>

Una vez definida el data source podremos definir un sessionFactoryBean de Hibernate, en nuestro caso hemos definido el AnnotationsSessionFactoryBean. Muy cómodo porque simplemente le indicaremos el paquete del modelo donde escanará los objetos a persistir y el se encargará de leerlos.

<bean id="sessionFactory"clas

s="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"

p:dataSource-ref="dataSource"p:packagesToScan="com.campus.model">

<property name="hibernateProperties">

Page 12: 07 Desarrollando Con Spring

<props><prop key="hibernate.dialect">${hibernate.dialect}</prop><prop key="hibernate.show_sql">${hibernate.show_sql}</prop><prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop><prop key="hibernate.hbm2ddl.auto">update</prop>

</props></property><property name="eventListeners">

<map><entry key="merge">

<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/></entry></map></property></bean>

El siguiente bean es un manager de transacción, en concreto el HibernateTransactionManager. Este manager nos permitirá hacer operaciones transaccionales asegurando la atomicidad.

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"

p:sessionFactory-ref="sessionFactory"/>

Como vimos en el capítulo de persistencia, Spring otorga varias soluciones para facilitar la creación de DAO, una de ellas son las plantillas, el siguiente código configura una plantilla.

<bean id="hibernateTemplate"class="org.springframework.orm.hibernate3.HibernateTemplate"p:sessionFactory-ref="sessionFactory"/>

El siguiente tag permite la configuración por anotaciones.

<context:annotation-config/>

El siguiente tag indica que utilizaremos transacciones con anotaciones.

<tx:annotation-driven/>

Definiremos nuestros DAO de hibernate con anotaciones en el paquete com.campus.orm.dao y la manera que tenemos de decirle a Spring que busque en ese paquete para encontrar beans es la siguiente:

<context:component-scan base-package="com.campus.orm.dao.hibernate">

<context:include-filter type="annotation"expression="org.springframework.stereotype.Repository"/>

</context:component-scan>

Page 13: 07 Desarrollando Con Spring

Hemos visto como se definen los distintos beans pertenecientes a la parte web así como a la persistencia. La siguiente parte a declarar será la seguridad.

En este fichero definiremos mediante aspectos la configuración del transactionManager, el bean necesario para las transacciones.

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"

p:sessionFactory-ref="sessionFactory"/>

<aop:config> <aop:pointcut id="all-service-methods" expression="execution(* com.campus.orm.dao.*Dao.*(..))"/> <aop:advisor advice-ref="local-tx-advice" pointcut-ref="all-service-methods"/></aop:config>

<tx:advice id="local-tx-advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="modify*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="*"/> </tx:attributes></tx:advice>

Así de simple, con esta configuración diremos a Spring que cada vez que se ejecute un método de una clase Dao del paquete com.campus.orm.dao y que responda a cada uno de los mapeos hechos con AOP realizará determinadas operaciones. Por ejemplo, con un método sabe se hará transacción y propagación.

De nada servirá esta configuración si no tenemos objetos DAO. Ellos serán nuestra conexión entre base de datos y aplicación. Más tarde veremos cómo se definen estos DAO.

Page 14: 07 Desarrollando Con Spring

Campus-security.xml

La seguridad es una parte imprescindible en toda aplicación, especialmente las web. A menudo vemos una infinidad de filtros tediosos y de difícil compresión cuya función es impedir que usuarios malintencionados alteren el funcionamiento de la aplicación o que obtengan información no permitida. Spring security, como ya comentamos en el capítulo dedicado a ello, facilita todo esto otorgando una gran potencia.

El tag http permite que bajo patrones de petición se precise tener o no un rol específico. En la siguiente definición vemos que por ejemplo para los recursos no se comprueba ningún permiso, al igual que para la parte pública o el logeo. Sin embargo para todas las rutas que comiencen por admin sólo un usuario con rol ADMIN tendrá acceso a ellas, así como teacher si tiene el rol TEACHER etc.

También podemos definir cuál será nuestro formulario de logeo así como capacidades como la posibilidad de introducir una cookie para recordarte en una máquina, o que haya un máximo de sesiones abiertas para un usuario así como se puede configurar cúal será la ruta para salir de la aplicación y su posterior redirección.

<http auto-config="true" use-expressions="true"> <intercept-url pattern="/resources/**" filters="none"/> <intercept-url pattern="/public/**" filters="none" /> <intercept-url pattern="/login*" access="permitAll" /> <intercept-url pattern="/admin/**" access="hasRole('ADMIN')" /> <intercept-url pattern="/teacher/**" access="hasRole('TEACHER')" /> <intercept-url pattern="/student/**" access="hasRole('STUDENT')" /> <intercept-url pattern="/**" access="hasRole('ADMIN') or hasRole('TEACHER') or hasRole('STUDENT')" /> <form-login login-page="/login" login-processing-url="/loginProcess" default-target-url="/home" authentication-failure-url="/login?login_error=1" />

<logout invalidate-session="true" logout-url="/logout" logout-success-url="/login?loggedout=true" />

<remember-me /></http>

En este fichero también definimos el authenticationManager y le asignamos un proveedor, en nuestro caso el DAO abstractUserDao el cuál implementa UserDetailService. También definiremos un codificador de contraseña que utilizará el algoritmo de cifrado sha.

<authentication-manager> <authentication-provider user-service-ref="abstractUserDao"> <password-encoder hash="sha"/> </authentication-provider></authentication-manager>

Page 15: 07 Desarrollando Con Spring

Diremos a Spring que él cifrado a utilizar será el algoritmo de autenticación sha gracias al tag <password-encoder>.

El método a implementar de UserDetailService es loadUserByUsername cuyo código es el siguiente:

@Overridepublic UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException {

AbstractUser user = (AbstractUser) getHibernateTemplate().execute(new HibernateCallback() {

@Overridepublic Object doInHibernate(Session session)

throws HibernateException {

String hql = "from AbstractUser au " + " where au.username = :username\n";

Query query = session.createQuery(hql);query.setString("username", username);AbstractUser abstractUser = (AbstractUser)

query.uniqueResult();return abstractUser;

}});if (user == null) {

throw new UsernameNotFoundException("User not found with the username of " + username);

}

return user;

}

El objeto AbastractUser tendrá que implementar la clase UserDetails, el código siguiente es la implementación de los métodos:

public String getPassword() {return password;

}

public String getUsername() {return username;

}

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = Authority.class, mappedBy = "user")public Collection<GrantedAuthority> getAuthorities() {

return authorities;}

@Override@Transient

Page 16: 07 Desarrollando Con Spring

public boolean isAccountNonExpired() {return true;

}

@Override@Transientpublic boolean isAccountNonLocked() {

return true;}

@Override@Transientpublic boolean isCredentialsNonExpired() {

return true;}

De igual modo hemos de crear una última clase que implemente GrantedAuthority, esta será Authority. Esta clase será la encargada de guardar cada uno de los roles, el siguiente código pertenece a Authority.java:

@Override@Transientpublic String getAuthority() {

return this.value.name();}

Sólo se ha de implementar este método, el cuál devolverá el nombre del rol en cuestión. La siguiente clase, AutorityType, muestra cuales son los distintos roles:

package com.campus.model.security;

public enum AuthorityType {

ADMIN, TEACHER, STUDENT

}

En nuestra aplicación sólo tendremos tres tipos de roles, el rol de estudiante, el de profesor y el de administrador.

El administrador será el encargado de crear usuarios, ya sean estudiantes o profesores, crear carreras y asignaturas. Al crear una asignatura el administrador asignará ésta a un profesor para que la imparta.

El profesor será el encargado de crear lecciones para cada una de sus asignaturas así como preguntas relacionadas con esas lecciones. Tendrá listados de sus alumnos así como podrá ver estadísticas de las respuestas. También tendrá la posibilidad de responder dudas surgidas sobre temas.

El estudiante será el encargado de apuntarse a asignaturas, ver y estudiar las lecciones, apuntar dudas sobre estas y responder preguntas.

Page 17: 07 Desarrollando Con Spring

Objetos de acceso a datos (DAO) y el modelo a persistir

En toda aplicación es aconsejable definir buenos interfaces que definan cuales serán las operaciones que se harán con nuestros objetos. En el caso de los DAO si cabe, cobra más importancia esta buena práctica, ya que gracias a Spring es muy fácil poder hacer uso así como cambiar entre distintas herramientas de persistencia. Para nuestra aplicación hemos elegido Hibernate, pero como ya dijimos, Spring es compatible con la mayoría de los framework de persistencia como iBatis, JPA etc. Así si tenemos bien definidos los interfaces podremos hacer que el cambio entre los framework sea relativamente sencillo.

Así tendremos dos paquetes:

com.campus.orm.dao.* en el que se declararán los distintos interfaces de cada uno de los DAO.

com.campus.orm.dao.hibernate.* en el que estarán las implementaciones mediante el uso de HibernateDaoSupport.

Como explicamos en el capítulo dedicado a la persistencia, HibernateDaoSupport facilita la creación de DAO con Hibernate. En nuestro caso tendremos una clase base de la que heredarán el resto de implementaciones. Esta se llamará BaseDao cuya implementación con el framework de Hibernate será BaseDaoHibernate. En ella definiremos las operaciones más comunes relativas al guardado, recuperación mediante clave etc de nuestros objetos de dominio. Todo objeto de dominio tendrá que implementar BaseEntity y heredar BaseEntityCampus.

El siguiente código pertenece a BaseEntityCampus:

@MappedSuperclasspublic abstract class BaseEntityCampus implements BaseEntity, Serializable {

private Long id;

public BaseEntityCampus() {super();

}

public BaseEntityCampus(Long id) {super();this.id = id;

}

@Id@GeneratedValue(strategy = GenerationType.TABLE)@Overridepublic Long getId() {

return id;}

Page 18: 07 Desarrollando Con Spring

@Overridepublic void setId(Long id) {

this.id = id;}

@Override@Transientpublic boolean isNew() {

return this.getId() == null;}

}

Con la anotación @MappedSuperclass diremos que es una clase padre que no será mapeada. Como vemos tan solo contiene un identificador como atributo.

Creda esta clase estamos preparados para ver nuestra clase BaseDao, el siguiente código pertenece a ella:

public interface BaseDao<T extends BaseEntity> {

public Long save(T entity);

public T get(Long id);

public T load(Long id);

public List<T> getAll();

public void update(T entity);

public void delete(T entity);

public void delete(Long id);

public void clear();

public T getFetch(Long id);

}

Y su imlementación en con HibernateDaoSupport:

public abstract class BaseDaoHibernate<T extends BaseEntityCampus> extends HibernateDaoSupport implements BaseDao<T> {

protected Class<T> entityClass;

public BaseDaoHibernate() { super(); }

public BaseDaoHibernate(Class<T> entityClass) { super();

Page 19: 07 Desarrollando Con Spring

this.entityClass = entityClass; }

@Override public void clear() { getHibernateTemplate().clear(); }

@Override public void delete(T entity) { getHibernateTemplate().delete(entity); getHibernateTemplate().flush(); }

@SuppressWarnings("unchecked") @Override public void delete(final Long id) { getHibernateTemplate().execute(new HibernateCallback() {

public Object doInHibernate(Session session) throws HibernateException, SQLException { T entity = (T) session.load(entityClass, id); session.delete(entity); return null; } }); }

@Override public T get(Long id) { return (T) getHibernateTemplate().get(entityClass, id); }

@Override public T load(Long id) { return (T) getHibernateTemplate().load(entityClass, id); }

@Override public List<T> getAll() { return getHibernateTemplate().loadAll(entityClass); }

@Override public Long save(T entity) { getHibernateTemplate().saveOrUpdate(entity); return entity.getId(); }

@Override public void update(T entity) { getHibernateTemplate().update(entity); }

public void flush() { getHibernateTemplate().flush(); }}

Page 20: 07 Desarrollando Con Spring

Como vemos existe un método constructor que tiene como parámetro una clase de tipo entidad base. Esta será la clase de la cual heredarán el resto de los objetos DAO como podemos comprobar en el siguiente código con StudentDaoHibernate:

@Repository(value = "studentDao")public class StudentDaoHibernate extends BaseDaoHibernate<Student> implements StudentDao {

...

}

La etiqueta @Repository dirá a Spring que esta clase será un bean cuyo nombre será studentDao, más tarde a la hora de utilizar este dao será tan sencillo como utilizar la anotación @Autowired, el siguiente código muestra a un cómo esta clase será inyectada:

@Autowiredprivate StudentDao studentDao;

La anotación @Autowired dirá a Spring que el tipo de este objeto es un bean definido en el contenedor y que es sujeto de ser inyectado.

Page 21: 07 Desarrollando Con Spring

Spring MVC

Como ya dijimos, esta aplicación será web y hará uso de la herramienta que Spring otorga para contruir aplicaciones web, Spring MVC.

Controllers

Tras ver cómo damos soporte a los componentes de Spring MVC con el fichero Campus-Servlet.xml es tiempo de ver el tipo más utilizado en Spring, el Controller.

En anteriores versiones de Spring, los controladores tenían que ser definidos en el fichero xml y tenían que heredar de alguna de las clases de soporte de Spring. Ahora esto se ha acabado gracias a la anotación @Controller, con ella cualquier POJO podrá ser un controlador sin necesidad de heredar clases específicas de Spring. El siguiente código de la aplicación muestra un ejemplo sencillo de controlador:

@Controller@RequestMapping("/subjectProfile")public class SubjectProfileController {

@Autowiredprivate SubjectDao subjectDao;

@InitBinderpublic void initBinder(HttpServletRequest request,

ServletRequestDataBinder binder) throws Exception {

binder.registerCustomEditor(Subject.class, new SubjectCustomEditor(this.subjectDao));

}

@RequestMapping("/{id}")public ModelAndView subjectProfile(@PathVariable("id") Subject subject){

ModelAndView mav = new ModelAndView("subjectProfile");mav.addObject("subjectProfile",subject);return mav;

}}

En este ejemplo hay varios conceptos a destacar, el primero sería ver qué sencillo es decirle a Spring que la clase es un controlador, simplemente con la anotación @Controller. Spring buscará en los paquetes de escaneo definidos en el fichero xml.

La anotación @RequestMapping hace referencia a qué ruta mapeará este controlador, es opcional el que esté encima de la definición de la clase, si es así será la raíz para el resto de los métodos. Cada método que tenga la anotación @RequestMapping() será mapeado por Spring como un método del controlador.

Page 22: 07 Desarrollando Con Spring

Los métodos de un controlador serán sujetos de ser utilizados dependiendo de la URL de la petición y del método de la petición (es decir, POST, GET,..etc).

Es decir, puede haber métodos con la misma URL pero con distintos RequestMethod.

En el ejemplo existe otra anotación a destacar, se trata de @InitBinder este método será del que se sirva para el enlace de los valores de los parámetros de la petición con objetos Java. En el ejemplo se define un objeto de tipo CustomEditor que esperará un identificador (un id de una entidad) irá a la base de datos y recuperará la entidad en concreto para nuestro método.

Tras la ejecución del método vemos que devuelve un objeto ModelAndView, este objeto es básico para comprender el funcionamiento de Spring MVC. ModelAndView definirá qué vista (por ejemplo un fichero jsp) será la que se le enviará en la respuesta y que modelo es necesario para mostrar dicha vista. En nuestro caso la vista será subjectProfile y contendrá el objeto subjectProfile de tipo subject como modelo.

También podremos devolver un objeto String, de este modo simplemente Spring buscará la vista y la devolverá. Si en los parámetros del método hemos pedido el objeto Model Spring lo inyetará y si hacemos alguna modificación esta estará presente en la vista designada por la cadena.

El siguiente código es un ejemplo de lo descrito:

@RequestMapping(method = RequestMethod.GET)public String setupForm(Model model) {

model.addAttribute("lessonDataPicker", new LessonDataPicker());return "teacher/createLesson";

}

En este método se devuelve el nombre lógico de la vista pero la modificación del objeto Model estará presente en la vista.

Ajax con Spring MVC

En nuestra aplicación haremos uso de la librería de javascript jQuery la cual facilita, entre otras funcionalidades, la creación de AJAX. Con Spring una manera de tratar estas peticiones es devolviendo el cuerpo de la petición gracias a @ResponseBody. Con @ResponseBody diremos que la cadena devuelta (u objeto) será el contenido de la respuesta y no el nombre lógico de la vista.

El siguiente código muestra cómo hacerlo:

@RequestMapping(method = RequestMethod.POST)@ResponseBodypublic String processSubmit(@RequestParam(value = "lessonNote") String lessonNote, @RequestParam("lessonId") Lesson lesson) {

if (lessonNote.trim().equals("")) {

Page 23: 07 Desarrollando Con Spring

return "false";}Note note = this.noteDao.saveFromActualUser(lesson, lessonNote);

return "{text:'" + note.getText() + "',creator:'" + note.getCreator() + "',creationDate:'" + note.getCreationDate() + "'}";}

Vemos como el método devuelve una cadena que será un objeto JSON.

VistaPara explicar la vista en Spring hemos de tener dos conceptos claros, los resolutores de vista y los tags de Jsp de spring.

Resolutores

Los resolutores de vista será el nexo entre una definición lógica y un física de la vista en concreto, podremos utilizar distintas tecnologías como Velocity o FreeMarker, pero en nuestra aplicación de ejemplo utilizaremos la más utilizada por al comunidad, los JSP.

Para la resolución de las vistas de lógico a físico utilizaremos dos clases, definidas en campus-servlet.xml:

<bean id="viewResolver"

class="org.springframework.web.servlet.view.ResourceBundleViewResolver" >

<property name="basename" value="views"/><property name="order" value="0"/>

</bean>

E InternalResourceViewResolver:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"

p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1"/>

He elegido dos debido a que voy a combinar el uso del framework para la vista de Apache Tiles con ResourceBundleViewResolver y para el resto con InternalResourceViewResolver.

Para poder mapear con ResourceBundleViewResolver necesitaremos mapear el bean tilesConfigurer, tener un fichero de propiedades donde definiremos las vistas y el fichero de plantillas de tiles.

La clase tilesConfigurer se define en campus-servlet.xml de la siguiente manera:

Page 24: 07 Desarrollando Con Spring

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" p:definitions="/WEB-INF/tiles/templates.xml"/>

EL siguiente código es un extracto de views.properties y muestra cómo mapearemos cada una de las vistas lógicas a usar por Spring con cada una de las plantillas de Tiles:

home.(class)=org.springframework.web.servlet.view.tiles2.TilesViewhome.url=home

El nombre (en este caso home) será el nombre lógico de la vista, el atributo .(class)= hace referencia a la clase a utilizar por Spring y el atributo url será la plantilla de Tiles, (también home).

Así en el fichero templates.xml podremos ver las diferentes vistas mapeadas con plantillas de tiles, como muestra el ejemplo:

<definition name="home" extends=".teacher"><put-attribute name="url" value="home"/><put-attribute name="content" value="/WEB-INF/jsp/home.jsp" />

</definition>

De este modo podremos utilizar otras tecnologías y combinarlas con nuestro framework. Este resolutor se ejecutará en primera posición, es decir a mismos nombres lógicos este será el encargado de resolver la vista.

El siguiente resolutor es el InternalResourceViewResolver, el comportamiento de este es muy sencillo, a el nombre lógico de la vista se le añadirán un prefijo y un sufijo, así si se pregunta por “login” se devolverá la vista “/WEB-INF/jsp/login.jsp”. Este resolutor se ejecutará tras descartarse la vista en el anterior resolutor.

Tags JSP

Spring MVC y Spring security tiene una serie de tags que nos serán muy útiles a la hora de construir nuestras aplicaciones. En esta aplicación de ejemplo utilizaremos principalmente las derivadas de <form: *>, las de <spring:*> así como las de <security:*>.

Form tag

Los tag <form:*> sirven principalmente para el mapeo y enlace de elementos de un formulario y estarán presentes en cada uno de los formularios de la aplicación. Las principales utilidades son las siguientes:

<Form:form> definirá el formulario en cuestión y lo enlazará con un controlador así como con el objeto comando u objeto del modelo a enlazar.

<form:input> este junto a otros como <form:select> , <form:checkboxes> o <form:hidden> serán cada uno de los elementos del formulario los cuales Spring enlazará con la ruta definida en path.

Page 25: 07 Desarrollando Con Spring

<form:errors> este objeto imprimirá los errores encontrados tras la validación del formulario en la parte servidora.

Spring tagCon <spring:*> tendremos múltiples funcionalidades como el enlace de objetos , la creación de URL absolutas, evaluación de expresiones, trasformaciones de variables a cadenas etc.

Security tagCon los tags que proveee <security:> podremos realizar comprobaciones de roles para el principal, es decir, para el usuario que haya realizado la petición. Este tag lo utilizaremos mucho para mostrar u ocultar información dependiendo del tipo de usuario que seas. Por ejemplo las opciones del menú no serán iguales para un profesor que para un alumno al igual que el alumno no podrá ver resultados de las preguntas, un profesor no va a responderlas.

Veamos un ejemplo con el siguiente código perteneciente a menu.jsp.

<div id="tabsQuestionHome"><security:authorize ifAnyGranted="STUDENT"> <p><a href='<spring:url value="/student/answerQuestion" htmlEscape="true" />'>

<fmt:message key="ANSWER_QUESTIONS"/></a></p></security:authorize><security:authorize ifAnyGranted="TEACHER"> <p><a href='<spring:url value="/teacher/createQuestion" htmlEscape="true" />'>

<fmt:message key="CREATE_QUESTION"/></a></p> <p>

<a href='<spring:url value="/teacher/statistics/select" htmlEscape="true" />'><fmt:message key="STATISTICS"/></a></p>

<p><a href='<spring:url value="/teacher/faqs/faqsSelect" htmlEscape="true" />'><fmt:message key="ANSWERS"/></a></p>

</security:authorize></div>

Este ejemplo es el código de parte del menú, para ser más concreto de las preguntas. Como hemos dicho un alumno sólo debería poder ver la opción de responder a preguntas y un profesor sólo debería poder crear preguntas o ver las estadísticas de ellas así como las respuestas de las preguntas.

Evidentemente de nada sirve que ocultemos su enlace si luego es accesible si lo tecleamos en la barra de direcciones, para impedir esto el fichero campus-security.xml muestra cuales son los roles permitidos a según que rutas.

Page 26: 07 Desarrollando Con Spring

Binding con WebDataBinder

Los componentes de Binding o enlace que Spring otorga tienen la finalidad de enlazar parámetros de la petición con objetos. Estos objetos pueden ser de diversos tipos, desde tipos simples como Long, Boolean, String, Intege..etc hasta objetos complejos como en nuestro caso Student, Teacher, Subject, Lesson así como datapickers y comandos. Todo ello será gestionado por WebDataBinder.

Por lo general esta aplicación se sirve de objetos datapickers para recoger los datos de los formularios. Estos datapicker pueden contener objetos de tipo complejo como Student, Teacher etc…La manera que tiene Srping de enlazar estos objetos es ver qué tipo se espera buscar en sus WebBindingInitializer definidos y en los métodos marcados con @InitBinder de los controladores.

Las siguientes propiedades pertenecen a LessonDataPicker:

private Subject subject;private String name;private String description;private String content;private Boolean active;private boolean edit = false;private Long id;private Date beginDate;private Date endDate;

Como vemos existen propiedades de diversos tipos, pero llama la atención cómo Spring podrá enlazar desde un formulario web un objeto complejo como pueda ser Subject.

Para tipos comunes como Date nos podemos servir de CustomEditors del framework de Spring, que intentarán hacer un casting de los objetos y nos devolverá o el objeto esperado o errores al forzar el casting (visibles posteriormente con <form:errors).

La respuesta a cómo hace Spring para traerse objetos complejos es tan simple como pasarle el identificador de base de datos al que hace referencia y si existe un customEditor definido para Subject este tendrá que buscar en la BBDD y devolver el objeto. Evidentemente el enlace puede ser tan complejo como el desarrollador quiera y no tiene por qué servirse de DAO.

Uso y creación de CustomEditor

Los custom editors son los objetos de los que se sirve Spring para realizar este enlace, como ya hemos dicho se pueden basar en todo tipo de estrategias para convertir un texto enviado en la petición a un objeto java.

El siguiente código muestra el uso de CustomDateEditor de Spring:

SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");

Page 27: 07 Desarrollando Con Spring

dateFormat.setLenient(false);binder.registerCustomEditor(Date.class,newCustomDateEditor(dateFormat,false));

En el podemos ver que se crea un objeto de tipo CustomEditor para la clase Date. Así cuando Spring espere un objeto de tipo Date en un datapicker o como cualquier tipo de parámetro de la petición Spring buscará y utilizará esta clase para su enlace.

Es posible enlazar nuestras clases, así con la ayuda y herencia de PropertyEditorSupport se podrá crear clases para ello.

Creando CustomEditors con PropertyEditorSupport

Cuando heredamos de PropertyCustomEditor tenemos que implementar el método setAsText el cuál tiene como parámetro el texto enlazado desde la petición. El siguiente ejemplo es un ejemplo de nuestro objeto CustomerEditor base para las entidades de nuestro modelo:

public class CampusCustomEditor extends PropertyEditorSupport {

@SuppressWarnings("unchecked")private final BaseDao dao;

@SuppressWarnings("unchecked")protected CampusCustomEditor(BaseDao dao) {

super(); this.dao = dao; }

@Override public void setAsText(String text) {

Long id = null; if (!StringUtils.hasLength(text.trim())) { setValue(null); return; } id = new Long(text); BaseEntity entity = dao.get(id); if (entity == null) { return; } setValue(entity); }}

Como vemos el constructor precisa de la implementación del objeto de tipo abstracto BaseDao el cual hará uso para obtener la entidad esperada, así simplemente pasándole el identificador de base de datos enlazará la entidad completa.

Luego la creación de un CustomEditor para un objeto del modelo será tan sencillo como el siguiente código:

Page 28: 07 Desarrollando Con Spring

public class LessonCustomEditor extends CampusCustomEditor {

public LessonCustomEditor(LessonDao dao) { super(dao); }}

Como vemos simplemente se hace una llamada al constructor con el Dao correspondiente.

Una vez definidos nuestros CustomerEditors podremos utilizarlos de dos maneras, con objetos BindingInitializer o con los métodos anotados con @InitBinder de nuestros controladores.

Page 29: 07 Desarrollando Con Spring

Uso de WebBindingInitializer: La clase CampusBindingInitializer

Para nuestra aplicación definiremos una clase con los CustomEditor más comunes. Esta clase tendrá que heredar de WebBindingInitializer e implementar el método initBinder.

El siguiente código pertenece a CampusBindingInitializer:

public class CampusBindingInitializer implements WebBindingInitializer {

public CampusBindingInitializer() { }

@Override public void initBinder(WebDataBinder binder, WebRequest request) { SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); binder.registerCustomEditor(String.class, new StringTrimmerEditor(false)); binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true)); }}

Asi cuando se esperen determinados objetos Spring los enlazará sirviéndose de estos CustomEditor.

Page 30: 07 Desarrollando Con Spring

Uso de la anotación @InitBinder

Otra forma que Spring tiene para enlazar es mediante métodos anotados con @InitBinder. El siguiente códig muestra cómo hacerlo:

@InitBinderpublic void initBinder(HttpServletRequest request,

ServletRequestDataBinder binder) throws Exception {

binder.registerCustomEditor(Lesson.class, new LessonCustomEditor(this.lessonDao));

}

Como vemos en el código, la manera de definirlo es igual que dentro del método initBinder de los WebInitializer.