Es conveniente mencionar la relación que presenta el uso de las tecnologías de
información y comunicación (TIC) con la música y los jóvenes. En la actualidad, los jóvenes
emplean diariamente las TIC para incrementar conocimiento, buscar información, relacionarse
con otras personas o para divertirse. En la música, las TIC influyen en la manera en que ésta se
crea, se produce y se difunde; éstas también han generado cambios que ofrecen gran cantidad de
opciones musicales, desde la composición hasta el consumo de la música, y que gracias a los
medios de comunicación han permitido la distribución musical masiva, inmediata y global

http://musica.rediris.es/leeme/revista/terrazasetal13.pdf

En la página oficial de spotify nos dice como debemos instalar el cliente de spotify en linux

  1. Agrega esta línea a tu lista de depósitos al # editar tu /etc/apt/sources.list

    deb http://repository.spotify.com stable non-free

  2. Si quieres verificar los paquetes descargados,
    necesitarás agregar nuestra clave pública

    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 94558F59

  3. Ejecuta la aplicación - obtén la actualización

    sudo apt-get update

  4. ¡Instala Spotify!

    sudo apt-get install spotify-client

Recientemente quise instalarlo y me arrojó errores debido a que no localizaba el servidor repository.spotify.com

Al parecer esto se debe a algún problema con sus servidores DNS, para instalarlo debes utilizar http://repository-origin.spotify.com en vez de http://repository.spotify.com

Fuentes:
https://www.spotify.com/mx/download/previews/
http://community.spotify.com/t5/Help-Desktop-Linux-Mac-and/Spotify-0-9-11-for-GNU-Linux/td-p/842969/page/4

Anuncios

Al invocar un servicio web (REST) desde una aplicación móvil (ioniccordova/phonegap), me aparecía el siguiente error:

XMLHttpRequest cannot load http://localhost:8080/equinox/catalogos/distritos. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin http://localhost:8100 is therefore not allowed access.

El servidor se está ejecutando mediante mvn jetty:run (localhost:8080) mientras que el cliente está ejecutandose mediante grunt serve (localhost:8100). Siendo dominios diferentes, el navegador bloquea la invocación del servicio por cuestiones de seguridad.

La solución es permitir el acceso CORS. El servicio web esta hecho con Spring, así que para permitir esto necesitamos crear y configurar un filtro.

SimpleCORSFilter.java

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class SimpleCORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {}

}

y para configurarlo

web.xml

...
<filter>
        <filter-name>SimpleCORSFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>simpleCorsFilter</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>SimpleCORSFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
...

applicationContext.xml

...
<bean class="mx.gob.tribunalqro.equinox.filters.SimpleCORSFilter" name="simpleCorsFilter"></bean>
...

Nota:
Esta es una implementación muy sencilla y que además agrega el encabezado a todas las peticiones, no sólo a los servicios web. Para ello, necesitamos colocar nuestros servicios en una url como api/v1/resource y mapear el filtro a esas url’s

Fuentes:

Hace poco escribí sobre como buscar/filtrar recursos en el servidor, para ello utilizamos un objeto Criteria -del mismo tipo que el recurso- para crear una consulta JDOQL que nos permitiera obtener los resultados.

En este artículo mostraré una forma de utilizar la clase AdvancedCriteria de SmartGwt para realizar una consulta más compleja. Al igual que la vez anterior, vamos a utilizar el framework Apache CXF para nuestros servicios REST, y Jackson para la serialización y deserialización de los objetos en formato JSON. También utilizamos Datanucleus para la persistencia de datos.

Me base en este artículo que explica cómo integrar SmartGwt con ASP.Net.

Lo primero que vamos a necesitar es una clase AdvancedCriteria del lado del servidor


