Noticias Weblogs Foros Wiki Código

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin

Archivos

xailer.info (esp)

Nueva versión de Xailer 2

Julio 3rd, 2009

Estimados usuarios de Xailer,

Ya está disponible una nueva versión de Xailer 2 Beta en la cual hemos intentado subsanar todos los problemas econtrados (de momento) por el rediseño de los ‘Datacontrols’ y además incluimos un nuevo control TListView que había sido solicitado desde hace mucho tiempo.

Se han incuido varios ejemplos para demostrar la potencia del control TListView. Os dejo unas imagenes de los mismos:

2009-07-03_143231.png 2009-07-03_143302.png 2009-07-03_143341.png

 

No obstante, aún falta por realizar una mejor integración del control en el IDE y su documentación, que seguro estará para la próxima publicación de Xailer 2.

Podéis encontrar la nueva versión de Xailer 2 Beta en el siguiente enlace:

http://www.xailer.com/files/xailer20beta.exe

También podéis descargar la versión xHarbour, que no ha cambiado desde la anterior publicación, desde la siguiente dirección:

http://www.xailer.com/files/xharboursetup2.exe

Os recordamos que está versión de Xailer sólo está disponible para usuarios con suscripción activa. Leer el artículo Novedades de Xailer 2.0 para más información.

Un cordial saludo,

[El equipo de Xailer]

» Leer más, comentarios, etc...

xailer.info (esp)

Componentes externos (II)

Julio 3rd, 2009

En esta ocasión aprenderemos qué son los editores de propiedades, cómo usarlos en nuestros componentes y desarrollaremos un sencillo editor de propiedades paso a paso.

Un editor de propiedades es una extensión del IDE que permite manipular una propiedad publicada de un componente en tiempo de diseño y ofrece una manera sencilla de establecer y/o recuperar su valor, haciendo más cómoda la utilización y configuración de un componente.

Los editores de propiedades son una poderosa herramienta que puede marcar la diferencia entre un componente aceptable y uno realmente formidable. A través de ellos podremos dotarlos de nuevas características que van más allá de la simple validación del valor de las propiedades, pudiendo mostrar una lista de valores, acotar valores, mostrar formularios que permitan la asignación del valor de una o varias propiedades, etc.

Para introducir el valor de las propiedades en tiempo de diseño se utiliza el inspector de objetos, el cuál muestra una columna con el nombre de la propiedad y otra con el valor de dicha propiedad. Su uso es sencillo, se introduce un nuevo valor y listo. ¿Fácil, eh? Aunque externamente parece algo trivial, internamente, el inspector de objetos se comunica con los componentes usando sus editores de propiedades para obtener o fijar el valor, para informar de cómo se debe almacenar una propiedad, etc.

Editores estándar

Xailer incluye una serie de editores de propiedades por defecto que son suficiente para la mayoría de las propiedades con las que trabajamos habitualmente, pero también permite que creemos nuestros propios editores, derivemos de ellos y que incluso reemplacemos los que una determinada propiedad tiene por defecto.

PE_Color
PE_Icon
PE_Anchors
PE_Font
PE_Cursor
PE_Brush
PE_StringOrNil
PE_ExtendedString
PE_Picture
PE_StringList
PE_NumberList
PE_MultiList
PE_Control
PE_Component
PE_ImageList
PE_Menu
PE_Edit
PE_DataField
PE_BrowseFolder
PE_BrowseFile
PE_BrwRecSel
PE_ButtonBitmap
PE_Tooltip
PE_DbfField
PE_ADOConnect
PE_DataSourceTables
PE_DbfDataSetName
PE_WideString
PE_DbfDataSource
PE_SQLiteDataSource
PE_DataSourceCatalogs
PE_TreeView

El editor básico TPropertyEditor

Estos editores derivan de la clase básica TPropertyEditor que provee el interface para que cualquiera de ellos se comunique con el inspector de objetos y el IDE.

  1. CLASS TPropertyEditor
  2.  
  3.    DATA oObj
  4.    DATA cProperty
  5.    DATA aProperty
  6.    DATA nProperty
  7.    DATA cType
  8.    DATA cDataType
  9.    DATA Value
  10.    DATA cValue
  11.    DATA Default
  12.    DATA aValues
  13.    DATA lRun      INIT .F.
  14.    DATA lExpand   INIT .F.
  15.  
  16.    METHOD Get( oObj, nProperty )
  17.    METHOD Set( Value, nIndex )
  18.    METHOD Load( cValue )
  19.    METHOD Save( cPrefix, cSufix )
  20.    METHOD DblClick( Value ) VIRTUAL
  21.    METHOD Run( Value )
  22.  
  23. ENDCLASS

* DATA cProperty
Contiene el nombre de la propiedad inspeccionada

* DATA cType
Es el tipo de dato del valor actual de la propiedad

* DATA cDataType
Es el tipo de dato indicado en la cláusula AS … de la declaración de la propiedad

* DATA Value
El valor actual de la propiedad

* DATA cValue
Es el valor actual de la propiedad en formato cadena para mostrar en el inspector

* DATA Default
Es el valor por defecto para la propiedad (cláusula INIT de la declaración)

* DATA aValues
Es el array de valores para mostrar en un combo en el inspector. Es un array de dos dimensiones (nºelementos x 2 columnas), donde la primera columna es el valor de cadena a mostrar en el combo del inspector, y la segunda columna es el valor real que hay que asignar a la propiedad.

* DATA oObj
Contiene una instancia del objeto al que pertenece la propiedad

* DATA lRun
Indica si se muestra el botón “…” cuando la propiedad tiene el foco.

* DATA lExpand
Muestra el pequeño triángulo naranja en el inspector que sirve para abrir un formulario con un memo con el valor de la propiedad.

* METHOD DblClick( @Value )
Se ejecuta al hacer doble click en el inspector y recibe el valor actual de la propiedad por referencia. Si no se define este método, por defecto, llama a Run().

* METHOD Run( @Value )
Se encarga de mostrar el interface de usuario si la propiedad lo requiere y se ejecuta al pulsar el botón “…”. También recibe el valor actual de la propiedad por referencia. Debe devolver .T. para actualizar la propiedad y .F. para dejar su valor anterior.

