viernes, 8 de noviembre de 2013

Ubuntu: Creando y reintegrando un archivo tar multi-partes

Necesitaba guardar una máquina virtual en mi disco duro externo y me di cuenta que lo tenía formateado en FAT32, por lo cual no podía guardar un archivo mayor a 4GB.

Así que indague la manera de crear un archivo tar multi partes:

tar -cvf - myVM.vdi | split --bytes=4000m --suffix-length=4 --numeric-suffix - MyVM.tar.

Para reintegrarlo:

cat MyVM.tar.* | tar xvf -



miércoles, 6 de noviembre de 2013

ATG Colorizer: libstdc++.so.6: cannot open shared object file: No such file or directory

Estaba en medio de mi configuración inicial de desarrollo de ATG y comenzé a tener problemas con una pequeña aplicación llamada ATG Colorizer.

Me daba el siguiente error:
libstdc++.so.6: cannot open shared object file: No such file or directory

Intenté instalar la librería como lo había hecho antes, pero parecía que mi nuevaversión de Ubuntu no reconocía la receta antigua de instalación.

Así que después de buzear un rato por Internet encontré una manera que sí le gustó a mi Ubuntu:
 
apt-get install lib32stdc++6

miércoles, 16 de octubre de 2013

Escribiendo imagen ISO de Ubuntu en USB

Comando para escribir una imagen (iso) booteable de Ubuntu a una llave USB:

sudo dd if=ubuntu-13.04-desktop-amd64.iso of=/dev/sdb oflag=direct  bs=1048576

En mi caso el dispositivo USB se montaba en /dev/sdb. Para chequar los mounts del sistema se puede ejecutar el siguiente comando:

sudo fdisk -l

martes, 10 de septiembre de 2013

ATG: Template Error: category error 2


Comencé a tener este problema después de un merge de código. Todavía no sé por qué sucedió, pero estaba impidiendo mi trabajo.

El error sucedía en el droplet: /atg/commerce/catalog/CategoryLookup. Encendí el debug para obtener más información:

14:01:43,960 INFO  [CategoryLookup] DEBUG Find item: id=BRAND_OPTIMUM; type=category
14:01:43,961 INFO  [CategoryLookup] DEBUG Item Found:category:BRAND_OPTIMUM
14:01:43,961 INFO  [CategoryLookup] DEBUG Is item in catalog catalog:masterCatalog
14:01:43,961 INFO  [CategoryLookup] DEBUG This item is not in the correct catalog.

Un colega experto en ATG me ayudó mostrandome una página de mantenimiento en Dyn Admin que se utilizar para solucionar este tipo de errores:

Basic Catalog Maintenance

http://localhost:8080/dyn/admin/atg/commerce/admin/en/maintenance/startService.jhtml?process=BasicMaintProcess


Así que solo corrí el proceso y los problemas desaparecieron.


lunes, 9 de septiembre de 2013

Flyway: No enum const class com.googlecode.flyway.core.api.MigrationState.SUCCESS

Esta excepción lanzada por Flyway:

java.lang.IllegalArgumentException: No enum const class com.googlecode.flyway.core.api.MigrationState.SUCCESS 
        at java.lang.Enum.valueOf(Enum.java:214)
        at com.googlecode.flyway.core.api.MigrationState.valueOf(MigrationState.java:21)
        at com.googlecode.flyway.core.metadatatable.MetaDataTable$MigrationInfoRowMapper.mapRow(MetaDataTable.java:382)
        at com.googlecode.flyway.core.metadatatable.MetaDataTable$MigrationInfoRowMapper.mapRow(MetaDataTable.java:370)
        at com.googlecode.flyway.core.util.jdbc.JdbcTemplate.query(JdbcTemplate.java:319)
...

sucede por un valor inválido in la columna 'state'. En mu caso estaba fallando por un espacio en blanco extra del cual no me había dado cuenta.

viernes, 6 de septiembre de 2013

Juniper VPN: sysdeps.error Failed to open /etc/resolv.conf with error 2 (sysdeps.cpp:715)




En mi trabajo usamos un cliente de Juniper para la conexión de VPN. De repente comenzé a tener problemas para conectarme. Al principio parece que la conexión se va a establecer pero de repente se cerraba e indicaba que la conexión se cerró satisfactoriamente.

Chequeando los logs me di cuenta de un error que se mostraba:

ncsvc[p10461.t10461] sysdeps.error Failed to open /etc/resolv.conf with error 2 (sysdeps.cpp:715)

Fui a revisar el dichoso archivo /etc/resolv.conf y efectivamente no existía. Así que simplemente lo cree (en mi caso tenía un symlink quebrado). Listo problema resuelto.





miércoles, 21 de agosto de 2013

Apache: Verificando errores en la configuración de VirtualHosts


Hace poco necesitaba verificar por qué una configuración de VHost no me estaban cargando. Para ello ejecuté el comando:

