Páginas

miércoles, 11 de julio de 2012

Request Entity Too Large en Apache Tomcat

En cierto proyecto donde la autenticación se realiza mediante certificados electrónicos en tarjeta, nos hemos encontrado con que algunos de los usuarios accedían sin problemas con sus tarjetas, mientras otros usuarios se quejaban de que al intentar acceder les salta una pantalla de error con el siguiente texto:

Request Entity Too Large

The requested resource
/idacelec-web/
does not allow request data with GET requests, or the amount of data provided in the request exceeds the capacity limit.
Indagando en el asunto hemos tedectado que:

1) Algunos usuarios tenían en sus tarjetas certificados APE de la FNMT, éstos accedían sin problemas.
2) Sin embargo, las tarjetas de los usuarios que no accedían tenían un certificado tipo "Adm Pública".
3) En ambas tarjetas los certificados son de 2048.
4) Evidentemente los certificados difieren en algo, dado que en la misma máquina con unos sí funcionaba y con los otros no.

Solución:

Googleando hemos dado con la página: http://builddeploy.blogspot.com.es/2009/04/resolving-http-error-413-request-entity.html donde se comenta qué hacer para corregir este problema (hay que ajustar el server apache y los server tomcat donde se ejecute la parte online). Los ajustes son:

 1) Tocar el fichero /opt/apache/conf/workers.properties pasando de:
worker.list=jkstatus,lb

worker.00.host=localhost
worker.00.port=10009
worker.00.type=ajp13
worker.00.fail_on_status=-404,500,503

worker.01.host=localhost
worker.01.port=10109
worker.01.type=ajp13
worker.01.fail_on_status=-404,500,503

worker.lb.type=lb
worker.lb.balance_workers=00,01
worker.lb.sticky_session=true
worker.lb.method=B

worker.jkstatus.type=status
a:
worker.list=jkstatus,lb

worker.00.host=localhost
worker.00.port=10009
worker.00.type=ajp13
worker.00.fail_on_status=-404,500,503
worker.00.max_packet_size=65536

worker.01.host=localhost
worker.01.port=10109
worker.01.type=ajp13
worker.01.fail_on_status=-404,500,503
worker.01.max_packet_size=65536

worker.lb.type=lb
worker.lb.balance_workers=00,01
worker.lb.sticky_session=true
worker.lb.method=B

worker.jkstatus.type=status

2) En cada uno de los tomcat afectados (en nuestro caso lo hemos metido en los dos tomcat balanceados para el portal online), hay que tocar los ficheros:
/xxxxxx/tomcat1/conf/server.xml cambiando la línea:
<Connector port="10009" protocol="AJP/1.3" redirectPort="10043" />
por:
<Connector port="10009" protocol="AJP/1.3" redirectPort="10043" packetSize="65536" />

Conclusión:


Hay que investigar en qué difieren ambas tarjetas con certificados "de empleado público" de la FNMT, pero sospechamos que es tema de la cadena de certificación, que en unas es mas corta que en otras. Deberemos hacernos con una de las tarjetas que fallan para investigarlo detenidamente.

Enlaces documentando el tema:

1) Página http://tomcat.apache.org/connectors-doc/reference/workers.html

This attribute sets the maximal AJP packet size in Bytes. The maximum value is 65536. If you change it from the default, you must also change the packetSize attribute of your AJP connector on the Tomcat side! The attribute packetSize is only available in Tomcat 5.5.20+ and 6.0.2+. Normally it is not necessary to change the maximum packet size. Problems with the default value have been reported when sending certificates or certificate chains.
This feature has been added in jk 1.2.19.

2) Página http://tomcat.apache.org/tomcat-5.5-doc/config/ajp.html


This attribute sets the maximum AJP packet size in Bytes. The maximum value is 65536. It should be the same as the max_packet_size directive configured for mod_jk. Normally it is not necessary to change the maximum packet size. Problems with the default value have been reported when sending certificates or certificate chains. The default value is 8192.

Un saludo

martes, 3 de julio de 2012

RAD JEE+Hibernate+Spring+JPA+Maven2+Generics

Vamos a montar un pequeño proyecto "Biblioteca" con un par de tablas, Autor y Libro relacionadas 1:N "un autor tiene varios libros". Lo montamos con diseño en tres capas con:

Hibernate
Spring
JPA
Maven2

JEE 1.6
Genericos Java
MySQL

La idea es trabajar lo mínimo, con lo que utilizaremos una clase BaseDaoJpa para la capa de acceso a datos y una clase GestorBaseImpl. Estas clases y sus correspondientes interfaces: IBaseDao e IGestorBase, las tenemos de proyectos anteriores o las podríamos tener en los arquetipos Maven2 que montásemos para construtir los esqueletos de nuestros proyectos.

Las clases son:

Capa de presentación:

AppTest.java. Clase con el código de la mini-aplicación. Vamos a entender este main como una posible capa de presentación. Se podría ver por ejemplo como un aplicativo Swing.

package org.dune.app;

import java.util.ArrayList;
import java.util.List;

import org.dune.model.Autor;
import org.dune.model.Libro;
import org.dune.negocio.IGestorAutor;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppTest {

    public static void main(String[] args) {

        //Carga del contexto
        final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");

        //Recogemos el gestor de Autores
        final IGestorAutor gestAutores = (IGestorAutor)ctx.getBean("gestorAutor");

        //Damos de alta un autor
        final Autor a = new Autor();
        a.setNombre("nombre");
        a.setApellido1("apellido1");
        a.setApellido2("apellido2");
        final Autor autorGrabado = gestAutores.save(a);

        //Damos de alta algunos libros
        final List<Libro> lstLibros = new ArrayList<Libro>();
        final Libro lib1 = new Libro();
        lib1.setTitulo("titulo1");
        lib1.setAutor(autorGrabado);
        final Libro lib2 = new Libro();
        lib2.setTitulo("titulo1");
        lib2.setAutor(autorGrabado);
        lstLibros.add(lib1);
        lstLibros.add(lib2);
        autorGrabado.setLibros(lstLibros);
        gestAutores.save(autorGrabado);
       
    }
}


Capa de negocio:

IGestorAutor.java. Interfaz de los servicios de gestión de autores.

package org.dune.negocio;

import org.dune.model.Autor;

public interface IGestorAutor extends IGestorBase<Autor, Long> {

}

GestorAutorImpl.java. Implementación de los servicios de gestión de autores.

package org.dune.negocio.impl;

import org.dune.dao.IAutorDao;
import org.dune.model.Autor;
import org.dune.negocio.IGestorAutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("gestorAutor")
public class GestorAutorImpl extends GestorBaseImpl<Autor, Long> implements IGestorAutor {

    private IAutorDao autorDao;
   
    @Autowired
    public GestorAutorImpl(IAutorDao autorDao) {       
        super(autorDao);
        this.autorDao = autorDao;
    }
}

IGestorBase.java. Interfaz base extendido por los restantes interfaces de servicios ofertados en la capa de negocio.

package org.dune.negocio;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import org.dune.dao.IBaseDao.Orden;

public interface IGestorBase<T, PK extends Serializable> {

    public T get(PK id);

    public List<T> getAll();
  
    public List<T> getAllNoBaja(Date fechaBaja);

    public void remove(PK id);

    public T save(T obj);
  
    public List<T> saveAll(List<T> l);

    public List<T> getPaginado(int startPosition, int maxResult);

    public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, Orden direccion);

}

GestorBaseImpl.java. Clase base de la que extienden todos los implementadores de servicios ofertados en la capa de nogocio.

package org.dune.negocio.impl;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dune.dao.IBaseDao;
import org.dune.dao.IBaseDao.Orden;
import org.dune.negocio.IGestorBase;
import org.springframework.transaction.annotation.Transactional;

public class GestorBaseImpl<T, PK extends Serializable> implements IGestorBase<T, PK> {
   
    protected final Log log = LogFactory.getLog(getClass());
    protected IBaseDao<T, PK> baseDao;

    public GestorBaseImpl(IBaseDao<T, PK> baseDao) {
        this.baseDao = baseDao;
    }

    @Transactional
    public T get(PK id) {
        return baseDao.get(id);
    }

    @Transactional
    public List<T> getAll() {
        return baseDao.getAll();
    }
   
    @Transactional
    public List<T> getAllNoBaja(Date fechaBaja) {
        return baseDao.getAllNoBaja(fechaBaja);
    }

