Mostrando entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando entradas con la etiqueta Java. Mostrar todas las entradas

lunes, 20 de diciembre de 2010

Comprimiendo Streams en GZIP con java.util.zip

Bueno, pues algo muy sencillo pero que espero que facilite la vida a más de uno. La cuestión es que tienes "algo" que puede ser en el caso más sencillo una cadena, y que quieres enviar por red por ejemplo, o tus propias movidas.

Como decía un gran sabio en una conferencia sobre protocolos cuando hacían debates sobre XML-RPC y REST...

"En ocasiones uso sockets."

Pues bien... aún así estas dos funciones aumentarían la velocidad de usar solo sockets, y podrían en un determinado momento ayudaros mucho (como a mí cuando las terminé) para optimizar lo que enviéis... como os dé la gana.
Aunque se pueden usar otros algoritmos de compresión, GZIP es el de mejor relación de compresión/computación, y no tienes que añadir ninguna librería. Te permitirán aumentar la eficiencia en el envío o almacenamiento bastante así que ahí van:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
...


/**
* Obtiene el array de bytes comprimido a partir de otro array de bytes
* que se quiere comprimir.
* @param file los datos descomprimidos
* @return los datos comprimidos.
* @throws IOException de vez en cuando
*/
public byte[] comprimirGZIP(byte[] file) throws IOException {
ByteArrayOutputStream gzdata = new ByteArrayOutputStream();
GZIPOutputStream gzipper = new GZIPOutputStream(gzdata);
ByteArrayInputStream data = new ByteArrayInputStream(file);
byte[] readed = new byte[1024];
int actual = 1;
while ((actual = data.read(readed)) > 0) {
gzipper.write(readed, 0, actual);
}
gzipper.finish();
data.close();
byte[] compressed = gzdata.toByteArray();
gzdata.close();
return compressed;
}

/**
* Obtiene el array de bytes descomprimido a partir de otro array de bytes
* comprimido
* @param file los datos comprimidos
* @return los datos descomprimidos.
* @throws IOException de vez en cuando
*/
public byte[] descomprimirGZIP(byte[] file) throws IOException {
ByteArrayInputStream gzdata = new ByteArrayInputStream(file);
GZIPInputStream gunzipper = new GZIPInputStream(gzdata, file.length);
ByteArrayOutputStream data = new ByteArrayOutputStream();
byte[] readed = new byte[1024];
int actual = 1;
while ((actual = gunzipper.read(readed)) > 0) {
data.write(readed, 0, actual);
}
gzdata.close();
gunzipper.close();
byte[] returndata = data.toByteArray();
csvdata.close();
return returndata;
}

domingo, 28 de noviembre de 2010

Integrar el pago electrónico con paypal en java

Es increíble lo complicado que a veces puede resultar encontrar información sencilla sobre algo concreto. A la hora de la integración de una aplicación con sistemas de pago electrónico puedes encontrar algunos de los mejores ejemplos. Lo que voy a explicar es producto de un número demasiado elevado de horas buscando, leyendo, consultando y quebrándome la cabeza.

Y no solo con el caso específico de PayPal, sino con el resto de plataformas de pago con las que me he encontrado. Estoy pensando seriamente en crear un grupo en facebook "En contra de las explicaciones ambiguas para integrar plataformas de pago".

Bueno pues lo primero es saber si es complicado lo que tienes que hacer, porque lo que voy a contar es un pago estandar, es decir, una vez que en tu app ya sabes la cantidad que hay que pagar, quieres que se haga. Si quieres hacer pagos o gestionar tus cobros, o cosas más complejas con la interfaz SOAP, no es este tu artículo, jeje... Lo que vas a encontrar es lo que llaman:

  • Pago Express
  • Interfaz para NVP (abreviado de name-value pair)
  • Pago Estándar

Siempre hay un entorno de pruebas, como ya esperabas, y que funcionará exactamente igual que en el entorno real. Esta es una de las diferencias (entre otras) con plataformas con las que he trabajado y PayPal.

En esta última, el proceso que en principio creemos sencillo, que es el de obtener un número de tarjeta ficticio que sea aceptado por la validación de la tarjeta en la plataforma de pruebas, me implicó crear una cuenta ficticia de cliente en Paypal Sandbox y asociarle tarjetas.

Después de mucho clickar y urgar por el sandbox, trás intentar comprender que tipo de pago quiero realizar (..ufff) o ver mil veces el esquema de pago, tienes que tener una cuenta de empresa (o también llamado comerciante) e ir al menú "Perfil" para encontrar lo que buscas.
  • Preferencias de notificación de pago instantánea (IPN)
  • Configuración de pago codificado

