La mayoría de aplicaciones requieren acceso a una base de datos. En java existe JDBC que nos permite realizar esta tarea.
En una aplicación distribuida y escalable nos vemos en la necesidad de utilizar un pool de conexiones para no causar un problema de rendimiento. Por ejemplo, he creado servicios web (REST) que deseo sean consumidos desde dispositivos móviles, para ello necesito que los servicios sean escalables.

Ya que utilicé Spring Roo para crear la plantilla inicial del proyecto, este me agregó la siguiente configuración inicial.

applicationContext.xml

...
    <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
        <property name="driverClassName" value="${database.driverClassName}"/>
        <property name="url" value="${database.url}"/>
        <!--<property name="username" value="${database.username}"/>-->
        <!--<property name="password" value="${database.password}"/>-->
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="true"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
        <property name="numTestsPerEvictionRun" value="3"/>
        <property name="minEvictableIdleTimeMillis" value="1800000"/>
        <property name="validationQuery" value="SELECT 1 FROM DUAL"/>
    </bean>
...

como podemos ver, esta configuración crea un DataSource de tipo org.apache.commons.dbcp.BasicDataSource. Este tipo de DataSource crea un pool de conexiones automáticamente. Genial!

Ahora, ¿cómo lo utilizamos en nuestras clases para poder ejecutar una consulta SQL?

Podríamos utilizarlo de la forma tradicional con JDBC

