Mostrando entradas con la etiqueta PoC. Mostrar todas las entradas
Mostrando entradas con la etiqueta PoC. Mostrar todas las entradas

miércoles, 15 de agosto de 2018

Flyway: Como migrar la base de datos

Hoy vamos a hablar de Flyway, un software que nos facilitará la migración de bases de datos. Sobre todo es util para la migración de los esquemas a lo largo de los distintos entornos de prueba, preproducción y producción. Ya que es facil que en el pase de un desarrollo de un entorno a otro podamos fallar y/o olvidar ejecutar algún script necesario.
  • Funcionamiento
Su funcionamiento se basa en la creación de un tabla de gestión que almacenará la información de los distintos script de migración que contenga la aplicación. En función de estos scripts, su orden de ejecución y la tabla de gestión, tendremos la hoja de ruta para poder pasar una base de datos de un entorno a otro. 
  • Ejecución
Flyway, al comenzar buscará la tabla de ejecución, denominada 'flyway_schema_history, y en caso de no encontrala la creará. Posteriormente, buscará los scripts en formato .sql o .java en una ruta configurable y comprobará si se han ejecutado ya o no, en caso negativo los ejecutará y almacenará la información asociada a dichos scripts en la tabla de gestión.

Dentro de esta información asociada se encuentra el nombre del fichero, la versión o el checksum del mismo. Al contener el checksum, en caso de que modifiquemos el fichero, flyway se daría cuenta de que la versión actual es diferente a la ejecutada y lanzaría una excepción. Por tanto si queremos modificar un script ya ejecutado, es obligatorio crear un nuevo script ya que no es modificable el anterior.

A tener en cuenta respecto a lo indicado anteriormente, que ejecutará el script siempre y cuando no se haya ejecutado anteriormente tal como hemos dicho y la versión del mismo sea posterior a la última versión registrada en la tabla de gestión.  
  • Configuración
Vamos a ver un ejemplo con gradle donde podemos ver como funciona. Lo configuramos indicando que vamos a usar el plugin de flyway y creando las variables de configuración necesarias.

plugins {
    id "org.flywaydb.flyway" version "5.1.4"
}
flyway {
    url = 'jdbc:mysql://localhost:3306/'
    user = 'root'
    password = 'root'
    schemas = ['flywayExample']
}

Lo siguiente será crear los ficheros tipo 'v${number}__${script_name}.sql' dentro de la carpeta 'src/main/resources/db/migration', que son el formato y ubicación por defecto.

Por último ejecutarlo a través del comando './gradlew flywayMigrate'. Y ya podremos acceder a la base de datos y ver como se han ejecutado los scripts. Aquí teneis un ejemplo que podeis usar para comprobar su funcionamente, como base de datos usa un docker de MySQL.

miércoles, 11 de julio de 2018

Jersey: Iniciación


Ya estuvimos hablando sobre como hacer microservicios con Spring Boot aquí. Hoy vamos ha hacerlo sobre Jersey. Jersey es una implementación open-source ( aunque nació dentro de Sun ) del estandar JAX-RS, que es la API de servicios REST.  Una de las cualidades que tiene este framework es que es bastante liviano como podremos ver en el ejemplo.

En este post veremos como hacer un CRUD básico, sin conexión a base de datos. Pero nos servirá para ver como funciona y algunas de sus anotaciones.

Para empezar tenemos la anotación '@Path' para controlar a través de una clase donde vamos a tratar todas las llamadas que se hagan a un determinado recurso. Para este ejemplo crearemos y rellenaremos un mapa de valores que nos permita realizar las distintas operaciones.

@Path("/book")
public class BookApi {
    private static Map<Integer, Book> bookMap;
}

Lo primero será hacer las llamadas para obtener valores. Para ellos usaremos la operación '@GET' y a través de la anotación '@Produces' podremos indicar que tipo de salida produciremos.

@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<Book> getAll() {
    return bookMap.values();
}
// http://localhost:8080/JerseyExample/rest/book

A través de la anotación '@Path' podremos indicar que se filtren determinadas llamadas que cumplan con el. Y con '@PathParam' podremos acceder a los parametros incluidos en la URL.

@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Book getOne(@PathParam("id") final Integer id) {
    return bookMap.get(id);
}
// http://localhost:8080/JerseyExample/rest/book/2

