Buscar este blog

sábado, 24 de enero de 2015

Java Servlet bypassFilter - Filter exclude path pattern - Filter wrapper

Servlet Filters are very useful components to check request parameters, authentication status, resource authorization, etc. But the main problem they have (at least one very big) is that you only can specify which URLs (patterns) are processed by de filter, but not which you don't want to apply.

For example, with this configuration the filter will intercept all '/secure/' and '/services/' paths.

<filter> 
 <filter-name>OneFilter</filter-name>
 <filter-class>com.MyFilterClass</filter-class>
</filter>

<filter-mapping>
 <filter-name>OneFilter</filter-name>
 <url-pattern>/secure/**</url-pattern>
 <url-pattern>/services/**</url-pattern>
</filter-mapping>

But, what I need is to have a filter which intercepts all paths, except some ones.

I found this great solution in gitHub, https://gist.github.com/superbob/5005291, developed by this superbob guy ;), but it was quite specific. So I decided to enhanced it and give some more config options.

The idea is the same, one filter, wrapper, wich has inside a inner filter. The wrapper filter is responsible of instanciate and initialize the inner filter.
There is a special class called FilterWrapperCondition which evaluate if the inner filter should be called or not. This is in fact an interface, and the particular implementations are configured with the filter config of the wrapper.

These are the main classes. The complete code is in github: https://github.com/evazquezma/JEE6/tree/master/wrapperFilter

WrapperFilter 
 package es.pruebas.jee6.filter.wrapper;  
 import java.io.IOException;  
 import javax.servlet.Filter;  
 import javax.servlet.FilterChain;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletException;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 /**  
  * This filter wrapp a inner filter and decides when invoke it.  
  *   
  * If you are using Spring, you can get Spring beans by using "WebApplicationContext springContext =  WebApplicationContextUtils.getWebApplicationContext(config.getServletContext());".   
  * In this way you will not need instanciate class by hand.       
  *   
  * @author SISIFO  
  *  
  */  
 public class WrapperFilter implements Filter {  
      private static final Logger LOGGER = LoggerFactory.getLogger(WrapperFilter.class);  
      private static final String FILTER_PARAM_INNER_FILTER_CLASS = "wrapper.param.filterClass";  
      private static final String FILTER_PARAM_WRAPPER_CONDITION_CLASS = "wrapper.param.wrapperConditionClass";  
      private Filter innerFilter;  
      private FilterWrapperCondition filterWrapperCondition;  
      @Override  
      public void init(final FilterConfig filterConfig) throws ServletException {  
           LOGGER.info("Initializing filter inhibitor");  
           this.innerFilter = createAndInitInnerFilter(filterConfig);                 
           this.filterWrapperCondition = createAndInitFilterWrapperCondition(filterConfig);  
           LOGGER.info("Inhibitor engaged !");  
      }  
      private Filter createAndInitInnerFilter(final FilterConfig filterConfig) throws ServletException {  
           Filter filter = null;  
           try {  
                LOGGER.info("Loading inner filter: "+ filterConfig.getInitParameter(FILTER_PARAM_INNER_FILTER_CLASS));  
                filter = (Filter) Class.forName(filterConfig.getInitParameter(FILTER_PARAM_INNER_FILTER_CLASS)).newInstance();  
           } catch (InstantiationException | RuntimeException | ClassNotFoundException | IllegalAccessException e) {  
                LOGGER.error("exception while creating inner filter", e);  
                throw new RuntimeException(e);  
           }   
           filter.init(filterConfig);  
           return filter;  
      }  
      private FilterWrapperCondition createAndInitFilterWrapperCondition(FilterConfig filterConfig) {  
           FilterWrapperCondition wrapperCondition = null;  
           try {  
                LOGGER.info("Loading filter condition: "+ filterConfig.getInitParameter(FILTER_PARAM_WRAPPER_CONDITION_CLASS));  
                wrapperCondition = (FilterWrapperCondition) Class.forName(filterConfig.getInitParameter(FILTER_PARAM_WRAPPER_CONDITION_CLASS)).newInstance();  
           } catch (InstantiationException | RuntimeException | ClassNotFoundException | IllegalAccessException e) {  
                LOGGER.error("exception while creating filter condition", e);  
                throw new RuntimeException(e);  
           }   
           wrapperCondition.init(filterConfig);  
           return wrapperCondition;  
      }  
      @Override  
      public void destroy() {  
           LOGGER.info("Destroying wrapper filter");       
           LOGGER.info("Destroying inner filter");  
           innerFilter.destroy();  
           LOGGER.info("Destroyed !");  
      }  
      @Override  
      public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {  
           if (filterWrapperCondition.allowInnerFilterCall(req, res)) {  
                innerFilter.doFilter(req, res, chain);  
           } else {  
                chain.doFilter(req, res);  
           }  
      }  
 }  