1. Preferencias de notificación de pago instantánea IPN

En la primera opción tienes que indicar la url a la que se notifica el pago mediante petición POST. Esto quiere decir que cuando el pago se confirma, desde paypal se hará una solicitud a esa url que pongas, e incluirá además de una copia de los valores en la petición que mandamos desde nuestra web (desde el navegador del usuario) algunos parámetros más como el identificador de operación, que es el que a ti te interesa. ¿Problemas con el IPN? Se pueden deber a varios factores:

  • El pago no se confirma: Esto ocurre con cantidades elevadas, paypal desde hace poco está obligado a retener las cantidades elevadas (no recuerdo ahora el intervalo exacto) y las contrasta. Tu tendrás que acceder a tu cuenta de paypal (del sandbox) y confirmar el pago manualmente. Probablemente ya te habrá caducado la sesión, y tendrás que tirarte un rato logeandote de nuevo, pero de tanto hacerlo, tienes la contraseña grabada a fuego en tu cerebro... ¿verdad? :P
  • La url de respuesta no es correcta: Claro suele pasar... cámbiala

2. Configuración de pago codificado

En la segunda opción, configuras el certificado que vas a usar para "firmar un botón". Esta impresionante acción, como todas, al final no es para tanto y no es más que tomar una cadena con la información del pago, firmarla, y añadir el texto de la firma (...en PEM) a un formulario que se dirija a Paypal y que los navegantes presionan en tu página cuando quieren realizar el pago. Y con esto pues ya está, ahora:
  • Te descargas El SDK para java, descomprimes e incluyes las librerías en tu aplicación en /WEB-INF/lib
  • Descárgate todos los jars de las librerías criptográficas bouncycastle latest releases e incluyes las librerías en tu aplicación en /WEB-INF/lib
  • Creas tus certificados para firmar e identificarte con paypal: Para esto necesitas instalar OpenSSL (en ubuntu con "sudo apt-get install openssl"... en windows... no lo sé...) y ejecutar:

    #Generas la clave privada, el CSR
    openssl genrsa -out prvkey.pem 1024
    #La auto firmas y obtienes un certificado público
    openssl req -new -key prvkey.pem -x509 -days 365 -out pubcert.pem
    #Generas un almacen de certificados pkcs12 con ellos
    openssl pkcs12 -export -inkey prvkey.pem -in pubcert.pem -out prvkey.p12


    Después de todo esto te aparecen un montón de ficheros de los cuales solo te hace falta el almacén de certificados prvkey.p12 y el certificado de paypal que debes descargarlo desde paypal en la opción de "Configuración de pago codificado".

Pues nada, ten a buen recaudo la contraseña para el acceso al almacen de certificados y el archivo p12 por que ahora viene cuando se usa.

Realización del pago, datos del pago y firma

Bueno pues ya estás en tu aplicación y estás desarrollando la parte en la que se realiza el pago, entonces, tu objetivo va a ser crear una página donde se indique al cliente lo que va a pagar y se muestre un botón que ponga "Pagar con Paypal". El código HTML del formulario lo pones tú, pero asegúrate de que tienes dos campos:

<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="encrypted" value="<%= signedPaymentStr %>" />


que se envían a la url que te pasan desde paypal (algo de documentación tendrás que leer).

Los datos del pago van en la cadena firmada del campo encrypted, y básicamente tendrás que rellenarlos creando una cadena con algunos parámetros. Aquí os dejo el código de un metodito que devuelve directamente la cadena que buscamos. Cópialo, pégalo y míra el Javadoc, te resultará fácil. Ten en cuenta que el pago está configurado para EUROS!!...



import com.paypal.wpstoolkit.util.PPCrypto;
import java.util.Properties;
import org.apache.log4j.Logger;

.....
.....



