Ir al contenido


Foto

¿ Un WebService no puede usar Componentes no gráficos ?


  • Por favor identifícate para responder
16 respuestas en este tema

#1 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 11 julio 2012 - 11:58

Hola.

Si os dicen de programar un servidor de WebService, aceptad mi consejo y negados en redondo, dejad que sea otro quien acabe tirándose por la ventana. Un diminuto WebService que debería ser la cosa más sencilla del mundo se ha ido convirtiendo a lo largo de estos últimos meses en la peor pesadilla que pueda recordar desde que cogí un ordenador por primera vez.

¿ Acaso la parte de implementación de un WebService no puede usar componentes ?. No lo entiendo, mi WebService no intenta hacer absolutamente nada con los datos que recibe, ningún tratamiento ni cálculo ni nada. Quiero evitar problemas así que simplemente transfiere los datos a un procedimiento almacenado de Firebird, donde se hacen cómodamente todas las operaciones necesarias.

Pero ni así consigo que funcione. He puesto los componentes FibDatabase, FibStoredProc, FibTransaction, ... en el datamodule principal, pero cuando intento acceder a ellos en la implementación del WebService me salta un "Access Violation" (y si intento poner los componentes en un segundo DataModule, independiente, el módulo ISAPI se queda bloqueado y el ejecutable CGI informa al arrancar de que esa aplicación solo puede cargar un DataModule).

Ejemplo :



delphi
  1. procedure TSolfaPort.AnulacioAtracada(AnyEscala: integer; NumeroEscala: integer; SequenciaAtracada: integer);
  2. var Log: TStringList;
  3.     Fitxer_Log: string;
  4. begin
  5.   Log := TStringList.Create;
  6.   Fitxer_Log := 'C:\Logs\AnulacioAtracada_' + FormatDateTime('yyyy-mm-dd', Now) + '.log';
  7.   if FileExists(Fitxer_Log) then Log.LoadFromFile(Fitxer_Log);
  8.   try
  9.     Log.Append('');
  10.     Log.Append('AnyEscala: ' + IntToStr(AnyEscala));
  11.     Log.Append('NumeroEscala: ' + IntToStr(NumeroEscala));
  12.     Log.Append('SequenciaAtracada: ' + IntToStr(SequenciaAtracada));
  13.     dmPrincipal.Cn.Open;
  14.     dmPrincipal.spAnulacio.ParamByName('ANY_ESCALA').Value := AnyEscala;
  15.     dmPrincipal.spAnulacio.ParamByName('NUMERO_ESCALA').Value := NumeroEscala;
  16.     dmPrincipal.spAnulacio.ParamByName('SEQUENCIA_ATRACADA').Value := SequenciaAtracada;
  17.     dmPrincipal.spAnulacio.ExecQuery;
  18.     dmPrincipal.Cn.Close;
  19.     Log.Add('>>> Correctamente Exportado a las ' + FormatDateTime('HH:MM', Now));
  20.   except
  21.     on E: Exception do begin
  22.       Log.Add('!!! ERROR !!! : ' + E.Message);
  23.     end;
  24.   end;
  25.   Log.SaveToFile(Fitxer_Log);
  26.   Log.Free;
  27. end;



El archivo de Log me informa de que salta un error de "Access Violation", aparentemente en : dmPrincipal.Cn.Open;

¿ Alguien que tenga un poco de experiencia le puede echar una ojeada a mi código (he adjuntado el proyecto, aunque está hecho en Delphi 2010 debería funcionar igual en cualquier Delphi anterior que tenga instalada la colección FibPlus) ?.

El código es sencillísimo y no entiendo porqué no funciona (el hecho de no poder depurar un WebService, no ayuda precisamente).

NOTA: Aunque ese proyecto genera tanto el WebService como módulo ISAPI y como ejecutable CGI, en el servidor distribuyo el módulo ISAPI.

Gracias por cualquier consejo que me podáis dar.
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 11 julio 2012 - 12:17