apachectl -S

El comando muestra cada uno los VirtualHosts que se cargan así como cualquier error de configuración:

VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:443                  is a NameVirtualHost
         default server www.example.com (/etc/apache2/sites-enabled/example-ssl:1)
         port 443 namevhost www.exampleg.com (/etc/apache2/sites-enabled/example-ssl:1)
*:80                   is a NameVirtualHost
         default server example.com (/etc/apache2/sites-enabled/example:29)

De ahí me pude dar cuenta que mi nuevo archivo de VHost no se estaba cargando porque no es suficiente tener el archivo en /etc/apache2/sites-available, sino que hay que crear un symlink en /etc/apache2/sites-enabled.

Alias para buscar texto recursivamente

Un alias muy útil para ejecutar el comando grep recursivamente en busca de un texto literal:

search() {
   grep -R "$1" .
}

alias search=search

$ search my_string

martes, 20 de agosto de 2013

Wordpress: Deshabilitando el título de una página


Pues algo que pareciera debiera ser una opción por defecto de Wordpress resulta ser ligeramente más complicado. Hay muchas formas de lograrlo pero la más sencilla a mi parecer es instalar un plugin que se llama Disable Title

Ya dentro del editor de la página aparecen varias opciones para deshabilitar el título.

miércoles, 14 de agosto de 2013

Java: Conviertiendo String a Enum


A veces es necesario convertir un valor String a un Enum porque quizás el valor lo tenemos en la base de datos como un String, pero deseamos manipularlo en el código como un Enum.

En el siguiente código muesto como obtener el valor Enum de un String. Básicamente se agrega un método estático a la clase Enum que retorna el valor específico mediante una iteración de todos sus valores para comparar el valor String con el pasado por parámetro. Si no se encuentra o se pasa un valor nulo, se retorna una excepción de argumento inválido.


public enum Volcano {

 IRAZU("Irazu"), POAS("Poas"), ARENAL("Arenal"), RINCON_DE_LA_VIEJA("Rincon de la vieja");

 private String name;

 private Volcano(String name) {
  this.name = name;
 }

 public static Volcano fromString(String name) {
  if (name == null) {
   throw new IllegalArgumentException();
  }
  for (Volcano volcano : values()) {
   if (name.equalsIgnoreCase(volcano.getName())) {
    return volcano;
   }
  }
  // Passed string value does not correspond to a valid enum value.
  throw new IllegalArgumentException();
 }

 public String getName() {
  return this.name;
 }

 public static void main(String[] args) {
  Volcano volcano1 = Volcano.fromString("Poas");
  System.out.println(volcano1);

  Volcano volcano2 = Volcano.fromString("rincon de la vieja");
  System.out.println(volcano2);

  Volcano volcano3 = Volcano.fromString("Fuji");
 }
}

Salida:


POAS
RINCON_DE_LA_VIEJA
java.lang.IllegalArgumentException
 at com.bodybuilding.common.enums.Volcano.fromString(Volcano.java:22)
 at com.bodybuilding.common.enums.Volcano.main(Volcano.java:36)

lunes, 22 de julio de 2013

Flyway Validate: Cannot determine latest applied migration. Was the metadata table manually modified?

En nuestro proyecto utilizamos Flyway para el control de las modificaciones a la base de datos. Hace poco me comenzó a dar este error que no daba mucha pista de por qué fallaba:


Flyway Validate: Cannot determine latest applied migration. Was the metadata table manually modified?

Siendo un proyecto Open Source me encontré el código con facilidad y pude ver qué tipo de consulta estaba tratando de ejecutar. Podemos ver en el código que busca por la columna "CURRENT_VERSION":
   /**
     * @return The latest migration applied on the schema. {@code null} if no migration has been applied so far.
     */
    public MetaDataTableRow latestAppliedMigration() {
        if (!hasRows()) {
            return null;
        }

        String query = getSelectStatement() + " where current_version=" + dbSupport.getBooleanTrue();
        @SuppressWarnings({"unchecked"})
        final List metaDataTableRows = jdbcTemplate.query(query, new MetaDataTableRowMapper());

        if (metaDataTableRows.isEmpty()) {
            if (hasRows()) {
                throw new FlywayException("Cannot determine latest applied migration. Was the metadata table manually modified?");
            }
            return null;
        }

        return metaDataTableRows.get(0);
    }

    ...

    /**
     * @return The select statement for reading the metadata table.
     */
    private String getSelectStatement() {
        return "select VERSION, DESCRIPTION, TYPE, SCRIPT, CHECKSUM, INSTALLED_ON, EXECUTION_TIME, STATE from " + schema + "." + table;
    }

Así que indetifiqué la última versión aplicada y manualmente le puse el valor de la columna en 1. Problema resuelto.

viernes, 28 de junio de 2013

Oracle: ORA-00600: internal error code, arguments: [keltnfy-ldmInit], [46], [1], [], [], [], [], []


