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

lunes, 2 de noviembre de 2020

Apache Camel + Quarkus

 Hoy vamos a ver como integrar Apache Camel con Quarkus. Y es que Apache Camel cuenta con diferentes proyectos y hasta ahora siempre lo habiamos visto en su versión con Spring Boot, pero hoy veremos el proyecto donde se despliega sobre Quarkus y tiene sus propios componentes. 

Para empezar explicaremos un poco que es Quarkus.  Es un Framework Java diseñado para ser desplegado como aplicación nativa de Kubernetes, es decir pensado especialmente para su despliegue en la nube a través de contenedores. Ideado también para ser utilizado por OpenJDK HotSpot y GraalVM. 

Así a simple vista, podemos pensar que es otro framework más para ser desplegado sobre otra JVM, y que con nuestra JVM habitual y Spring no necesitamos nada más. Pero tenemos que hacer hincapié en que es altamente eficaz, de ahí su gran interés. Permitiéndonos rendimientos y consumos de memoría mucho mejores que con nuestra JVM o Framework habitual. En este post veremos como instalarlo y al final haremos unas pruebas de su rendimiento comparándolo con una versión de Apache Camel con Spring. 

Podemos hacerlo funcionar en una JVM 8 u 11, pero es mejor si instalamos GraalVM. Una vez que tengamos lista la JVM, empezaremos con la creación de nuestro proyecto. 

Nuestro proyecto heradará de camel-quarkus-bom:1.1.0, tendrá las librerías de SQL para la conexión a BBDD y las de Rest y Netty para la conexión mediante HTTP. Uno de los principales inconvenientes a la hora de realizar un proyecto con Apache Camel + Quarkus, es que los componentes y la documentación no están tan testeados o elaborados, como puede pasar en comparación con los de Spring Boot. 

<parent>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-bom</artifactId>
    <version>1.1.0</version>
</parent>
<dependencies>
    <!-- start -->
    <dependency>
      <groupId>org.apache.camel.quarkus</groupId>
      <artifactId>camel-quarkus-main</artifactId>
    </dependency>
    <!-- HTTP -->
    <dependency>
      <groupId>org.apache.camel.quarkus</groupId>
      <artifactId>camel-quarkus-netty-http</artifactId>
    </dependency>
    ...
</dependencies>

El código será similar al de Apache Camel. Tendremos una clase que aglutinará los Route que desarrollemos. Y tendremos un fichero de aplicación donde indicaremos aspectos comunes y el dataSource. 

@ApplicationScoped
public class BookRouter extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        restConfiguration().component("netty-http").port(8099).bindingMode(RestBindingMode.json);

        rest("/book").get().route().to("sql:SELECT ID, NAME, AUTHOR FROM BOOK").log("--- select all books. Body: ${body} ---");

        rest("/book/{id}").get().route().to("sql:SELECT ID, NAME, AUTHOR FROM BOOK WHERE ID = :#id").log("--- 2 select a book ${body} ---");

        rest().post("book").route().routeId("postBookRoute")
        .to("sql:INSERT INTO BOOK (NAME, AUTHOR) VALUES (:#name, :#author)").setHeader(Exchange.HTTP_RESPONSE_CODE, constant(201))
        .setBody(constant(null));

        rest().delete("book/{id}").route().to("sql:DELETE FROM BOOK WHERE ID = :#id")
        .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));
        //need camel-bean
        rest().put("book/{id}").route().choice().when()
        .simple("${header.id} < 1").bean(BookRouter.class, "negativeId").otherwise()
        .to("sql:UPDATE BOOK SET NAME = :#name, AUTHOR = :#author WHERE ID = :#id").setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
        .setBody(constant(null));
    }
    public void negativeId(final Exchange exchange) {
        exchange.getIn().setBody("{\"msg\":\"id can not be negative\"}");
        exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
    }
}

Lo que no tendremos es una clase como Spring que nos permita arrancar la aplicación y debugarla fácilmente en eclipse, esta puede ser otra desventaja a la hora de desarrollar. Para arrancar la aplicación lo podremos hacer mediante comandos maven:

mvn clean compile quarkus:dev

