viernes, 27 de febrero de 2015

Usando CreateObject(). No se libera memoria


Batallando en la creacion de un webservices, quería poner un validador de XSD,
de esta manera, el recibir un XML, podemos ver si cumple o no cumple.

Lo curioso es que me dio por ver como va de rendimiento y me topo otra vez con el problema de la memoria.

En la primera imagen, se muestra la memoria antes del bucle, 1.8Mb;



En segunda, imagen , después del test , 100 llamadas ,tenemos un consumo de 7.2Mb :




He aquí el test para que podais ver si os paso a vosotros.

/* Show not liberate memory */
FUNCTION HB_GTSYS()
   REQUEST HB_GT_WVT_DEFAULT
   REQUEST HB_GT_WIN
   REQUEST HB_LANG_ES
RETURN NIL

Function Main()

    Alert( "Begin...")

    for x := 1 to 100
        ? _Validate_XSD()
    next
   
    Alert( "Finish")

return

function  _Validate_XSD( )
   Local oXmlDoc, oSchema, oSchemaCache, oParseError
   Local lOk := .f.
   Local cMensaje
   Local cString_XSD := [<?xml version="1.0" encoding="UTF-8"?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="GetInfoRQ">   <xs:annotation>     <xs:documentation>Devolvemos información de kiosko</xs:documentation>   </xs:annotation>    <xs:complexType>      <xs:attribute name="HotelID" type="xs:int" use="required">        <xs:annotation>         <xs:documentation>ID del Hotel</xs:documentation>       </xs:annotation>      </xs:attribute>     <xs:attribute name="StationID" type="xs:string" use="required">       <xs:annotation>         <xs:documentation>ID del Kiiosko</xs:documentation>       </xs:annotation>      </xs:attribute>     <xs:attribute name="UserID">        <xs:annotation>         <xs:documentation>Usuario:password que realiza la peticion</xs:documentation>       </xs:annotation>      </xs:attribute>   </xs:complexType> </xs:element></xs:schema> ]


   Local cString_XML := [<?xml version="1.0" encoding="UTF-8"?><GetInfoRQ HotelID="1a2" StationID="1" ></GetInfoRQ>]
  
   oXmlDoc := CreateObject("Msxml2.DOMDocument.6.0")
   oXmlDoc:async := .F.
   oXmlDoc:validateOnParse := .F.
   oXmlDoc:resolveExternals = .F.

   oXmlDoc:loadXML( cString_XML )  // Usar variable con contenido XML

   oSchema := CreateObject("Msxml2.DOMDocument.6.0")
   oSchema:async := .F.
   oSchema:loadXML( cString_XSD )

   oSchemaCache := CreateObject("Msxml2.XMLSchemaCache.6.0")
   oSchemaCache:add( "", oSchema )

   oXmlDoc:schemas := oSchemaCache

   oParseError := oXmlDoc:validate()

   if !empty( oParseError:errorCode )
      cMensaje := "ERROR! Failed to validate Reason:"+ cStr( oParseError:reason ) +;
                   " Error code: " + Alltrim( Cstr( oParseError:errorCode ) ) +;
                   " Line: " + Alltrim( Cstr(  oParseError:line ) ) +;
                   " Character: " + Alltrim( Cstr(  oParseError:linepos ) ) +;
                   " Source: " + cStr( oParseError:srcText )
      llError := .T.
   else
      lOk := .t.
   endif 
  
   oParseError := NIL
   oSchemaCache := NIL
   oSchema := NIL
   oXmlDoc := NIL

   hb_gcall( .F. )

return cMensaje


viernes, 13 de febrero de 2015

Validar XML con XSD en HARBOUR


Harbour, parece, que no aporta alguna librería de validación de XML a través de un XSD.
Si usamos Windows, vamos a usar las librerías de Microsoft que nos aporta.

¿ Para que sirve validar un XML ? Para evitarnos dolores de cabeza.

Si nuestra XSD determina que el tipo de dato  un atributo es un valor del tipo int, si
nos lleva un tipo String, con la validación, saltará la alarma antes de continuar mirando
donde esta el error.

Esta función recibe el fichero XML y el XSD que tiene que usar y devuelve true si se valida.

if Validate_XSD( "test.xml", "test.xsd" )
   ... SIGO con XML
endif
 //-----------------------------------------------------------------------
// Source Code
//-----------------------------------------------------------------------
function Validate_XSD( cFileXML, cFileXSD )
   Local oXmlDoc, oSchema, oSchemaCache, oParseError
   Local lOk := .f.
 
   oXmlDoc := CreateObject("Msxml2.DOMDocument.6.0")
   oXmlDoc:async := .F.
   oXmlDoc:validateOnParse := .F.
   oXmlDoc:resolveExternals = .F.

   oXmlDoc:load( cFileXML )
   //oXmlDoc:loadXML( cString_XML )  // Usar variable con contenido XML

   oSchema := CreateObject("Msxml2.DOMDocument.6.0")
   oSchema:async := .F.
   oSchema:load( cFileXSD )
   //oSchema:loadXML( cString_XSD )

   oSchemaCache := CreateObject("Msxml2.XMLSchemaCache.6.0")
   oSchemaCache:add( "", oSchema )

   oXmlDoc:schemas := oSchemaCache

   oParseError := oXmlDoc:validate()
 
   if !empty( oParseError:errorCode )
      Alert( Cstr( oParseError:errorCode ) + "#"+ cStr( oParseError:reason ) )
   else
      lOk := .t. 
   endif  