Hola Marc,

Me parece que el problema está aquí:



delphi
  1. procedure TdmPrincipal.CnBeforeConnect(Database: TFIBDatabase; LoginParams: TStrings; var DoConnect: Boolean);
  2. var Config: TIniFile;
  3. begin
  4.   Config := TIniFile.Create('C:\Solfa.ini');
  5.   Database.DBName := Config.ReadString('Conexion', 'database', '');
  6.   Config.Free;
  7. end;



Yo encontré que la forma como se hace es como lo sugiero en el tutorial de WebService con conexión a base de datos:



delphi
  1. procedure TfrmDataModule.SoapDataModuleCreate(Sender: TObject);
  2. var
  3.   IniFile: TIniFile;
  4.   DATABASE,SERVER: string;
  5.   S: array[0..MAX_PATH] of Char;
  6.  
  7. begin
  8.   windows.GetModuleFileName(hInstance, S, SizeOf(S));
  9.   IniFile  := TIniFile.Create(ExtractFileDir(S)+'\params.ini');
  10.   DATABASE := IniFile.ReadString('PARAMETERS','Path','');
  11.   SERVER  := IniFile.ReadString('PARAMETERS','Host','');
  12.   IniFile.Free;
  13.   Clientes.Params.Values['HOSTNAME'] := SERVER;
  14.   Clientes.Params.Values['DATABASE'] := SERVER+':'+DATABASE;
  15. end;



De cualquier forma, llegando a casa le doy una mirada, no es tan complejo como parece.

Saludos

  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 11 julio 2012 - 12:51

Por cierto, estoy preparando un tutorial que va a hablar precisamente como depurar un webServices :)

saludos
  • 0

#4 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 11 julio 2012 - 12:52

Vaya, gracias Eliseo.

Justamente me has solucionado otra cosa que me volvía loco, la de como identificar la carpeta donde se encuentra el WebService para poder usar un archivo de configuración adjunto. Ya veo que lo averiguas con windows.GetModuleFileName(hInstance, S, SizeOf(S));

Pero fíjate que en mi caso, al no saber esa ruta, había dejado el archivo de configuración en la raíz de C:, para así poder utilizar una ruta fija.

Ya he movido el archivo de configuración a la carpeta del WebService, y he adaptado el código con tus indicaciones :



delphi
  1. procedure TdmPrincipal.CnBeforeConnect(Database: TFIBDatabase; LoginParams: TStrings; var DoConnect: Boolean);
  2. var Config: TIniFile;
  3.     S: array[0..255] of Char;
  4. begin
  5.   Windows.GetModuleFileName(hInstance, S, SizeOf(S));
  6.   Config := TIniFile.Create(ExtractFileDir(S) + '\Solfa.ini');
  7.   Database.DBName := Config.ReadString('Conexion', 'database', '');
  8.   Config.Free;
  9. end;



Aunque no creo que sea este el problema, está claro que así es mucho mejor.

NOTA: Yo prefiero configurar la conexión en el OnBeforeConnect en lugar del OnCreate, para evitar problemas si la conexión queda abierta en tiempo de diseño. Puesto que al pasar siempre por OnBeforeConnect antes de abrirse, se puede enlazar bien la base de datos, en cambio en el OnCreate ya se me había abierto la base de datos durante la carga de propiedades del DFM y falla al intentar enlazar con la base de datos que hay enlazada en el DFM. He utilizado la configuración en el OnBeforeConnect tanto en aplicaciones como en Intraweb, Servicios Windows, ... y siempre me ha funcionado bien, no debería ser distinto en un WebService.

Hoy parece que ya no van a enviar más movimientos, así que tendré que esperar a mañana para ver si ha resuelto el problema.

Gracias de nuevo.
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 11 julio 2012 - 12:57

Que bien que haya servido a solucionar otra duda, espero poder darte una respuesta hoy mismo (GMT-6 :))

Saludos
  • 0

#6 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 11 julio 2012 - 01:00