Con '@DELETE' podremos gestionar las llamadas HTTP Delete. Y con el objeto 'Response' podremos configurar que tipo de respuesta vamos a devolver. Tanto su código de respuesta como su contenido.

@DELETE
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response deleteOne(@PathParam("id") final Integer id) {
    bookMap.remove(id);
    return Response.status(200).build();
}
// http://localhost:8080/JerseyExample/rest/book/2

A continuación veremos como hacer las operaciones de actualización con '@PUT' y creación a través de '@POST'.

@PUT
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response updateOne(@PathParam("id") final Integer id, final Book book) {
    bookMap.put(id, book);
    return Response.status(200).build();
}
// http://localhost:8080/JerseyExample/rest/book/2 . Body: {"id":4,"author":"Frederik Pohl","name":"Gateway"}
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response saveOne(final Book book) {
    bookMap.put(bookMap.size() + 1, book);
    return Response.status(200).build();
}
// http://localhost:8080/JerseyExample/rest/book . Body: {"id":4,"author":"Frederik Pohl","name":"Gateway"}

Aparte de esto ya solo nos quedaría configurar nuestra aplicación a través del fichero web.xml y desplegar el proyecto en algún servidor. Nosotros para este ejemplo hemos usado jetty y lo lanzamos a través de gradle.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>Jersey Example</display-name>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>es.home.example.api</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Por último indicar que aquí (JerseyExample) puedes encontrar el proyecto de ejemplo y dentro de src/test/resources podras ver una colleción de json con las llamadas de pruebas. 

sábado, 13 de agosto de 2016

Spring Boot Security: nivel básico

En este post, vamos a intentar explicar como configurar de forma básica Spring Security 4 con Spring Boot. Accediendo a los roles a través de JDBC y con los passwords encriptados. Se puede configurar en 4 sencillas clases y tenéis todo el código aquí (ExampleSpringSecurity) (en src/test/resources teneis incluidos los scripts BBDD).

1. Application: Esta clase tendrá la anotación 'SpringBootApplication'  y un método main que nos permitirá arrancar la aplicación.
2. MvcConfig: En esta clase tendremos la configuración para la navegación url-pantallas/vistas. Y también indicaremos a través de 'viewResolver' como resolver las  el viewResolver, que nos permitirá indicar la pantalla/vista.


@Override
public void addViewControllers(final ViewControllerRegistry registry) {
 registry.addViewController("/home").setViewName("home");
 registry.addViewController("/").setViewName("home");
 registry.addViewController("/hello").setViewName("hello");
 registry.addViewController("/login").setViewName("login");
 registry.addViewController("/403").setViewName("403");
}
@Bean
public InternalResourceViewResolver viewResolver() {
 InternalResourceViewResolver resolver = new InternalResourceViewResolver();
 resolver.setPrefix("/WEB-INF/jsp/");
 resolver.setSuffix(".jsp");
 return resolver;
}

En el código podemos ver que si indicamos una vista denominada 'hello', buscará y redireccionará a la pantalla /WEB-INF/jsp/hello.jsp'.

3. AuthenticationProviderConfig: En esta clase declararemos el 'dataSource' que nos permitirá conectarnos a la BBDD. Y lo mas importante el bean 'userDetailService', en el cual implementaremos como obtener el usuario y contraseña almacenados en BBDD y como obtener los roles asociados al usuario.

@Bean(name = "userDetailsService")
public UserDetailsService userDetailsService() {
 JdbcDaoImpl jdbcImpl = new JdbcDaoImpl();
 jdbcImpl.setDataSource(dataSource());
 String usersByUserNameQuery = "select username,password, enabled from users where username=?";
 String authoritiesByUsernameQuery = "select u.username, ur.authority from user_roles ur, users u where username=? and u.user_id = ur.user_id";
 jdbcImpl.setUsersByUsernameQuery(usersByUserNameQuery);
 jdbcImpl.setAuthoritiesByUsernameQuery(authoritiesByUsernameQuery);
 return jdbcImpl;
}