...
    @Autowired
    public DistritosServiceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public List<Distrito> getDistritos() {
        List<Distrito> distritos = new ArrayList<Distrito>();
        String sql = 
                "select id, nombre " +
                        "from distritos";
        Connection con = null;
        try{
            con = this.dataSource.getConnection();
            PreparedStatement stmt = con.prepareStatement(sql);
            ResultSet rs = stmt.executeQuery();
            while(rs.next()){
                distritos.add(new Distrito(rs.getInt("id"), rs.getString("nombre")));
            }
        }catch (Exception e){
            e.printStackTrace();
            if(con != null){
                try {
                    con.close();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return distritos;
    }
...

pero existe una mejor manera, utilizando JdbcTemplate

...
    @Autowired
    public DistritosServiceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public List<Distrito> getDistritos() {
        String sql =
                "select id, nombre " +
                        "from distritos";
        return jdbcTemplate.query(sql, new RowMapper<Distrito>() {
            @Override
            public Distrito mapRow(ResultSet rs, int i) throws SQLException {
                return new Distrito(rs.getInt("id"), rs.getString("nombre"));
            }
        });
    }
...

Fuentes:

Anuncios

Después de haber instalado un servidor LAMP, para depurar aplicaciones web, con el comando

sudo apt-get install lamp-server^

creé un enlace simbólico a una carpeta de mi directorio, /home/cirovladimir/Projects/websites/agenda que representa mi proyecto.

cd /var/www
sudo ln -s /home/cirovladimir/Projects/websites/agenda
Al intentar acceder desde un navegador a la URL

http://localhost/agenda

me arrojaba un error 403 – Forbidden, y en el log de apache el mensaje “Symbolic link not allowed or link target not accessible”

después de hacer una búsqueda exhaustiva y probar diferentes configuraciones. Llegué a esta página dónde se encontraba la respuesta.
El problema es que el directorio raíz, Projects, no tenía permisos ni de lectura ni de ejecución. Lo solucioné con el comando

chmod 755 -R /home/cirovladimir/Projects

Fuentes:
google search
stackexchange

Al desarrollar una aplicación web necesitamos ir viendo como funciona desde nuestro navegador. La forma más sencilla que he encontrado hasta ahora es abrir una terminal, ir a la carpeta del proyecto y ejecutar el siguiente comando

python -m SimpleHTTPServer

Si necesitas algo más avanzado que eso, por ejemplo, Apache con PHP. También puedes ejecutarlo desde la línea de comandos. Lo primero que tienes que hacer es instalarlo.

sudo apt-get install apache2
sudo apt-get install php5
sudo apt-get install libapache2-mod-php5
sudo service apache2 restart

Una vez instalado, crea el archivo ~/bin/pache con el siguiente contenido

    #!/bin/bash
    /usr/sbin/apache2 -DFOREGROUND -d . -f .htaccess -C"PidFile `mktemp`" -C"Listen 8000" -C"Include /etc/apache2/mods-enabled/*.load" -C"Include /etc/apache2/mods-enabled/*.conf" -C"ErrorLog /dev/stdout" -C"DocumentRoot ." -e debug

Hazlo ejecutable

chmod a+x ~/bin/pache

Ve a la carpeta de tu proyecto y ejecuta el comando

cd ~/Projects/demo
pache

En mi caso utilicé initializr para no comenzar el proyecto desde cero. Si te aparece el siguiente mensaje de error o alguno otro parecido

Syntax error on line 86 of ./.htaccess: Invalid command ‘CommandName’, perhaps misspelled or defined by a module not included in the server configuration

tienes que activar algún módulo. En el script que creamos intentamos cargar todos los módulos que estén configurados en tu instalación local (/etc/apache2/mods-enabled). También podrías especificarlos en el archivo .htaccess y la directiva LoadModule Fuentes: http://www.howtogeek.com/howto/ubuntu/installing-php5-and-apache-on-ubuntu/ http://stackoverflow.com/questions/13695391/start-an-apache-server-in-any-directory-from-command-line http://systembash.com/content/apache-2-4-upgrade-and-the-invalid-command-order-error/ http://www.senin.org/2006/11/07/invalid-command-order-perhaps-misspelled-or-defined-by-a-module-not-included-in-the-server-configuration/ http://stackoverflow.com/questions/933959/apache-is-incorrectly-converting-jsp-pages-to-text-plain

Apache CXF – Contexts

noviembre 8, 2012

Apache CXF – Contexts

Si necesitas información del contexto en un servicio web basado en Apache CXF, lo puedes hacer mediante la anotación @Resource y las clases WebServiceContext (JAX-WS) y MessageContext (JAX-RS).

@Path("/customers")
@WebService
public class CustomerService {

   @Resource WebServiceContext jaxwsContext;
   @Resource MessageContext jaxrsContext;

   @WebMethod
   @POST
   public void doIt(String b) {
       isUserInRole();
   };

   private void isUserInRole() throws WebApplicationException {
       if (jaxwsContext.getSecurityContext() != null) {
           // soap invocation
           jaxwsContext.getSecurityContext().isUserInRole(theRole);
       } else {
           // http-only jaxrs one
           jaxrsContext.getSecurityContext().isUserInRole(theRole);
       }  
   }
}

via Apache CXF — JAX-RS and JAX-WS.

Apache CXF – Ejemplo de firma de un documento XML

Los beneficios de firmar digitalmente un documento XML en nuestros servicios web son que nos provee autenticación, integridad de los datos y el no repudio de la fuente que invoca el servicio.

Para ver un ejemplo de cómo puedes configurar Apache CXF para que utilice y verifique las firmas en un servicio web, sigue el enlace.

via Adding X.509 security headers to Apache CXF SOAP calls | Glen Mazza’s Weblog.

Para configurar Apache CXF en una aplicación web, lo puedes hacer de muchas formas. Esto me confunde, ya que hay distintos ejemplos en internet.

Mi forma preferida es configurar un servlet en el archivo web.xml y configurar los servicios en un archivo beans.xml. Un ejemplo básico de estos archivos sería como el siguiente:

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<init-param>
<param-name>config-location</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>

beans.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" />

<jaxws:endpoint id="HelloSOAP" address="/ws/helloService"
implementor="com.acme.ws.soap.HelloServiceImpl" />

<jaxrs:server id="rest-api" address="/rest">
<jaxrs:serviceBeans>
<bean id="HelloREST" class="com.acme.ws.rest.HelloREST" />
</jaxrs:serviceBeans>
</jaxrs:server>

</beans>

Las dependencias necesarias en Maven son las siguientes:


<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>

La dependencia a la librería spring-web es necesaria desde la versión 2.6 de Apache CXF. Las dependencias de SLF4J son opcionales, pero muy recomendables para ver la información del Log. Si quieres saber como configurar SLF4J puedes verlo aquí.

Fuentes:
Apache CXF — Servlet Transport.
http://stackoverflow.com/questions/6349424/apache-cxf-rs-extensions-issue-in-2-4-0

En Apache CXF JAX-RS existen los ParameterBeans que nos permiten crear una instancia de un objeto a partir de los parámetros enviados al servicio.

@POST
public String create(@FormParam("")Empleado empleado){
 try {
 DAO dao=new DAO();
 dao.ofy().put(empleado);
 Map<String, Object> response = new HashMap<String, Object>();
 Map<String, Object> body = new HashMap<String, Object>();
 body.put("status", 0);
 body.put("data", empleado);
 response.put("response", body);
 return json.writeValueAsString(response);
 } catch (Exception e) {
 log.error("", e);
 }
 return "{\"response\":{\"status\":-1, \"data\":\"Hubo un problema al guardar el Empleado\"}}";
}

En el ejemplo anterior se contruye un objeto Empleado a partir de los parámetros @FormParam enviados al servidor.

Los parámetros pueden ser de tipo String o cualquier tipo que tenga un constructor que reciba como parámetro una cadena o un método estático valueOf(String s). Adicionalmente CXF JAXRS puede utilizar el método fromString(String s) si existiera.


public class Empleado {

 Long id;
 String nombre;
 Date fechaNacimiento;

 public Empleado() {
 }

//getters y setters
}

Al utilizar la clase Empleado cómo parámetro, me arrojaba el siguiente error
ERROR [org.apache.cxf.jaxrs.utils.InjectionUtils] – Class java.util.Date can not be instantiated using a constructor with a single String argument
El mensaje del error es muy explícito. Pero entonces, ¿cómo le indicamos a Apache CXF que genere un objeto de tipo Date a partir de una cadena?
Bueno, pues tenemos que utilizar un ParameterHandler como el siguiente


/**
 * Converts ISO8601 parameters into date objects.
 *
 */
@Provider
public class ISO8601DateParamHandler implements ParameterHandler<Date> {

/**
 * Coerce an ISO8601 time string to a date object.
 *
 * @param s the string to parse.
 * @return the date object or null if the string cannot be parsed.
 */
 @Override
 public Date fromString(String s) {
 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
 try {
 return new Date(dateFormat.parse(s).getTime());
 } catch (ParseException e) {
 return null;
 }
 }
}

y configurarlo en nuestro archivo beans.xml

<jaxrs:server id="rest-api" address="/rest/gwt-audiencias">
 <jaxrs:providers>
 <bean class="com.mycompany.server.api.rest.ISO8601DateParamHandler" />
 </jaxrs:providers>
 <jaxrs:serviceBeans>
 <bean id="empleados"
 class="com.mycompany.server.api.rest.EmpleadosResource"></bean>
 </jaxrs:serviceBeans>
</jaxrs:server>

Fuentes:
http://cxf.547215.n5.nabble.com/ParameterHandler-not-invoked-for-Date-parameter-td2267734.html
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Parameterbeans
http://cxf.apache.org/docs/jaxrs-services-configuration.html