GetLocation() y sus errores en producción.
En GeneXus para hacer paramétrico el consumo de servicios web SOAP, se necesita usar la funcion GetLocation() . Por ejemplo:
&Location = GetLocation('LocationName')
El 'LocationName', es un string que depende de:
* Nombre de objeto External Object SOAP
* Modulos en los cuales esta el external object.
Por ejemplo, si tengo el external object
&Calc basado en el EO &Calculator y está en el modulo CALCULADORA, que a su vez esta en el modulo FUNCIONES, el nombre será
FUNCIONES_CALCULADORA_Calculator
Entonces en mi codigo voy a tener:
&Location = GetLocation('FUNCIONES_CALCULADORA_Calculator')
&Location.Host=DevuelveHost()
...
&Resultado=&Calc.Add(10,20)
Que problema tiene este código?
El problema es que dicho código es demasiado frágil ante cambios de la KB.
Si cambio el nombre del external object, o si cambio la modularizacion (nombre de modulo, muevo un modulo, etc) la función devuelve un &Location vacio y no es tomado en cuenta al ejecutar el servicio. Ademas, no tenemos forma de detectar dicho problema, pues la función no devuelve ningun codigo de error.
Esto hace que tengamos errores en producción, sin que se haya cambiado "nada". Es dificil detectar que el cambiar la forma en que la KB este modularizada, afecte la forma en que se consume un servicio web.
Una posible solución.
Desde hace bastante tiempo en el blog (aqui y aqui), en los foros de betatesting de la Salto y posteriores, en SACs he intentado de tener una forma nativa en Genexus para obtener el nombre de la Location, pero aun no lo han implementado, pues hay dificultades técnicas que hacen que no se puedan hacer fácilmente.
Mientras la solucion definitiva no este disponible, podemos usar un WA para lograr el objetivo, pero depende del lenguaje de generacion que usemos.
En .NET puede hacerse:
csharp [!&ExternalObjectName!] = [!&CertificadosDerivados!].GetType().FullName;
Esto devuelve algo asi
GeneXus.Programs.declaraciones.cargas.SdtCertificadosDerivadosService
o
<KBNameSpace>.<Modulo>.<SubModulo>.Sdt<NombreExternalObject>
Para traducir esto en el LocationName, hay que:
Sacar el <KBNameSpace> y el primer punto
Sacar la ultima aparicion de .Sdt
Sustituir los "." por "_"
Llegamos a algo asi:
declaraciones_cargas_CertificadosDerivadosService
que es lo que necesita el GetLocation para ejecutar correctamente.
Entonces el codigo completo seria:
csharp [!&ExternalObjectName!] = [!&CertificadosDerivados!].GetType().FullName;
&Location = GetLocation(ReturnLocationName(&ExternalObjectName))
Donde el ReturnLocationName() es un procedure que recibe un string con el nombre del objeto externo y devuelve el nombre del location.
&NETNameSpace="GeneXus.Programs." //Depende de las propiedades de la KB
//Obtengo el nombre del tipo de la clase del external object
//Va a ser algo asi como <Namespace>.<Modulo1>.<Modulo2>.SdtNombreLocation
//Le saco el Namepsace
&LocationName=&ClassName.Replace(&NETNameSpace,"")
//Saco el primer SDT
if &LocationName.StartsWith("Sdt")
&LocationName=&LocationName.Substring(4,&LocationName.Length())
endif
//Le saca el .Sdt (hay que rezar para que no tenga en el nombre ese string)
&LocationName = &LocationName.Replace(".Sdt",".")
//Los puntos se sustituyen por underscore.
&LocationName = &LocationName.Replace(".","_")
Dista muchísimo de ser una solución elegante y ademas es bastante frágil (depende que GeneXus nombre las clases de los EO que empiecen con "Sdt", etc, pero al menos elimina el uso de constantes string con el nombre de las location en el código.
&Location = GetLocation('LocationName')
El 'LocationName', es un string que depende de:
* Nombre de objeto External Object SOAP
* Modulos en los cuales esta el external object.
Por ejemplo, si tengo el external object
&Calc basado en el EO &Calculator y está en el modulo CALCULADORA, que a su vez esta en el modulo FUNCIONES, el nombre será
FUNCIONES_CALCULADORA_Calculator
Entonces en mi codigo voy a tener:
&Location = GetLocation('FUNCIONES_CALCULADORA_Calculator')
&Location.Host=DevuelveHost()
...
&Resultado=&Calc.Add(10,20)
Que problema tiene este código?
El problema es que dicho código es demasiado frágil ante cambios de la KB.
Si cambio el nombre del external object, o si cambio la modularizacion (nombre de modulo, muevo un modulo, etc) la función devuelve un &Location vacio y no es tomado en cuenta al ejecutar el servicio. Ademas, no tenemos forma de detectar dicho problema, pues la función no devuelve ningun codigo de error.
Esto hace que tengamos errores en producción, sin que se haya cambiado "nada". Es dificil detectar que el cambiar la forma en que la KB este modularizada, afecte la forma en que se consume un servicio web.
Una posible solución.
Desde hace bastante tiempo en el blog (aqui y aqui), en los foros de betatesting de la Salto y posteriores, en SACs he intentado de tener una forma nativa en Genexus para obtener el nombre de la Location, pero aun no lo han implementado, pues hay dificultades técnicas que hacen que no se puedan hacer fácilmente.
Mientras la solucion definitiva no este disponible, podemos usar un WA para lograr el objetivo, pero depende del lenguaje de generacion que usemos.
En .NET puede hacerse:
csharp [!&ExternalObjectName!] = [!&CertificadosDerivados!].GetType().FullName;
Esto devuelve algo asi
GeneXus.Programs.declaraciones.cargas.SdtCertificadosDerivadosService
o
<KBNameSpace>.<Modulo>.<SubModulo>.Sdt<NombreExternalObject>
Para traducir esto en el LocationName, hay que:
Sacar el <KBNameSpace> y el primer punto
Sacar la ultima aparicion de .Sdt
Sustituir los "." por "_"
Llegamos a algo asi:
declaraciones_cargas_CertificadosDerivadosService
que es lo que necesita el GetLocation para ejecutar correctamente.
Entonces el codigo completo seria:
csharp [!&ExternalObjectName!] = [!&CertificadosDerivados!].GetType().FullName;
&Location = GetLocation(ReturnLocationName(&ExternalObjectName))
Donde el ReturnLocationName() es un procedure que recibe un string con el nombre del objeto externo y devuelve el nombre del location.
&NETNameSpace="GeneXus.Programs." //Depende de las propiedades de la KB
//Obtengo el nombre del tipo de la clase del external object
//Va a ser algo asi como <Namespace>.<Modulo1>.<Modulo2>.SdtNombreLocation
//Le saco el Namepsace
&LocationName=&ClassName.Replace(&NETNameSpace,"")
//Saco el primer SDT
if &LocationName.StartsWith("Sdt")
&LocationName=&LocationName.Substring(4,&LocationName.Length())
endif
//Le saca el .Sdt (hay que rezar para que no tenga en el nombre ese string)
&LocationName = &LocationName.Replace(".Sdt",".")
//Los puntos se sustituyen por underscore.
&LocationName = &LocationName.Replace(".","_")
Dista muchísimo de ser una solución elegante y ademas es bastante frágil (depende que GeneXus nombre las clases de los EO que empiecen con "Sdt", etc, pero al menos elimina el uso de constantes string con el nombre de las location en el código.
Comentarios
Publicar un comentario
1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.