    @Transactional
    public void remove(PK id) {
        baseDao.delete(id);
    }

    @Transactional
    public T save(T obj) {
        return baseDao.save(obj);
    }
   
    @Transactional
    public List<T> saveAll(List<T> l) {
        return baseDao.saveAll(l);
    }

    @Transactional
    public List<T> getPaginado(int startPosition, int maxResult) {
        return baseDao.getPaginado(startPosition, maxResult);
    }

    @Transactional
    public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, Orden direccion) {
        return baseDao.getPaginadoOrdenado(startPosition, maxResult, campo, direccion);
    }

}


Capa de acceso a datos:

IAutorDao.java. Interfaz de la capa de acceso a datos para la tabla Autores.

package org.dune.dao;

import org.dune.model.Autor;

public interface IAutorDao extends IBaseDao<Autor, Long> {
  
}

AutorDaoJpa.java. Implementación JPA de la capa de acceso a datos para la tabla Autores.

package org.dune.dao.jpa;

import org.dune.dao.IAutorDao;
import org.dune.model.Autor;
import org.springframework.stereotype.Repository;


@Repository
public class AutorDaoJpa extends BaseDaoJpa<Autor, Long> implements IAutorDao {

    public AutorDaoJpa() {
        super(Autor.class);
    }
}

IBaseDao.java. Interfaz base extendido por los restantes interfaces de la capa de acceso a datos.

package org.dune.dao;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public interface IBaseDao<T, PK extends Serializable> {

    public static enum Orden {
        ASC,
        DESC;
    };

    public T get(PK id);

    public List<T> getAll();
  
    public List<T> getAllNoBaja(Date fechaBaja);
  
    public T save(T t);

    public List<T> saveAll(List<T> l);
  
    public void delete(PK id);

    public List<T> getPaginado(int startPosition, int maxResult);

    public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, IBaseDao.Orden direccion);
}

BaseDaoJpa.java. Clase base de la que extienden todos los implementadores de la capa de acceso a datos.

package org.dune.dao.jpa;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaTemplate;

import org.dune.dao.IBaseDao;

public class BaseDaoJpa<T, PK extends Serializable> implements IBaseDao<T, PK> {

    protected final Log log = LogFactory.getLog(getClass());

    private Class<T> persistentClass;
  
    private JpaTemplate jpaTemplate;
  
    public BaseDaoJpa(Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    @Autowired
    public void setEntityManager(EntityManager entityManager) {
        this.jpaTemplate = new JpaTemplate(entityManager);
    }
  
    public JpaTemplate getJpaTemplate() {
        return jpaTemplate;
    }
  
    public T get(PK id) {

        final T t = (T) getJpaTemplate().find(persistentClass, id);

        if (t == null) {
            throw new ObjectRetrievalFailureException(persistentClass, id);
        }
        return t;
    }

    @SuppressWarnings("unchecked")
    public List<T> getAll() {

        final StringBuilder query = new StringBuilder("from ").append(persistentClass.getName()).append(" c ");

        return (List<T>) getJpaTemplate().find(query.toString());
    }
  
    @SuppressWarnings("unchecked")
    public List<T> getAllNoBaja(Date fechaBaja) {

        final StringBuilder query = new StringBuilder("from ").append(persistentClass.getName()).append(" c").append(" where nvl(c.fechaBaja, to_date('31129999', 'DDMMYYYY')) > ?1");

        return (List<T>) getJpaTemplate().find(query.toString(), fechaBaja);
    }  

    public void delete(PK id) {
        getJpaTemplate().remove(get(id));
    }

    public T save(T t) {
        return (T) getJpaTemplate().merge(t);
    }
  
    public List<T> saveAll(List<T> l) {
        if (l != null) {
            for (T t : l) {
                getJpaTemplate().merge(t);
            }
        }
        return l;
    }

    @SuppressWarnings("unchecked")
    public List<T> getPaginado(int startPosition, int maxResult) {

        final StringBuilder sql = new StringBuilder("select c from ").append(persistentClass.getSimpleName()).append(
                " c");
        final Query query = getJpaTemplate().getEntityManager().createQuery(sql.toString());
        query.setFirstResult(startPosition);
        query.setMaxResults(maxResult);

        return (List<T>) query.getResultList();
    }

    @SuppressWarnings("unchecked")
    public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, IBaseDao.Orden direccion) {

        final StringBuilder sql = new StringBuilder("select c from ").append(persistentClass.getSimpleName())
                .append(" c order by c.").append(campo).append(" ").append(direccion);
        final Query query = getJpaTemplate().getEntityManager().createQuery(sql.toString());
        query.setFirstResult(startPosition);
        query.setMaxResults(maxResult);

        return (List<T>) query.getResultList();
    }
}