4. WebSecurityConfig: En esta clase realizaremos la configuración propia de Spring Security, en el aspecto de que indicamos como se va a realizar la obtención de los datos (a través de userDetailsService). Como vamos a realizar la encriptación de la contraseña (a través de una clase incluida dentro de Spring como es: BCryptPasswordEncoder). Y por último configurar todas las urls de acceso de la aplicación e indicar si son accesible por todos los usuarios, si solo son accesible por un determinado rol y configuraciones normales como cuales son las paginas de login y logout.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 UserDetailsService userDetailsService;
 @Bean(name = "passwordEncoder")
 public PasswordEncoder passwordencoder() {
  return new BCryptPasswordEncoder();
 }
 @Autowired
 public void configAuthentication(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
 }
 @Override
 protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").access("hasRole('ROLE_ADMIN')").anyRequest()
    .permitAll()
    .and().formLogin().loginPage("/login").usernameParameter("username")
    .passwordParameter("password")
    .and().logout().logoutSuccessUrl("/login?logout")
    .and().exceptionHandling().accessDeniedPage("/403")
    .and().csrf();
 }
}

Toda la configuración se puede crear en un único fichero sin problemas. Pero la idea de realizarlo así es aumentar la claridad en la configuración de la aplicación.

Este ejemplo lo he sacado de esta Web, donde además podemos ver un post algo más complejo con JPA Data.

domingo, 4 de octubre de 2015

Como hacer un WebService REST (JAX-RS)

Hace poco indicamos como hacer un prototipo de Servicio Web con JAX-WS (En dos post: primero y segundo). Ahora vamos a realizar un prototipo de Servicio Web pero utilizando JAX-RS. Para empezar explicaremos que es JAX-RS y REST:
  • REST: Interfaz entre sistemas que utiliza directamente HTTP para obtener datos o indicar la ejecución de operaciones sobre los datos, en cualquier formato (XML, JSON, etc) sin las abstracciones adicionales de los protocolos basados en patrones de intercambio de mensajes, como SOAP.
  • JAX-RS: API que proporciona soporte en la creación de servicios web de acuerdo con REST. Desde su versión 1.1 ya se encuentra incluido en JEE.
Para empezar crearemos una aplicación web mavenizada básica y a partir de ella construiremos nuestro servicio. Una vez creada le indicamos las dependencias que va a tener:
  • jersey-servlet: Para importar todas las librerías necesarias para el uso de REST. Jersey es la implementación de Sun/Oracle del estándar.
  • jersey-json: Para incluir las librerías necesarias para el manejo de JSON, que será como transmitamos la información.
  • hibernate-entitymanager: Para el manejo de información contra la BBDD.
Para evitar hacerlo muy extenso, explicaremos brevemente la capa del modelo y BBDD puesto que es exactamente igual que en los post de JAX-WS. El modelo constará de dos clases 'User' con idéntica información:
  • Una clase con anotaciones JPA que contendrá la representación de la tabla y con la cual podremos hacer las operaciones contra la BBDD.
  • Una con anotaciones 'javax.xml.bind' que nos permitirá enviar la información a través del servicio con un determinado formato.
La configuración de BBDD la haremos de la siguiente forma:
  • Un fichero 'persistence.xml' donde configuraremos la conexión a BBDD.
  • Una clase genérica 'AbstractDao' que nos permitirá crear la configuración y el 'entityManger' para realizar las operaciones.
  • Una clase especifica 'UserDaoImpl' que implemente 'AbstractDao' y que nos permita realizar las operaciones contra la tabla 'User'.
Ahora vamos a lo interesante, como crear el servicio una vez que tenemos la estructura. Para empezar crearemos el servicio en sí:

@Path("/webserviceSample")
@Produces(MediaType.APPLICATION_JSON)
public class WebServiceSampleImpl implements WebServiceSample {
  • La anotación @Path nos indica en que dirección de la aplicación va a estar disponible el servicio web.
  • La anotación @Produces indica que va producir el servicio web, en nuestro caso JSON.
Ahora vamos a crear un método dentro del servicio web:
@GET
@Path("/users")
public List getPersonas() {
 UserDao dao = new UserDaoImpl();
 List list = dao.getUsers();
 List result = new ArrayList();
 for (User p : list) {
  result.add(UserXml.valueOf(p));
 }
 return result;
}
  • La anotación @Path indica la dirección de la aplicación desde la cual va a ser accesible dicho método. Esta dirección debe de ser concatenada a la dirección de la aplicación, por tanto quedaría '/webserviceSample/users'.
  • La anotación @GET indica que método HTTP será el utilizado para acceder a ese método en la ruta indica.
El método HTTP con el cual acceder a una URL de servicio JAX-RS es algo muy importante, hay cuatro posibles: POST, GET, PUT y DELETE. Normalmente se les asocia con una función específica. Esto nos permita que si atacamos a una misma dirección de un servicio web REST, realizará una función u otra en base al método que hayamos utilizado. Este es el uso común: POST para insertar información, GET para solicitarla, PUT para actualizarla y DELETE para borrarla. Si implementase los 4 servicios pasaría a denominarse un servicio CRUD (CREATE, READ, UPDATE y DELETE).

Por último solo nos queda la configuración del servicio a nivel de aplicación, a través del web.xml. En el tendremos que crear la configuración para que un servlet de la librería Jersey filtre todas las llamadas que queramos sean atendidas por el servicio web. En nuestro caso hemos puesto la url '/rest' por lo que ahora si queremos llamar a nuestro servicio necesitaremos acceder a la siguiente dirección: http://localhost:8080/ExampleJaxRS/rest/webserviceSample/users/.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	      version="3.0">
	<display-name>ExampleJaxRS</display-name>
	<servlet>
		<servlet-name>jersey-serlvet</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<init-param>
		     <param-name>com.sun.jersey.config.property.packages</param-name>
		     <param-value>es.home.ws.example.service</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>jersey-serlvet</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>
</web-app>
En este enlace (ExampleJaxRS) podréis encontrar el código fuente subido a Github.

martes, 4 de agosto de 2015

WebService con JAX-WS, JAXB y JPA - II

Vamos a mejorar el ejemplo que teniamos del webservices con JAX-WS. Ahora para introducirle la capa de acceso a datos. Esta parte la haremos usando otro estandar de Java EE: JPA.

¿Que es JPA o JAXB?
  • JPA: Es la API que permite hacer representaciones Java de objetos de BBDD relacionales y facilitar su manejo. Enlace Wikpedia Enlace Oficial
  • JAXB: Es la API que permite hacer representaciones java de documentos XML y facilitar su manejo. 
Para empezar le añadimos las librerías necesarias para que funcione JPA. Para ello elegimos la implementación de Hibernate:
  • hibernate-entitymanager: La implementación para el uso del entity manager por Hibernate.
  • hibernate-jpa-2.1-api: La implementación de Hibernate de JPA
  • Conector de base de datos: En mi caso MySQL. 
Una vez configurado las librerías vamos a crear el fichero de configuración de BBDD, con JPA normalmente es el conocido 'persistence.xml':