/**
*
* Obtiene la cadena del botón encriptada para una compra. Una cadena de
* ejemplo sería:
*
* cert_id=...
* cmd=_xclick
* business=....
* item_name=Lorem Ipsum Dolor sit amet...
* item_number=1234
* custom=sc-id-789
* amount=500.00
* currency_code=EUR
* tax=41.25
* shipping=20.00
* address_override=1
* address1=Calle inventada
* city=Ciudad
* state=Estado
* zip=23452345
* country=ES
* no_note=1
* cancel_return=http://url.de.retorno
* @param paymentData Variables adicionales que se quieran adjuntar.
* @param concepto Descripción de concepto por el que se va a pagar.
* @param codigoVenta Identificador de la venta
* @param cantidad Cantidad en euros que se va a cobrar
* @param impuesto Cantidad que se va a cobrar de impuestos
* @param direccion Dirección del pagador
* @param numero numero de la direccion postal
* @param cp Codigo postal
* @param localidad Localidad de la dirección postal
* @param pais País de la dirección postal
* @param nombre Nombre del pagador
* @param apellidos Apellidos del pagador
* @param pruebas Booleano que indica si se dirigirá al entorno de pruebas o no
* @return cadena de la firma de los datos.
* @throws Exception
*/
public String getButtonEncryptionValue(String paymentData,
String concepto, String codigoVenta, Float cantidad, Float impuesto,
String direccion, String numero, String piso, String puerta,
String cp, String localidad, String pais, String nombre,
String apellidos, String email, Boolean pruebas) throws Exception {

Properties oProps = new Properties();
ClassLoader oCL = Thread.currentThread().getContextClassLoader();

InputStream oIStr = oCL.
getResourceAsStream("archivodeconfiguracionenclasspathquesiempreexiste.properties");
oProperties.load(oIStr);
oIStr.close();

String env = ".sandbox";
if (!pruebas) {
env = "";
}

String signedStr = null;

String _keyPass = "password_que_tienes_que_poner_bien";
String user = "userquetienesqueponerbien";
String certId = oProps.getProperty("propiedad_con_el_valor_que_te_da_paypal_al_registrarte");
String returnURL = oProps.getProperty("propiedad_url_de_vuelta_en_navegador_despues_de_pagar");
String returnCURL = oProps.getProperty("propieda_de_la_url_si_cancela_el_pago");
if (piso == null) {
piso = "";
}
if (puerta == null) {
puerta = "";
}
if (_data == null) {
_data = "";
}
if (numero != null && numero.length() > 0) {
direccion += " nº " + numero;
}
if (piso != null && piso.length() > 0) {
direccion += " - " + piso + "º";
}
if (puerta != null && puerta.length() > 0) {
direccion += " " + puerta;
}

paymentData += "cert_id=" + certId + ","
+"item_name=" + concepto.replaceAll(",", "") + ","
+"item_number=" + codigoVenta + ","
+"amount=" + String.format("%1.2f", cantidad).replaceAll(",", ".") + ","
+"tax=" + String.format("%1.2f", impuesto).replaceAll(",", ".") + ","
+"address_override=1,"
+"address1=" + direccion.replaceAll(",", "") + ","
+"city=" + localidad + ","
+"zip=" + cp + ","
+"country=" + pais + ","
+"first_name=" + nombre + ","
+"last_name=" + apellidos + ","
+"payer_email=" + email + ","
+"cmd=_xclick,"
+"business=" + user + ","
+"currency_code=EUR,"
+"lc=" + pais + ","
+"cbt=Volver a la página,"
+"return=" + returnURL + ","
+"cancel_return=" + returnCURL + "";

logger.info("Encoding data: " + paymentData);

paymentData = paymentData.replace(',', '\n');
try {
signedStr = new String(PPCrypto.getButtonEncryptionValue(paymentData.getBytes("UTF-8"),
oProps.getProperty("ruta_al_archivo_p12"),
oProps.getProperty("ruta_al_certificado_de_paypal"),
_keyPass));
} catch (Exception ex) {
logger.error("Excepcion firmando: " + ex.getMessage());
}
return signedStr;
}

domingo, 6 de junio de 2010

El usuario de una petición WS con Axis y WSS4J

Hace tiempo que andaba buscando la manera de obtener las credenciales de identificación enviadas en una petición a un Web Service.

El problema viene dado por la necesidad de asegurar el acceso a los métodos que se ofrecen por XML-RPC y posteriormente en su implementación, identificar al usuario, pero resulta que Axis 1.4 (en su configuración más.. cómoda), realiza el despliegue de un servicio a partir de un POJO especificado en el wsdd, y entonces ¿Como podemos hacer referencia al contexto del mensaje recibido?

Pues todo viene a partir de la clase MessageContext de Axis. En cualquier momento de una petición, está disponible la llamada a dicho objeto, y a su método estático getCurrentContext mediante el cual tendremos acceso al mensaje objeto de la petición realizada.

La identificación del usuario ya se realiza por parte de WSS4J, y ahora nosotros queremos asociar los datos enviados al usuario que se identificó, de forma que en estos POJO's podremos escribir el siguiente código:


String username = null;
Iterator<SOAPHeaderElement> oItHeaderEnv = MessageContext.
        getCurrentContext().getMessage().getSOAPHeader().
        examineAllHeaderElements();
