Archive

Posts Tagged ‘Spring’

Hola Mundo con Spring BlazeDS y Flex

May 2nd, 2010

Hola amigos, acá comparto un ejemplo de como desarrollar un “Hola mundo” con Spring BlazeDS y con Flex.

Vamos directo al código :)

Maven

Las principales cosas que definimos en el archivo maven son:

  1. Dependencias JSP/Servlet.
  2. Dependencias para el Spring framework.
  3. Dependencias para el Spring BlazeDS.
  4. Plugin para compilar java con 1.5 que tiene soporte para anotaciones.
<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/maven-v4_0_0.xsd">
    ...
 
    <!-- defining some properties -->
    <properties>
        <org.springframework.version>3.0.0.RELEASE</org.springframework.version>
    </properties>
 
    <!-- dependencies -->
    <dependencies>
        ...
        <!-- jsp/servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
 
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- spring 3 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.flex</groupId>
            <artifactId>spring-flex</artifactId>
            <version>1.0.3.RELEASE</version>
        </dependency>
 
        <!-- blazeds -->
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-core</artifactId>
            <version>3.2.0.3978</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-proxy</artifactId>
            <version>3.2.0.3978</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-opt</artifactId>
            <version>3.2.0.3978</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-remoting</artifactId>
            <version>3.2.0.3978</version>
        </dependency>
 
        ...
    </dependencies>
    <build>
        <finalName>helloblazeds</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            ...    
        </plugins>
    </build>
</project>

web.xml

En el web.xml tenemos:

  1. Definición de un Filtro para soportar caracteres UTF8.
  2. Mapeo de la servlet de Spring para manejar cualquier pedido que pase por /messagebroker/*.
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="ifm_service_catalog" version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
    <display-name>Hello World with SpringBlazeDS</display-name>
 
    <!-- filter -->
    <filter>
        <filter-name>encoding-filter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
 
    <filter-mapping>
        <filter-name>encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 
    <!-- servlet -->
 
    <servlet>
        <servlet-name>spring-flex</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/flex-servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>spring-flex</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>
 
    <!-- Welcome file list -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

flex-servlet-context.xml, services-config.xml y remoting-config.xml

El flex-servlet-context.xml es bien sencillo. Solo le indicamos a Spring que paquete escanear para buscar clases anotadas y la definición del bean MessageBroker para la integración con Flex.

<?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:context="http://www.springframework.org/schema/context"
       xmlns:flex="http://www.springframework.org/schema/flex"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/flex
                           http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
    <context:component-scan base-package="test.flex" />
    <flex:message-broker/>
</beans>

El services-config.xml sirve para definir los canales y servicios que serán soportados. Véase también que en el canal estamos indicando como vienen los pedidos, en este caso “/messagebroker/amf”

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service-include file-path="remoting-config.xml" />
        <default-channels>
           <channel ref="my-amf-channel"/>
        </default-channels>
    </services>
 
    <channels>
        <channel-definition id="my-amf-channel" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
                      class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>
    <logging>
        <target class="flex.messaging.log.ConsoleTarget" level="Error">
            <properties>
                <prefix>[BlazeDS]</prefix>
                <includeDate>false</includeDate>
                <includeTime>false</includeTime>
                <includeLevel>false</includeLevel>
                <includeCategory>false</includeCategory>
            </properties>
            <filters>
                <pattern>Endpoint.*</pattern>
                <pattern>Service.*</pattern>
                <pattern>Configuration</pattern>
            </filters>
        </target>
    </logging>
    <system>
        <redeploy>
            <enabled>false</enabled>
        </redeploy>
    </system>
</services-config>

En el remoting-config.xml definimos el adaptar java para AMF.

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
         class="flex.messaging.services.RemotingService">
    <adapters>
        <adapter-definition id="java-object"
                            class="flex.messaging.services.remoting.adapters.JavaAdapter"
                            default="true"/>
    </adapters>
    <default-channels>
        <channel ref="my-amf-channel"/>
    </default-channels>
</service>

Clase SayHello

La siguiente clase esta anotada como servicio y también como destinación remota para AMF :)

package test.flex;
 
import java.io.*;
import java.util.List;
import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.stereotype.Service;
 
@Service("SayHello")
@RemotingDestination
public class SayHello {
 
    public String hello(){
        return "Hola Mundo";
    }
}

Finalmente el código en Flex :)

En la aplicación Flex tenemos:

  1. Definición de object RemoteObject.
  2. Un botón pulsante para llamar al metodo hello().
  3. Definición de dos funciones flex para manejar la respuesta en caso de éxito y fallo de la llamada.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.utils.ObjectUtil;
import mx.controls.Alert;
import mx.utils.StringUtil;
 
/**
 * Response handler
 */            