 org.hibernate.ejb.HibernatePersistence
 
  
  
  
  
  
  
  
  
 


En el podemos ver la siguiente configuración:
  • Hemos llamado a nuestra configuración 'myPersistenceUnit'.
  • Hemos indicado los datos de conexión: URL, usuario, password, driver de conexión, etc
  • Hemos indicado que se autodetecten las clases que utilizaremos para conectar a la BBDD. De lo contrario deberiamos indicar especificamente que clase serán las que representen los objetos relacionales de BBDD. 
  • Hemos indicado configuración adicional sobre las querys. En este caso que se muestren y se muestren formateadas. 
Para la parte de acceso a datos hemos creado una clase abstracta que permite obtener la entidad gestora de BBDD configurada en nuestro archivo, pero para eso deberemos indicarle cual es nuestra configuración

public abstract class AbstractDao {
 private static EntityManagerFactory emf;
 private final ThreadLocal em = new ThreadLocal() {
  @Override
  protected EntityManager initialValue() {
   return getEntityManagerFactory().createEntityManager();
  }
 };
 private EntityManagerFactory getEntityManagerFactory() {
  if (emf == null) {
   emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
  }
  return emf;
 }
 protected EntityManager getEntityManager() {
  return em.get();
 }
}

Por otro lado creamos una interface DAO y su implementación con los cuales acceder a los datos de BBDD.

Ya casi estaría listo, crearemos un método del Web Service que llame a ese servicio DAO y que pueda obtener los datos de BBDD. Pero nos quedaría un pequeño asunto, ¿Como indicamos el formato de la respuesta?. Para esto utilizaríamos el JAXB, crearemos clases que tengan el formato de la información que queremos devolver en el servicio. Este formato se lo podemos indicar a través de anotaciones. Aquí un ejemplo de clase JAXB

@XmlRootElement
public class UserXml implements java.io.Serializable {
 private Integer userId;
 private String username;
 private String password;
 private boolean enabled;
 @XmlElement
 public Integer getUserId() {
  return userId;
 }
 @XmlElement
 public String getUsername() {
  return username;
 }
 @XmlElement
 public String getPassword() {
  return password;
 }
 @XmlElement
 public boolean isEnabled() {
  return enabled;
 }
        //setters
 public void setEnabled(final boolean enabled) {
  this.enabled = enabled;
 }
 public static UserXml valueOf(final User user) {
  UserXml userXml = new UserXml();
  userXml.setEnabled(user.isEnabled());
  userXml.setPassword(user.getPassword());
  userXml.setUserId(user.getUserId());
  userXml.setUsername(user.getUsername());
  return userXml;
 }

}

Y aquí un ejemplo del servicio web:

public List getUsers() {
 List list = dao.getUsers();
 List result = new ArrayList();
 for (User p : list) {
  result.add(UserXml.valueOf(p));
 }
 return result;
}

domingo, 2 de agosto de 2015

WebService con JAX-WS - I

La idea de este post y si puede ser de una serie de post futuros, es crear pequeños prototipos de los que partir para hacer futuros desarrollos.

Para este primer caso vamos a hacer un webservices con JAX-WS pero nada de spring, algo básico. Además aparte del post iré poniendo la dirección GIT donde lo tengo subido para que cualquiera pueda descargarlo y continuar con el mismo.

Si no estas familiarizado con la tecnología te hago aquí un pequeño resumen de que es cada estandar con el que crearemos la aplicación:
  • JAX-WS: Es la API de Java para la creación de servicios web que forma parte de JAVA EE. 
Para empezar creamos un proyecto web y lo reconfiguramos como proyecto maven. Las librerías que hemos necesitado han sido:
  • jaxws-rt: La implementación para el funcionamiento de JAX-WS
  • slf4j-api y log4j: Librerías para realizar logs
Una vez configurado el cliente vamos ha hacer dos cosas. Crear la propia interfaz del servicio web:

@WebService
public interface WebServiceSample {
 @WebMethod
 Saludo saludar();
 @WebMethod
 String despedir();
}

Y aquí esta su implementación, una cosa sencillita. La importancia del asunto recae en las anotaciones que marcan a la clase y a su implementación como servicio web.

@WebService(endpointInterface = "es.home.ws.example.WebServiceSample")
public class WebServiceSampleImpl implements WebServiceSample {
 @Override
 public Saludo saluda() {
  Saludo s = new Saludo();
  s.setNombre("John");
  s.setSaludo("Doe");
  return s;
 }
 @Override
 public String despedir() {
  return "Goodbye";
 }
}

En principio con esto nos valdría si desplegásemos en un servidor de aplicaciones web como weblogic o jboss. Pero nosotros vamos a hacerlo en un contenedor de servlets como Tomcat. Y para ello necesitaremos de dos configuraciones más. modificar el web.xml y crear el fichero sun-jaxws.xml

El fichero web.xml configuramos para la ruta por la que queremos que pasen las llamadas a nuestro webservices e indicamos el servlet que captará estas llamadas.


  com.sun.xml.ws.transport.http.servlet.WSServletContextListener
 
 
  webservice-sample
  com.sun.xml.ws.transport.http.servlet.WSServlet
  1
 
 
  webservice-sample
  /webservice-sample
 

En el fichero sun-jaxws.xml indicamos la configuración para que el servicio funcione correctamente. Esta configuración será leída por el servlet indicado en el web.xml y en ella le indicamos la url por la cual va a ser llamado el servicio (configurado en el web.xml) y que implementación realizará las llamadas


 


Una vez configurado si arrancamos el tomcat y accedemos a la URL:  http://localhost:8080/ExampleWS/webservice-sample veremos la configuración del servicio web y tendremos acceso a su WSDL.

En este enlace (ExampleWS) podreis encontrar el código fuente subido a Github.

Anotación: He tenido problema con maven al resolver las dependencias de jax-rt en su versión 2.2.10. La he cambiado por al 2.2.3 y ha funcionado correctamente.