public class AdvancedCriteria {

static ObjectMapper om;

{

om=new ObjectMapper();

om.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

}

public static class JDOQLData{

public String filter="";

public Map<String,Object> params=new HashMap<String, Object>();

public Class<?> type;

&nbsp;

public JDOQLData(Class<?> type) {

this.type=type;

}

}

&nbsp;

String _constructor;

Integer _startRow;

Integer _endRow;

String fieldName;

String operator;

Object value;

AdvancedCriteria[] criteria;

&nbsp;

public AdvancedCriteria() {

}

&nbsp;

public static AdvancedCriteria fromString(String value){

try {

return om.readValue(value, AdvancedCriteria.class);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

&nbsp;

// Getter's y Setter's ...

&nbsp;

public void toJDOQL(JDOQLData data){

if(this.criteria==null){

if(this.operator.equals(OperatorId.EQUALS.getValue())){

data.filter+=this.fieldName+" == ";

}else if(this.operator.equals(OperatorId.NOT_EQUAL.getValue())){

data.filter+=this.fieldName+" != ";

}else if(this.operator.equals(OperatorId.LESS_THAN.getValue())){

data.filter+=this.fieldName+" < ";

}else if(this.operator.equals(OperatorId.GREATER_THAN.getValue())){

data.filter+=this.fieldName+" > ";

}else if(this.operator.equals(OperatorId.LESS_OR_EQUAL.getValue())){

data.filter+=this.fieldName+" <= ";

}else if(this.operator.equals(OperatorId.GREATER_OR_EQUAL.getValue())){

data.filter+=this.fieldName+" >= ";

}

data.params.put(String.valueOf(data.params.size()+1), fixType(getPropertyType(data.type, this.fieldName), this.value) );

data.filter+=":"+data.params.size();

}else{

data.filter+="(";

for(int index=0;index<this.criteria.length;index++){

this.criteria[index].toJDOQL(data);

if(index+1<this.criteria.length){

if(this.operator.equals(OperatorId.AND.getValue())){

data.filter+=" && ";

}else if(this.operator.equals(OperatorId.OR.getValue())){

data.filter+=" || ";

}else if(this.operator.equals(OperatorId.NOT.getValue())){

data.filter+=" ! ";

}

}

}

data.filter+=")";

}

}

&nbsp;

private Object fixType(Class<?> type, Object value) {

return om.convertValue(value, type);

}

&nbsp;

private Class<?> getPropertyType(Class<?> clazz,String property){

try{

LinkedList<String> properties=new LinkedList<String>();

properties.addAll(Arrays.asList(property.split("\\.")));

Field field = null;

while(!properties.isEmpty()){

field = FieldUtils.getField(clazz,properties.removeFirst(),true);

clazz=field.getType();

}

return field.getType();

}catch(Exception e){

throw new RuntimeException(e);

}

}

}

Lo interesante de esta clase es que tiene un método estático fromString(String) que será utilizado por Apache CXF para crear un objeto de esta clase a partir de una cadena. También tiene un método recursivo toJDOQL(JDOQLData) que nos regresa un filtro y el listado de parámetros en un objeto JDOQLData para poder realizar la consulta en datanucleus. El método fixType y getPropertyType son para utilizar el tipo correcto -de acuerdo a la clase- en nuestro listado de parámetros.

Nuestro servicio REST quedaría de la siguiente forma


@GET

public String retrieve(@QueryParam("")Libro criteria,@QueryParam("")AdvancedCriteria advancedCriteria){

try {

List<Libro> libros = null;

if(advancedCriteria.get_constructor()!=null){

libros = Libro.getByAdvancedCriteria(advancedCriteria);

}else{

libros = Libro.getByCriteria(criteria);

}

Map<String, Object> response = new HashMap<String, Object>();

Map<String, Object> body = new HashMap<String, Object>();

body.put("status", 0);

body.put("data", libros);

response.put("response", body);

return json.writeValueAsString(response);

} catch (Exception e) {

log.error("", e);

}

return "{\"response\":{\"status\":-1, \"data\":\"Hubo un problema al buscar los Libros\"}}";

}

El framework Apache CXF se encarga de crear los objetos criteria(Libro) y advancedCriteria. Para saber cuál fue el que recibimos basta con verificar la propiedad _constructor, si es diferente de null sabemos que hemos recibido un AdvancedCriteria.

Finalmente realizamos la consulta en nuestra clase Libro


public static List<Libro> getByAdvancedCriteria(AdvancedCriteria criteria) {

List<Libro> libros;

PersistenceManager pm = PMF.get().getPersistenceManager();

Transaction tx = pm.currentTransaction();

try{

tx.begin();

JDOQLData data=new JDOQLData(Libro.class);

criteria.toJDOQL(data);

Query query = pm.newQuery(Libro.class,data.filter);

libros=(List<Libro>) query.executeWithMap(data.params);

tx.commit();

}finally{

if(tx.isActive()){

tx.rollback();

}

pm.close();

}

return libros;

}

En el cliente, utilizamos la clase AdvancedCriteria de SmartGwt para realizar la consulta


&nbsp;

AdvancedCriteria criteria=new AdvancedCriteria(OperatorId.OR,

new AdvancedCriteria[]{

new AdvancedCriteria("isbn",OperatorId.EQUALS, txtISBN.getValueAsString()),

new AdvancedCriteria(OperatorId.AND, new AdvancedCriteria[]{

new AdvancedCriteria("autor.nombre", OperatorId.EQUALS, "Gabriel García Márquez"),

new AdvancedCriteria("titulo", OperatorId.EQUALS, "Crónica de una muerte anunciada")

})

});

librosListGrid.fetchData(criteria);

&nbsp;

Finalmente al realizar el ‘fetch’ se realiza una petición a nuestro servicio con los siguientes párametros. Los cuales serán transformados a un objeto de nuestra clase AdvancedCriteria


&nbsp;

__gwt_ObjectId:793

operator:or

criteria:{"__gwt_ObjectId":788,"fieldName":"isbn","operator":"equals","value":"978-1400034956"}

criteria:{"__gwt_ObjectId":791,"operator":"or","criteria":[{"__gwt_ObjectId":789,"fieldName":"autor.nombre","operator":"equals","value":"Gabriel García Márquez"},{"__gwt_ObjectId":790,"fieldName":"titulo","operator":"equals","value":"Crónica de una muerte anunciada"}]}

_constructor:AdvancedCriteria

_operationType:fetch

_startRow:0

_endRow:75

_textMatchStyle:exact

_componentId:isc_ListGrid_0

_dataSource:isc_LibrosModule_3_0

isc_metaDataPrefix:_

isc_dataFormat:json

&nbsp;

Fuentes:
http://wiki.smartclient.com/display/Main/Integrating+with+ASP.Net+MVC
http://wiki.smartclient.com/display/Main/5.+Adding+support+for+AdvancedCriteria
http://www.objectdb.com/database/jdo/manual/chapter7#Query_Parameters
http://www.objectdb.com//database/jdo/manual/chapter7
http://stackoverflow.com/questions/935762/how-to-dynamically-build-jdo-queries-on-multiple-parameters

La forma de filtrar los datos de un ListGrid en el cliente dependerá de la fuente de datos, es decir, si utilizas un DataSource o no. Aquí explicare la primera opción, ya que en nuestro caso utilizamos un RestDataSource. Si quieres saber como filtrar un ListGrid que contiene datos locales puedes consultar esta pregunta y la documentación.

Supongamos que tenemos un servicio REST que regresa un listado de usuarios con los campos id y oficina. Para poder filtrar los datos en el cliente, configuramos el ListGrid de la siguiente manera:

ListGrid grdUsuarios=new ListGrid();
grdUsuarios.setDataSource(new RestDataSource(){
{
    setDataURL("api/rest/usuarios");
    setDataFormat(DSDataFormat.JSON);
    DataSourceField fldId=new DataSourceField("id", FieldType.TEXT);
    fldId.setPrimaryKey(true);
    DataSourceField fldOficina=new DataSourceField("oficina", FieldType.TEXT);
    setFields(fldId, fldOficina);
}
});
grdUsuarios.setAutoFetchData(true);
grdUsuarios.setDataProperties(new ResultSet(){
    {
        setFetchMode(FetchMode.LOCAL);
        setUseClientFiltering(true);
    }
});
grdUsuarios.setShowFilterEditor(true);
grdUsuarios.setFilterOnKeypress(true);

Fuentes:
http://forums.smartclient.com/showthread.php?p=84118#post84118 

GWT cuenta con varias clases para el manejo y manipulación de datos en formato json.

Supongamos que obtenemos la siguiente respuesta del servidor

{“id”:”766″,”nombre”:”cirovladimir”}

Para obtener los valores, lo podemos hacer de la siguiente manera

JSONValue value=JSONParser.parseStrict(json);
String id=value.isObject().get(“id”).isString().stringValue();
String nombre=value.isObject().get(“nombre”).isString().stringValue();

Pero, ¿qué pasa si uno de los valores es nulos? ¿cómo podemos hacer una validación? Supongamos ahora que el servidor nos regresa la siguiente cadena

{“id”:”766″,”nombre”:null}

value.isObject().get(“nombre”)==null //debería ser true, sin embargo, regresa false
value.isObject().get(“nombre”).isNull()==JSONNull.getInstance() //regresa true, esta es la forma correcta

Hace poco integré el CKEditor en un proyecto. A la hora de enviar el código HTML al servidor tuve algunos problemas con los caracteres especiales.
Y es que hay que asegurarnos de que nuestro JSON sea compatible con su definición; afortunadamente el metodo toString de la clase JSONObject se encarga de “escapar” los caracteres no permitidos en JSON (” \ / \b \f \n \r \t \uXXXX) y nos regresa una cadena válida.

String jsonAcuerdo=new JSONObject(acuerdo).toString();

Finalmente, tenemos que enviar los datos dentro de una variable codificada a HTML de la siguiente forma

RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, URL.encode(jsonUrl));
rb.setHeader("Content-type", "application/x-www-form-urlencoded");
StringBuffer postData = new StringBuffer();
postData.append("acuerdo=").append(URL.encodeComponent(jsonAcuerdo));
rb.sendRequest(postData.toString(),new RequestCallback(){
//TODO on response received
});

aquí es importante utilizar el método “encodeComponent” de la clase URL ya que esté si codifica los caracteres delimitadores de componentes en una URL (; / ? : & = + $ , #) a diferencia del método “encode” que no lo hace. Estos caracteres seguramente estarán presentes en el código HTML que deseemos enviar, por ejemplo en &quot; y ni hablar de los otros. Esta variable será decodificada “automaticamente” cuando la leamos en el servidor mediante el método getParameter de la interfaz ServletRequest

Update: Puedes utilizar la clase DateUtil de SmartGWT para calcular fechas del lado del cliente.

Para mostrar una alerta de acuerdo al tiempo que había pasado, necesitaba hacer un calculo simple; si la fecha actual es mayor -por treinta días- a mi fecha de referencia, entonces debía mostrarla.
Tenía 2 problemas, obtener la fecha actual y sumar 30 días a una fecha.
Para obtener la fecha actual

Date today=new Date();

solo que nos incluye la hora actual y a la hora de comparar se puede volver una pesadilla, para remover la información de la hora

Date time=new Date();
Date today=new Date(time.getYear(),time.getMonth(),time.getDate());

Utilizamos métodos obsoletos, pero bueno, funciona.

El mayor problema viene cuando intentamos hacer calculos con fechas, como sumar días.
Dada la definición de las fechas en GWT/javascript como

All dates are calculated in milliseconds from 01 January, 1970 00:00:00
Universal Time (UTC) with a day containing 86,400,000 milliseconds.

w3schools
Podríamos hacerlo mediante la siguiente función

public Date addDays(Date date,int days){
return new Date(date.getTime()+days*1000*60*60*24);
}

Genial! lamentablemente no funciona, a mi no me arrojó ningún error, simplemente los calculos estaban mal. No se cual sea el problema, supongo que se debe a que el método Date.getTime() regresa un tipo de dato “long” y de acuerdo a la guía de GWT, este tipo de dato no se puede pasar de Java/GWT a javascript ni cómo parámetro ni como valor de retorno.
La única forma que encontré de hacer este cálculo fue mediante JSNI pasando los valores de tipo long como double, como lo hace la clase DateUtil de Gwt-Ext.

public static class DateUtils{

		protected DateUtils() {
		}

		public static Date createDate(double time){
			return new Date((long) time);
		}
		public static double getTime(Date date){
			return date.getTime();
		}

		public static native Date currentDate()/*-{
			var time=new $wnd.Date();
			var today=new $wnd.Date(time.getFullYear(),time.getMonth(),time.getDate(),0,0,0,0);
			return @package.DateUtils::createDate(D)(today.getTime());
		}-*/;

		public static native Date addDays(Date date,int days)/*-{
			var millis=@package.DateUtils::getTime(Ljava/util/Date;)(date);
			var jsDate= new $wnd.Date(millis+days*1000*60*60*24);
			return @package.DateUtils::createDate(D)(jsDate.getTime());
		}-*/;

	}

Fuentes:
Coding Basics – JavaScript Native Interface (JSNI)
GwtExt DateUtil source code
w3schools – JavaScript Date
SmartGwt Forum
blog.gerardin.info – GWT and Javascript Date Hell