private function resultHandler(event:ResultEvent):void{
    Alert.show( ObjectUtil.toString(event.result) );   
}
/**
 * Failure handler
 */
private function faultHandler(event:FaultEvent):void{
    Alert.show( ObjectUtil.toString(event.fault) );
}
]]>
 
</mx:Script>
 
<!-- Remote object that is accessed from the server.-->
<mx:RemoteObject id="remoteObject" 
 destination="SayHello"   
 result="resultHandler(event)"
 fault="faultHandler(event)"     
 endpoint="http://localhost:8080/helloblazeds/messagebroker/amf"/>
 
<mx:Panel width="457" 
    height="228" 
    layout="vertical" 
    title="Hola Mundo con AMF/Flex y BlazeDS" 
    horizontalAlign="center">
    <mx:Button label="Hola Mundo!" click="remoteObject.hello()"/>
</mx:Panel>    		
</mx:Application>

Renan Huanca Flex , ,

Escaneando anotaciones con Spring

April 18th, 2010

Hola Amigos, acá comparto un poco de código que escribí esta semana.

El Problema

Esta semana estaba trabajando con AMF, mi problema era listar las classes que estaban anotadas con la anotación @RemotingDestination, para luego poder listarlas en una consola. (Con el objetivode mostrar los servicion AMF disponibles en el servidor)

Aca un ejemplo de una clase anotada con @RemotingDestination

package some.good.packague.to.work; // :)
 
import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.stereotype.Service;
 
@Service("userService")
@RemotingDestination
public class UserService{
 
    public String getUserName(String userId) {
        .....
        return "The best name in the world :)";
    }
 
}

La Solución

Primeramente estaba viendo Javassist, pero al final me quede con una solución con Spring.

El ejemplo lo encontré el blog de Java Chimaera, pero le hice algunas modificaciones para que ahora soporte buscar clases tanto en linux como en windows.

El Código

package yep.this.is.a.very.good.package;
 
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
 
import java.util.Set;
import java.util.HashSet;
import java.io.IOException;
 
/**
 * This class is a adaptation of:
 *
 *    http://java-chimaera.blogspot.com/2008/10/scanning-classpath-annotated-classes.html
 */
/**
 * @author Renan Huanca
 * @since Apr 16, 2010 9:52:12 PM
 */
public class AnnotatedClassFinder {
    private String basePackage;
    private ResourcePatternResolver resourceResolver = null;
    private MetadataReaderFactory metadataReaderFactory = null;
    private TypeFilter annotationFilter = null;
 
    public AnnotatedClassFinder(String basePackage) {
        this.basePackage = basePackage;
        this.resourceResolver = new PathMatchingResourcePatternResolver(
                Thread.currentThread().getContextClassLoader());
        this.metadataReaderFactory = new SimpleMetadataReaderFactory();
    }
 
    public static AnnotatedClassFinder getInstance(String basePackage){
        return new AnnotatedClassFinder(basePackage);
    }
 