* METHOD Get( oObj, nProperty )
Obtiene el valor asignado a la propiedad | del objeto y asigna el resto de las datas del editor de propiedades (cProperty, cType, Value, etc.). Siempre conviene llamar a Super:Get(…)

* METHOD Set( Value, nIndex )
Asigna el valor a la propiedad correspondiente del objeto inspeccionado. sólo tiene valor cuando en el inspector aparece un combo para cambiar el valor de la propiedad. Siempre conviene llamar a Super:Set(…)

* METHOD Load( cValue )
Asigna el valor de la propiedad a partir de la cadena leida desde el fichero .xfm

* METHOD Save( cPrefix, cSufix )
Devuelve la cadena que hay que guardar en el fichero .xfm a partir del valor de la propiedad

Escribir un editor de propiedades

Vamos a desarrollar nuestro primer editor de propiedades, que será editable sobre el propio inspector de objetos y también mostrará un formulario para cambiar el valod de la propiedad.

Los pasos necesarios para escribir un editor de propiedades son los siguientes:

* Crear una nueva clase que descienda de TPropertyEditor o de algún editor estándar.
* Desarrollar los métodos necesarios para dotar al editor de propiedades de la funcionalidad deseada.

Como norma general, se suele anteponer el prefijo PE_ a las clases que definen un editor de propiedades, por ejemplo: PE_MyColorEditor, aunque no es obligatorio.

Creando nuestro primer editor

Imaginemos que tenemos un componente con una propiedad que guarda una dirección de correo electrónico:

  1. CLASS TMyComponent FROM TComponent
  2.  
  3. PUBLISHED:
  4.    PROPERTY cEmail INIT ""
  5.  
  6. ENDCLASS

La opción más sencilla cuando definimos el componente sería dejarlo como está en el ejemplo pero deberíamos pensar en hacer la vida más fácil a quien vaya a usarlo, así que vamos a establecer un editor que haga una validación básica de la propiedad.

  1. CLASS PE_EmailEditor FROM TPropertyEditor
  2.  
  3. PUBLIC:
  4.    DATA lRun   INIT .T.
  5.  
  6.    METHOD Run( Value )
  7.    METHOD DblClick( Value ) INLINE ::Run( @Value )
  8.  
  9.    METHOD MailValidate( oSender )
  10.  
  11. ENDCLASS

En la definición del editor establecemos lRun a .T. para indicar que la propiedad debe mostrar el botón de acción (los puntos “…”) y hacemos que se lance la misma acción al hacer doble click que al pulsar el botón de acción.

  1. METHOD Run( Value ) CLASS PE_EmailEditor
  2.  
  3.    LOCAL oForm
  4.    LOCAL oEditMail
  5.    LOCAL lReturn := .F.
  6.  
  7.    WITH OBJECT oForm := TMailEditForm():New( Application )
  8.       :SetBounds( 235, 197, 302, 145 )
  9.       :SetClientSize( 294, 118 )
  10.       :cText := "Email PropertyEditor"
  11.       :nBorderStyle := bsDIALOG
  12.       :lHideOnClose := .T.
  13.       :Create()
  14.    END
  15.  
  16.    WITH OBJECT TLabel():New( oForm )
  17.       :SetBounds( 12, 12, 27, 14 )
  18.       :cText := "EMail"
  19.       :Create()
  20.    END
  21.  
  22.    WITH OBJECT oEditMail := TEdit():New( oForm )
  23.       :SetBounds( 12, 28, 270, 20 )
  24.       :Create()
  25.    END
  26.  
  27.    WITH OBJECT TButton():New( oForm )
  28.       :SetBounds( 12, 80, 80, 25 )
  29.       :cText := "&Validate"
  30.       :lDefault := .T.
  31.       :OnClick := "MailValidate"
  32.       :Create()
  33.    END
  34.  
  35.    WITH OBJECT TButton():New( oForm )
  36.       :SetBounds( 112, 80, 80, 25 )
  37.       :cText := "Ok"
  38.       :nModalResult := mrOK
  39.       :Create()
  40.    END
  41.  
  42.    WITH OBJECT TButton():New( oForm )
  43.       :SetBounds( 204, 80, 80, 25 )
  44.       :cText := "Cancel"
  45.       :nModalResult := mrCANCEL
  46.       :Create()
  47.    END
  48.  
  49.      IF :ShowModal() == mrOK
  50.         IF ::MailValidate( oEditMail:Value )
  51.            Value := oEditMail:Value
  52.            lReturn := .T.
  53.         ENDIF
  54.         :End()
  55.      ENDIF
  56.    END
  57.  
  58. RETURN lReturn
  59.  
  60. METHOD MailValidate( oSender ) CLASS PE_EmailEditor
  61.  
  62.    LOCAL lValid := .F.
  63.  
  64.    //Comprobamos que incluye la arroba y el punto
  65.    IF Empty( oSender:Value ) .OR. At( "@", oSender:Value ) > 0 .OR. At( ".", oSender:Value ) > 0
  66.       lValid := .T.
  67.    ENDIF
  68.  
  69. RETURN lValid

Este sencillo editor muestra un formulario con un TEdit para introducir la dirección de email y tres botones para validar la dirección y para Aceptar o Cancelar el formulario.

Tras cerrar el formulario, volvemos a comprobar si la dirección es correcta llamando a ::MailValidate() y sólo actualizamos la propiedad y devolvemos .T. en caso de que la validación sea correcta.

Sólo nos queda enlazar a nuestra propiedad con su nuevo editor, así que la declaramos:

  1. CLASS TMyComponent FROM TComponent
  2.  
  3. PUBLISHED:
  4.    PROPERTY cEmail INIT "" EDITOR PE_EmailEditor
  5.  
  6. ENDCLASS

En el ZIP adjunto se encuentran todos los fuente del componente de ejemplo y una DLL ya compilada para probarla en el IDE (ver post)

» Leer más, comentarios, etc...

xailer.info (esp)

Componentes externos (I)

Julio 3rd, 2009

En la serie de artículos que empieza hoy, vamos a aprender paso a paso cómo crear un componente para Xailer 2.0 y cómo integrarlo en el IDE.

En este capítulo aprenderemos lo más básico para crear un nuevo componente, generar la DLL para el IDE y la librería estática (.LIB) para enlazar con las aplicaciones en las que queramos usarlo.

