AbanQ: Integrando con JasperServer / JasperReports (II)
Bien, la idea es usar una librería C++ para interaccionar con webservices. Para ello, escogí el toolkit gSOAP. Librería en software libre que permite crear aplicaciones tanto clientes (consumidores) como servidores de webservices.
Nosotros vamos a hacer un uso muy parcial de la misma, ya que no vamos a instalar ningún servidor ni nada por el estilo, y sólo vamos a crear un consumidor. No me voy a meter en detalles con gSOAP (ya que se me escapa de los artículos) y sólo comentaré aquellos aspectos que nos interesan para integrar Webservices.
Lo primero, es obtener el WSDL de JasperServer. Para ello, y desde una instalación del mismo, se puede obtener sin más que abrir un navegador y solicitar http://servidor:puerto_tomcat/jasperserver/services/repository?wsdl
Bien. gSOAP proporciona dos ejecutables wsdl2h y soapcpp2 para generar los “proxys”, esto es, código C++ que encapsula el acceso por webservices. Los ejecutamos tal que así: (INCLUDEGSOAP es una variable que apunta al directorio de includes de gsoap
echo "" > env.h
soapcpp2 -L -C -d../proxy -penv env.h
# Las dos lineas anteriores son para generar un esqueleto de código de acceso a los webservices, que permitan
# una compilación sin problemas.
wsdl2h -nJasperServer -NJasperServer -y -g -I${INCLUDEGSOAP}/ -o JasperServer.h JasperServer.wsdl
soapcpp2 -C -d../proxy -I${INCLUDEGSOAP}/ -n -pjasperserver -qJasperServer JasperServer.h
Con esto se generan una serie de ficheros, .c, .cpp y .h que incluiremos en AbanQ. Concretamente, a flbase.pro, le añadiremos los siguientes ficheros:
-De la distribución de gsoap directamente stdsoap2.cpp -Archivos generados con el código anterior JasperServerClientLib.cpp envC.cpp envClient.cpp stdsoap2.h JasperServerrepositorySoapBindingProxy.h JasperServer.nsmap
Ya podemos meter código propio en AbanQ. En el zip que puse días anteriores, viene todo esto integrado. Destaco aquí el código de acceso a los webservices.
Voy a crear un nuevo método en FLUtil que permita, desde QSA, acceder a los webservices. Ese método será tal que así
// dpinelo
bool FLUtil::downloadJasperServerFile(const QString & xmlRequest, const QString & rutaDestino, QString & mensajeSalida)
{
bool value = false;
int r;
std::string result;
std::string xml = xmlRequest;
QSettings settings;
settings.setPath( "InfoSiAL", "FacturaLUX", QSettings::User );
QString keybase( "/facturalux/lite/jasperserver/" );
// Lo hago así para garantizar que los objetos existen hasta el final
QString *userJasperServer = new QString;
QString *passwordJasperServer = new QString;
QString *endPointJasperServer = new QString;
*userJasperServer = settings.readEntry( keybase + "User", "" );
*passwordJasperServer = settings.readEntry( keybase + "Password", "" );
*endPointJasperServer = settings.readEntry( keybase + "EndPoint", "" );
JasperServer::repositorySoapBinding rep;
// si lo hago sin punteros, esto es peligroso
rep.soap->userid = (*userJasperServer).ascii();
rep.soap->passwd = (*passwordJasperServer).ascii();
rep.endpoint = (*endPointJasperServer).ascii();
qDebug("Antes de llamar a jasperserver");
r = rep.JasperServer__runReport(xml, result);
if ( r == SOAP_OK ) {
qDebug("Lo ha llamado y es correcto");
soap_multipart::iterator attachment = rep.soap->mime.begin();
++attachment;
if ( attachment != rep.soap->mime.end() ) {
saveDocument (*attachment, rutaDestino);
value = true;
} else {
value = false;
mensajeSalida = result;
}
} else {
qDebug("Hubo un fallo");
soap_print_fault(rep.soap, stderr);
value = false;
}
delete userJasperServer;
delete passwordJasperServer;
return value;
}
// dpinelo
void FLUtil::saveDocument(const struct soap_multipart & attachment, const QString & path)
{
QFile file (path);
file.open ( IO_WriteOnly | IO_Truncate );
file.writeBlock(attachment.ptr, attachment.size);
file.close();
}
Listo. ¿Cómo invocar a un informe en JasperServer? Desde QSA es tan sencillo como: Por ejemplo, para imprimir un recibo de proveedor
function oficial_imprimir(codRecibo:String)
{
var util:FLUtil = new FLUtil;
var xml:String = "<request operationName=\"runReport\" locale=\"es\">";
xml = xml + "<argument name=\"RUN_OUTPUT_FORMAT\">PDF</argument>";
xml = xml + "<resourceDescriptor name=\"\" wsType=\"\" ";
xml = xml + "uriString=\"/Documentos/Pagare_a_Proveedores/PagareProveedores\" ";
xml = xml + "isNew=\"false\">";
xml = xml + "<label>null</label>";
xml = xml + "<parameter name=\"P_ID_PAGARE\"><![CDATA[100]]></parameter>";
xml = xml + "<parameter name=\"ANIO\"><![CDATA[09]]></parameter>";
xml = xml + "</resourceDescriptor>";
xml = xml + "</request>";
var rutaDestino:String = "/home/david/prueba.pdf";
var mensajeSalida:String;
util.downloadJasperServerFile ( xml, rutaDestino, mensajeSalida);
}
Como veis, construyo un XML con el formato adecuado, según describe JasperServer, indicando los valores de los parámetros del propio report. El archivo se obtiene directamente en PDF (aunque se puede obtener en otros muchos formatos cambiando el RUN_OUTPUT_FORMAT), y por supuesto, podéis ponerle el nombre que querais. (construid adecuadamente rutaDestino).
La integración seía aún mejor en la medida en la que se oculte ese código XML que se construye en QSA, asociando parámetros de JasperReports/JasperServer con columnas de FLSqlCursor. Pero, a mí, este nivel me sirve.