Problema de Encoding UTF-8 en Spring MVC
CharacterEncodingFilter es un filtro que nos ayuda a especificar un tipo de codificación de caracteres concreto tanto para solicitudes y como para las respuestas.
Se puede configurar en el fichero web.xml de tu aplicación. De la siguiente forma:
encodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true encodingFilter /*
En el caso de que estés usando Spring Security, en lugar de configurar el CharacterEncodingFilter en el web.xml necesitas crear un filtro personalizado y añadirlo a la cadena de filtros en primer lugar. Puedes asegurarte que el filtro esté el primero si lo añades con el addFilterBefore en tu clase WebSecurityConfigurerAdapter. Ejemplo:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { ... @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(new EncodingFilter(), ChannelProcessingFilter.class); ... } } public class EncodingFilter extends GenericFilterBean { @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } }
Además, para que funcione el encoding, se debe tener en cuenta lo siguiente:
1 – Es necesario añadir en las jsps la siguiente definición.
< %@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" % >
2 – En caso de estar utilizando Tomcat puede se sea necesario setear el valor URIEncoding en el fichero server.xml.
Uso de Profiles en Spring-boot
Distintas formas de manejar de los profiles con spring-boot para personalizar las ejecuciones:
1 – Establecer el profile en la property spring.profiles.active. Esto lo que hace es que si por ejemplo le establecemos el profile prof1 la aplicación irá a buscar la configuración al application-prof1.properties.
//file: application.properties spring.profiles.active = prof1
2 – Podemos en ejecución decirle a una clase que utilice un profile distinto al marcado. Utilizando para ello la anotación @ActiveProfiles
@SpringBootTest @ActiveProfiles("prof2") class Example2ProfileTest { ... }
3 – Puedes definir beans en función de un profile utilizando la anotacion @Profile en la clase que vas a definirlos.
A continuación, un ejemplo de como se genera un bean en función de si el profile es prof3 u otro con un test de prueba.
@Profile("prof3") @Configuration public class Prof3Configuration { @Bean public String helloWorld() { return "Hello World prof3"; } } @SpringBootTest @ActiveProfiles("prof3") class Example3ProfileTest { @Resource(name = "helloWorld") private String helloWorld; @Test void dummyTest() { Assertions.assertEquals(helloWorld, "Hello World prof3"); } }
@Profile("!prof3") @Configuration public class NotProf3Configuration { @Bean public String helloWorld() { return "Hello World other not prof3"; } } @SpringBootTest @ActiveProfiles("other") class ExampleNot3ProfileTest { @Autowired private String helloWorld; @Test void dummyTest() { Assertions.assertEquals(helloWorld, "Hello World other not prof3"); } }
4 – Se puede utilizar la property spring.profile.include para incluir profiles adicionales a la ejecución:
//application.properties spring.profiles.include=prof4a,prof4b //application-prof4a.properties custom.hello.world.4a=Hello World prof4a custom.hello.world.4b=x //application-prof4b.properties custom.hello.world.4b=Hello World prof4b
@SpringBootTest class Example4ProfileTest { @Value("${custom.hello.world.4a}") private String customHelloWorld4a; @Value("${custom.hello.world.4b}") private String customHelloWorld4b; @Test void dummyTest() { Assertions.assertEquals(customHelloWorld4a, "Hello World prof4a"); Assertions.assertEquals(customHelloWorld4b, "Hello World prof4b"); } }
5 – Uso de profiles de maven junto con los de Spring-Boot
prof1 true prof1 prof5 prof5 ... src/main/resources true
//application.properties spring.profiles.active=@spring.profiles.active@
@SpringBootTest class Example5ProfileTest { @Value("${custom.hello.world}") private String customHelloWorld; @Value("${spring.profiles.active}") private String profileActive; @BeforeEach public void beforeMethod() { assumeTrue( profileActive.equals("prof5")); } @Test void dummyTest() { Assertions.assertEquals(customHelloWorld, "Hello World prof5"); } }
Al contruir con maven podemos indicar un profile definido en el pom.xml o bien no poner ninguno y que coje el indicado como default «prof1»
mvn clean test -Pprof5
Cifrado de contraseñas en Spring-Boot con Jasypt
Jasypt se trata de una herramienta de cifrado simplificado de Java. Te permite agregar funciones básicas de cifrado a los proyectos sin mucha complejidad.
Un ejemplo de uso muy sencillo con Spring-Boot, sería tal que así:
1 – Incorporamos la dependencia al pom.xml.
com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.4
2 – Podemos incorporar el plugin de maven para jasypt que nos va permitir ejecutarlo via mvn.
com.github.ulisesbocchio jasypt-maven-plugin 3.0.4
# Encriptamos 'theValueYouWantToEncrypt' con la contraseña 'the password' mvn jasypt:encrypt-value -Djasypt.encryptor.password="the password" -Djasypt.plugin.value="theValueYouWantToEncrypt" [INFO] ENC(MQoklieA16Tq1gTsyuaGm63ii3skRWMaWyAyObD8Ca7Nqx/SouDq3J4HigJUKDn90xxMIrZQmoQ7hDW7HjNDaQ==) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.361 s [INFO] Finished at: 2022-03-04T08:11:23-08:00 [INFO] ------------------------------------------------------------------------ # Desencriptamos la clave 'MQoklieA16Tq1gTsyuaGm63ii3skRWMaWyAyObD8Ca7Nqx/SouDq3J4HigJUKDn90xxMIrZQmoQ7hDW7HjNDaQ==' con la contraseña 'the password' mvn jasypt:decrypt-value -Djasypt.encryptor.password="the password" -Djasypt.plugin.value="MQoklieA16Tq1gTsyuaGm63ii3skRWMaWyAyObD8Ca7Nqx/SouDq3J4HigJUKDn90xxMIrZQmoQ7hDW7HjNDaQ==" [INFO] theValueYouWantToEncrypt [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.801 s [INFO] Finished at: 2022-03-04T08:12:45-08:00 [INFO] ------------------------------------------------------------------------
3 – Configuración
Jasypt tiene una serie de propiedades para su configuración. Todas tienen un valor por defecto a excepción de jasypt.encryptor.password que contiene la contraseña que se va utilizar para la encriptación.
Por otro lado, a las propiedades encriptadas se les debe poner como valor el prefijo ENC( y el sufijo ) (esto también es configurable).
Para un ejemplo sencillo voy definir unicamente en el application.properties la password de encriptación y la propiedad encriptada:
jasypt.encryptor.password=the password prop.encrypt=ENC(m2laknHcOeMybfARtNAPY6E8490/XBNFovqcaX1cvlcNt+roju7RF0IaE1XByYTKXxfQD39F5e/UfYBHLyWjwA==)
3 – Test de prueba
@SpringBootTest @EnableEncryptableProperties class ExampleJasyptApplicationTests { @Autowired Environment environment; @Test void encryption_test() { assertEquals( "theValueYouWantToEncrypt", environment.getProperty("prop.encrypt")); } }
Medición de tiempo de ejecución con StopWatch
StopWatch es un simple cronómetro que permite medir el tiempo de las tareas. Expone el tiempo total de ejecución y el tiempo de ejecución de cada tarea nombrada.
Documentación Oficial – StopWatch
A continuación, se exponen una serie de formas de utilizarlo con Spring-Boot:
Medición tests configurando el StopWatch en las fases ( @AferAll, … )
@SpringBootTest class StopwatchApplicationTests { static StopWatch stopWatch = new StopWatch(); @BeforeAll static void setUp() { stopWatch = new StopWatch(); } @BeforeEach void timeSetUp(TestInfo testInfo) { stopWatch.start( testInfo.getTestMethod().get().getName()); } @AfterAll static void setDown() { System.out.print( stopWatch.prettyPrint() ); } @AfterEach void timeSetDown() { stopWatch.stop(); } @Test void test_one() throws InterruptedException { Thread.sleep(2000); } ... }
Resultado obtenido
StopWatch '': running time = 6011364520 ns --------------------------------------------- ns % Task name --------------------------------------------- 2008182259 033 % test_one 2001327545 033 % test_two 2001854716 033 % test_three
Medición de los test utilizando un TestExecutionListener
Es necesario crear un listener que extienda de AbstractTestExecutionListener, el cuál nos permitirá sobreescribir los métodos beforeTestClass, afterTestClass, afterTestMethod,…
public class ExecutionTimeTestListener extends AbstractTestExecutionListener { private StopWatch stopWatch; @Override public void beforeTestClass(TestContext testContext) throws Exception { super.beforeTestClass(testContext); stopWatch = new StopWatch(testContext.getTestClass().getSimpleName()); } @Override public void beforeTestMethod(TestContext testContext) throws Exception { super.beforeTestMethod(testContext); stopWatch.start(testContext.getTestMethod().getName()); } @Override public void afterTestMethod(TestContext testContext) throws Exception { if (stopWatch.isRunning()) { stopWatch.stop(); } super.afterTestMethod(testContext); } @Override public void afterTestClass(TestContext testContext) throws Exception { System.out.println(stopWatch.prettyPrint()); super.afterTestClass(testContext); } }
Forma de utilización:
@ExtendWith(SpringExtension.class) @TestExecutionListeners({DependencyInjectionTestExecutionListener.class, ExecutionTimeTestListener.class}) class StopwatchWithListenerApplicationTest { @Test void test_one() throws InterruptedException { Thread.sleep(2000); } ... }
Resultado obtenido
StopWatch 'StopwatchWithListenerApplicationTest': running time = 6017421650 ns --------------------------------------------- ns % Task name --------------------------------------------- 2013933526 033 % test_one 2001191940 033 % test_two 2002296184 033 % test_three
Configuración utilizando Aspectj
Se crea un aspecto para que capture todas las ejecuciones del paquete service. Ademas se le pone el @Around para que envuelva el método que captura. Así podemos poner el StopWatch para que medir el tiempo que tarda en ejecutarlo.
Requiere añadir la dependencia spring-boot-starter-aop.
org.springframework.boot spring-boot-starter-aop
@Aspect @Component public class StopWatchAOP { @Around("execution(* es.com.disastercode.examplesspringboot.stopwatch.service.*.*(..))") public Object measureMethod(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(); Object retVal; try { sw.start(pjp.getTarget()+"."+pjp.getSignature()); retVal = pjp.proceed(); } catch (Throwable e) { throw e; } finally { sw.stop(); System.out.println(sw.prettyPrint()); } return retVal; } }
Ejemplo de test.
@SpringBootTest class StopwatchWithAopApplicationTests { @Autowired private ProductService productService; @Test void test_one() throws InterruptedException { assertEquals("name 2", this.productService.findNameProductById(2)); } }
Resultado obtenido
StopWatch '': running time = 2050920963 ns --------------------------------------------- ns % Task name --------------------------------------------- 2050920963 100 % es.com.disastercode.examplesspringboot.stopwatch.service.ProductServiceImpl@3d20e575.String es.com.disastercode.examplesspringboot.stopwatch.service.ProductServiceImpl.findNameProductById(Integer)
Spring-Boot y variables de entorno
Formas de pasar información a una aplicación java realizada con spring-boot:
Variable de entorno
Podemos crear una variable de entorno añadiendola directamente desde la consola o bien en el propio entorno de desarrollo (la imagen es con un eclipse.)
set MI_VARIABLE=Hola!!
Podemos acceder a ellas desde la apliación así:
String myVar = System.getenv().get("MI_VARIABLE");
Línea de comandos
Podemos pasar propiedades por línea de comandos, por ejemplo:
java -jar -DmiVariable=Hola programa.jar
Para su lectura bastaria con
@Autowired private Environment env; ..... String value = env.getProperty( "miVariable" ); .....
application.properties y application-xxx.properties
En el fichero application.properties puedes añadir también variables:
var.prueba.1=valor1 var.prueba.2=valor2 ....
Adicionalmente en Spring-Boot podriamos crear otros ficheros application-xxx.properties. XXX hace referencia al profile que quieras usar, podemos definir que profile se va a utilizar mediante la propiedad spring.profiles.active. Es decir, si queremos que los valores de las variables se obtengan prioritariamente del fichero application-local.properties deberíamos asignar el valor local a la propiedad spring.profiles.active.
spring.profiles.active=local
Agrupaciones en consultas con javax.persistence.criteria.CriteriaBuilder
Vamos a poner un par de ejemplos de uso del CriteriaBuilder en JPA para poder hacer consultas usando JpaSpecificationExecutor e incluso usar agrupaciones con groupBy.
Se van a ir poniendo las diferentes clases desde el Entity hasta el Service:
Entity de la tabla cuenta con un par de campos.
@Entity(name="cuentaEntity") @Table(name="CUENTA") public class CuentaEntity implements Serializable { @Id private Integer id; @Column(name="balance") private Integer balance; @Column(name="empresa") private String empresa; @Column(name="fecha") @Temporal(TemporalType.TIMESTAMP) private Date fecha; //getter setter }
Su correspondiente JPAMetalModelEntityProcessor, la vamos a utilizar para las consultas.
@Generated(value = "org.hibernate.jpamodelgen.JPAMetalModelEntityProcessor") @StaticMetamodel(CuentaEntity.class) public abstract class CuentaEntity_ { public static volatile SingularAttribute< CuentaEntity, Integer> id; public static volatile SingularAttribute< CuentaEntity, Integer> balance; public static volatile SingularAttribute< CuentaEntity, String> empresa; public static volatile SingularAttribute< CuentaEntity, Date> fecha; }
Un clase repositorio. Hay que hacer que herede tanto de la JpaRepository como del JpaSpecificationExecutor.
@Repository public interface CuentaRepository extends JpaRepository< CuentaEntity, Integer>, JpaSpecificationExecutor< CuentaEntity> { }
Creamos nuestra clase criteria donde meteremos las especificaciones de las querys. Se añaden unos ejemplos de condicionales
public class CuentaCriteria implements Serializable{ private Integer id; private Integer balance; private String empresa; private Date fecha; ...... public static Specification< CuentaEntity> search(final CuentaCriteria cri){ return new Specification< CuentaEntity>(){ @Override public Predicate toPredicate(Root< CuentaEntity> root, CriteriaQuery< ?> criQuery, CriteriaBuilder criBuilder) { List< Predicate> predicates = new ArrayList< Predicate>(); //el nombre de la empresa sea igual al que se pasa en CuentaCriteria. Si no está vacia. if( !StringUtils.isEmpty( cri.getEmpresa())){ predicates.add( criBuilder.like(root.get(CuentaEntity_.empresa), cri.getEmpresa()) ); } //la fecha debe ser igual a la que se pasa en CuentaCriteria. Si no está vacia. if( !StringUtils.isEmpty( cri.getFecha())){ predicates.add( criBuilder.equal(root.get(StatementEntity_.fecha),cri.getFecha()) ); } //podemos añadir tambien una condicion de tipo OR ( id = 0 OR id = 1). List< Predicate> predOr = new ArrayList< Predicate>(); predOr.add( criBuilder.equal(root.get(CuentaEntity_.id), 0) ); predOr.add( criBuilder.equal(root.get(CuentaEntity_.id), 1) ); predicates.add( criBuilder.or(predOr.toArray(new Predicate[predOr.size()]) ) ); //Si tubieramos un objeto Embeddable u otro tipo de objeto podriamos acceder //a las propiedades de ese objeto //predicates.add( criBuilder.like(root.get("OBJETO").get("CAMPO_OBJETO"), "qqqqqq") ); .............. //Concatenamos todos los predicados como ands return criBuilder.and(predicates.toArray(new Predicate[predicates.size()])); } }; } }
Interfaz con nuestro servicio con unos métodos de prueba.
public interface CuentaService{ public Page< CuentaEntity> find(CuentaCriteria criteria, Pageable pageable); public List< Object[]> findGroupBy(CuentaCriteria criteria); public Long count(CuentaCriteria criteria); ......... }
Y su implementación:
@Component("cuentaService") @Transactional public class CuentaServiceImpl implements CuentaService { @PersistenceContext EntityManager em; @Autowired private CuentaRepository cuentaRepository; //En este método permite realizar una consulta que devuelve el listado de todas las cuentas que //lo cumplen. Las condiciones de la consulta se le pasan con CuentaCriteria.search(criteria). //No devolverá todos los elementos al pasarle un objeto Pageable para paginación. public Page< CuentaEntity> find(CuentaCriteria criteria, Pageable pageable){ return cuentaRepository.findAll( CuentaCriteria.search(criteria), pageable); } //En este método deveulve la cuenta de la consulta anterior. Las condiciones de la consulta //se le pasan con CuentaCriteria.search(criteria). public Long count(StatementCriteria criteria){ return this.statementRepository.count( StatementCriteria.search(criteria) ); } //El groupBy no funciona bien si se mete en StatementCriteria.search(criteria), eso es porque //aunque hagas un multiselect() con sólo los elementos que quieres sacar en la consulta te lo va //a sobreescribir por el select(). //Una posible solución sería hacerlo como se hace en este método. public List< Object[]> findGroupBy(CuentaCriteria criteria){ CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery> cq = cb.createQuery(); Rootroot = cq.from(StatementEntity.class); //declaramos el multiselect con las columnas a devolver cq.multiselect( //fecha mayor cb.greatest( root.get(CuentaEntity_.fecha).as(java.util.Date.class) ), //fecha menor cb.least( root.get(CuentaEntity_.fecha).as(java.util.Date.class) ), //balance mayor cb.max( root.get(CuentaEntity_.balance) ), //balance menor cb.min( root.get(CuentaEntity_.balance) ), //media del balance cb.avg( root.get(CuentaEntity_.balance) ), //suma del balance total cb.sum( root.get(CuentaEntity_.balance).as(java.lang.Integer.class) ), //suma del balance siempre que sea mayor de cero, es decir, positivo cb.sum( cb. selectCase() .when(cb.greaterThan(root.get(CuentaEntity_.balance), 0), root.get(CuentaEntity_.balance) ).otherwise(0)), //contador con todas las filas agrupadas cb.count( root.get(CuentaEntity_.id) ) ); //aplicamos todas la condiciones de consulta al where cq.where( CuentaCriteria.search(criteria).toPredicate(root, cq, cb) ); //le decimos que agrupe por el nombre de las empresas cq.groupBy( root.get( CuentaEntity_.empresa) ); Query query = em.createQuery(cq); //Por último lo podemos retornar como una lista de objetos return (List< Object[]>)query.getResultList(); } }
@ElementCollection vs @OneToMany
Estas dos anotaciones nos permiten elaborar relaciones 1 a N entre dos objetos con JPA. A continuación se va a exponer su funcionamiento:
@ElementCollection
Te permite de una forma muy simple definir una relación de una entidad con otros objetos. Se puede por ejemplo, dado un empleado, definir una lista de strings que se corresponden con los telefonos del empleado. Sería así:
@Entity(name="Employee") @Table(name="EMPLOYEE") public class Employee { @Id @Column(name = "_id") private String id; @Column(name="name") private String name; @ElementCollection @CollectionTable(name="PHONE", joinColumns=@JoinColumn(name="phone_id")) @Column(name="phone_number") private List< String> phones; ..... }
Teniendo en cuenta este ejemplo tendremos en BD:
– Una tabla EMPLOYEE con las columnas -> _id y name.
– Una tabla PHONE con las columnas -> phone_id y phone_number. (el campo phone_id hara de foreign key, mientras que phone_number contendrá el String del número de teléfono)
En el ejemplo anterior usábamos una lista de String pero podríamos usar un objeto @Embeddable, tal que así:
@Entity public class Employee { @Id @Column(name = "_id") private String id; @Column(name="name") private String name; @ElementCollection @CollectionTable(name="PHONE",joinColumns=@JoinColumn(name="phone_id")) private List< Phone> phones; ... } @Embeddable public class Phone { @Column(name="phone_number") private String number; ... }
El resultado de hacerlo así en la base de datos sería exactamente el mismo pero nos va a permitir añadir mas campos en el objeto PHONE.
@OneToMany
Te permite relacionar dos objetos Entity.
@Entity public class Employee { @Id @Column(name = "_id") private String id; @Column(name="name") private String name; @OneToMany(cascade={CascadeType.ALL}, mappedBy="empl", fetch=FetchType.LAZY, targetEntity = Phone.class) @OnDelete(action = OnDeleteAction.CASCADE) private List< Phone> phones; ... } @Entity public class Phone { @Id private long id; @Column(name="phone_number") private String number; @ManyToOne @JoinColumn(name="phone_id") private Employee empl; ... }
Como se puede ver hay que marcar la relacion OneToMany en la entidad Employee, marcándole el campo donde debe apuntar (en este caso «empl»). En ese campo de la entidad Phone se debe marcar la ManyToOne indicandole el nombre de la columna (phone_id) que hará de foreign key.
Además, se ha añadido la anotación @OnDelete para indicarle que haga un borrado en cascada de la lista de teléfonos al eliminar al empleado. Esto es significativo por el hecho de que en el caso del ElementCollection no se permite indicar el tipo de borrado.
Nota: Hay que decir que en el borrado en cascada se eliminan de golpe todos los relacionados mientras que sin él, va haciendo un borrado de cada uno de sus relacionados (es decir, si un empleado tiene 10 teléfonos al hacerlo en cascada se lanzaría un único delete mientras que en el otro caso se lanzan 10 sentencias delete).
Resumen final
– BD: el esquema de tablas y relaciones que va a construir JPA en nuestra BD va a ser muy similar en ambos casos.
– Código: Se puede decir que es mucho más cómodo utilizar @ElementCollection al simplificar el código.
– Potencia: Si queremos poder utilizar toda la potencia de las relaciones optimizando nuestros recursos es mejor decantarse por @OneToMany.
Fuente
– @ElementCollection
– @OneToMany
Aplicación que integra Spring-Boot + AngularJS + Thymeleaf
En mi repositorio de GitHub he dejado una apicación simple que integra Spring-Boot con AngularJS utilizando lo siguiente:
– Para la configuración de Spring se ha utilizado Spring Boot junto con los siguientes módulos
. . . . 1- Spring Data JPA -> como capa de persistencia.
. . . . 2- Spring Data REST con Spring HATEOAS -> para la capa de servicios rest con los que interactuará AngularJS.
. . . . 3- Spring Security -> para la Autenticación y Autorización de la aplicación.
. . . . 4- Spring MVC con Thymeleaf -> se utiliza para gestionar principalmente los listados de consulta, así como todas las vistas que no tienen formularios.
. . . . 5- AngularJS -> se utiliza para las vistas de los formularios de detalles.
. . . . 6- Bootstrap + Angular-ui + Font Awesome + Angular-Show-Errors -> para el diseño y maquetación del front end.
. . . . 7- JUnit -> pruebas unitarias.
– Se utilizan Base de datos y servidor embebidos a modo de ejemplo: H2 y Tomcat.
– Para la creación de informes se utiliza JasperReports. Se ha configurado el pom para que a partir de los ficheros /src/main/resources/static/reports/*.jrxml y genere los /src/main/webapp/jasper/*.jasper. Para ello basta con ejecutar mvn generate-resources.
– La gestión de dependencias se realiza con: Maven y bower.
Instalación
=========
1- Instalar maven (utilizada la 3.0.5)
2- Instalar el wraper de maven para spring-boot:
mvn -N io.takari:maven:wrapper
3- Se puede ejecutar de varias formas:
//Con maven: mvn spring-boot:run. //o bien //Construyendo el jar: mvn clean package //Y haciéndolo correr con: java -jar target/springangularjs-0.0.1-SNAPSHOT.jar
4- Si todo va bien la aplicación correrá en: http://localhost:8080/
5- Copiar proyecto e importarlo al eclipse como proyecto de maven.
6- Recordar que para modificar las librerías de js es necesario bower. Para ello:
. . . . a- se necesita tener instalado node.js (https://nodejs.org/)
. . . . b- se necesita también tener instalado git (https://git-scm.com/download/win)
. . . . c- instalamos bower con:
npm install -g bower
. . . . d- finalmente ejecutando «bower install» en el proyecto nos colocara en el directorio /src/main/resources/static/bower_components las versiones de las librerías indicadas en el bower.json.
Todo el código esta subido al GitHub
Esquema de la Arquitectura
Digrama de clases
Despliegue aplicación Spring-Boot en Openshift
En este tutorial vamos a explicar como se debe desplegar una aplicación Spring-Boot en Openshift. La aplicación correrá en un tomcat y utilizará como Base de Datos H2.
Creamos la aplicación en OpenShift. Para ello:
Paso 1: Nos registramos en https://www.openshift.com/
Paso 2: Creamos una aplicación que contenga un Tomcat 7 (JBoss EWS 2.0).
Instalamos y configuramos las Openshift Client Tools
Paso 1: Lo primero que debemos hacer es instalar Ruby, la versión que recomienda Openshift es la 1.9.3.
http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-1.9.3-p551.exe
(Cuando estamos instalando Ruby debemos marcar la opcion que dice Add Ruby executables to your PATH, para que se añada al Path del sistema.)
Para saber si tenemos Ruby añadido correctamente al Path, abrimos un terminal y ejecutamos:
ruby –v.
Paso 2: Instalamos la última versión de Git.
https://github.com/msysgit/msysgit/releases/download/Git-1.9.5-preview20150319/Git-1.9.5-preview20150319.exe
para comprobar si git también está bien instalado poedemos ejecutar:
git --version.
Paso 3: Instalamos las Client Tools (Openshift rhc).
//Para instalar las rhc sólo debemos escribir en nuestro terminal: gem install rhc //A continuación, configuramos con: rhc setup
Nos pedirá logarnos con nuestras credenciales. Al introducirlas, si son correctas el sistema de configuración nos indicará que va a crear un Token para que podamos acceder a OpenShift sin necesidad de colocar nuestras credenciales de acceso, este Token se guardará en nuestra carpeta de usuario en una carpeta llamada .openshift.
Generando War
Usando maven se puede hacer directamente por la consola con:
mvn clean package
Desplegando WAR en OpenShift
Paso 1: Primero debemos crear un SSH public key para poder acceder al servidor por ssh. Se crean desde la propia consola web de Openshift. O usamos la que nos ha creado por defecto openshift en nuestro directorio de usuario cuando se instaló rhc.
Si queremos crear una nueva podemos crearla con el Putty Key Generator. Luego se debe añadir en el openshift a través de la consola web.
Paso 2: Descargamos el repositorio git.
Paso 3: Borrar la carpeta src y el pom.xml
Paso 4: Colocamos nuestro WAR en la carpeta webapp
Paso 5: Editar el archivo server.xml que está en .openshift/config/server.xml. Buscamos el siguiente conector y en el redirectPort pone 8443, le quitamos el 8 y guardamos.
Paso 6: Al archivo .openshift/config/context.xml le añadimos la siguiente Valve:
< Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="x-forwardedproto"/>
Paso 7: Hacemos commit y push
Paso 8: Reiniciamos el servidor.
Base de datos H2
Se debe colocar el fichero de bd en app-root/data y darle permisos de lectura y escritura.
i18n con Spring Boot
Paso 1: Añadir los properties con las keys en src/main/resources/ uno por cada idioma tal que así:
messages.properties (este será el de por defecto)
messages_es.properties
Paso 2: Hay que configurar la aplicación para que la request obtenga el locale. Para ello se pueden añadir estos beans:
@Bean public LocaleResolver localeResolver() { return new CookieLocaleResolver(); } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); lci.setParamName("lang"); return lci; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); }
Paso 3: Modo de uso.
Desde un controller de Spring basta con inyectar un MessageSource y ya podríamos usarlo.
..... @Autowired private MessageSource messageSource; @RequestMapping(value = "/list", method = RequestMethod.GET) public String listItems(Model uiModel, Pageable page, HttpServletRequest request){ String msg = messageSource.getMessage(request.getParameter("key.successMessage") .toString(),null,LocaleContextHolder.getLocale()); .... } ....
Desde el front-end con thymeleaf bastaria con usar el #{key.successMessage}
< html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3" > .... < span th:text="#{key.successMessage}"> ....
Categorias
- adobe (2)
- agile (1)
- Alfresco (1)
- Android (26)
- Angular (6)
- angularjs (10)
- apache (1)
- axis (2)
- Bases de datos (14)
- Bootstrap (1)
- C# (3)
- Cámara (1)
- chrome (3)
- Codeigniter (2)
- Control de Versiones (2)
- CSS (25)
- CVS (1)
- Django (9)
- Django Rest Framework (1)
- DNS (1)
- Docker (3)
- dominio (1)
- eclipse (5)
- Entity Framework (2)
- ETL (1)
- Firefox (6)
- flash (1)
- freecad (1)
- Git (12)
- GitHub (4)
- gpg (2)
- Groovy (1)
- Handlebars (1)
- hibernate (4)
- hosting (1)
- HTML (50)
- HTML 5 (26)
- Impresión 3D (9)
- Inkscape (1)
- IOS (2)
- ireports (3)
- Java (44)
- Javascript (55)
- JBoss (5)
- JPA (2)
- JQuery (20)
- Json (7)
- JSP (6)
- Keycloak (1)
- Lamp (1)
- LDAP (2)
- lean (1)
- linkedin (1)
- LINQ (1)
- linux (13)
- Livecycle (1)
- log (1)
- microcontroladores (1)
- MongoDB (4)
- MySQL (8)
- Node.js (5)
- OC4J (1)
- Openshift (2)
- Oracle (6)
- Patrones de Diseño (1)
- Photoshop (2)
- php (20)
- PostgreSQL (1)
- python (19)
- rabbitmq (1)
- Raspberry PI (13)
- Raspherry PI (5)
- React (6)
- seguridad (3)
- Selenium (3)
- Sencha Touch (1)
- Sin categoría (29)
- Spring (17)
- spring-boot (3)
- SQL (7)
- SQLServer (1)
- SSO (1)
- struts (2)
- SVN (1)
- Talend (1)
- Tomcat (6)
- unity (3)
- Visual Studio Code (2)
- vmware (5)
- Web Services (11)
- windows (18)
- wordpress (10)
- Xiaomi (1)
- xml (2)
Trabajos Realizados
- App Android – Autoka Fr
- App Android – Cartelera Cántabra
- App Android – Gramática y Vocabulario Ingles
- App Android – Hoja de Gastos
- App Android – Hotel Torre Cristina
- App Android – OcioEnjoy
- App Android – Visor CardBoard
- App Firefox – Managapp
- DiamanteBomba – DisasterCode
- Generador de Partes de Trabajo
- GitHub – Android Web Generator
- GitHub – Dynamic Angular Gallery
- GitHub – Dynamic React Gallery
- GitHub – Sotilizator
- GitHub – SpringAngularJS
- GitHub – Swiper Dynamic Angular Gallery
- HazParejas – DisasterCode
- RompeCabezas – DisasterCode
- Unity Game – English Couple
- Unity Game – Kill Wasp
- WordPress – El Buen Apicultor
- WordPress – El Cajón de los Retales
- WordPress – El Vestidito Azul
- WordPress – Feuchas
- WordPress – Fragua de Navajas Ponce
- WordPress – Humor a las Tres
- WordPress – Photo Places