Lo primero que vamos a hacer es crear un nuevo proyecto, al que llamaremos MyFirstComponentDll, en la carpeta samples\MyFirstComponent.

Cuando el IDE muestre el formulario “Propiedades del proyecto”, seleccionaremos la opción DLL en “Tipo de proyecto” y haremos un pequeño cambio. En la opción carpetas, en la entrada Obj el IDE nos ofrecerá c:\xailer2\samples\MyFirstComponentDll\Obj como directorio por defecto, pero le añadiremos “Dll” a esta ruta, quedando c:\xailer2\samples\MyFirstComponentDll\ObjDll. Una vez hecho esto, aceptaremos el resto de opciones por defecto para continuar.

En estos dos pasos anteriores hemos generado el archivo de proyecto para construir la librería dinámica para el IDE. Lo siguiente será repetir estos pasos pero cambiando un par de cosas:

Creamos otro proyecto en el mismo directorio (esto es importante), donde hemos guardado el anterior, pero con otro nombre, esta vez le llamaremos “MyFirstComponent”.

Otra vez el IDE nos muestra el formulario “Propiedades del proyecto”, seleccionaremos la opción LIB en “Tipo de proyecto” y aceptamos.

Si los dos pasos anteriores se han completado correctamente, debemos tener dos archivos de proyecto “MyFirstComponentDll” y “MyFirstComponent” en la carpeta samples\MyFirstComponent.

Para no complicar demasiado este artículo, el componente que vamos a crear, TMyLabel, será un control derivado de TLabel y cuya única diferencia con la clase padre es que va a tener como color de fondo por defecto el blanco y no va a ser transparente.

Construir la librería

Una vez que ya tenemos el espacio de trabajo listo, podemos empezar con la definición del componente. Añadimos un nuevo módulo PRG (CTRL+N) y lo salvamos (CTRL+S) con el nombre MyLabel.prg

En este nuevo módulo desarrollamos la clase TMyLabel:

  1. CLASS TMyLabel FROM TLabel
  2.  
  3. PUBLISHED:
  4.    PROPERTY nClrPane       INIT clWhite
  5.    PROPERTY lTransparent   INIT .F.
  6.  
  7. ENDCLASS

Compilamos (F9) y si todo ha ido bien, el IDE habrá construído MyFirstComponent.Lib en la carpeta del proyecto.

Construir la DLL

Para terminar de construir el componente, abrimos el proyecto MyFirstComponentDll que creamos al principio y con Proyecto->Añadir archivo al proyecto, añadimos el archivo MyLabel.prg con la definición de la clase.

Creamos un nuevo módulo PRG (CTRL+N) y lo salvamos (CTRL+S) con el nombre MyLabelDll.prg y le añadimos el código que se encargará de registrar el componente en el IDE:

  1. INIT PROCEDURE InitDLL()
  2.  
  3.    RegisterComponent( "Samples", "mylabel", "TMyLabel", {|| TMyLabel() } )
  4.  
  5. RETURN


En el caso de un proyecto que tenga más de un componente, la única diferencia es que será necesario llamar a RegisterComponent() tantas veces como necesitemos, por ejemplo: supongamos que tenemos un proyecto que contiene tres controles tal que TMyLabel, TMyEdit y TMyButton, registrarlos es tan sencillo como:

  1. INIT PROCEDURE InitDLL()
  2.  
  3.    RegisterComponent( "Samples", "mylabel", "TMyLabel", {|| TMyLabel() } )
  4.    RegisterComponent( "Samples", "myedit", "TMyEdit", {|| TMyEdit() } )
  5.    RegisterComponent( "Samples", "mybutton", "TMyButton", {|| TMyButton() } )
  6.  
  7. RETURN


Por supuesto, recordando siempre que deberemos añadir al archivo de recursos una imagen por cada componente para identificarlos correctamente en la paleta del IDE.

Para completar la construcción del componente, necesitaremos una imagen que lo identifique en la paleta del IDE. Para ello abrimos el Gestor de recursos del menú Vista, pulsamos en el botón “Nuevo recurso” y aceptamos el nombre por defecto que nos ofrece el IDE.

Una vez creado el archivo de recursos, añadimos el bitmap que identificará al nuevo componente y cerramos el gestor de recursos.

Consideraciones

La función RegisterComponent() le indica al IDE el nombre de la paleta en la que colocar el componente, el nombre del bitmap, la tooltip que se mostrará al poner el puntero del ratón encima del componente y la función de clase que crea el componente y hay que llamarla siempre que queramos registrar un componente externo.

Como norma general, el nombre del procedimiento que registra el componente puede ser cualquiera, la única condición es que sea un INIT PROCEDURE.

Hay que recordar que el bitmap debe tener unas dimensiones de 24×24 para que se muestre correctamente en la paleta y en caso de que el componente no tenga ningún bitmap asociado, el IDE le asignará uno por defecto.

Es posible incluir información en el propio componente usando la opción Proyecto->Propiedades del proyecto->Versión. La información de número de versión, copyright, etc. que se introduzca será accesible luego desde el botón “Información…” del Gestor de componentes.

Una vez completados los pasos anteriores, compilamos (F9) el proyecto y se creará la DLL con el nuevo componente listo para ser usado desde el IDE.

Instalar el componente

Para utilizar el componente desde el IDE, seleccionamos la opción Componentes->Gestor de componentes del menú principal, pulsamos el botón “Añadir” y desde el cuadro de diálogo que se muestra, apuntamos a samples\MyFirstComponent para seleccionar la DLL que acabamos de construir.

Una vez que pulsemos en el botón Aceptar y comprobamos que la pestaña “Samples” ya tiene correctamente registrado el nuevo componente.

Utilizar el componente

En aquellos proyectos donde queramos utilizar el componente, deberemos informar al IDE para que enlace la librería con nuestra aplicación, así que abrimos el formulario “Propiedades del proyecto” y en la opción “Librerías” añadimos la librería que construimos al principio y que estará en samples\MyFirstComponent\MyFirstComponent.lib

Para probar el nuevo componente, creamos un nuevo formulario en un proyecto nuevo (o en uno de nuestros proyectos), seleccionamos la pestaña “Samples” de la paleta de componentes, pinchamos en el componente y lo soltamos en el formulario.

¡Funciona! Ahora habrá que probarlo también en tiempo de ejecución, así que F9 y …