Otro error más a mi colección de mensajes encriptados de Oracle. Mi bases de datos locales no estaban pudiendo ser accesadas, así que me conecte a la instancia idle e inicie manualmente el servidor. De ahí me di cuenta del bello mensaje:

gabo@gabo-Precision-M6600:~$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Jun 28 10:11:37 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> shutdown
ORA-01034: ORACLE not available
ORA-27101: shared memory realm does not exist
Linux Error: 2: No such file or directory
SQL> startup
ORA-00600: internal error code, arguments: [keltnfy-ldmInit], [46], [1], [], [], [], [], []

Según la leído en varios foros, este mensaje tiene que ver con la incapacidad de Oracle de resolver el host de la base de datos, lo cual me parecía raro porque según yo estaba usando localhost el cual es siempre una típica entrada quemada en el archivo hosts (linux: /etc/hosts, Windows: c:\windows\system32\drivers\etc\hosts).

Indagano el archivo listener.ora, en mi caso instalado en este path: /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/network/admin/listener.ora, me di cuenta que el host usado hacía referencia al nombre de mi laptop: "gabo-Precision-M6600".

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = PLSExtProc)
      (ORACLE_HOME = /usr/lib/oracle/xe/app/oracle/product/10.2.0/server)
      (PROGRAM = extproc)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
      (ADDRESS = (PROTOCOL = TCP)(HOST = gabo-Precision-M6600)(PORT = 1521))
    )
  )

DEFAULT_SERVICE_LISTENER = (XE)

Así que agruegué este nombre a mi hosts apuntando a 127.0.0.1 y listo. Problema resuelto.

jueves, 27 de junio de 2013

Oracle: ORA-12519, TNS: no appropriate service handler found


Este error puede tener varias causas, pero en el caso particular me estaba molestando este error al tratar de abrir nuevas conexiones porque aparantemente había excedido la cantidad máxima de procesos y/o sessiones permitidas.

Afortundamente este valor se puede modificar con una simple sentencia SQL(es necesario reiniciar el Oracle):

alter system set processes=150 scope=spfile;

SQL*Plus: Release 10.2.0.1.0 - Production on Thu Jun 27 14:46:58 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> select * from v$resource_limit where resource_name in ('processes','sessions');

RESOURCE_NAME                  CURRENT_UTILIZATION MAX_UTILIZATION
------------------------------ ------------------- ---------------
INITIAL_ALLOCATION
----------------------------------------
LIMIT_VALUE
----------------------------------------
processes                                       38              40
        40
        40

sessions                                        42              49
        49
        49

RESOURCE_NAME                  CURRENT_UTILIZATION MAX_UTILIZATION
------------------------------ ------------------- ---------------
INITIAL_ALLOCATION
----------------------------------------
LIMIT_VALUE
----------------------------------------


SQL> alter system set processes=150 scope=spfile;

System altered.

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production
gabo@gabo-Precision-M6600:/usr/lib/oracle/xe/app/oracle/product/10.2.0/server$ sudo /etc/init.d/oracle-xe restart
Shutting down Oracle Database 10g Express Edition Instance.
Stopping Oracle Net Listener.

Starting Oracle Net Listener.
Starting Oracle Database 10g Express Edition Instance.

miércoles, 19 de junio de 2013

CakePHP: Warning: _cake_core_ cache was unable to write 'cake_dev_en-us' ...


Este error me dio al acceder el sitio después de clonar una copia fresca del framework:

Warning: _cake_core_ cache was unable to write 'cake_dev_en-us' to File cache in /var/www/html/cakephp/lib/Cake/Cache/Cache.php on line 310 Warning: /var/www/html/cakephp/app/tmp/cache/persistent/ is not writable in /var/www/html/cakephp/lib/Cake/Cache/Engine/FileEngine.php on line 337


Evidentemente es un error de permisos que solucioné cambiando el usuario owner del proyecto al del servidor web que en este caso es 'apache':

chown -R apache:apache /var/www/html/cakephp

lunes, 17 de junio de 2013

SEO: Contenido amigable con Robots


Hoy día los sitios web no solamente deben verse bonitos en apariencia, sino que tambien deben ser amigables con los robots (conocidos tambien como web crawlers). Después de todo, de qué vale un sitio si nadie lo visita. Y precisamente un punto importante de entrada a un sitio son los buscadores web.

Como los sitios cada vez son más dinámicos aprovechando tecnologías como JavaScript, se podría en muchas ocasiones pecar de ignorante en el sentido de que una buena porción de contenido no es indexado por los robots pues estos no interpretan código JavaScript. 

Una sana práctica es simular el comportamiento de un robot navegando el sitio con un "user agent" de algún robot específico deshabilitando completamente el JavaScript. Esto nos puede dar una idea que tan amigable es nuestro sitio con los robots.

Recomiendo para esto dos addons de FireFox: "Web Developer" y "User Agent Switcher".