ExclusionFilterWrapperCondition 
 package es.pruebas.jee6.filter.wrapper;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import javax.servlet.http.HttpServletRequest;  
 /**  
  * Example basic implementation.  
  *   
  * @author SISIFO  
  *  
  */  
 public class ExclusionFilterWrapperCondition implements FilterWrapperCondition {  
      private static final String PARAM_EXCLUSION_PREFIX = "wrapper.exclusionFilterWrapperCondition.param";  
      private String[] exclusionsRules;  
      @Override  
      public void init(FilterConfig filterConfig) {            
           exclusionsRules = filterConfig.getInitParameter(PARAM_EXCLUSION_PREFIX).split(",");  
           for (int i=0; i<exclusionsRules.length; i++) {  
                exclusionsRules[i] = filterConfig.getServletContext().getContextPath() + exclusionsRules[i].trim();                                
           }  
      }  
      @Override  
      public boolean allowInnerFilterCall(ServletRequest req, ServletResponse res) {  
           HttpServletRequest httpReq = (HttpServletRequest) req;  
           for (String pattern : exclusionsRules) {  
                if (httpReq.getRequestURI().startsWith(pattern)) {  
                     return false;  
                }  
           }  
           return true;  
      }  
 }  

web.xml
 <?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"  
      version="3.0">  
      <display-name>wrapperFilter</display-name>  
      <filter>  
           <display-name>wrapperFilter</display-name>  
           <filter-name>wrapperFilter</filter-name>  
           <filter-class>es.pruebas.jee6.filter.wrapper.WrapperFilter</filter-class>  
           <init-param>  
                <param-name>wrapper.param.filterClass</param-name>  
                <param-value>es.pruebas.jee6.filter.SayHelloFilter</param-value>  
           </init-param>  
           <init-param>  
                <param-name>wrapper.param.wrapperConditionClass</param-name>  
                <param-value>es.pruebas.jee6.filter.wrapper.ExclusionFilterWrapperCondition</param-value>  
           </init-param>  
           <init-param>  
                <param-name>wrapper.exclusionFilterWrapperCondition.param</param-name>  
                <param-value>  
                     /dontCallFilter/,  
                     /neitherInThisURL/   
                </param-value>  
           </init-param>  
           <!-- Any other params for the inner filter -->  
           <!-- .... -->            
      </filter>  
      <filter-mapping>  
           <filter-name>wrapperFilter</filter-name>  
           <url-pattern>/*</url-pattern>  
      </filter-mapping>  
      <filter>  
           <display-name>SayWorldFilter</display-name>  
           <filter-name>SayWorldFilter</filter-name>  
           <filter-class>es.pruebas.jee6.filter.SayWorldFilter</filter-class>  
      </filter>  
      <filter-mapping>  
           <filter-name>SayWorldFilter</filter-name>  
           <url-pattern>/*</url-pattern>  
      </filter-mapping>  
 </web-app>  



martes, 20 de enero de 2015

JSP, TAG - Remove whitespaces

Para eliminar los espacios en blanco generados dentro de una JSP:

Dentro del web.xml


<jsp-config>
 <jsp-property-group>
  <url-pattern>*.jsp</url-pattern>
  <trim-directive-whitespaces>true</trim-directive-whitespaces>
 </jsp-property-group>
</jsp-config>

O, dentro de cada JSP:


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>

Para eliminar los espacios en blanco generados dentro de un TAG:


<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>

sábado, 17 de enero de 2015

JBoss, Apache mod-cluster - Configure SSL to read client certificates

Update:
Tras un tiempo trabajando con este tipo de configuración he visto que no es la más adecuada. He creado una nueva entrada donde se especifica un mejor solución. Se puede consultar aquí: http://trabajosdesisifo.blogspot.com.es/2015/04/jboss-modcluster-ssl-windows.html


Instalación y configuración de SQLServer 2008 - Transacciones distribuidas

Sofware necesario

Se emplearán las siguientes versiones:
  • Windows Server 2008 R2
  • SQLServer 2008 R2 - Express  - SP2
  • SQL Server JDBC Driver versión 4
En la página de descarga de SQL server hay que seleccionar el idioma de instalación de Windows Server, así como la arquitectura (x86 o x64). De los distintos paquetes a descargar, se optará por la versión ADV (Advance) que ya también trae el Management Studio.

Instalación y Configuración
Instalar SQLServer con todas las opciones por defecto. Tardará aproximadamente 30 minutos.

Crear usuario (login)

Para crear un usuario:
  • Acceder al Management Studio empleando la autenticación de windows.
  • Dentro de la instancia, acceder a Security > Logins, y pulsar sobre nuevo.
  • Crear un nuevo usuario indicando login y password (marcar la opción de usar contraseña deshabilitando las comprobaciones de seguridad)
  • En la opción de User Mapping, asignarle las bases de datos que correspondan. En principio se le añadirá master
Una vez creado se cierra sesión, y se vuelve a conectar seleccionando autenticación de SQL Server e indicando el login y password.

Permitir conexiones desde otras máquinas

Para que ese usuario se pueda conectar desde otra máquina, hay que configurar una serie de parámetros tanto en el PC como en el servicio:
  • Permitir conexiones remotas
    En el Management Studio, una vez logeado, pulsar sobre la instancia para ver sus propiedades. Seleccionar la pestaña de connections y asegurarse de que está marcado el check de "Allow remote connections to this server"
         
  • Permitir autentiación con usuario y contraseña
    En el Management Studio, una vez logeado, pulsar sobre la instancia para ver sus propiedades. Seleccionar la pestaña de Security y, dentro del grupo de Server authentication, marcar "SQL Server and Windows Authentication mode"
  • Activar protocolo TCP/IP
    Ejecutar el programa "Sql Server Configuration Manager". Está dentro del directorio de Configuration Tools en el menú de inicio del SQL Server 2008 R2.
    Ir a SQL Server Network Configuration > Protocols for SQLEXPRESS. Activar el protocolo TCP/IP.
    Dentro de ese mismo, ir a propiedades, pestaña IP Addresses. Ahí hay que asegurarse de que existe un grupo IP con la dirección IP de la máquina cliente, que esté Active y Enabled, y con el puerto 1433.
    En el grupo de IPAll, asignar el puerto 1433 a TCP port.
    Finalmente, reiniciar el servicio.
  • Desactivar firewall de windows
    En la máquina servidor lo más rápido es deshabilitar el firewall de windows, o abrir específicamente el puerto TCP 1433.
    Para comprobar que el fierewall no interfiere, hacer un ping desde la máquina cliente al servidor y ver que se obtiene respuesta.

Permitir transacciones distribuidas

El servicio MS DTC debe estar marcado como Automático en el Administrador de servicios para asegurarse de que esté en ejecución cuando se inicia el servicio de SQL Server. Para habilitar MS DTC para transacciones XA, debe seguir estos pasos:
  • Inicio > Administrative Tools > Component Services
  • Seleccionar Component Services > Computers > My Compunter > Distributed Transaction Coordinator > Local DTC > properties
  • Dentro de las propiedades > pestaña Security > Enable XA Transactions
  • Reiniciar el servicio SQL Server (SQLEXPRESS)

Descargar SQL Server JDBC Driver.
  • Valen las versiones 3 y 4.
  • Se descarga:
    • Microsoft JDBC Driver 4.0 for SQL Server
    • Sistemas operativos: Linux, Unix, Windows 7, Windows Server 2008 R2, Windows Server 2012, Windows Vista
    • Versiones SQL Server:
      • Microsoft® SQL Server® 2012
      • Microsoft® SQL Server® 2008 R2
      • Microsoft® SQL Server® 2008
      • Microsoft® SQL Server® 2005
      • Microsoft® SQL Azure
  • Copiar la extensión de procedimientos almacenados en el directorio del SQL Server:
    • En la carpeta del driver descargado ir a enu > xa > x64 > sqljdbc_xa.dll
    • Copiar el fichero en C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\Binn
Esta dll no existía antes.
    • Reiniciar el servicio SQL Server (SQLEXPRESS)

Instalar los procedimientos almacenados extendendidos que vienen con el driver:
  • En la carpeta del driver descargado ir a enu > xa > xa_install.sql
  • Abrir y ejecutar ese archivo desde el Management Studio:
    • Crea los procedimientos almacenados. Si ya existieran, primero los borrar
    • Crea un nuevo rol y le asigna permisos sobre esos procedimientos
    • Opcionalmente, se podrían indicar los usuarios a los que darles ese rol, si no, se hace más tarde manualmente

Asignar a un usuario el rol SqlJDBCXAUser en la tabla base de datos master:
  • Desde el Management Studio, navegar a Security > Logins en el panel lateral, y pulsar sobre las propiedades del usuario
  • Ir a User Mapping y seleccionar la base de datos master. En la parte inferior, asignar el rol correspondiente.

También se puede hacer ejectuando el siguiente script (en este 'xumco-java' es el nombre del usuario de la conexión):
Asginar rol
USE master
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'xumco-java'


viernes, 9 de enero de 2015

Ejemplos JEE6, Hibernate, Spring-MVC y HTML5

Estos son los ejercicio y ejemplos que preparé para unos cursos que impartí. Están todos en github.


servlets001: Ejemplo básico con servlet, filtro y listener por web.xml
servlets002: Ejemplo básico con servlet, filtro y listener por anotaciones
servlets003: Ejercicio 1, filtro de autenticación.
servlets004: Ejemplo básico con jsp
servlets005: Ejercico 2, meter formulario jsp al Ejercicio 1

ejb001: Ejemplo básico con los tres tipos de session bean
ejb001-client: Cliente stand-alone de los tres ejbs

ejb002-war: Ejercicio 3, integrar ejb en servlet para el carrito

ejb003: Ejemplo con dos tipos de session bean
ejb003-client: Cliente para comprobar si guardan estado o no

jpa001: Ejemplo básico jpa
jpa001-client: Cliente stand-alone

jpa002: Ejercicio 4, filtro de log en base de datos

mdb001: Configuración de JMS de tipo Queue
mdb001-client: Cliente para enviar mensajes a la cola

jaxws001: Ejemplo jax-ws
jaxws001-client: Cliente jax desde servlet

jaxrs001: Ejemplo de servidor jax-rs

seguridad001: Ejemplo de seguridad básico
seguridad002: Ejemplo de seguridad con login



hibernate001: Ejemplo básico con configuración por XML
hibernate002: Ejemplo básico con configuración por anotaciones
hibernate003: Ejercicio sobre los métodos de la sesion
hibernate004: Ejemplo de uso de componenentes
hibernate005: Ejemplo de mapeo de relaciones unidireccionales
hibernate006: Ejemplo de mapeo de relaciones bidireccionales
hibernate007: Ejemplo de herencia
hibernate008: Ejemplo de consulta
hibernate009: Ejemplo de transaccionalidad con spring
hibernate010: Ejemplo de test unitarios con dbUnit
hibernate011: Ejemplo de eventos en interceptores
hibernate012: Ejemplo de hibernate validator
hibernate013: Ejemplo de hibernate search
hibernate014: Ejemplo de hibernate cache
hibernate012: Ejercicio de creación de daos a partir de sus tes unitarios



001: Hola Mundo configurado con web.xml

002: Hola Mundo configurado con anotaciones y sin web.xml

003: Listado de usuarios

004: Implementar método de búsqueda

005: Formulario de creación de usuarios

006: Añadir validaciones, internalización y opción de actualización

007: Reorganizar JSPS con includes, crear tags y control de excepciones

008: Tiles

009: Themas



css001: Ejercicio CSS para montarlayout básico 960

css002 : Ejercicio para maquetar un formulario básico
css002-sol

css003 : Ejercicio para maquetar el cuerpo central de una página que incluye un listado
css003-sol

css003 : Ejercicio para maquetar una barra de navegación
css003-sol

css003 : Ejemplo de animación creada con CSS3


JS001 :  Ejercicio para modificar mediante javascript la apariencia de los enlaces de una página
JS001-sol

JS002 :  Ejercicio para añadir dinámicamente elmentos a una página
JS002-sol

JS003 : Ejemplo de manejo de objetos con javascript

conway : Ejemplo de aplicación creada con javascript y las canvas de HTML5


HTML5Layout01 : Ejercicio para diseñar una página usando los nuevos tags de HTML5
HTML5Layout01-sol

validation01: Ejemplo de validación de formularios directamente con HTML5
validation02: Ejemplo de validación de formularios con jQuery-Validation

webCache001: Ejemplo de uso de caché

ajaxmvc001: Ejemplo básico sobre la carga dinámica de datos mediante JSON
ajaxmvc002: Ejercicio para rellenar dinámicamente con JSON dos combos enlazados

websocket001: Ejemplo básico de uso de webShockets
websocket002: Ejercicio para recuperar información de la bolsa con webSockets

webWorker001: Ejemplo básico de uso de webWorkers
webWorker002: Ejercicio para implementar la aplicación de la bolsa de forma más eficiente

jueves, 8 de enero de 2015

Centos 6 configure GIT

Gran parte de la información de esta entrada está obtenida de https://www.digitalocean.com/community/tutorials/how-to-use-git-effectively.

Propósito
Instalar y configurar GIT en un CentOS 6.6 y conectarse contra un repositorio de github

Instalación
Para instalar GIT se usa yum:

yum install git


Configuración

Variables globales
Lo primero que hay que hacer es añadir dos variables globales que se usarán en los commits.

git config --global user.name "sisifo"
git config --global user.email "sisifo@efiramail.com"

Estas variables se alamcenan en el archivo ~/.gitconfig

Clave pública del repositorio
Para poder conectarse con un repositorio remoto es  necesario generar una clave SSH en el cliente y registrar su parte pública en el servidor, en github en este caso.

Las instrucciones de generación de la clave están en https://help.github.com/articles/generating-ssh-keys/
No obstante existe un particularidad con CentOS a la hora de añadir la clave al sistema, ya que no funciona el comando ssh-agent -s. Para solucionarlo se usa lo siguiente:
exec ssh-agent bash
ssh-add .ssh/id_rsa

La parte pública de la clave hay que añadirla a la configuración de github en la página https://github.com/settings/ssh, también como se indica en la documentación. Lo único con lo que hay que tener cuidado es copiar el contenido del fichero .ssh/id_rsa.pub que es el que contiene la parte pública.


Utilización
A continuación se muestran los comandos básicos para crear un directorio en el que almacenar un proyecto, añadir un par de ficheros, hacer el commit en local y enviar los datos a la cuenta de github.
cd opt/
mkdir -p git/testing
cd git/testing/
touch prueba.txt
touch README.md
git init
git add .
git commit -m "prueba inicial" -a
git remote add testRepo git@github.com:miCuentaGithub/test.git
git remote -v
git push -u testRepo master

Hay que tener en cuenta que previamente se tubo que haber creado el repositorio test dentro github.

lunes, 5 de enero de 2015

JBoss Fuse - Maven settings.xml

Settings.xml para trabajar con JBoss Fuse.


<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

 <profiles>   
  <profile>
   <id>jboss-fuse</id>        
   <repositories>
    <repository>
     <id>Apache public</id>
     <url>https://repository.apache.org/content/groups/public/</url>
    </repository>  
    <repository>
     <id>JBoss Fuse</id>
     <url>https://repo.fusesource.com/nexus/content/groups/public/</url>
    </repository>
    <repository>
     <id>JBoss Fuse - SNAPSHOTS</id>
     <url>https://repo.fusesource.com/nexus/content/groups/public-snapshots/</url>
    </repository>
   </repositories>
  </profile>
 </profiles>

 <activeProfiles>
  <activeProfile>jboss-fuse</activeProfile> 
 </activeProfiles>
</settings>

domingo, 4 de enero de 2015

Error JBoss Developer Studio - Fuse JMX Navigator - Empty

Error
En la perspectiva Fuse Integration, dentro la vista de Fuse JMX Navigator, en el grupo de Local Processes no se muestra nada.


Solución
El problema se debía a que la jvm del JBoss Developer Studio estaba apuntando a una instalación de 32 bits, mientras que el JDK empleado para desplegar las rutas es de 64 bits.

A la hora de instalar el Dev. Studio se da la opción de emplear la jvm por defecto o especificar una. En esta pantalla lo mejor es escoger un JDK que luego se usará para compilar y ejecutar los proyectos.



La única solución que vi fue reinstalar todo el IDE, ya que toqueteando el jbdevstudio.ini, al intentar indicarle que usase el JDK como jvm, daba error al iniciar.


Otra cosa que se puede revisar es que dentro de las preferencias del JMX Explorer esté bien configurado el JDK. En mi caso esto estaba correcto, pero hasta que no se hizo la reinstalación completa no se arregló el problema.