jueves, enero 24, 2013

Removiendo dependencias Maven no necesarias, de proyecto Spring generado con ROO

En mi actual proyecto tenemos una aplicacion que fue generada con Spring Roo, despues removimos Rooya que no planeamos usarlo como framework. El proyecto es en si un REST API por lo cual no necesitamos tener vistas, utiliza Spring MVC para asociar los controladores con los REST services usando las caracteristicas out of the box de Spring MVC.

Sin embargo al momento de crear el proyecto en Roo viene toda la serie de dependencias que se usarian en una aplicacion estandar web con UI, en mi caso la aplicacion ocupaba 48.6 MB de espacio en disco. Decidi dar una limpieza a las dependencias maven, para conseguir finalmente un espacio en disco de 34 MB osea 14.6 MB de diferencia.

Estoy usando Spring 3.1.1, Spring Roo 1.2.2

Una herramienta util para localizar las dependencias es el comando:  

mvn dependency:tree

Este comando inspecciona el proyecto y construye un arbol de dependencias de los jars configurados el el pom.xml, al ejecutarlo se ve algo como lo siguiente:

[INFO] +- junit:junit:jar:4.10:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] +- log4j:log4j:jar:1.2.16:compile
[INFO] +- org.slf4j:slf4j-api:jar:1.6.4:compile
[INFO] +- org.slf4j:jcl-over-slf4j:jar:1.6.4:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.4:compile
[INFO] +- org.aspectj:aspectjrt:jar:1.7.0.RC1:compile
[INFO] +- org.aspectj:aspectjweaver:jar:1.7.0.RC1:compile
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
...
[INFO] +- org.apache.tiles:tiles-jsp:jar:2.2.2:compile
[INFO] |  +- org.apache.tiles:tiles-servlet:jar:2.2.2:compile
[INFO] |  |  \- org.apache.tiles:tiles-core:jar:2.2.2:compile
[INFO] |  \- org.apache.tiles:tiles-template:jar:2.2.2:compile
[INFO] |     \- org.apache.tiles:tiles-api:jar:2.2.2:compile
[INFO] \- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.11:compile
[INFO]    \- org.codehaus.jackson:jackson-core-asl:jar:1.9.11:compile

Las dependencias que encontre que en mi caso no son requeridas para crear un REST API y que removi de mi pom son las siguientes:

POM.xml

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

        <dependency>
            <groupId>net.sf.flexjson</groupId>
            <artifactId>flexjson</artifactId>
            <version>2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.roo</groupId>
            <artifactId>org.springframework.roo.annotations</artifactId>
            <version>${roo.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.webflow</groupId>
            <artifactId>spring-js-resources</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>jstl-impl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>el-api</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>

    <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-jsp</artifactId>
            <version>2.2.2</version>
        </dependency>

Ademas removi el siguiente plugin:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
    ...

Tambien hay que editar el archivo webmvc-config.xml y quitar la configuracion del view resolver que apunta a tiles:

webmvc-config.xml

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
  </bean>
    <bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
    <property name="definitions">
      <list>
        <value>/WEB-INF/layouts/layouts.xml</value>
<!--        Scan views directory for Tiles configurations -->
        <value>/WEB-INF/views/**/views.xml</value>
      </list>
    </property>
  </bean>

 Ademas ya no es necesario usar aspectj para el manejo de transacciones, asi que podemos remover mode="aspectj" en el archivo: applicationContext.xml de la siguiente linea de codigo:

applicationContext.xml

<t:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

Quedando de la siguiente manera:

 <tx:annotation-driven transaction-manager="transactionManager"/>

Espero que esto sea de utilidad a otras personas con un caso similar.




miércoles, enero 16, 2013

Spring MVC / Jackson : como ignorar bean metadata y otras propiedades

Estaba tratando de usar jackson en mi proyecto actual con Spring MVC 3, tengo un metodo en el controlador parecido al siguiente:

@Controller
@RequestMapping("/usuario")
public class UsuarioController {

    @Autowired
    private GenericResponse genericResponse;

    @RequestMapping(value = "/{nombre}", method = GET)
    public @ResponseBody GenericResponse regresaDatos(@PathVariable String nombre) {
        try {
            genericResponse.setData(servicio.getDatos(nombre));
        }
       
        catch(Exception e) {
            genericResponse.setMessage("Error al obtener datos");
        }
       
        return genericResponse;
    }

La clase GenericResponse

public class GenericResponse {
    private String message;
    private IWrapper data;
   ...
    setters y getters aqui
    ...
}

Cuando utilizas Jackson con spring mvc y la anotacion @ResponseBody en el retorno de un metodo, jackson convertira el retorno del metodo a JSON

El problema es que el objeto genericResponse es un proxy bean con scope request:

    <bean id="genericResponse" class="com.mycompany.common.GenericResponse" scope="request">
        <aop:scoped-proxy />
    </bean>

Esto implica que Spring agrega al proxy infinidad de propiedades y metadatos que Jackson intenta convertir a JSON tambien, como eso no es lo que quiero me puse a investigar como evitar que Jackson convierta las propiedades no deseadas.

La solucion es agregar las siguientes anotaciones en la clase GenericResponse:

@JsonIgnoreProperties({"proxyTargetClass", "exposeProxy", "frozen", "preFiltered"})
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE)
public class ErrorResponse {
    private String message;
    private IWrapper data;

    @JsonProperty
    public String getMessage() {
        return message;
    }
   
    public void setMessage(String message) {
        this.message = message;
    }


    @JsonProperty    
    public IWrapper getData() {
        return data;
    }
   
    public void setData(IWrapper data) {
        this.data = data;
    }
}

Con esto Jackson convierte el objeto a JSON de la manera esperada.