Notas finales

Si se quiere hacer algún cambio en el código del componente y la DLL está cargada en el IDE, es importante no olvidar que antes de recompilar, hay que eliminarla con el Gestor de componentes, si no el enlazador nos dará un mensaje de error avisando que la DLL está siendo usada.

El ZIPadjunto incluye el proyecto completo para generar la librería y la DLL.

» Leer más, comentarios, etc...

Cerebro en la Sombra » Técnico

Webservices: Tratando con cabeceras SOAP en PHP

Julio 2nd, 2009

Llevo ya un tiempo bastante liado con webservices a los que debo llamar con PHP y hoy me ha tocado lidiar con cabeceras SOAP. La verdad es que es un mundo bastante oscuro y me he encontrado con muchas trabas. Os contaré cuales y cómo las he solucionado, pero veamos primero algo de teoría.

Los servicios web se han convertido en el principal modo de intercambio de  información entre aplicaciones independientemente de plataformas, sistemas operativos y lenguajes de programación. SOAP es uno de los protocolos sobre los que se realiza el intercambio de los datos y está basado en XML, de manera que la parte cliente interroga al servidor con un código XML en el formato adecuado y recibe la respuesta en otro XML. Para entender de qué estamos hablando veamos la estructura de una petición SOAP y su respuesta.

Llamada (request):

  1. version="1.0" encoding="UTF-8"?>
  2. xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="com.xplota.ws">
  3.     >
  4.         >
  5.             >1>
  6.             >>
  7.         >
  8.         >
  9.             >1>
  10.             >>
  11.         >
  12.         >
  13.             >1>
  14.             >>
  15.         >
  16.     >
  17.     >
  18.     >
  19. >

Respuesta (response):

  1. version="1.0" encoding="utf-8"?>
  2. xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  3.     >
  4.         xmlns="com.xplota.ws">
  5.             >0>
  6.             >Ok>
  7.         >
  8.     >
  9.     >
  10.  
  11.     >
  12. >

Como se puede ver en los listados anteriores, tanto el request como el response constan de dos nodos XML, header y body. El que se utiliza habitualmente es el body (que yo he dejado en blanco pues no nos interesa en este momento) y es el que contendría tanto los parámetros que se envían al webservice en el request como los que devuelve en el response.

Enviando headers soap

En el caso que nos ocupa debía enviar determinados parámetros en el header y leer de allí los potenciales códigos de error si los hubiese habido. El envío, pese a ser una estructura en vez de un parámetro simple, fue sencillo, se define una clase con los parámetros adecuados y se le envía directamente. El motor de SOAP de PHP se encarga de la traducción. Veamos un caso práctico.

  1. //definimos la clase para las cabeceras
  2. class wsHeader
  3. {
  4.     public $Code = 0;
  5.     public $Desc = ;
  6.  
  7.     public function __construct($code, $desc){
  8.         $this->Code=$code;
  9.         $this->Desc=$desc;
  10.     }
  11. }
  12.  
  13. //instanciamos el cliente soap
  14. $par=array();
  15. $client = new SoapClient("http://midominio.com/ws?wsdl", $par);
  16.  
  17. //añadimos las cabeceras a las peticiones
  18. $headers=array();
  19. $headers[] = new SoapHeader("com.xplota.ws", ‘entity’, new wsHeader(1, ));
  20. $headers[] = new SoapHeader("com.xplota.ws", ‘language’, new wsHeader(1, ));
  21. $headers[] = new SoapHeader("com.xplota.ws", ‘userId’, new wsHeader(1, ));
  22. $client->__setSoapHeaders($headers);
  23.  
  24. //lanzamos la llamada al metodo del ws
  25. $result = $client->TuMetodo($parametros);

Como veis es bastante sencillo de entender. Al añadir una cabecera hay que indicarle el namespace al que pertenece para que el motor SOAP sepa como tratarla, se le da un nombre y el objeto que la contiene.

Con esto hemos solucionado la parte del envío de nuestras cabeceras SOAP y tendremos un request como indicábamos en el primer XML.

Recibiendo headers SOAP

Ahora resulta que el método de nuestro webservice nos responde con otras cabeceras que debemos saber interpretar según el XML de response del segundo listado. Pues tenemos un problema y muy gordo. No hay forma de obtener estas cabeceras, el motor SOAP de PHP sólo devuelve el body, nunca los headers.

Según el manual de PHP el método __soapCall del cliente SOAP permite definir un array en el que se devolverán estas cabeceras, pero no fui capaz de hacer funcionar la invocación de un método del webservice con esta sintaxis mientras que invocándolos directamente en el cliente (cómo la documentación indica que se puede hacer) sí que me funcionaba perfectamente. Es decir, la teoría dice que con el primer método puedo recibir las cabeceras pero no me funcionó mientras que el segundo me funcionaba pero no me devuelve las cabeceras ni hay ningún método para recuperarlas.

Tras pelearme mucho con las funciones SOAP e investigar todavía más no llegué a ninguna conclusión, es como si no le hubiese pasado a nadie, no encontré absolutamente nada útil. Sólo me quedaba una solución, hacer mi propia clase SOAP a partir de la original y procesar el XML del response a mano para obtener los datos que necesitaba. Dicho y hecho. Veamos la solución.

Primero creo mi propia clase de SOAP y compruebo si voy a poder hacer lo que quiero.

  1. class XSoapClient extends SoapClient{
  2.     public function __construct($wsdl, $options){
  3.         parent::__construct($wsdl, $options);
  4.     }
  5.  
  6.     public function __doRequest($request, $location, $action, $version){
  7.         $response=parent::__doRequest($request, $location, $action, $version);
  8.         return $response;
  9.     }
  10. }
  11. $client = new XSoapClient("http://midominio.com/ws?wsdl", $par);

Parece que voy a tener suerte, si pruebo este nuevo cliente SOAP funciona perfectamente, pero además si compruebo el contenido de $response veo que contiene íntegramente el XML de la respuesta del webservice. Cómo veis lo único que cambia al instanciarlo es que le paso el nombre de la nueva clase. Buen comienzo, si juego bien mis cartas podré sacar las cabeceras en el método __doRequest :) .