Una vez que tenemos desarrollada nuestra aplicación, vamos a ver que tal se comporta. Y para crearemos una imagen de ambas aplicaciones, las desplegaremos en un docker-compose con Portainer y las probaremos con la utilidad Hey.

Para hacer la imagen de Docker es muy parecido a como lo realizabamos con Spring boot, la diferencia es que en este caso al crear el empaquetado tendremos una aplicación Kubernetes-nativa con sufijo runner. Para crear este empaquetado si tenemos instalado GraalVM utilizamos el comando:

mvn clean package -Pnative -Dquarkus.native.container-build=true \
 -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-native-image:20.2.0-java8 \
 -Dquarkus.native.container-runtime=docker

Y ahora procederiamos a crear la imagen. Ejemplo de Dockerfile

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8099
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Ahora procedemos a arrancar nuestro docker-compose y ejecutar el siguiente comando de Hey. 

hey -n 1000 -m GET -T "application/json" http://localhost:8099/book/1

Sobre quarkus Hey nos da estos resultados: Average: 0.0757 secs

Y Portainer estos otros: 200 MB de Memoría y consumos mínimos de CPU

Si ejecutamos lo mismo sobre una aplicación Apache Camel + Spring Boot. Hey nos da estos resultados: Average: 0.1712 secs , bastante mas que Quarkus

Y Portainer estos otros: 400 MB de Memoria y consumos de CPU también bajos pero mucho más altos que los de Quarkus.

Como veis, una gran diferencia. Lo suficiente como para que nos haga decantarnos por Quarkus sobre Spring Boot. Sobre todo si es una aplicación básica que no vayamos a necesitar de mucha documentación o de ayuda de los foros.

Como siempre puedes ver el código aquí

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.

miércoles, 16 de marzo de 2016

Llamar a una función con parámetro de salida y JDBC

Hoy vamos ha hacer algo sencillo. Como obtener el valor de retorno de una función mediante una básica llamada JDBC.

Para empezar creamos una función básica que nos devuela algo:

CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50)
RETURN CONCAT('Hello, ',s,'!');

La forma de obtener el parámetro de salida es muy sencillo. Lo más importante es saber como indicar que vamos a obtener un parámetro de salida y como registrarlo a través del 'CallableStatement'. Por lo demás es una operación JDBC normal. Aquí un ejemplo:

Connection conn = DriverManager.getConnection(${JDBC_URL}, ${BBDD_USER}, ${BBDD_PWD});
String procedure = "{? = call hello (?)}";
CallableStatement statement = conn.prepareCall(procedure);
statement.registerOutParameter(1, java.sql.Types.VARCHAR);
statement.setString(2, "WORLD");
statement.execute();
String salida = statement.getString(1);

En la variable 'salida' tendremos la salida de la función con el valor 'hello WORLD!'.

Si la variable de salida tiene un nombre, podemos utilizar esta otra llamada para registrar su valor.

statement.registerOutParameter(1, java.sql.Types.VARCHAR, ${var_name});

domingo, 24 de enero de 2016

JDBC 4 y BBDD Derby

Hablando de Java SE 6, indicábamos que había novedades en JDBC y además traía incluida una BBDD de poco peso y realizada completamente en Java.

Las novedades de JDBC son:
  • Carga automática del Driver. Ya no es necesario al crear la conexión indicar el driver específico que debe utilizar
  • Nuevas subclases de SQLException. Lo que permite mayor legibilidad, manejo y comprensión de los errores. 
  • Nuevos métodos para las interfaces DatabaseMetaData, Connection y Statement. 
    • Connection ahora tiene nuevos métodos para crear BLOB y otro para comprobar si la conexión es válida.
    • Statement ahora tiene nuevos métodos para saber si proviene de un pool y si ya esta cerrado el statement. 
  • Nuevos métodos en CallableStatement, Statement y ResultSet que permite manejar java.io.InputStream y java.io.Reader.
  • Eventos en Statements que permite al pool de conexiones estar atento a sus eventos asociados: addStatementEventListener y removeStatementEventListener.
  • Añade métodos para el manejo de RowId
En cuanto a Derby, podemos indicar lo que ellos mismos dicen en su página oficial:

  • Ocupa muy poco espacio, alrededor de 2.6 Mb con driver incluido
  • Realizado en Java y en base a los estandares JDBC y SQL
  • Es facil de instalar, desplegar y usar. 