jueves, 13 de junio de 2013

Oracle: Buscar texto en nombre de tablas y columnas


Un par de sentencias SQL de ORACLE para buscar un texto en nombre de tablas y nombre de columnas. Particularmente útil cuando se da mantenimiento a sistemas sobre los cuales uno no desarrolló desde el principio (legacy).

Para buscar en nombre de las tablas, índice, etc:

SELECT * FROM dba_objects WHERE object_name LIKE '%STRING%';

Para buscar en nombre de columnas:

SELECT owner, table_name, column_name FROM all_tab_columns WHERE column_name LIKE '%COL_STRING%';


lunes, 3 de junio de 2013

Oracle: Desinstalando completamente


Tengo instalado Oracle XE 10.2.0.1-1.1 en mi Ubuntu. Hace unos días quería reinstalarlo porque las bases de datos se desbarataron después de que se corrompieron por un freeze del sistema. Intentaba deinstalarlo de muchas maneras y por alguna razón me quedaba mal instalado y tampoco podía reconfigurarlo apareciendome el mensaje:

Oracle Database 10g Express Edition is already configured

Después de sufrir un rato intentando varias soluciones, tuve éxito ejecutando el siguiente comando que limpia totalmente la instalación: 'sudo apt-get --purge remove oracle-xe-universal'

$ sudo apt-get --purge remove oracle-xe-universal
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following package was automatically installed and is no longer required:
  libdb4.8
Use 'apt-get autoremove' to remove them.
The following packages will be REMOVED:
  oracle-xe-universal:i386*
0 upgraded, 0 newly installed, 1 to remove and 58 not upgraded.
After this operation, 451 MB disk space will be freed.
Do you want to continue [Y/n]? Y
(Reading database ... 223556 files and directories currently installed.)
Removing oracle-xe-universal:i386 ...
Shutting down Oracle Database 10g Express Edition Instance.
Stopping Oracle Net Listener.

Purging configuration files for oracle-xe-universal:i386 ...
Processing triggers for ureadahead ...
Processing triggers for man-db ...
Processing triggers for bamfdaemon ...
Rebuilding /usr/share/applications/bamf.index...
Processing triggers for desktop-file-utils ...
Processing triggers for gnome-menus ...

sábado, 1 de junio de 2013

CakePHP: Warning: strtotime(): It is not safe to rely on the system's timezone settings.


Recién copié un proyecto que tenía de CakePHP a una máquina virtual y comencé a tener este mensaje de warning:


Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/Los_Angeles' for 'PDT/-7.0/DST' instead in /path/to/cake/lib/Cake/Cache/CacheEngine.php on line 45 Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'America/Los_Angeles' for 'PDT/-7.0/DST' instead in /path/to/cake/lib/Cake/Cache/CacheEngine.php on line 45 


La solución más rápida es ubicar el archivo de 'core.php': /path/to/cake/app/Config/core.php y descomentar esta línea:

/**
 * Uncomment this line and correct your server timezone to fix 
 * any date & time related errors.
 */
date_default_timezone_set('UTC');

miércoles, 15 de mayo de 2013

Indeces basados en funciones (function-based indexes) usados como constraints


Supongamos que quisieramos evitar duplicidad de valores de una columna por una mal chequeo de formularios de un programa que termina permitiendo agregar valores que ya existen, con la única diferencia que le agrega espacios en blanco o cambia alguna letra a mayúscula o minúscula.

Un índice basado en función no solamente permite agilizar las búsquedas por expresión. Recomiendo este video para una breve explicación:




Un índice basado en función también puede usarse como un tipo constraint. Dado el caso explicado al principio, podríamos crear un índice de tipo "UNIQUE" para prevenir duplicidad con una función que limpia el valor a insertar y chequea si el valor limpiado es único:

create unique index INDEX_NAME on TABLE (lower(trim(COL_1)));

Una vez agregado el índice a la tabla, cualquier inserción de un valor duplicado (que tiene espacios o letras cambiadas a minúscula/mayúscula) lanzará el siguiente error:

SQL Error: ORA-00001: unique constraint (TABLE.INDEX_NAME) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
           For Trusted Oracle configured in DBMS MAC mode, you may see
           this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.

lunes, 22 de abril de 2013

Ejemplo de uso de librería Jackson de Spiring para consumir JSON por servicio REST


Un breve ejemplo de cómo utilizar la librería Jackson de Spring para consumir un JSON que retorna un servicio REST. La clase de abajo es un Singleton que inicializa el RestTemplate una sola vez.

import java.util.ArrayList;
import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.http.client.CommonsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

public class RestJsonTest {
 
 private static RestJsonTest instance;
 private RestTemplate restTemplate;
 private String url ="http://rest.service.url";
 
 public static RestJsonTest getInstance() {
  if (instance == null) {
   instance = new RestJsonTest();
  }
  return instance;
 }
 