Tratemos pues ese XML para obtener lo que buscamos. Gracias a las funciones DOM y XPATH de PHP será muy sencillo. Este es el resultado final de mi cliente SOAP con recuperación de cabeceras:

  1. class XSoapClient extends SoapClient
  2. {
  3.     private $responseHeaders = array();
  4.  
  5.     public function __construct($wsdl, $options){
  6.         parent::__construct($wsdl, $options);
  7.     }
  8.  
  9.     public function __doRequest($request, $location, $action, $version){
  10.         $response=parent::__doRequest($request, $location, $action, $version);
  11.  
  12.         $dom = new DOMDocument;
  13.         $dom->loadXML($response, LIBXML_NOWARNING);
  14.         $path = new DOMXPath($dom);
  15.         $path->registerNamespace(’soap’, ‘http://schemas.xmlsoap.org/soap/envelope/’);
  16.         $xml = $path->query(‘//soap:Header/*’);
  17.         $this->responseHeaders=$this->headers2array($xml);
  18.  
  19.         return $response;
  20.     }
  21.  
  22.     public function getResponseHeaders(){
  23.         return $this->responseHeaders;
  24.     }
  25.  
  26.     private function headers2array($response){
  27.         $headers=array();
  28.         foreach ($response as $node) {
  29.             if($node->hasChildNodes()){
  30.                 $headers[$node->nodeName]=$this->headers2array($node->childNodes);
  31.             }else{
  32.                 $headers[$node->nodeName]=$node->nodeValue;
  33.             }
  34.         }
  35.         return $headers;
  36.     }
  37. }
  38. "php">$client = new XSoapClient("http://midominio.com/ws?wsdl", $par);
  39. $result = $client->TuMetodo($parametros);
  40. $soapheaders=$client->getResponseHeaders();

Problema solucionado y de manera bastante elegante. Si alguien sabe cómo conseguir las cabeceras sin montar todo este lio que me lo cuente por favor.

» Leer más, comentarios, etc...

Blog de Julio César Pérez Arques

Cuanto vale un experto en programacion

Julio 2nd, 2009

Estamos acostumbrados a enfrentarnos a nuevos problemas por nosotros solos a diario. No nos queda otra. El aprendizaje a base de ostias investigación, prueba y error es una realidad por la que todos pasamos y que viene a cubrir las carencias de los sistemas de formación profesionales.



Pero muchas veces podemos dedicar días y días, incluso semanas, para dar con la solución. Se soluciona sí. Se aprende también. Pero no es eficiente. Por un lado, tiene un coste psicológico en la persona o equipo en forma de stress, desmotivación y quemado. Por otro, tiene también un coste económico en las cuentas del proyecto. Y hasta puede tener un coste en imagen si el cliente se entera o simplemente por entregar con retraso.



Contar con un experto podría resolver el problema de forma eficiente a la vez que mejoraría la formación del equipo. Por problema no sólo me refiero a un marrón, puede ser un conjunto de tareas importantes sobre las que el equipo no tiene aptitudes ni experiencia.



Conozco algunas compañías donde se cuenta con esa figura de experto al más puro estilo Señor Lobo de Pulp Fiction. Aunque son muy pocas y a veces el experto no es tan experto. Pero siempre está la opción de subcontratar a un verdadero experto para resolver el problema. En España no se lleva. Una pena.



Y todo este rollo al hilo de un sorprendente anuncio de Kent Beck en ebay, donde ofrece una sesión remota de programación en pareja de 2 horas. La sesión se hará mediante VNC para compartir la pantalla y Skype para comunicarse. Aunque puede ser para revisar código, las dos horas se pueden emplear en temas de diseño, testing o lo que se quiera. Va por 305 dolares pero aún faltan días para que se cierre la puja. Veremos como termina la cosa.

» Leer más, comentarios, etc...

Picando Código

Disponible revista Linux+ DVD julio 2009

Julio 2nd, 2009

Linux+ 7-8/2009 (56)
El tema central de este número es Seguridad y Ethical Hacking

Linux + DVD Julio 2009

Linux + DVD Julio 2009

En el DVD adjunto:

# Ubuntu 9.04 Jaunty Jackalope
La última versión de la distribución Linux más popular
Versión 32 bit, bootable

# BackTrack
Distribución basada en Slax con más de 300 herramientas de seguridad
Versiones 3.0 (estable) y 4 beta

También podréis leer los siguientes artículos:

BackTrack
Herramienta perfecta para ethical hacking

Xprobe2
Técnicas de fingerprinting

Criptografía de curva elíptica
Aplicaciones básicas de la criptografía ECC a nivel de usuario

Programación en Gambas
Acceso a base de datos SQLite

Historia del Hacking
¿Quiénes fueron los primeros hackers?

Formularios Web
Creación, validación y filtros con MUUU

Seguridad en Linux
¿Cómo protegernos?

¡¡¡ CONCURSO !!!

Toma parte en nuestro concurso y gana el libro Hacking Ethico por Carlos Tori y otros…

http://www.lpmagazine.org/prt/view/pag-prin/issue/1050.html

» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

JDeveloper 11g R1.

Julio 1st, 2009

A

» Leer más, comentarios, etc...

más que código - Articulo - Programación

PragPub

Julio 1st, 2009

La gente de The Pragmatic Programmer han lazado una revista digital: PragPub.

Portada de PragPub

Sólo he leído el índice así que no es que pueda decir si está bien o no, pero viniendo de quien viene, merece la pena echarle un vistazo.

Como curiosidad: Aparte del PDF, lo proporcionan en formatos de “libro electrónico” como epub y mobi. Los tiempos están cambiando.

» Leer más, comentarios, etc...

MadeInFlex

El principio de Inversion of control y Flex

Julio 1st, 2009

Inversion of Control o IoC, es un principio de la ingeniería del software. Este patrón que se ha puesto de moda actualmente gracias a frameworks como Swiz. En este artículo veremos indagaremos un poco en todos estos conceptos usando Swiz como base para el estudio...

(more...)

» Leer más, comentarios, etc...

más que código - Articulo - Programación

Libro: Founders at Work

Julio 1st, 2009

Portada de Founders at Work

Leyendo hoy la entrada sobre una entrevista al fundador de HOTorNOT en Pensamientos ágiles he recordado un libro de entrevistas a fundadores de empresa de tecnología que ley hace algún tiempo: Founders at Work

Es un libro que me gustó mucho y que recomiendo a cualquiera interesado en estos temas. En él se entrevista a muchísimos emprendedores, los fundadores de empresas como flickr, delicious, paypal, 37signals y así 33 entrevistas a cual más interesante.

Una cosa que me gustó mucho fue las distintas perspectivas que te ofrece toda esta gente: Ideas, escalabilidad, momentos bajos y altos, …

Se puede leer una buena parte del mismo en Google Books.

Eso sí, es un libro infeccioso. Leerlo y querer montarnos nuestra propia empresa, es todo uno :-)

» Leer más, comentarios, etc...

4 bits blog

Windows System Programming

Junio 30th, 2009

Windows System ProgrammingWindows System Programming es un libro de introducción a la programación sobre Windows (Win32, aunque hay una breve introducción a Win64), con el lenguaje C.

He de decir que este libro no me ha sorprendido mucho, ya que en cuanto llevas algo de tiempo programando sobre Win32 y visitando la MSDN a menudo, te acabas conociendo las partes más utilizadas. Además, este libro es bastante antiguo (del 2004).

En general, no me convence la forma de explicar los temas por parte del autor, casi todos los capítulos se limita a copiar las diferentes funciones que se utilizan para hacer lo que sea, y a explicar cada uno de sus parámetros. Por lo que el libro se transforma en una especie de resumen de la MSDN.

De todo el contenido del libro me quedaría con los capítulos sobre los servicios de Windows y las estructuras de seguridad (SIDs, ACLs, ACEs) de Windows.

Aún así, puede servir como punto de partida para empezar a programar sobre Windows utilizando Win32, si no fuera por las pegas que he comentado.

Recomendación: Baja.
Nivel: Medio.
Idioma: Inglés.

» Leer más, comentarios, etc...

programania

Sale la versión definitiva de PHP 5.3

Junio 30th, 2009

La blogosfera ya comienza a hacerse eco, y se puede descargar aquí. Recordemos que ésta versión trae mayor velocidad, nuevas características de POO, closures, namespaces, y todo de lo que ya hemos hablado por aquí.

Recordemos también que comienza la cuenta atrás para la llegada de Zend Framework 2.0


» Leer más, comentarios, etc...

Picando Código

[Video] – Hug a developer / Abraza a un desarrollador

Junio 29th, 2009

Creo que vi este video por primera vez en Microsiervos, pero lo encontré de nuevo hace poco recorriendo el archivo de Casi un buen blog.

Muchos probablemente ya lo hayan visto, tiene su tiempo. Pero vale la pena verlo de nuevo, seguro con algo se sentirán identificados si trabajan en la industria del software. Abajo la traducción de los comentarios…

Los desarrolladores en todos lados están sufriendo un dolor terrible.

Siento dolor

Vamos 4 meses de un cronograma de 5 meses y acabo de recibir los requerimientos finales ayer (¡y cambiaron de nuevo!)

Paso la mitad de mis días en reuniones sobre cómo terminar mas trabajo (en vez de estar trabajando)

Mi jefe leyó en una revista que los desarrolladores que usan el lenguaje de programación “______” son dos veces más productivos. Así que nos compró una coopia y recortó nuestras fechas a la mitad.

Todos los días mi jefe cambia de parecer sobre lo que estamos construyendo.

La gente me sigue pidiendo que les arregle el e-mail, así que no tengo tiempo de escribir código.

Mi papá no tiene más tiempo para mí.

Un consultor le dijo a mi jefe que podían construir nuestra próxima versión en la mitad del tiempo, por la mitad del dinero. Él le creyó pero ahora se gastaron todo su presupuesto, usaron todo su tiempo y…
Apenas terminaron la mitad. Ahora se han ido y su código es un desastre. Tenemos que arreglarlo y terminar lo que comenzaron.

Abraza a un desarrollador hoy.

Y el último, gigante para los programadores de Visual Basic un

Acabo de terminar un curso intensivo de 6 semanas de Visual Basic.

El video fue creado por Devshop una aplicación hosteada de gestión de proyectos diseñada para planear proyectos de software específicamente.

» Leer más, comentarios, etc...

MadeInFlex

Flex y SEO

Junio 29th, 2009

A continuación la presentación sobre Flex + SEO que Gonzalo Pezzi y yo dimos es la última reunión de MIF en Barcelona con motivo del Adobe Global Tour 2009.

La presentación trata sobre como tener los mismos beneficios que ya disfrutábamos con webs HTML tradicionales aplicados a una web Flex. Es decir, que el contenido sea indexable por buscadores, enlaces de profundidad ha contenidos, que tengamos habilitados los botones del navegador para ir adelante o atrás por las páginas de la web Flex y otros temas adicionales como es el multidioma, por poner un ejemplo.

La presentación se basa en la nueva web de Codeoscopic donde se implementan todos estos conceptos y que todavía estamos terminando de completar pero que ya vuestra a nivel técnico todas estas posibilidades.

También y como se anunció al final de la charla, pretendemos hacer la base de esta plataforma Open Source de forma que Codeoscopic empiece a realizar aportes a la comunidad dentro del ámbito OS.

Flex + SEO

» Leer más, comentarios, etc...

MadeInFlex

Fx4 I: Espacios de nombre

Junio 28th, 2009

En esta cuarta versión de Flex se han producido cambios considerables en la arquitectura de componentes así como en el lenguaje MXML. Esto se ha hecho para potenciar varios aspectos como son la productividad, facilitar el workflow designer / developer o la integración con herramientas externas.
En esta entrada se hará una introducción a los espacios de nombres de Flex 4.

(more...)

» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

Productividad y mejoras en JDeveloper 11g

Junio 26th, 2009

A

» Leer más, comentarios, etc...

Joan Garnet :: Arquitectura y desarrollo RIA

rialvalue.com/blog: Xavi Beumala

Junio 26th, 2009

Ayer me enteré de que nuestro querido exiliado Xavi Beumala tiene nuevo blog: http://www.rialvalue.com/blog/.
Con lo ocupado que anda no sé qué nivel de actualización tendrá pero estoy seguro que el contenido será excelente y de gran interés para todos los Flasheros / Flexeros.

» Leer más, comentarios, etc...

Picando Código

JUGUY: Evento Web 2.0 en Java EE y nuevo sitio web

Junio 26th, 2009

El JUGUY renueva su sitio web, y anuncia un evento en Montevideo.

Lanzamiento de la nueva Web del Juguy – www.juguy.org

Se lanza oficialmente la nueva página Web del Grupo de Usuarios – www.juguy.org

Ya se encuentra disponible y esperamos sus comentarios y críticas sobre la misma.

Mayor interacción
Esta nueva Web tiene como objetivo presentar el sitio mucho más simple y moderno, publicando noticias en forma más constante.

Mas eventos presenciales
Una nueva idea que el grupo promotor está encarando es la de generar mós y mejores eventos presenciales en distintos lugares como Universidades, Empresas, etc.
Pronto se publicará el calendario de eventos ya confirmados que tendremos en el 2009

Sitio del JUGUY renovado

Sitio del JUGUY renovado

El primero de estos eventos presenciales:

Evento – Web 2.0 en Java EE (frameworks y productividad)

Agenda

18:00 – Charla de presentación del evento
18:30 – Grails in action
Descripción: Grails es uno de los principales frameworks de desarrollo de aplicaciones Web en Java EE. Esta charla pretende presentar este framework ejemplificando y viendo casos prácticos.
Expositores: Ing. Alejandro Mateo, Ing. Pablo Kamil, Ing. Leandro Bertolami

19:30 – JBoss Seam + Richfaces + JGenUI
Descripción: Analizar Jboss Seam como framework de aplicación utiilizando un proyecto open source uruguayo para generar código sobre esta tecnología
Expositores: Ing. Horacio Vico, Lic. Rodolfo Vazquez

20:20 – JBoss Seam experiencia real. Medidas de productividad e integración
Expositores: Ing. Ariel Ludueña, Ing. Martin Cabrera

21:00 – Mesa redonda sobre tecnologías para la capa de presentación (GWT, Seam, Grails, Java FX, JSF)

¿Dónde y Cuándo?

Lugar: Aula Magna de la Universidad Catolica del Uruguay
Fecha: 30 de julio de 2009
Horario: de 18:00 a 21:30 horas.

Contacto

Correo: mailto:info@juguy.org
Sitio web JUGUY: – www.juguy.org

» Leer más, comentarios, etc...

Yet Another Programming Weblog

MinGW publica GCC 4.4.0

Junio 25th, 2009

El equipo de MinGW ha publicado los binarios de GCC 4.4.0 para Windows. De entre las novedades destacan un mejor tratamiento de excepciones, una versión de libstdc++ en forma de librería compartida, y soporte para TLS (thread-local storage), además de todas las novedades de la versión 4.4.0. Hay que recordar que la anterior versión soportada oficialmente era GCC 3.4.5. Más en reddit.



El manejo de las excepciones ha mejorado drásticamente debido a que se ha usado una implementación basada en DWARF, dejando de lado el viejo modelo SJLJ, que ya no estará disponible. Además con esta versión las excepciones ya pueden atravesar las fronteras de las DLL sin problemas.



La misma noticia y más comentarios en MinGW publica GCC 4.4.0 en barrapunto

» Leer más, comentarios, etc...

MadeInFlex

Transparencias de las presentaciones

Junio 25th, 2009

Al final he encontrado el tiempo para colgar las presentaciones y el codigo que utilizé para mis sesiones de MIF Onsite IV.

Cualquier duda no dudeis en preguntar.

» Leer más, comentarios, etc...

Blog de Julio César Pérez Arques

Sobre pruebas y errores

Junio 24th, 2009

Veo en las noticias las imagenes de una prueba de carga en un nuevo puente de una autovía en Galicia (España). Durante un momento me quedo absorto mirando la larga flota de camiones sobre el majestuoso puente. Impresiona. No puedo evitar pensar: Vaya! eso sí que es una prueba manual. Y luego yo quejándome sobre las pruebas manuales que hacemos en el desarrollo de proyectos software...



Bastan un par de segundos para darme cuenta de la tontería de mi reflexión. Si eso fuera una primera ejecución de una prueba exigente sobre un proyecto software recien construido, el puente se habría caido arrastrando a la flota de camiones tras él.



En el caso del puente, lo que no estamos viendo es todo el proceso de pruebas y control de calidad, que se ha realizado previamente en cada una de las fases del proyecto para evitar errores.



Pero comparar nuestro mundo de desarrollo software -¿ingeniería software?- con el de la construcción o cualquier otra ingeniería es una equivocación. Una equivocación torpe, injusta y desafortunada, porque, entre otras cosas, hace imaginarse a un programador como a un obrero. Así nos va.



Aunque centrémonos en los errores. Errar es humano. Obvio. A veces estamos distraidos, cansados, estresados, desmotivados o simplemente no somos perfectos y se nos escapan cosas.



Otro hecho es que los proyectos software salen a producción con demasiados errores. También obvio si estudiamos el ineficaz proceso de pruebas que se sigue en la mayoría de proyectos, basado en unas pruebas mínimas, manuales y sin documentar. Por lo que las pruebas dificilmente se repiten en el tiempo, aunque se modifique código afectado, haciendo aparecer nuevos errores o, peor aun, haciendo reaparecer viejos errores. ¿Vosotros compraríais algo cuyo proceso de pruebas fuera así?



Lo primero no tiene solución. Pero sí se puede minimizar el efecto. Contratando talento y con motivación.

Lo segundo sí tiene solución. Aplicar un proceso profesional de pruebas, basado en pruebas automatizadas, completas, independiente y repetibles. Los errores seguirán produciéndose, pero la mayoría serán detectados antes de llegar a producción.



Por supuesto, siempre hará falta alguna prueba manual pero, al igual que en el puente, será más un mero trámite de aceptación que el actual infierno de prueba y error sin fin.

» Leer más, comentarios, etc...

programania

Integración continua en PHP: ¿ Xinc o phpUnderControl?

Junio 24th, 2009

Un servidor de integración continua es algo mucho más sencillo de lo que parece. Básicamente sólo se dedica a ejecutar un proceso periódicamente (cada cierto tiempo, o cada commit del repositorio) que automatiza el proceso de construcción del software (build). Para entendernos, uno podría programar un script de terminal que llamara al framework de pruebas unitarias, y al generador de la documentación y a los analizadores de métricas de código, etc… y que se ejecutara cada cierto tiempo mediante el cron, y con eso estaría haciendo integración continua. Siendo esto así, el servidor de integración continua es independiente del lenguaje en el que esté escrito el software que está integrando (todo esto ya lo comentaba Blaxter en un post anterior de este blog).

Sin embargo, me inclino por utilizar un servidor de integración continua específico para PHP. Y es que estos aportan soporte específico para herramientas de PHP como phpUnit, phpDocumentor, etc… con lo que simplifica mucho la configuración y, sobre todo, la visualización de los resultados obtenidos (Front End). Como siempre, dentro del software libre, tenemos bastantes soluciones de calidad variable. Para mí destacan principalmente dos: Xinc y phpUnderControl.

phpUnderControl es una adaptación de Cruise Control a PHP. Cruise Control es el servidor de integración continua (originalmente para Java, pero ahora con versión para .NET, Ruby, etc.) con una comunidad de desarrolladores más grande y activa. Como consecuencia, tiene una enorme cantidad de documentación y de extensiones desarrolladas. Para funcionar, utiliza herramientas típicas de Java como Ant, etc. aunque a efectos prácticos, configurar un proyecto para utilizarlo con CruiseControl es escribir un XML.

Xinc significa Xinc is not CruiseControl. Como se puede ver por su propio nombre, nace como alternativa a phpUnderControl. Su idea es crear una herramienta más específica para el desarrollo en PHP, escrita en PHP y que, por ejemplo, utilice Phing (el Ant de PHP) en vez del propio Ant. A efectos prácticos, también supone escribir un XML. Una de sus ventajas es que no necesita Java, así que puede funcionar en un LAMP necesidad de instalar nada más. Su comunidad de desarrolladores es mucho menor que la de CruiseControl, pero también es más específica para PHP.

Finalmente, David y yo nos hemos inclinado por phpUnderControl, por varias razones:


» Leer más, comentarios, etc...

Arragonán

Acerca del Sun Open Communities Forum

Junio 24th, 2009

Los pasados 18 y 19 de Junio, estuve en Madrid para asistir al Sun Open Communities Forum, mi opinión sobre el evento es que hubieron más oscuros que claros, aunque tampoco me sorprendió demasiado vista la web del evento y la agenda publicada.

Sobre las charlas, opino como Abel Mendivil, se pueden agrupar en fuera de lugar, falta de profundidad y las que tenían fundamento.

Como suele ser habitual en estos eventos, los patrocinadores tenían charlas y algunas eran paja en un evento para programadores, o lo parecían, ya que el segundo día no fuí a ninguna de las que tenían esa pinta: la mesa redonda sobre open source, la de redes sociales, madridonrails… al menos para mi no son productivas. Otras me resultaron aburridas, un tanto básicas, y sobre todo en las que eran en inglés, desconectaba con facilidad. Por lo que, como ya he dicho, me pegué parte del segundo día trabajando en unas mesas fuera de las salas, que en algunos momentos casi se quedaban hasta pequeñas.

Pero también hubieron charlas interesantes:

En cuanto a los talleres, el primer día asistí al de Spring 3.0 que impartía Sergi Almar, donde hubieron problemas de espacio en la sala, no habían suficientes tomas de corriente y los usuarios de Mac no pudimos seguir el taller por un problema de última hora, una pena que ante tantos problemas nos quedáramos sin tiempo para ver finalizar el taller completo, tal y como pasó el año anterior :S. El segundo día, fuí al de Grails que impartían conjuntamente Nacho Brito y Álvaro Sánchez-Mariscal, donde de nuevo nos quedamos sin concluir el taller completo, realmente había mucho que decir y el tiempo se terminó consumiendo :S.

Desarrollo y test de componentes Ajax

Sobre el tema puramente organizativo, hubieron algunos detalles mejorables: Las salas estaban bien pero no habían enchufes, nada más llegar al campus Montepríncipe no había ningún cartel ni indicación, tampoco se nos dieron identificaciones a los asistentes y ponentes, la tarde del segundo días se cambió la sala de las charlas del track 1 y no se pudieron emitir por streaming…
Pero lo que más eché en falta fué más tiempo para charlar/saludar/conocer a otros asistentes y ponentes, que hubiera sido fácilmente solucionable “convocando” a quien quisiera a quedar en algún lugar a tomar unas cervezas(lo que los modernos llaman networking :P), cosa que sí pude hacer con unos pocos y a otros no pude más que saludarlos y poco más :S.

Como podéis ver, un poco de todo en el evento. Veremos si Sun/Oracle siguen celebrando un evento de estas características en España, y si lo hacen, esperemos que intenten organizarlo para que resulte más interesante.

» Leer más, comentarios, etc...

programania

Control de las funcionalidades de un producto con Google Docs

Junio 23rd, 2009

Animado por el artículo de Enrique sobre el uso de Google Docs para la gestión funcional de un proyecto, publico un artículo que llevaba tiempo pensando sobre cómo lo estoy haciendo yo ahora mismo. Obviamente, el sistema que os expongo está basado en SCRUM, aunque no me atrevería a decir que estoy “haciendo SCRUM” precisamente. Si los términos que utilizo en el artículo (Product Backog, Iteration Backlog, etc…) te son nuevos, quizá te interese leerte éste estupendo libro de introducción a SCRUM. La captura que muestro a continuación, como se puede ver claramente, es inventada.

captura-backlog

La razón por la que utilizamos Google Docs y no la clásica pizarra de SCRUM es porque el equipo no se encuentra físicamente en el mismo sitio. Quizá en el futuro utilicemos herramientas más específicas para la gestión de las user stories, iteration backlog e increment … pero a día de hoy éste sistema nos está funcionando por lo sencillo y visual que es.

El objetivo de la gestión es planificar, ejecutar y controlar. Veamos a continuación cómo planificamos, ejecutamos y controlamos el valor funcional de la aplicación.

Algunos apuntes sobre la ejecución:

Forma de detectar cómo va la iteración (controlar):

Algunos apuntes sobre planificación de las funcionalidades que se incluyen en cada iteración. En cada iteración se meten una serie de funcionalidades de el/los Product Backlog. Esas funcionalides cumplen que:


» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

JDeveloperLA en el Kaleidoscope 2009

Junio 22nd, 2009

A

» Leer más, comentarios, etc...

Información legal y técnica