Mapeos:

Autor.java. Clase que mapea la tabla Autor

package org.dune.model;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="AUTOR")
public class Autor {

    protected Long id;
    protected String nombre;
    protected String apellido1;
    protected String apellido2;
    protected List<Libro> libros;
   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    public Long getId() {
        return id;
    }
   
    public void setId(Long id) {
        this.id = id;
    }

    @Column(name="NOMBRE")
    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    @Column(name="APE1")
    public String getApellido1() {
        return apellido1;
    }

    public void setApellido1(String apellido1) {
        this.apellido1 = apellido1;
    }

    @Column(name="APE2")
    public String getApellido2() {
        return apellido2;
    }

    public void setApellido2(String apellido2) {
        this.apellido2 = apellido2;
    }

    @OneToMany(mappedBy = "autor", cascade = CascadeType.ALL)
    public List<Libro> getLibros() {
        return libros;
    }

    public void setLibros(List<Libro> libros) {
        this.libros = libros;
    }
}

Libro.java. Clase que mapea la tabla Libro

package org.dune.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="LIBRO")
public class Libro {

    protected Long id;
    protected String titulo;
    protected Autor autor;
   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
   
    @Column(name="TITULO")
    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    @ManyToOne
    @JoinColumn(name = "AUTOR")
    public Autor getAutor() {
        return autor;
    }

    public void setAutor(Autor autor) {
        this.autor = autor;
    }
}

Como elementos de configuración tendremos tres ficheros:

applicationContext.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/biblioteca"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"            <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven/>   
<context:annotation-config base-package="org.dune" />
<context:component-scan base-package="org.dune" />
</beans>

persistence.xml.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
   
    <persistence-unit name="PERSISTENCE_UNIT" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>org.dune.model.Autor</class>
        <class>org.dune.model.Libro</class>      
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

pom.xml.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.dune</groupId>
  <artifactId>Test</artifactId>
  <version>1.0</version>
  <dependencies>
      <dependency>
          <groupId>es.ine.sgtic.comun.lib.spring</groupId>
          <artifactId>spring-2.5.0-lib</artifactId>
          <version>1.0</version>
          <type>pom</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.j2ee</groupId>
          <artifactId>persistence</artifactId>
          <version>9.0</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.myfaces.1.2.5</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.1.1</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.hibernate</groupId>
          <artifactId>hibernate-annotations</artifactId>
          <version>3.3.0.ga</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.comun.util.utilidades</groupId>
          <artifactId>ine-util-cripto</artifactId>
          <version>2.0</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.mysql.5.0.4</groupId>
          <artifactId>mysql-connector-java-5.0.4-bin</artifactId>
          <version>5.0.4</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.comun.lib.hibernate</groupId>
          <artifactId>hibernate-3.2.5.GA-lib</artifactId>
          <version>1.0</version>
          <type>pom</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.jboss</groupId>
          <artifactId>jboss-common</artifactId>
          <version>1.2.1.ga</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.dom4j</groupId>
          <artifactId>dom4j</artifactId>
          <version>1.6.1</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.jboss</groupId>
          <artifactId>concurrent</artifactId>
          <version>4.2.2.ga</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.jboss</groupId>
          <artifactId>javassist</artifactId>
          <version>4.2.2.ga</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.jakarta-commons</groupId>
          <artifactId>commons-collections</artifactId>
          <version>3.2</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.cglib</groupId>
          <artifactId>cglib-nodep</artifactId>
          <version>2.1_3</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>es.ine.sgtic.lib.spring.2.5.0.j2ee</groupId>
          <artifactId>jta</artifactId>
          <version>1.1</version>
          <type>jar</type>
          <scope>compile</scope>
      </dependency>
  </dependencies>
</project>