 private RestJsonTest() {
  // Setup the RestTemplate configuration.
  restTemplate = new RestTemplate();
  restTemplate.setRequestFactory(new CommonsClientHttpRequestFactory());
  List<HttpMessageConverter<?>> messageConverterList = restTemplate.getMessageConverters();
  
  // Set HTTP Message converter using a JSON implementation.
  MappingJacksonHttpMessageConverter jsonMessageConverter = new MappingJacksonHttpMessageConverter();
  
  // Add supported media type returned by BI API.
  List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
  supportedMediaTypes.add(new MediaType("text", "plain"));
  supportedMediaTypes.add(new MediaType("application", "json"));
  jsonMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
  messageConverterList.add(jsonMessageConverter);
  restTemplate.setMessageConverters(messageConverterList);
 }
 
 public SearchResults searchResults() {
  return restTemplate.getForObject(url, SearchResults.class);  
 }
 
 public static void main(String[] args) {
  RestJsonTest jsonTest = RestJsonTest.getInstance();
  SearchResults results = jsonTest.searchResults();
 }
}

El mapeo del JSON a clases Java se puede hacer por medio de anotaciones como se muestra a continuación.ç
package com.bodybuilding.api.commerce.clientservice;

import java.util.List;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;


/**
 *{
 * "search_keywords":"Social Networks",
 *  "total_time":200,
 *  "results":{
 *   "result_01":{
 *    "url":"http://www.facebook.com",
 *    "rank": "1"
 *   },
 *   "result_02":{
 *   "url":"http://www.twitter.com",
 *    "rank": "2"
 *   }
 *  }
 * }
 */
@JsonIgnoreProperties(ignoreUnknown=true)
public class SearchResults {
 
 @JsonProperty("search_keywords")
 private String keywords;
 
 @JsonProperty("total_time")
 private long totalTime;
 
 @JsonProperty("results")
 private List<SearchResult> results;
 
 public String getKeywords() {
  return keywords;
 }
 public void setKeywords(String keywords) {
  this.keywords = keywords;
 }
 public long getTotalTime() {
  return totalTime;
 }
 public void setTotalTime(long totalTime) {
  this.totalTime = totalTime;
 }
 public List<SearchResult> getResults() {
  return results;
 }
 public void setResults(List<SearchResult> results) {
  this.results = results;
 } 
}

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown=true)
public class SearchResult {
 
 @JsonProperty("url")
 private String url;
 
 @JsonProperty("rank")
 private int rank;
 
 public String getUrl() {
  return url;
 }
 public void setUrl(String url) {
  this.url = url;
 }
 public int getRank() {
  return rank;
 }
 public void setRank(int rank) {
  this.rank = rank;
 }
}

También se puede configurar el bean de RestTemplate por medio de una inyección de Spring:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
     <property name="requestFactory">
      <bean id="clientHttpRequestFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory" />   
     </property>
     <property name="messageConverters">
      <list>
       <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="supportedMediaTypes">
         <list>
          <bean id="jsonMediaTypeTextPlain" class="org.springframework.http.MediaType">
           <constructor-arg value="text"/>
          <constructor-arg value="plain"/>
          </bean>
          <bean id="jsonMediaTypeApplicationJson" class="org.springframework.http.MediaType">
           <constructor-arg value="application"/>
          <constructor-arg value="json"/>
          </bean>
         </list>
        </property>
       </bean>
      </list>
     </property>  
</bean>

domingo, 24 de marzo de 2013

Instalando GIT en distribuciones Fedora


Estaba tratando de instalar GIT en una máquina virtual que me descargué con sistema operativo Linux basado en Fedora. Cuando corría el comando:

$ yum install git

me daba el siguiente error:

yum No package git available.

Despues de buscar un rato por Internet encontré que hay que descargar el repositiorio EPEL (Extra Packages for Enterprise Linux)

sudo rpm -Uvh http://mirrors.ucr.ac.cr/epel/5/i386/epel-release-5-4.noarch.rpm

La URL de arriba es la de un mirror de la Universidad de Costa Rica puesto que es el mirror más cercano que encontró. Si no hace conexión es cuestión de buscar otro que esté activo.

Listo. Se vuelve a ejecutar el comando YUM y esta vez sí debería funcionar.



jueves, 14 de marzo de 2013

Oracle Drop Schema

La manera en que uno borra una base de datos en Oracle (típico DROP SCHEMA), o al menos la forma que conozco, es borrando el usuario dueño del esquema con la opción de cascada:

sqlplus "/ as sysdba";
SQL> drop user user_name cascasde;

Oracle: Importar/Exportar un dump


Una actividad muy recurrente cuando se manejan bases de datos es la de exportar algún esquema para respaldar la base de datos, y luego importarla para restaurar un esquema a un estado en particular.

En Oracle esto se puede hacer desde la línea de comandos usando "expdp" y "impdp" para exportar un dump e importar un dump.