while (oItHeaderEnv.hasNext()) {
    Iterator<MessageElement> oItHeaderElems = oItHeaderEnv.next().
         getChildElements();
    while (oItHeaderElems.hasNext()) {
        Iterator<MessageElement> oItParams = oItHeaderElems.next().
            getChildElements();
        while (oItParams.hasNext()) {
            MessageElement oWSSParam = oItParams.next();
            if("Username".equals(oWSSParam.getName())) {
                username = oWSSParam.getValue().trim();
                break;
            }
        }
    }
}


Este código nos permitirá acceder al nombre de usuario enviado, o a cualquier otro parámetro enviado en la cabecera Security.

Otra de las opciónes, es la implementación de sesiones de Axis, ya que en la misma PasswordCallbackClass del Handler de WSS4J, podríamos implementar el inicio de la sesión, y después recuperarla en el método ofrecido por XML-RPC, previa configuración del manejador de sesiones de Axis en el wsdd.

lunes, 27 de julio de 2009

Montar GWT-Ext en Netbeans

Requisitos

Todo esto ha sido probado con el siguiente software y con las versiónes mencionadas a continuación, los cuales habrá que descargar:

  1. Netbeans (version 6.7)
  2. GWT (version linux-1.6.4)
  3. Gwt-Ext (versión 2.0.6)
  4. Ext (v2.0.2 - es la version que soporta GWT-Ext 2.0.6)

Proceso

  1. Instalar el Plugin que soporta GWT desde Netbeans
  2. Instalar la librería Gwt-Ext en Netbeans.
  3. Iniciar un proyecto GWT
  4. Agregar librería Javascript ExtJS al proyecto
    1. Modificación del archivo: welcomeGWT.html
    2. Modificación del archivo: Main.gwt.xml
  5. Compilar y corregir errores de compilación en GWT4NB
    1. Desde Netbeans
    2. Desde nuestro Proyecto
  6. Prueba

Proceso detallado de creación de un proyecto GWT-Ext

Instalar el Plugin que soporta GWT desde Netbeans

  1. Vamos a Netbeans y en el menu de arriba buscamos: Tools > Plugins > pestaña Available Plugins, y en el casillero de busqueda escribimos para filtrar: GWT y seleccionamos el plugin GWT4NB
  2. Si no lo encontramos, podemos descargar el .nbm desde aqui y después desde Tools > Plugins > pestaña de Downloaded buscamos el archivo
  3. Después descomprimimos GWT en la carpeta que queramos, por ejemplo en /home/juan/gwt-xxx-1.5.x….

Instalar la librería Gwt-Ext en Netbeans

Descomprimimos Gwt-Ext y creamos una carpeta dentro de la carpeta de GWT con el nombre lib y copiamos gwtext.jar ahí (/home/juan/gwt-xx../lib/gwtext.jar) y después la agreguamos al proyecto así:
  1. Hacemos click Tools > Libraries. Se abrira una ventana y
  2. Elegimos New Library, ponemos un nombre representativo como gwtext y Library Type lo dejams como esta (Class Libraries). Le damos al ok.
  3. Le damos a Add JAR/Folder y buscamos el archivo gwtext.jar dentro de la carpeta que descomprimimos (/home/juan/gwt-xx/lib/gwt-ext/gwtext.jar)

Le damos al ok hasta terminar y listo

Iniciar un Proyecto GWT

Vamos a crear un nuevo proyecto en Netbeans con GWT-EXT una vez hecho todo lo anterior.

  1. Elegimos:Web > Web Application Le ponemos un nombre y le damos Siguiente/Next,
  2. Elegimos el servidor, la versión de jvm y el context path, y damos a Siguiente/Next.
  3. En Framework elegimos Google Web Toolkit y abajo:
    1. En GWT Installation Folder ponemos la ruta donde descomprimimos gwt (/home/juan/gwt-xx…)
    2. En GWT Module ponemos el nombre de la clase donde implementaremos el punto de entrada a la aplicación. Es decir, la página de bienvenida para la aplicación, por lo que deberíamos poner el nombre de paquete que vayamos a utilizar, y un nombre de clase como Main, Index, o Welcome.. a vuestro gusto. yo por ejemplo prueba.gwt.Main.
  4. Una vez creado, desplegamos el proy ecto, y hacemos click derecho sobre Libraries > Add Library, y añadimos la librería creada anteriormente como gwtext.

Listo para jugar con GWT.

Agregar la librería Javascript ExtJS