Por cierto, estoy preparando un tutorial que va a hablar precisamente como depurar un webServices :)

saludos


¿ Se puede ?, wow esto es muy necesario para poder hacer cualquier cosa mínimamente compleja.
  • 0

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 11 julio 2012 - 01:38


Por cierto, estoy preparando un tutorial que va a hablar precisamente como depurar un webServices :)

saludos


¿ Se puede ?, wow esto es muy necesario para poder hacer cualquier cosa mínimamente compleja.


Teóricamente si, necesito hacer la prueba y la documentaré para hacer el tutorial.

saludos
  • 0

#8 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 11 julio 2012 - 11:30

Hola Marc,

No pude compilar tu webservice porque utilizas componentes que yo no tengo  :(, sin embargo, veo que no utilizas el SOAP Server DataModule que es la versión del DataModule que normalmente utilizamos y donde se agregan los objetos de base de datos. Te muestro dos imagenes para que veas lo que te comento.

Saludos

Archivos adjuntos


  • 0

#9 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 02:55

Hola Marc,

No pude compilar tu webservice porque utilizas componentes que yo no tengo  :(, sin embargo, veo que no utilizas el SOAP Server DataModule que es la versión del DataModule que normalmente utilizamos y donde se agregan los objetos de base de datos. Te muestro dos imagenes para que veas lo que te comento.

Saludos


Gracias Eliseo, no conocía este tipo de DataModule.

Cuando tuve este problema, lo primero que intenté fue crear un DataModule normal nuevo donde trasladar los componentes de base de datos. Pero la aplicación CGI me devolvía un error de que no puede tener dos datamodules en el proyecto (y el módulo ISAPI se quedaba directamente bloqueado).

Voy a probar estos SOAP Server DataModule.

Saludos.
  • 0

#10 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 03:45

Eliseo, ¿ me puedes indicar como se debería crear este DataModule y como se referencia desde otra Unit ?. No veo que el Asistente lo haya puesto en creación automática (ni de ninguna otra forma) y no tiene ninguna variable global asociada para referenciarlo.

(Tengo los libros de Marteens, Cantú y Chartre, y en ninguno explica como usar los SOAP Server DataModules lo que los convierte en poco más que inútiles para hacer nada útil con WebServices. La Cara Oculta es el único que se digna a citar el problema, pero sin mostrar la solución).

Gracias.
  • 0

#11 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 04:35

He estado mirando tu tutorial y veo que tú si que enseñas a utilizar el SOAP Server DataModule.

He cambiado el código de ejemplo que puse anteriormente por :



delphi
  1. procedure TSolfaPort.AnulacioAtracada(AnyEscala: integer; NumeroEscala: integer; SequenciaAtracada: integer);
  2. var Log: TStringList;
  3.     Fitxer_Log: string;
  4.     dm: TdmData;
  5. begin
  6.   Log := TStringList.Create;
  7.   Fitxer_Log := 'C:\Logs\AnulacioAtracada_' + FormatDateTime('yyyy-mm-dd', Now) + '.log';
  8.   if FileExists(Fitxer_Log) then Log.LoadFromFile(Fitxer_Log);
  9.   try
  10.     try
  11.       dm := TdmData.Create(nil);
  12.       Log.Append('');
  13.       Log.Append('AnyEscala: ' + IntToStr(AnyEscala));
  14.       Log.Append('NumeroEscala: ' + IntToStr(NumeroEscala));
  15.       Log.Append('SequenciaAtracada: ' + IntToStr(SequenciaAtracada));
  16.       dm.Cn.Open;
  17.       dm.spAnulacio.ParamByName('ANY_ESCALA').Value := AnyEscala;
  18.       dm.spAnulacio.ParamByName('NUMERO_ESCALA').Value := NumeroEscala;
  19.       dm.spAnulacio.ParamByName('SEQUENCIA_ATRACADA').Value := SequenciaAtracada;
  20.       dm.spAnulacio.ExecQuery;
  21.       dm.Cn.Close;
  22.       Log.Add('>>> Correctamente Exportado a las ' + FormatDateTime('HH:MM', Now));
  23.     except
  24.       on E: Exception do begin
  25.         Log.Add('!!! ERROR !!! : ' + E.Message);
  26.       end;
  27.     end;
  28.   finally
  29.     Log.SaveToFile(Fitxer_Log);
  30.     Log.Free;
  31.     if dm.Cn.Connected then dm.Cn.Close;
  32.     dm.Free;
  33.   end;
  34. end;



Nunca hubiese sospechado que hay que crear una instancia del DataModule para cada llamada. Vamos a ver que tal se porta ahora.  :)
  • 0

#12 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 05:14

Mi gozo en un pozo :(.

Ya puede acceder correctamente a los componentes de base de datos, pero ahora me encuentro con que salta el Error de : "Can't load library fbclient.dll".

Precisamente tengo copias de esa librería tanto en la carpeta del WebService como en la carpeta System32. Pero a diferencia de en una aplicación normal no parece localizarla y/o poder cargarla.

No tengo ni idea de cual es el problema con la librería ni como manejarlo, ¿ que se puede hacer al respecto (linkar fbclient.dll dentro del WebService, etc. ...) ?.

Gracias.
  • 0

#13 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 06:00

Mi gozo en un pozo :(.

Ya puede acceder correctamente a los componentes de base de datos, pero ahora me encuentro con que salta el Error de : "Can't load library fbclient.dll".

Precisamente tengo copias de esa librería tanto en la carpeta del WebService como en la carpeta System32. Pero a diferencia de en una aplicación normal no parece localizarla y/o poder cargarla.

No tengo ni idea de cual es el problema con la librería ni como manejarlo, ¿ que se puede hacer al respecto (linkar fbclient.dll dentro del WebService, etc. ...) ?.

Gracias.


Nada, no era un problema de que no pudiese localizar/cargar una librería, sino que el equipo no tenía el Run-Time de C++, necesario para que funcione la fbclient.dll

He añadido las librerías msvcp71.dll y msvcr71.dll, y ahora los componentes de datos ya parecen funcionar correctamente.

Como no podía ser de otra forma, sigue sin funcionar correctamente (creo que esto me ha generado una cantidad tan alucinantemente increíble de problemas, que el día que funcione no me lo voy a creer).

Ahora la llamada al procedimiento almacenado me devuelve un error de conversión de tipo.

No debería darlo, he revisado todos los parámetros. Pero no hay más remedio que volver a revisarlo todo. Al menos ahora por fin ya me muevo en territorio conocido.

Gracias por tu ayuda, Eliseo, y por tu Tutorial, sin ellos me habría sido imposible avanzar hasta aquí (la documentación y los libros que tocan el tema son pésimos, es imposible hacer un WebService con ellos).

Saludos.
  • 0

#14 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 12 julio 2012 - 06:49

Sin saber mucho del tema, ojo con los componenetes de conexion a datos que no adsmitan multitarea, en un servicio dee stos puedes terminar con un uso multihilo y eso te puede dar el problema.

No se si van los tiros por aqui, mira a ver si el error inicial salia al conectar 2 simultaneamente y no te sale si solo conecta 1.

Nosotors para otras cosas similares (web server dentro del programa) usamos unos componentes multihilo solo para evitar esto (el resto de la applicacion usan componenetes "normales"), los UIB, pero no se si te servirian en tu caso.
  • 0

#15 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 12 julio 2012 - 08:18

Nada, no era un problema de que no pudiese localizar/cargar una librería, sino que el equipo no tenía el Run-Time de C++, necesario para que funcione la fbclient.dll

He añadido las librerías msvcp71.dll y msvcr71.dll, y ahora los componentes de datos ya parecen funcionar correctamente.

Como no podía ser de otra forma, sigue sin funcionar correctamente (creo que esto me ha generado una cantidad tan alucinantemente increíble de problemas, que el día que funcione no me lo voy a creer).

Ahora la llamada al procedimiento almacenado me devuelve un error de conversión de tipo.

No debería darlo, he revisado todos los parámetros. Pero no hay más remedio que volver a revisarlo todo. Al menos ahora por fin ya me muevo en territorio conocido.

Gracias por tu ayuda, Eliseo, y por tu Tutorial, sin ellos me habría sido imposible avanzar hasta aquí (la documentación y los libros que tocan el tema son pésimos, es imposible hacer un WebService con ellos).

Saludos.


Que bien Marc, ya valió la pena haber hecho ese tutorial (que por cierto está bastante desactualizado).

Yo también me tope con muchos problemas como tú, y efectivamente por la poca información que hay al respecto en libros y en la web.


Sin saber mucho del tema, ojo con los componenetes de conexion a datos que no adsmitan multitarea, en un servicio dee stos puedes terminar con un uso multihilo y eso te puede dar el problema.

No se si van los tiros por aqui, mira a ver si el error inicial salia al conectar 2 simultaneamente y no te sale si solo conecta 1.

Nosotors para otras cosas similares (web server dentro del programa) usamos unos componentes multihilo solo para evitar esto (el resto de la applicacion usan componenetes "normales"), los UIB, pero no se si te servirian en tu caso.


Vaya, nunca he reparado en eso, yo normalmente uso dbExpress y no he tenido problemas, pero es un buen apunte para hacer unas pequeñas pruebas.

La verdad es que ahora que leí  de nuevo el tutorial me parece demasiado básico. Ya me dieron ganas de hacer uno más enfocado a la realidad, me he topado con situaciones bastante adversas al intentar consumir servicios Web, sobre todo a la hora de la serialización de la información, aunque les he dado la vuelta por otros lados y he salido avante no deja de molestarme no poder hacerlo por las vías normales.

Pues nada, espero tenerlo lo antes posible.

Saludos


  • 0

#16 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 julio 2012 - 09:27

Sin saber mucho del tema, ojo con los componenetes de conexion a datos que no adsmitan multitarea, en un servicio dee stos puedes terminar con un uso multihilo y eso te puede dar el problema.

No se si van los tiros por aqui, mira a ver si el error inicial salia al conectar 2 simultaneamente y no te sale si solo conecta 1.

Nosotors para otras cosas similares (web server dentro del programa) usamos unos componentes multihilo solo para evitar esto (el resto de la applicacion usan componenetes "normales"), los UIB, pero no se si te servirian en tu caso.


Esta me temo que será la segunda parte de la pesadilla.

Ahora el WebService ya traslada correctamente los datos a la base de datos, para que los pueda tratar allí.

Pero me temo que como bien dices, tengo problemas de concurrencia. Fuera de las secciones protegidas try ... except tengo la grabación de los registros de Log, y el consumidor del WebService me dice que ocasionalmente le salta un error al consumir el WebService indicando que no ha podido escribir en el archivo de Log por encontrarse en uso (parece que las excepciones no tratadas en el WebService se trasladan al consumidor del servicio).

También me sorprende el contenido de los registros Log. Las secciones try ... except ... finally deberían asegurar que cada entrada en el Log debería finalizar con la confirmación de Acierto o de Error. Pero me aparecen entradas a medio rellenar, cosa que no debería ser posible :(.

Voy a tener que estar atento por si además de estos problemas de concurrencia, también se ocasionan los posibles problemas por la utilización de FibPlus en multihilo que comentas.

En fin, ya he avanzado mucho, pero veo que esto no se ha terminado.
  • 0

#17 seoane

seoane

    Advanced Member

  • Administrador
  • 1.259 mensajes
  • LocationEspaña

Escrito 12 julio 2012 - 12:30

Del tema de los webservices no tengo mucha idea, pero con los logs "threadsafe" si que puedo echar una mano:
http://delphi.jmrds.com/?q=node/37

Saludos
  • 0




IP.Board spam blocked by CleanTalk.