expdp db_user/db_pass schemas=schema_name dumpfile=file_name.dmp
impdp db_user/db_pass schemas=schema_name dumpfile=file_name.dmp

El comando exporta el dump en el path donde está instalado el Oracle. En mi caso lo tira en este directorio:

/usr/lib/oracle/xe/app/oracle/admin/XE/dpdump/

A la hora de importarlo hay que colocarlo ahí tambien puesto que el comando no permite indicar el path  en el parámetro del archivo.

lunes, 4 de marzo de 2013

Tests de Unidad, Integración y Funcionales


Parece existir cierto grado de confusión entre los desarrolladores a la hora de referirse a los conceptos de tests de unidad, integración y funcionales. De hecho eso estababa sucediendo en mi trabajo, así que me hice a la tarea de investigar alguna autoridad en el campo que definiera de manera clara estos conceptos, y de esta forma que no quedara como una opinión más de la Internet.

Para estos fines encontré el libro escrito por Gerard Meszaros: 
















Tests de Unidad


Los tests de unidad verifican el comportamiento de una sola clase o método que es consecuencia de una decisión de diseño. Este comportamiento está típicamente no directamente relacionado a los requerimientos, excepto cuando un pedazo clave de la lógica de negocio está encapsulado dentro de la clase o el método en cuestión. Estos tests son escritos por los desarrolladores para su propio uso; estos ayudan a los desarrolladores a describir cómo "luce lo que está hecho" al resumir el comportamiento de la unidad en forma de tests.

Tests de Integración/Componente


Los tests de componente verifican los componentes que consisten en un grupo de clases que colectivamente proveen un servicio. Estos se localizan entre los tests de unidad y los tests de cliente en términos del tamaño del SUT (System Under Tests/Sistema Bajo Prueba) siendo verificado. Aunque algunas personas llaman a estos "tests de intregración" o "tests de subsistema", estos términos pueden significar algo enteramente diferente de "tests para un subcomponente específico largamente granulado del sistema entero".

Tests Funcionales/Cliente

Los tests de cliente verifican el comportamiento del sistema entero o de la aplicación. Tipicamente corresponden a escenarios  de uno o más casos de uso, características, o historias de usuario. Estos test muchas veces son referidos por otros nombres como tests funcionales, tests de aceptación, o tests de usuario final. Aunque muchas veces pueden estar automatizados por desarrolladores, su característica principal es que un usuario final debería ser capaz de reconocer el comportamiento especificado por el test aún cuando el usuario no pueda leer la representación del test.




Figura: Un resumen de los tipos de tests que escribimos y por qué. La columna de la izquierda contiene los tests que escribimos que describen la funcionalidad del producto a varios niveles de granularidad; ejecutamos estos tests para apoyar al desarrollo.
La columna de la derecha contiene los tests que abarcan pedazos específicos de funcionalidad; ejecutamos estos tests para criticar el producto. Al final de cada celda se describe qué se intenta comunicar o verificar. 





martes, 29 de enero de 2013

Instalando JAD (Java Decompiler) en Ubuntu 12

Estaba instalando JAD (decompilador para Java) en mi distribución de Ubuntu, descargándolo de este sitio web. Descargué el ejecutable y lo moví a /usr/local/bin, pero cuando lo llamé desde la consola me dió este error:

jad: error while loading shared libraries: libstdc++-libc6.2-2.so.3: cannot open shared object file: No such file or directory

El problema se saba porque descargué la versión incorrecta. Bajé la que dice ¨Jad 1.5.8e for Linux (statically linked) ¨ y listo, funcionando correctamente.

sábado, 5 de enero de 2013

Aplicaciones Web y Frameworks


La web hoy en día se ha vuelto un ecosistema donde las personas cada vez más se mueven mucho más naturalmente a través de  las diversas interacciones que se dan en este complejo ambiente. Los usuarios finales navegan, suben archivos, hacen consultas, realizan búsquedas, en fin, las interfaces de la web cada vez son más comunes a los ojos de las personas, que esa sensación de tiempos antiguos de que todo lo que tenía que ver con computadoras era super complejo, y solo los "nerds" lo entendían, esta prácticamente en extinción.

Para aquellos que nos ganamos la vida produciendo “1’s y 0’s” para que otros usuarios puedan seguir habitando el mundo de la web, resultamos ser un poco más orgullosos porque podemos “jactarnos” que conocemos algunas capas más abajo del subsuelo de la web. Pero la verdad es que la web es tan compleja que los mismos desarrolladores, debido a las herramientas que nos hemos acostumbrado a usar, somos muchas veces grandes desconocedores de todo lo que sucede más allá de las capas superficiales de la web.