return lOk


Aqui dejo un fichero XML y XSD de prueba. Cambiar la cadena texto por un entero para que comprobeis que esta haciendolo ok.

//-----------------------------------------------------------------------
// test.xml
//-----------------------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<testfile xsi:noNamespaceSchemaLocation="test.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <companyName>texto</companyName>
</testfile>



//-----------------------------------------------------------------------
// test.xsd
//-----------------------------------------------------------------------
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="testfile">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="companyName" type="xs:int"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

viernes, 30 de enero de 2015

Usando mxml


Debido a los problemas de memoria usando la clase TXMLDocument, he decidido usar directamente la librería mxml.

Pongo un ejemplo de COMO se obtienen los datos a partir de una cadena que contiene el XML.

Parece una tonteria, pero no he encontrado un ejemplo tan simple como esto.
Espero os ayude 


function read_xml()
  Local htree, hNode, cType, hTarifa, hNext, hDias, hDia, hHab, hPrecios
  Local xml := [<?xml version="1.0" encoding="UTF-8"?> ]+;
                [<Precios>]+;
                [    <Tarifa Codigo="BAR" Tipo="DIARIO" ImpuestosIncluidos="true" >]+;
                [        <Habitacion Tipo="DOB1" PAX="1">]+;
                [            <DIAS>]+;
                [                <Dia Precio="1" Fecha="01-08-2013" Estado="A"/>]+;
                [                <Dia Precio="2" Fecha="02-08-2013" Estado="C"/>]+;
                [            </DIAS>]+;
                [        </Habitacion>]+;
                [        <Habitacion Tipo="DOB2" PAX="2">]+;
                [            <DIAS>]+;
                [                <Dia Precio="3.1415" Fecha="01-08-2013" Estado="A"/>]+;
                [                <Dia Precio="3.1415" Fecha="02-08-2013" Estado="A"/>]+;
                [            </DIAS>]+;
                [        </Habitacion>]+;
                [    </Tarifa>]+;
                [</Precios>]

      hTree = mxmlLoadString( nil, xml )

      IF Empty( hTarifa := mxmlFindElement( hTree, hTree, "Tarifa",,, MXML_DESCEND ) )
         OutErr( "Unable to find first <tarifas> element in XML tree!" + hb_eol() )

         mxmlDelete( hTree )
         ErrorLevel( 1 )
         QUIT
      endif  

      Alert( "Tarifa coger atributos:"+ mxmlGetElement( hTarifa )   )
      Alert( "Codigo Tarifa.:" + mxmlElementGetAttr( hTarifa, "Codigo" ) + hb_osnewline() + ;
             "Tipo de Tarifa:" + mxmlElementGetAttr( hTarifa, "Tipo" )   + hb_osnewline() +;
             "Impuestos.....:" + mxmlElementGetAttr( hTarifa, "ImpuestosIncluidos" ) )
    
       hNext := mxmlWalkNext( hTarifa, hTree, MXML_DESCEND )   // Cogo habitacion1 porque hTarifa es el nodo principal
       while hNext != NIL
             Alert( "Hab:" + mxmlElementGetAttr( hNext, "Tipo" ) )
             hDias := mxmlWalkNext( hNext, hTree, MXML_DESCEND )        // Cojo nodo DIAS, descendiendo un nivel
                   hDia  := mxmlWalkNext( hDias, hTree, MXML_DESCEND )  // Cojo nodo DIA , descendiendo un nivel
                   while hDia != NIL
                         alert( "DIA :" + mxmlGetElement( hDia ) + "-->" + mxmlElementGetAttr( hDia, "Precio" )  )
                         alert("      " + mxmlGetElement( hDia ) + "-->" + mxmlElementGetAttr( hDia, "Fecha" )  )
                         alert("      " + mxmlGetElement( hDia ) + "-->" + mxmlElementGetAttr( hDia, "Estado" )  )
                         hDia := mxmlGetNextSibling( hDia ) // Siguiente Node dia, al mismo NIVEL
                   end while     

             hNext := mxmlGetNextSibling( hNext ) // El siguiente nodo es la habitacion, mismo NIVEL
       end while

    alert( "mira:" + MXMLSAVEALLOCSTRING( hTree ))
    mxmlDelete( hTree )

return nil

jueves, 29 de enero de 2015

Cuando estás a punto de guardar....


Hacia muucho tiempo que no conseguia que Windows 7 se me rompiera.

Ayer programando el servidor Web en Harbour, haciendo pruebas de estres, parece ser que se estresó el dichoso Windows y me dejo una bonita pantalla azul 'paraiso'. ;-(


Android y Git. Disponer del hash automáticamente.

Una de las cosas a las que estoy acostumbrado, es tener siempre en mi código, el hash/tag/versión del control de versiones que estoy usan...