Descomprimimos Ext. Vamos a copiar los archivos necesarios en una carpeta dentro de la carpeta web de nuestro proyecto creada como js/ext… algo parecido a: /home/juan/NetBeansProjects/ProyectoGWT/web/js/ext, y los archivos que tenemos que copiar de la carpeta de origen a esta son:
  • ext-all.js
  • ext-all-debug.js
  • ext-core.js
  • ext-core-debug.js
  • /resources (carpeta completa)
  • /adapter (carpeta completa)
  • /build/locale/ext-lang-es.js

Una vez hecho esto tenemos que hacer unas modificaciones al proyecto para que use estos scripts.

Modificación del archivo: welcomeGWT.html

En Netbeans dentro de nuestro proyecto vamos a Web pages y abrimos el archivo:welcomeGWT.html. Ese archivo tendrá que incluir en las cabeceras los scripts de ExtJS que acabamos de agregar al proyecto, así tendremos que agregar dentro de la entiqueta <head> las siguientes líneas.

<link rel="stylesheet" type="text/css" href="js/ext/resources/css/ext-all.css">
<script type="text/javascript" src="js/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="js/ext/ext-all.js"></script>
<script type="text/javascript" src="js/ext/ext-lang-es.js"></script>

Modificación del archivo: Main.gwt.xml

Vamos ahora a los Source Packages y buscamos en el paquete que especificamos como Gwt Module (prueba.gwt en mi caso) el archivo de configuración de gwt que el plugin ha creado Main.gwt.xml (en mi caso) y abrimos el archivo para editarlo, agregando esta linea en la etiqueta module:

Quedaría algo así:

<inherits name="com.google.gwt.user.User">
<inherits name="com.gwtext.GwtExt">
<entry-point class="org.yournamehere.client.MainEntryPoint">
<!-- Do not define servlets here, use web.xml -->
</module>

Compilando y corrigiendo error en el plugin GWT4NB

Cuando compilemos probablemente (al menos con las versiónes de Netbeans 6.7, Ext-2.0.2, Gwt-Ext 2.0.6 y gwt-linux-1.6.4) dará un error en el archivo /home/juan/NetbeansProjects/ProyectoGwt/nbproject/build-gwt.xml (en mi caso recordad) del tipo java returned: -1 y de deprecación de la clase com.google.gwt.dev.GWTCompiler. Podemos modificar este archivo. Una vez que nos da el error, Netbeans va a resaltar donde esta así es que le hacemos click para que nos lleve hasta alli, donde vamos a ver este codigo:

<java classpath="${javac.classpath}:${src.dir}" failonerror="true"
classname="com.google.gwt.dev.GWTCompiler" fork="true" jvmargs="${gwt.compiler.jvmargs}">
<arg value="-out">
<arg path="${build.web.dir}/">
<arg value="-style">
<arg value="${gwt.compiler.output.style}">
<
<arg value="${gwt.compiler.logLevel}">
<arg value="${gwt.module}">
</java>

Tiene que quedar igual que este:

<java classpath="${javac.classpath}:${src.dir}" failonerror="true"
classname="com.google.gwt.dev.Compiler" fork="true" jvmargs="${gwt.compiler.jvmargs}">
<arg value="-war">
<arg path="${build.web.dir}/">
<arg value="-style">
<arg value="${gwt.compiler.output.style}">
<arg value="-logLevel">
<arg value="${gwt.compiler.logLevel}">
<arg value="${gwt.module}">

<jvmarg value="-Xmx512m">
</java>

Que sustituye al parámetro en el tag El principal inconveniente es que algunas veces (con Netbeans 6.1) tras hacer un Clean & Build, el archivo volvía a su estado original y volvía a fallar. Otra forma de solucionar esto esta en el archivo de nuestro proyecto:/home/juan/NetbeansProjects/ProyectoGwt/nbproject/gwt.properties, donde se definen todos los parámetros y donde podremos asignar:

gwt.compiler.class=com.google.gwt.dev.Compiler
gwt.compiler.jvmargs=-Xmx512m

Y hacer la referencia a ella en build-gwt.xml mediante el siguiente código en build-gwt.xml.

<java classpath="${javac.classpath}:${src.dir}" failonerror="true"
classname="${
gwt.compiler.class}" fork="true" jvmargs="${gwt.compiler.jvmargs}">
<arg value="-war">
<arg path="${build.web.dir}/">
<arg value="-style">
<arg value="${gwt.compiler.output.style}">
<arg value="-logLevel">
<arg value="${gwt.compiler.logLevel}">
<arg value="${gwt.module}">

<jvmarg value="-Xmx512m">
</java>

Eso nos permite que en la próxima compilación, asigne más memoria al proceso de compilación y no falle...Y con esto y un bizcocho pues comele a la burra el… Espero que os funcione…