En este post, que es más una traducción libre del capítulo 1 del libro Struts 2 In Action: “Struts 2: the modern web application framework" (Struts 2: el framework de aplicaciones web moderno),  quiero describir algunos puntos básicos que todo ingeniero de la web, particularmente el ingeniero que desarrolla aplicaciones web en la plataforma de Java, debería conocer. Examinaremos la pila tecnológica sobre la cual los frameworks de aplicaciones se montan para poder ocultar y facilitar todas esas funciones, que de lo contrario harían la vida muy difícil a los desarrolladores. 

La pila tecnológica


En una manera muy resumida, según lo muestra la figura, los frameworks de aplicación (de Java) se sitúan sobre dos tecnologías importantes: el protocolo HTTP y la especificación de servlets de java.


La API de Servlet maneja todas las comunicaciones de bajo nivel con la capa HTTP. En tiempos modernos no es considerado una buena práctica programar usando directamente la API, pero conocer lo básico de ella nos ayuda a entender mejor la arquitectura sobre la cual construimos nuestras aplicaciones.

Ya que se menciona el protocolo HTTP, cabe destacar los conceptos básicos que se encierran en este protocolo tan común en nuestra vida diaria. Sin él no podríamos hacer ningún tipo de navegación en Internet. El protocolo HTTP es un protocolo que no guarda estado. Consiste en una serie de mensajes entre un cliente, típicamente un navegador, y un servidor, que puede ser una web o aplicación web, por medio de los cuales se solicitan distintos tipos de datos que al final se despliegan para un usuario. Cabe mencionar que el protocolo HTTP no fue originalmente pensado para considerar la cantidad de información compleja e interacciones de usuarios que ahora se utilizan actualmente en la Internet. Este es un tipo de problema que las aplicaciones web deben solucionar en tiempos modernos.

Debido a la falta de estado entre las peticiones, y al hecho de que todas las peticiones son basadas en texto plano, las aplicaciones web deben ingeniárselas para resolver esa brecha resultante de seguir trabajando sobre un protocolo que fue pensado para servir documentos HTML. Los casos de uso más complicados, como una simple validación de usuario, sería como programar en ensamblador (bueno, tal vez no tanto) si no tuviéramos la ayuda de los frameworks web. Otro detalle quizás desapercibido a la hora de utilizar un framework como Struts es la conversión de datos en la comunicación. Esto se vuelve bastante transparente para la mayoría de los desarrolladores. El protocolo es muy tieso en su comunicación por medio de texto plano, mientras que Java es un lenguaje altamente tipeado. Si tuviéramos que pensar todo el tiempo en nuestras aplicaciones web como convertir texto a entero, por dar un ejemplo, pasaríamos menos tiempo resolviendo los verdaderos casos de uso de la aplicación, y más tiempo haciendo tareas repetitivas y aburridas.

La Java Servlet API


La API de Servlets de Java da un paso más adelante para resolver la gran complejidad de lidiar directamente con el protocolo HTTP. Por medio de la API un programador Java puede interactuar con el protocolo usando una encapsulación en el típico paradigma de programación orientación a objetos. Es por ello que en la API hayamos objetos como el HTTPRequest o el HTTPResponse. En síntesis la API permite recibir requests HTTP, incluyendo los parámetros del query string y datos de formularios, hacer un proceso back-end, y retornar una respuesta al usuario. Todo a traves de código Java.

La unidad básica de empaquetamiento de un servlet se conoce como una aplicación web. Aunque parece una definición simple, esto tiene un sentido en el contexto de servlets. La especificación de servlets define una aplicación web como una colección de servlets, páginas HTML, clases, y otros recursos. Típicamente, una aplicación web requiere de más de un servlet para satisfacer todas las solicitudes de sus clientes. Los servlets y recursos de una aplicación web son empacados en una estructura de directorio estándar  y nombrado con una extensión .war. Un archivo WAR es una versión especializada de un archivo .jar. Significa archivo de aplicación web (Web Application Archive).

Una vez que se tiene este archivo web empaquetado, hay que desplegarlo (deploy) en un contenedor de servlets. El servlet es un tipo especial de aplicación llamado aplicación manejada por ciclo de vida. En este tipo de aplicación uno no ejecuta directamente el servlet sino que el contenedor es el encargado de ejecutar el servlet a través de sus métodos de ciclo de vida, donde el principal es el llamado service(). Cuando una solicitud llega al contenedor, este primero determina cuál servlet es el que tiene que usarse para satisfacerla.

En la siguiente figura se muestra un contenedor con tres aplicaciones web y como a través de una URL se determina cual aplicación y servlet debe satisfacer la solicitud



Otra funcionalidad que agrega la API es la capacidad de manejar sesiones. Como se había mencionado, el protocolo HTTP no tiene estado, lo cual significa que no se puede correlacionar las distintas solicitudes a un servidor con un cliente específico. Sino fuera por la API, habría que ingeniarselas con el uso de galletas y llaves de sesión empotradas en el query string para poder mantener una sesión activa con un usuario específico. Aparte de esta funcionalidad, la API no provee más funciones adicionales, pero es el fundamento a partir del cual se construyen sólidas aplicaciones web, incluyendo los frameworks.