    public Set findByAnnotation(Class annotation) {
        this.annotationFilter = new AnnotationTypeFilter(annotation);
        Set <Class>annotatedClasses = new HashSet();
        /*
        * First of all we load all resources that are under a specific package by using a ResourcePatternResolver.
        * By doing so we will use class files as simple resources without passing
        * by the ClassLoader which can for example cause the execution of a static initialization block.
        * It resolves also transparently resources in jars.
        */
        String candidateClassesLocationPattern = "classpath*:" + basePackage + "/**/*.class";
        Resource[] resources = null;
        try {
            resources = resourceResolver.getResources(candidateClassesLocationPattern);
        } catch (IOException e) {
            throw new RuntimeException(
                    "An I/O problem occurs when trying to resolve ressources matching the pattern : "
                            + candidateClassesLocationPattern, e);
        }
 
        /*
        * then we proceed resource by resource, using a MetadataReaderFactory to create
        * MetadataReader wich hides the ASM related interface and complexity.
        *
        */
        for (Resource resource : resources) {
            MetadataReader metadataReader = null;
            try {
                metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
 
                if (this.annotationFilter.match(metadataReader, metadataReaderFactory)) {
 
                    /*
                    * the AnnotationMetadata is a simple abstaction of the informations
                    * that holds the annotation
                    */
                    String className = convertResourceToClassName(resource, basePackage);
                    try {
                        annotatedClasses.add(ClassUtils.forName(className));
                    } catch (Exception e) {
                        throw new RuntimeException("problems occurs when trying to load the annotated class : " + className, e);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException("An I/O problem occurs when trying to process resource : " + resource, e);
            }
        }
 
        return annotatedClasses;
    }
 
    static String convertResourceToClassName(Resource resource, String basePackage) throws IOException {
        String path = resource.getFile().getPath();
        String pathWithoutSuffix = path.substring(0, path.length() - ClassUtils.CLASS_FILE_SUFFIX.length());
        String relativePathWithoutSuffix = "";
        if(System.getProperty("file.separator").equals("\\")) {
            relativePathWithoutSuffix = pathWithoutSuffix.substring(pathWithoutSuffix.indexOf(basePackage.replace('/', '\\')));
            relativePathWithoutSuffix = relativePathWithoutSuffix.replace('\\', '/');
        } else if(System.getProperty("file.separator").equals("//")) {
            relativePathWithoutSuffix = pathWithoutSuffix.substring(pathWithoutSuffix.indexOf(basePackage));
        } else {
            throw new RuntimeException("File separator is not recognized");
        }
 
        // taking out extra \ or /
        relativePathWithoutSuffix = relativePathWithoutSuffix.substring(1);
 
        return relativePathWithoutSuffix.replace('/', '.');
    }
}

Como llamarlo?

AnnotatedClassFinder finder = AnnotatedClassFinder.getInstance("/the/package/you/want/to/search");
Set classes = finder.findByAnnotation(RemotingDestination.class);

Renan Huanca Spring , ,

Como usar el MultiActionController de Spring 2.5

March 4th, 2009

Hola spring adictos,

Esta vez les mostrare un pequeño ejemplo de como usar el MultiActionController de Spring 2.5

Descripción del ejemplo

Bueno, esta pequeña aplicación lo único que hace es mostrar un formulario con un solo campo que corresponde al nombre de una mascota. Cada vez que se hace submit del nombre de la mascota, esta se adiciona a una lista en session llamada pets (no recomiendo usar variables en session). Luego esta lista se va desplegando en la parte inferior del formulario.

Para este ejemplo voy a asumir que ya tienen las librerías de spring y también configurado en el archivo web.xml de tu aplicación.

A  grandes rasgos lo que vamos a hacer es lo siguiente:

  • Creamos el bean Pet (solo para los propósitos de este ejemplo)
  • Crear y declarar PetController en el contexto de spring.
  • Crear el view JSP y declararlos en el contexto de spring.
  • Probar la funcionalidad.

Creamos el bean Pet (solo para los propósitos de este ejemplo)

package bo.sumasoftware.hellospring;
/**
 * @author renan
 */
public class Pet {
    String name;
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

Este bean, es solo para nuestro ejemplo y solo tiene una propiedad llamada name con su respectivo get y set. Se utilizara para leer datos del formulario y tambien para desplegar.

Crear y declarar PetController en el contexto de spring.

package bo.sumasoftware.hellospring;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.ArrayList;
 
/**
 * @author renan
 */
public class PetController extends MultiActionController {
    /**
     * This is called form http://localhost:8080/&lt;context&gt;/pet.do?method=form
     *
     * @param request
     * @param response
     * @param pet
     * @return
     */
    public ModelAndView form(HttpServletRequest request, HttpServletResponse response, Pet pet) {
        if(pet.getName()!=null){
            List pets = (List) request.getSession().getAttribute("pets");
            if(pets == null) {
                pets = new ArrayList();
                request.getSession().setAttribute("pets",pets);
            }
            pets.add(pet);
        }
        ModelAndView mav = new ModelAndView("pet");
        mav.getModel().put("pet", new Pet());
        return mav;
    }
}

Fuiu, es un poco largo la clase no? :)  pero ni modo, lo mas importante es que que estamos declarando un método con el nombre form, por si acaso esto no es un nombre especial ni nada por el estilo, puede ser cualquier nombre que ustedes definan. Lo importante es saber solo usando este nombre se podrá llamar a este método desde url o formulario.

pet, es el nombre del beanForm o commandName como se lo llama en spring. Es en este objeto que spring colocar los valores de los import del html from que viene del browser. Algo también interesante es que no es requerido definir este parámetro, también se puede colocar otros, mas información en http://static.springframework.org/spring/docs/2.0.x/reference/mvc.html#mvc-controller-multiaction.

pets, es la lista que desplegaremos en el jsp, lo interesante es que se hace proceso para crearla si no esta en session.

new ModelAndView(”pet”), de esta forma le decimos a spring a que view queremos que direccione la salida del controller.

ahora veremos como se define el controller en el contexto de spring

<bean name="/pet.do" class="bo.sumasoftware.hellospring.PetController">
	<property name="methodNameResolver">
		<bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
			<property name="paramName" value="method"/>
		</bean>
	</property>
</bean>

/pet.do,  es la forma de mapear la llamada desde un url. Cabe señalar que esta forma de mapeo es una de las mas basicas, spring tienes algunas como por ejemplo basado en archivo xml y tambien archivo de propiedades. ParameterMethodNameResolver, este es el que me interesa mas, con esto le estamos diciendo al controller como se van a llamar a sus metodos. Aca estamos definiendo que para llamar a algun metodo de la clase PetController, ya sera por un get(url) o post(formulario) se tiene que incluir como dato un valor llamado ‘method‘, esta variable contendra el nombre del metodo dentro del controller que queremos llamar. (para el emplo el valor que tendra sera ‘form’)

Crear el view JSP y declararlos en el contexto de spring.

Creamos un archivo con el nombre pet.jsp dentro de un folder llamado /jsp. (mas adelante la explicacion del por que)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 
<html>
<head><title>Pet</title></head>
<body>
<form:form action="pet.do" method="post" commandName="pet">
    <h4>Pet</h4>
    Pet Name:<form:input path="name"/><br/>
    <input type="hidden" name="method" value="form"/><br/>
    <input type="submit"/>
</form:form>
<h4>pets</h4>
<c:forEach items="${sessionScope.pets}" var="pet">
    ${pet.name} <br/>
</c:forEach>
 
</body>
</html>

Este jsp, no estan largo. que bueno :), la primera parte solo es para definir los tags que vamos a usar en este caso el tag form de spring y el tag c de jstl.

Como veran se define un formulario donde se define que el action sera pet.do, osea que cuando se haga submir del formulario el request se dirigira a este. Despues se define tambien un input, que sirve para declarar un input field, que tiene el nombre name.

name=”method” value=”form”, este es el mas, mas importante, acá indicamos cual sera el método a llamar del controller, sino hacemos esto, les aseguro que tendremos muchos dolores de cabeza. Cabe tambien señalar que no es necesario que sea una constante, puede cambiar, lo puedes setear con java script o algun otro truco que conozcas. Lo importante es enviar un valor.

Despues lo que hacemo es iterar una lista que esta en session que se llama pets, se despliega el nombre y nada mas.

Ahora a mapear las views

Bueno, esta es la forma mas basica de mapear view, todos los jsps tienen que estar dentro de el folder /jsp/. Como es tan basica y tambien la mas sencilla  a veces uno no se da cuenta como es que se puede llamar a una view especifica. :)

La clave esta en la clase ModelAndView en el controller, si vuelven al codigo del controller veran que hay una sentencia como esta: new ModelAndView(”pet”). Lo que hara spring es buscar un archivo pet.jsp que este dentro de la carpeta jsp.

Probar la funcionalidad.

Ahora a probar,  el url para llamar al controller sera:

http://localhost:8080/hello/pet.do?method=form

Como veran, estoy colocando como paremetro extra que metodo del controller voy a llamar.

Luego aparecera un formulario como esto:

hellospring1

En el input field pueden colocar nombres de sus mascotas y veran que se iran adicionando en una lista debajo de la palabra pets.

Finalmente para aquellos que quieren ver algo mas, acá tengo el código fuente. (para los que usan maven, tambien esta el pom.xml, para los que no, tendran que incluir los jars necesarios)

Encontré un buen post acerca de como funciona el SpringMVC veanlo.

Comentarios son bien recibidos, hasta la próxima :)



Renan Huanca Spring , ,