Para demostrarlo vamos a ver un ejemplo. La parte JDBC es igual que con cualquier otra, así que el ejemplo va a ser de como instalarlo en Ubuntu con open-jdk (que es lo que uso):
  • Para empezar lo descargamos y creamos las variables del sistema
sudo apt-get install sun-javadb-client sun-javadb-core
export PATH=$PATH:/usr/share/javadb/bin/
export DERBY_HOME=/usr/share/javadb
  • Ahora a través del comando 'ij' creamos la BBDD y sus esquemas. Con el comando 'dblook' podemos hacer exportación del esquema y con 'sysinfo' ver información del mismo. Simplemente debemos teclear en la consola 'ij'.
  • Y a través de comandos SQL vamos a crear nuestra BBDD y su esquema. 
connect 'jdbc:derby:dbPrueba;create=true';
create table persona (clave integer, nombre varchar(50));
insert into persona (clave, nombre) values (1, 'Nombre');

Una cosa muy importante es indicar el ';' tras los comandos SQL, sino parecerá que no hace nada. 

Con esto ya tendriamos nuestra propia BBDD activa y lista para trabajar con ella. De forma facíl y ligera. 

martes, 15 de septiembre de 2015

Pool de Conexiones en aplicaciones Web - II

Ya en un post anterior indicábamos como conectar la aplicación con un pool de conexiones. En este post, vamos a explicar como configurar el pool de conexiones pero gestionado por el servidor de aplicaciones y no por el software de turno (Hibernate, etc). Para este ejemplo vamos a configurarlo con un Tomcat 7.

En primer lugar debemos configurar el pool de conexiones en el servidor de aplicaciones de una forma muy similar a como lo hicimos para el C3P0. Para configurar el pool de conexiones en Tomcat 7 debemos modificar el fichero 'context.xml' que esta incluido en el propio servidor. Y es muy importante el nombre que le damos al recurso compartido puesto que este será el que utilicemos para poder usar dicho recurso compartido a través de JNDI. 



Otro punto importante al ser una aplicación web, es configurar el recurso compartido para ser usado por dicha aplicación. Esto lo hacemos a través del fichero web.xml.

 DB Connection
 jdbc/mysqlalmacen
 javax.sql.DataSource
 Container

Por último solo tendremos que modificar el fichero de acceso a datos de Spring. Debemos que quitar la configuración de pool de conexiones e indicar mediante el nombre JNDI el recurso compartido que hemos creado en el contexto del Tomcat.



 
  
 
 
  
 
 
 
  
   
  
 
 

lunes, 14 de septiembre de 2015

Pool de Conexiones en aplicaciones Web - I

Mirando el otro día como realizar la conexión JPA con un pool de conexiones me di cuenta de un fallo más o menos común y es que se puede usar la clase 'org.springframework.jdbc.datasource.DriverManagerDataSource' para realizar el pool de conexiones. 
  
          
              
          
          
              
          
        
     
        
  
   
  
 
 
  

 
 
 
 

Pero leyendo un poco más hasta en la propia documentación oficial indican que no es un verdadero pool de conexiones y por lo tanto debes de intentar no utilizarlo (Documentación oficial). 

NOTE: This class is not an actual connection pool; it does not actually pool Connections. It just serves as simple replacement for a full-blown connection pool, implementing the same standard interface, but creating new Connections on every call. 

Debido a esto lo mejor es cambiar esta clase para poder utilizar un verdadero pool de conexiones. Hay varias alternativas, desde DBCP (ya existe una versión 2.0 que no esta tan obsoleta como la 1.0) o C3P0 (absorvida por Hibernate) u otros software menos conocidos (como BoneCP o HikariCP).

Para configurar por ejemplo con C3P0 es bastante sencillo. Por un lado añadimos la librería de C3P0 correspondiente mediante maven:

 org.hibernate
 hibernate-entitymanager
 ${hibernate.version}


 org.hibernate
 hibernate-c3p0
 ${hibernate.version}

Y por otro lado, modificamos el fichero de configuración para quitar la 'falsa' clase de Spring y poner la clase que permite el manejo de las conexiones por parte de C3P0. Así de sencillo