Siguiente nivel


Habiendo entendido, de una manera sintetizada, cuáles son las labores de bajo nivel de la API de servlets, podemos entonces continuar con las tareas que no son tomadas en cuenta por la API, y que por tanto son los frameworks web los que suministran estas capacidades a los desarrolladores.

Ligación (binding) de los parámetros de la solicitud (request) y validación de datos.

Recordamos de nuevo que los parámetros del request viajan en texto plano en el protocolo HTTP, y la API de servlets no provee un proceso automático de conversión a distintos tipos de variables en Java. Puede que esto no sea algo extremadamente complejo de implementar por cuenta de un desarrollador, pero es lo suficientemente repetitivo y monótono como para no dejarlo como una funcionalidad de un framework. La necesidad de la validación de datos es obvio de entender para cualquier desarrollador. El framework lo que permite es definir una manera estructurada y consistente para la validación de formularios.

Llamados a las capas de lógica de negocio y datos

Esta es una de las facultades que no son específicas de una aplicación web. Cualquier aplicación sin importar si es web o de escritorio necesita manejar un tipo de flujo de trabajo donde una solicitud de usuario pasa por las capas de presentación, lógica de negocio y acceso a base de datos, y luego viceversa para enviar la respuesta. Este flujo representa algún tipo de tarea que debe completarse. En algunos frameworks web como Struts, a estas tareas se le conoce como acciones, y el framework es conocido como un framework orientado a acciones.

Renderizacion e internacionalizacion

Lejos estamos de los días aquellos del comienzo de la web donde utilizabamos páginas simples construidas en HTML. Cada vez más los sitios web son mucho más complejos en su interfaz para dar una experiencia mucho más rica al usuario. Tecnologías como Ajax, HTML5 y la creciente penetración de los dispositivos móviles, hacen que sea necesario el soporte de un framework para aliviar la complejidad de la capa de presentación.

Agregado a esto tenemos la necesidad de la internacionalización que es la capacidad de una aplicación de adaptar varios elementos de la misma para acomodarse a una región, país o localidad. Ejemplo simples son el texto desplegado para cambiar a un idioma según el país, el formato de la hora y fecha, la moneda, etc. Ciertamente esto es algo que buscamos que un framework nos lo probea.

¿Qué es un framework?


Un framework es una pieza de software estructural. Se dice que es estructural porque la estructura es quizás la meta principal del framework, más que cualquier otro requerimiento funcional. Un framework trata hacer generalizaciones acerca de las tareas comunes y el flujo de trabajo de un dominio específico. El framework entonces intenta proveer una plataforma sobre la cual las aplicaciones de un dominio pueden ser construidas de manera rapida y eficaz. El framework hace esto de dos maneras principales. Primero, el framework trata de automatizar todas aquellas tareas tediosas del dominio. Segundo, el framework intenta introducir una solución arquitectural elegante al flujo de trabajo común del dominio en cuestión.

En una sola definición podemos decir que un framework es una pieza de software estructural que provee automatización de tareas comunes al dominio, así como una incorporación de una solución arquitectural que puede ser fácilmente extendida por aplicaciones que implementan el framework.

¿Por qué utilizar un framework?


Uno necesariamente no tiene la obligación de utilizar un framework, pero pensar en la alternativas que son omitir el uso de un framework, o crear uno propio, realmente acarrea más desventajas que beneficios.

En el primer caso, al menos que sea una aplicación sea sencilla, el hecho de tener que implementar todas las tareas tediosas y repetitivas del dominio en cuestión, resulta en un código altamente complejo y difícil de mantener, sin mencionar que cansado y agotante. Lo ideal en un proyecto de desarrollo de software es gastar la mayor cantidad de esfuerzo en tareas de alto nivel acorde con el negocio, en lugar de invertirlas en reinventar el agua tibia.

En el segundo caso, son muy limitados los escenarios donde es necesario tener que desarrollar un framework desde cero en lugar de utilizar uno existente, principalmente porque deben darse varias raras condiciones en disponibilidad de recursos: desarrolladores sumamente hábiles, tiempo y dinero, todo esto para gastarlo en un proyecto en primera instancia, sin retorno de inversión a corto o mediano plazo. Y aún si estas condiciones se dan, siguen habiendo desventajas con esta estrategia. Hay que invertir tiempo y dinero en preparar nuevos desarrolladores en una solución local, mientras que si se utiliza un framework popular se pueden contratar desarrolladores ya entrenados. Además los frameworks caseros tienden a erosionarse con el tiempo, mientras que los modernos se mantienen en continua actualización.

Sin duda los frameworks web son casi mandatorios para cualquier proyecto de desarrollo web en la actualidad, sin embargo conocer los pilares de las soluciones web nos ayudan a entender mejor la arquitectura para solucionar los problemas que no son triviales, como rendimiento y escalabilidad.