Buscar

Limpiar filtro
Artículo
Jose-Tomas Salvador · 31 mar, 2020

Configurar un entorno para utilizar Docker en máquinas virtuales Ubuntu sobre Hyper-V de Windows 10

Esta vez quiero hablar de algo que no es específico de InterSystems IRIS, pero que creo que es importante si quieres trabajar con Docker y tu máquina de trabajo es un PC o portátil con Windows 10 Pro o Enterprise. Como probablemente sabes la tecnología de contenedores viene básicamente del mundo Linux y, a día de hoy, es en los hosts que corren Linux donde pueden mostrar su máximo potencial. Los que usamos Windows vemos que tanto Microsoft como Docker han hecho grandes esfuerzos estos últimos años y nos permiten correr contenedores Linux en nuestro sistema Windows de una manera muy sencilla... pero no está soportado para entornos productivos y, aquí viene el gran problema, no es fiable si queremos mantener persistencia de datos fuera del contenedor, en el sistema host,... debido principalmente a las importantes diferencias entre los sistema de archivos de Windows y Linux. Al final el propio Docker for Windows utiliza una pequeña máquina Linux virtual (MobiLinux) sobre la que realmente se levantan los contenedores.... lo hace de forma transparente para el usuario de windows... y de hecho funciona muy bien hasta que, como digo, quieren hacer que tus bases de datos sobrevivan más allá de la vida del contenedor... En fin,... que me enrollo,... el caso es que muchas veces, para evitar problemas y simplificar, lo que se precisa es de un sistema Linux completo... y, si nuestra máquina es Windows, la única forma de tenerlo es vía una máquina virtual. Al menos hasta que salga WSL2 en Windows 10 en unos meses, pero eso es otra historia. En este artículo te voy a contar, paso a paso, como instalar un entorno en el que puedas trabajar con contenedores Docker sobre un Ubuntu en tu servidor Windows. Vamós allá... 1. Activa Hyper-V Si no lo tienes ya activado, entra en añadir características de Windows y activa Hyper-V. Tendrás que reiniciar. 2. Crea una máquina virtual Ubuntu sobre Hyper-V Creo que no hay forma más fácil de crearse una máquina virtual (MV). Simplemente abre la venta de Administrador de Hyper-V, ve a la opción Creación Rápida... (a la derecha de la ventana) y crea tu máquina virtual en alguna de las versiones de Ubuntu que te ofrezca (podrías bajarte el iso de otra distribución de Linux y crearte la MV con una distro distinta). En mi caso he elegido la última versión disponible de Ubuntu, la 19.10. En todo caso, lo mismo que hagamos vale para la 18.04. En unos 15 o 20 minutos, según tarde en bajarse la imagen, tendrás tu nueva máquina virtual creada y lista. Importante: Deja las opciones de conmutador por defecto (Default Switch). Esto te dará acceso a Internet tanto desde el host windows como desde la máquina virtual. 3. Crea una red local Uno de los problemas de utilizar máquinas virtuales que yo me he encontrado a menudo tiene que ver con la configuración de red... unas veces funciona, otras no, funciona si estoy con Wi-fi pero no si estoy con cable o al revés,... si tenemos una VPN en el host windows, a lo mejor no nos va el internet en la MV o no tenemos acceso entre la MV y mi host (Windows),... en fin, un poco locura. Genera inseguridad si utilizas tu máquina para hacer desarrollo, pequeñas demos o presentaciones en las que muy probablemente no es tan importate tener acceso a internet como que la comunicación entre tu host y tu(s) MV(s) funcione de forma fiable. Con una red local ad-hoc, compartida por tu host Windows y tus máquinas virtuales, lo solucionas. Para comunicarse entre ellas, utilizas esa red y listo. Asignas IPs fijas a tu host y a las MVs que vayas creando y ya lo tienes. Hacerlo es muy fácil con estos pasos que te voy a dar. Simplemente debes ir a "Administrador de Conmutadores Virtuales" que encontrarás en tu Adminitrador de Hyper-V: Una vez dentro, ve a la opción Nuevo Conmutador de red virtual (vendría ser una nueva tarjeta de red): Nos aseguramos eso sí que sea una Red Interna, le damos el nombre que nos guste y el resto de opciones por defecto: Si ahora nos vamos al Panel de Control de Windows --> Centro de Redes y Recursos compartidos, veremos que tenemos ahí el conmutador que acabamos de crear: 4. Configura la Red Local compartida por el Host y las Máquinas Virtuales Ahora ya puedes terminar de configurar tu nueva red local... coloca el cursor sobre la conexión asociada a Mi Nuevo Conmutador LOCAL, pincha y ve a propiedades y de ahí al protocolo IPv4 para asignar una IP fija: Importante: La IP que asignes aquí, será la IP de tu host (Windows) en esta subred local. 5. Asocia y configura la nueva red local a la máquina virtual Ahora vuelve a la ventana del Administración de Hyper-V. Si tienes la MV arrancada debes pararla. Una vez parada, entra en su configuración y añádele el nuevo conmutador interno: (Nota.- En la imagen verás que hay otro conmutador, el Hyper-V Conmutador INTERNO , es para otra subred que tengo creada en mi caso. Pero no es necesario para este ejemplo) Cuando le des a Agregar, sólo te quedará seleccionar el conmutador que has creado antes: Bien, pues hecho esto... le das a Aplicar y Aceptar y listo. Ya puedes iniciar y entrar de nuevo en tu máquina virtual para terminar de configurar la conexión interna. Para ello, una vez rearrancada la máquina, pincha en el icono de red (arriba a la derecha) y verás que tienes 2 redes: eth0 y eth1. La eth1 aparece apagada... por ahora: Entra en la configuración de Ethernet (eht1) e indica una IP fija en esa subred local, por ejemplo: 155.100.101.1, la máscara 255.255.255.0 y listo. Ya tienes tu máquina virtual, identificada con la IP 155.100.101.1 en la misma subred que el host. 7. Permite el acceso a Windows 10 desde tu máquina virtual Lo que probablemente te encuentres es que Windows 10 no te permite conexiones desde otros servidores y, para él, la máquina virtual que acabas de crear es precisamente eso, un servidor externo... así que tendrás que añadir una regla en el Firewall para poder conectarte desde estas máquinas virtuales. ¿Cómo? Muy fácil, busca Firewall de Windows Defender en el Panel de Control de Windows, ve a Configuración Avanzada y crea una nueva Regla de Entrada: Indica un rango o rangos de puertos... (o también puedes indicar todos los puertos)... Como acción queremos que se permita la conexión... Para todos los tipos de red... Le das un nombre a la regla... E importante, inmediatamente a continuación, abre las propiedades de la regla que acabas de crear y limita el ámbito de aplicación, para que sólo se aplique en conexiones dentro de tu Subred local... 8. LISTO. Instala Docker y cualquier otra aplicación en tu nueva Máquina Virtual Ubuntu Una vez que hayas pasado por todo el proceso de instalación y tengas tu nueva MV lista y actualizada, con acceso a internet, etc... puedes instalarte las aplicaciones que quieras, ... como mínimo Docker, que de eso se trata en este caso, también puedes instalarte un cliente VPN corporativo, VS Code , Eclipse+Atelier... En concreto, para instalar Docker, entra en tu MV y sigue las instrucciones que verás aquí: https://docs.docker.com/install/linux/docker-ce/ubuntu/ Asegúrate de que Docker funciona, bájate alguna imagen de prueba, etc... Una vez que estés seguro de que todo está bien, estás listo. Y bueno, con esto... ¡ya lo tienes!, ahora podrás tener contenedores ejecutándose a pleno potencial en tu máquina virtual Ubuntu, con la que te podrás conectar desde tu host Windows 10, desde un navegador u otra aplicación y a la inversa, desde la máquina o máquinas Ubuntu, a tu host Windows 10. Todo ello utilizando direcciones IP locales en tu subred, que funcionarán independientemente de si tienes una VPN conectada o no, o de si estás por Wi-Fi o por cable. Ah... un último consejo. Si quieres intercambiar ficheros entre Windows 10 y tus máquinas virtuales, una opción muy sencilla y práctica es WinSCP. Es gratuito y funciona muy bien. Bueno, seguro que hay otras configuraciones... pero esta me ha servido a mí. Espero que te ayude también a tí y te evite dolores de cabeza. ¡Happy Coding! ¡ Hola Salva ! ( @Jose-Tomas.Salvador ) Sabes que no soy fanático de Docker Desktop for Windows. Pero me motivaste a probarlo.Mi contribución al concurso Open Exchange WebSocket Client JS with IRIS Native API as Docker Micro Server funciona tan bien en Windows como en Linux. Tiene un poco de ajuste de código común .Una sorpresa positiva. ¡ Gracias por la sugerencia ! ¡Hola Robert! Me alegro de que te haya servido!! Ahora toca probar con el soporte nativo a Linux que incorpora la última versión de Windows... veremos que tal el WSL2. Un fuerte abrazo. Este artículo está etiquetado como "Mejores prácticas" ("Best practices") (Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems).
Artículo
Joel Espinoza · 2 jul, 2019

Desarrollar un backend de servicios REST para una aplicación Angular 1.x con Caché - comencemos aquí

Resulta que un día estás trabajando en Widgets Direct, distribuidor líder de widgets y accesorios, y tu jefe te pide que desarrolles un nuevo portal dedicado a los clientes, el cual permita que la cartera de clientes tenga acceso a la siguiente generación de widgets... y él quiere que utilices Angular 1.x para comunicarte con el servidor Caché del departamento. Solamente hay un problema: Nunca has utilizado Angular y no sabes cómo hacer que se comunique con Caché (o IRIS).En esta guía veremos cómo se realiza todo el proceso de configuración de un conjunto de subsistemas en Angular, el cual se comunica con un backend de Caché utilizando JSON para llamar un API REST. Esta es la página principal de una serie que incluye varias partes sobre la creación de una aplicación con Documentos-Angular1x-REST-JSON-Caché. La lista de artículos disponibles es la siguiente:ArtículoEnlaceParte 1 - Configuración y Hello Worldhttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-1-many Parte 2 - "De modo que tu jefe te gritó por enviarle una página web con un Hello World sin formato" o por trabajar con JSONhttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-2Parte 3 - Vamos a iterar con algunos conjuntos y ng-repeathttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-3Parte 4 - Aplicación en la IUhttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-4Parte 5 - Manipulemos nuestros Widgetshttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-5Parte 6 - Regresemos a los Datos persistenteshttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-6Parte 7 - Las cosas van a rompersehttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-7Parte 8 - Puntos extra por el rompimientohttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-8Parte 9 - Veamos un poquito de CRUDhttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-9Parte 10 - Reconociendo los méritos de EDIThttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-10Parte 11 - Modificar nuestro formulariohttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-11Parte 12 - WWWidgetshttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-12Parte 13 - Cómo crear un Widgethttps://community.intersystems.com/post/lets-write-angular-1x-app-cach%C3%A9-rest-backend-part-13Requisitos previosEn estos ejemplos se utilizó Caché 2016.2, donde se implementó una nueva sintaxis para manejar el contenido en JSON. En Caché 2016.1 había soporte preliminar para JSON, pero la sintaxis es diferente e incompatible con las versiones posteriores. El código que se mostró en estos ejemplos fue escrito de una forma que pueda adaptarse fácilmente a la sintaxis de 2016.1, ya que no se utilizó ninguna de las funciones extendidas de JSONAunque hay cierto grado de compatibilidad con REST y JSON en las versiones anteriores, recomiendo ampliamente conocer más sobre la versión 2016.1 como un requisito mínimo.En este tutorial no veremos Angular detalladamente. Pero hay muchos cursos excelentes sobre los Principios básicos de Angular, que están disponibles de forma gratuita en línea y se mencionan en las Partes 1 y 2. Tanto AngularJS como Angular 2 y superiores son ampliamente compatibles con la tecnología InterSystems, ya sea Caché 2016.2 como IRIS 2019.1 permiten implementar APIs REST y WebServices con rapidez y simplicidad.EnlacesEl código que se utilizó en estos ejemplos podrá consultarse en Github poco tiempo después de que se publique el artículo. Puedes tener acceso a él desde https://github.com/iscChris/CacheRESTStack
Artículo
Bernardo Linarez · 29 ago, 2019

Conectarse a Caché con SQuirreL SQL, un cliente externo de SQL

¡Hola a tod@s!El Portal de Administración del Sistema Caché incluye una potente herramienta de consultas en SQL basada en la web, aunque para algunas aplicaciones lo más conveniente es utilizar un cliente dedicado SQL que esté instalado en la PC del usuario.SQuirreL SQL es un conocido cliente SQL de código abierto construido en Java, que utiliza JDBC para conectarse a un DBMS. Como tal, podemos configurar SQuirreL para que se conecte a Caché usando el controlador JDBC en Caché.Encontrar el controlador JDBC de Caché en archivos JAREl archivo JAR que contiene el controlador JDBC de Caché se instala automáticamente por el instalador de Caché cuando se instala una instancia completa de Caché o cuando se instalan únicamente los componentes de cliente. Puede encontrarse en el directorio lib, debajo del directorio de instalación principal.Para esta instalación Caché del cliente en un equipo PC con Windows, el archivo JAR puede encontrarse en la siguiente ruta:C:\InterSystems\CACHEClient\lib\cachejdbc.jarInstalación de SQuirreL SQLSQuirreL SQL puede descargarse desde el sitio web principal de SQuirreL:http://www.squirrelsql.org/Siga las instrucciones del sitio web para instalar SQuirreL SQL y compruebe que funciona adecuadamente.Añadir una entrada para el controlador en SQuirreL SQLAbra SQuirreL SQL y seleccione la pestaña "Drivers" que se encuentra en el lado izquierdo de la ventana. Haga clic en el icono “+” para crear una nueva entrada del controlador.En el cuadro de diálogo "Add Driver", seleccione la pestaña "Extra Class Path" y haga clic en "Add" para añadir una nueva entrada para el archivo JAR del controlador JDBC de Caché.En la parte superior de la ventana "Add Driver", introduzca un nombre para el controlador como “Caché 2016.1”. Al crear una conexión hacia una base de datos se mostrará una URL de ejemplo, la cual sirve como una excelente referencia. Para "Example URL", introduzca este ejemplo de una URL:jdbc:Cache://127.0.0.1:56772/Samples O bien, puede utilizar una URL que etiquete cada parte de la URL:jdbc:Cache//[HOST NAME OR IP]:[SUPERSERVER PORT]/NAMESPACEHaga clic en el botón "List Drivers" que se encuentra a la derecha de la ventana "Add Drivers" y, a continuación, seleccione "com.intersys.jdbc.CacheDriver" en la lista desplegable Class Name.Más información sobre la configuración de la conexión JDBC y las propiedades del controlador JDBC de Caché, en la siguiente documentación:http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BGJD_connecting#BGJD_connecting_urlhttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BGJD_connecting#BGJD_connecting_connpropsHaga clic en "OK" para guardar la nueva entrada del controlador.Incorporar una entrada para la conexión (Alias)En SQuirreL SQL, un perfil de conexión para el servidor de una base de datos específica se llama Alias. Seleccione la pestaña "Aliases" que se encuentra en la parte izquierda de la ventana principal y haga clic en el icono "+" para añadir un nuevo alias.En la ventana "Add Alias", introduzca un nombre para este Alias. Seleccione nuestro nuevo controlador en el menú desplegable.Después de seleccionar el controlador, el cuadro URL se completará automáticamente utilizando la URL de ejemplo que añadimos a la configuración del controlador. Edite esta URL para utilizar el nombre de host o dirección IP correctos y el número de puerto del super servidor para el servidor de Caché, y configure el espacio de nombres correcto.Introduzca el nombre de usuario y la contraseña de un usuario de Caché, que tenga los privilegios SQL adecuados.Haga clic en el botón "Test" y compruebe que la conexión se realizó correctamente.Haga clic en OK para guardar el nuevo Alias.Conectarse a CachéAhora puede conectarse al servidor de la base de datos haciendo doble clic en la entrada de la pestaña Alias que se encuentra en la ventana principal.Para obtener instrucciones sobre cómo utilizar SQuirreL SQL para ejecutar consultas, visualizar esquemas de información y realizar otras tareas, consulte la documentación de la aplicación en el sitio web de SQuirreL SQL. ¿Como se pueden crear los usuarios? Hola Paola ! Si te refieres a los usuarios de Caché / IRIS / Ensemble, estos se crean en la SMP (System Management Portal), en la opción System Administration > Security > Users. Cuando instalas el producto en el modo de seguridad intermedio, normalmente te pide que ingreses una contraseña, esta será asignada a los usuarios SuperUser, _SYSTEM y Admin. Puedes usar alguno de ellos para realizar directamente consultas SQL en Squirrel, pero también puedes ingresar con ellos al SMP para crear un usuario particular que será usado para usar Squirrel. Muchos éxitos.
Artículo
Robert Cemper · 1 feb, 2022

El futuro de ObjectScript

Si desarrollas en IRIS, te enfrentas a dos fenómenos principales: un motor de almacenamiento de datos increíblemente rápido y con un excelente diseño un lenguaje para trabajar en este motor de almacenamiento, llamado ObjectScript Motor de almacenamiento Se trata de un concepto de almacenamiento organizado jerárquicamente y se consideró "anticuado" cuando "relacional" era la palabra de moda del momento.Sin embargo, si usas sus fortalezas, con su sencilla y eficiente construcción, supera a todos sus competidores "gigantes".Y esto sigue siendo cierto desde el día 1. Me sorprende cómo esos competidores copiaron a lo largo del tiempo varias funciones de este motor de almacenamiento, confirmando indirectamente la calidad del concepto básico. ObjectScript Mirando el lenguaje, es bastante fácil separar el subconjunto perfectamente delimitado de elementos del lenguaje que manipulan y navegan por el motor de almacenamiento, que ha visto solo algunas extensiones y ajustes a lo largo de su vida. Yo llamo a esto el NÚCLEO.Sus compañeros son la Navegación (en su mayoría similar a otros lenguajes) y la Estética (todo para manipular el contenido de los datos). La navegación es un requisito estructural inevitable, pero ha tenido varias mejoras, principalmente para comodidad de los programadores. No es obligatorio, pero es similar a los lenguajes más nuevos. La estética es el campo de más rápido crecimiento y hasta hoy sus propuestas me sorprenden de vez en cuando. Su existencia viene de cuando el motor de almacenamiento también formaba parte de un sistema operativo (y nada más) sobre el HW. Historia Desde el pasado, los desarrolladores solían escribir aplicaciones con ObjectScript. Incluso InterSystems hizo esto a gran escala (Interoperability, es decir, Ensemble), Analytics (es decir, DeepSee, Health*...). ObjectScript era/es el "puede con todo, hace de todo". Pero ObjectScript era más bien un "llanero solitario". El intento de tener algo más popular en el mercado fue BASIC (ya era un "caballo muerto" en ese momento). La oportunidad de saltar a la ola de JavaScript se perdió ~ hace unos 15 años. La disponibilidad de una amplia gama de adaptadores de lenguajes pudo ocultar la dimensión del problema durante algún tiempo, pero nunca alcanzó la fuerza que el principal competidor mostró cuando PL/SQL fue congelado y reemplazado por Java. Hoy en día Veo que el número de desarrolladores formados en ObjectScript se reduce con el tiempo por puros efectos demoscópicos. Encontrar y educar desarrolladores que quieran escribir en ObjectScript es un ejercicio duro, que experimenté personalmente varias veces. E incluso si son brillantes y muy hábiles, no es garantía de que se queden con uno. La demanda sigue siendo espectacular. Delante de este trasfondo, veo la llegada de Python embebido como lenguaje completo al mismo nivel que ObjectScript como un gran paso adelante con IRIS.La navegación y la estética están cubiertas y añadir las funcionalidades NUCLEARES no requiere un aprendizaje dramático. De modo que vemos un mercado mucho más grande de recursos de desarrollo especializados que acaban con la limitación que en ese aspecto teníamos con ObjectScript . Futuro Con Python, veo mucha energía nueva entrando en el mundo de IRIS. Después de todo, es un paso que algunos competidores dieron hace bastante tiempo y el inconveniente de "nunca visto en ningún otro lugar" queda superado. El paradigma: "Lo programo porque es posible"; se romperá. Nos libera de una serie de ruedas re-inventadas. ObjectScript seguirá funcionando en paralelo y tendrá un papel importante en todas las actividades cercanas al NÚCLEO de almacenamiento. Nadie (seguramente) lo usará para lógica empresarial o (espero que no) para replicar interfaces que ya están disponibles fuera a mayor escala. ObjectScript se reducirá a una dimensión que vemos hoy para C, C ++ o para código en cualquier lenguaje ensamblador. Las aplicaciones existentes no se verán afectadas. La forma en que se integró Python parece ser una garantía para una migración sin problemas y sin presión de tiempo. Los desarrolladores de ObjectScript de hoy no perderán sus trabajos. Hay suficientes tareas complejas en torno al diseño y mantenimiento de bases de datos. Pero podrían deshacerse de tareas poco atractivas (en mi opinión): estilos CSS, colorear páginas web,. . . . Sin olvidar el exigente desafío de leer y comprender el código existente e interpretar y comunicar su contenido y lógica. Los "Golf Code" (1) pueden ser divertidos, pero se trata de una lógica de negocio muy seria en muchas aplicaciones escritas tradicionales antiguas e incluso en algunas utilidades en "%SYS". Veo a los desarrolladores de hoy como los sacerdotes de ObjectScript, del mismo modo que los sacerdotes egipcios que comprenden los jeroglíficos y leen "El libro de los muertos" y celebran sus milagros. 15 de julio de 2021
Artículo
Esther Sanchez · 5 oct, 2022

Cómo aprovechar al máximo las publicaciones en la Comunidad de Desarrolladores

¡Hola Comunidad! ¿Sabéis cómo publicar en la Comunidad de Desarrolladores? ¿Y conocéis todos los tipos de publicaciones que hay? ¿Y sabéis que podéis, por ejemplo, publicar encuestas en una publicación? ¿o adjuntar PDFs? Si queréis sacar el máximo partido a las publicaciones y, por tanto, a la Comunidad... seguid leyendo, porque os vamos a contar tooooodos los detalles de las publicaciones: Reglas generales Preguntas Artículos y Anuncios Debates Reglas generales Para empezar a participar en la Comunidad, haced clic en el botón "Nueva publicación" arriba del todo en la página de inicio de la Comunidad: Aparecerá el editor para crear una Pregunta, un Anuncio, un Artículo o un Debate. Cada tipo de publicación tiene su propio conjunto de campos, unos obligatorios y otros opcionales. Primero vamos a hablar sobre los campos comunes a todos los tipos de publicación y después veremos los particulares de cada uno. Básicamente, todas las publicaciones tienen un Título*, Cuerpo*, Etiquetas y otras opciones adicionales. Todos los campos marcados con un asterisco (*) son obligatorios. Así que primero hay que elegir el tipo de publicación: Pregunta, Anuncio, Artículo o Debate. Después, hay que expresar la idea principal de forma clara y concisa y escribirla en el Título. En el Cuerpo de la publicación hay que escribir lo que quieres compartir con la Comunidad. Hay dos opciones de edición: WYSIWYG o Markdown. Elegid la que prefiráis y el resultado será el mismo, por supuesto. vs. Después de escribir el texto, hay que escoger el Grupo, que es la tecnología, producto o servicio de InterSystems del que vais a hablar en la publicación. Después, en el campo Etiquetas se pueden añadir etiquetas relacionadas con el contenido de la publicación. Hay muchas etiquetas, así que hay que elegir las adecuadas, porque otras personas utilizan las etiquetas para buscar información en la Comunidad. Después hay un enlace para ver más opciones. Estas son: Adjuntar PDF, indicando el nombre del documento. Añadir una encuesta. Hay que rellenar los campos con la pregunta, posibles respuestas, duración de la encuesta, etc. Una vez cumplimentados todos los campos, se puede visualizar la publicación en Vista previa para comprobar cómo se verá una vez publicada; se puede Guardar para seguir editándola después; o se puede Publicar. También se puede programar la publicación. Solo hay que hacer clic en la flecha hacia abajo, elegir Programar publicación y fijar el día y la hora cuando se va a publicar la publicación. Por último, hay que hacer clic en Programar publicación y se publicará el día y la hora escogida. Y básicamente estos son los campos comunes a todos los tipos de publicaciones. Preguntas Como su nombre indica, hay que escoger este tipo de publicación cuando necesitéis ayuda y/o queráis consultar algo. En la Comunidad de Desarrolladores en español hay un montón de especialistas y alguien puede haberse encontrado en vuestra misma situación. Así que no hay que dudar en preguntar... y en responder! 😉 Al escribir una pregunta, hay que formular la idea principal y escribirla como título. A continuación, elegir la "Versión del producto" que estéis utilizando, porque diferentes versiones tienen diferentes funcionalidades y clases; y una respuesta puede ser válida para unas versiones, pero no para otras. Para ser incluso más preciso, se puede indicar en el campo $ZV la versión completa que se está usando. Para obtenerla, se puede abrir Terminal y ejecutar el comando: write $ZV Se puede hacer lo mismo en el IDE que estéis usando o verlo en el "Management Portal": El resto de campos de la pregunta son los mismos que hemos descrito anteriormente. Artículos y Anuncios Los Artículos se utilizan para compartir conocimientos y/o tu experiencia. Y los Anuncios para dar una noticia, anunciar algo, etc. Estos dos tipos de publicaciones tienen los campos comunes y otros adicionales, como son: Anuncio anterior, Anuncio siguiente y Enlace a la aplicación en Open Exchange. Es decir, si el Artículo/Anuncio está relacionado con otro, se puede añadir el enlace de ese Artículo/Anuncio anterior en el campo "Artículo/Anuncio anterior" y así, al final de la publicación, se pueden ver todas las publicaciones relacionadas. No hay que abrir el Artículo/Anuncio anterior para enlazarlo con la nueva publicación, ya que se hace automáticamente. También se puede ir fácilmente de una publicación a otra con los botones de navegación situados en la parte de arriba de los artículos que están relacionados. Por último, si la publicación está relacionada con una aplicación en Open Exchange, se puede añadir el enlace al proyecto en el campo "Enlace a la aplicación en Open Exchange". Debates Si queréis comentar alguna funcionalidad o compartir vuestra experiencia o preguntar la opinión de otros desarrolladores, se puede iniciar un Debate. Este tipo de publicación tiene todos los campos comunes y también los enlaces a Publicación anterior y Siguiente publicación. ¡Y eso es todo lo que necesitáis saber para participar en la Comunidad y aprovechar al máximo todo lo que ofrece! Esperamos que os resulte útil... ¡y esperamos vuestros comentarios!
Artículo
Alberto Fuentes · 6 jul, 2021

Cómo aprovechar las consultas y ObjectScript con el framework AppS.REST

Hace un tiempo se publicó el [paquete AppS.REST](https://community.intersystems.com/post/appsrest-new-rest-framework-intersystems-iris). AppS.REST es un *framework* para exponer fácilmente clases persistentes de IRIS como recursos REST. Las clases que tienen habilitado AppS.REST soportan operaciones CRUD con poco esfuerzo del desarrollador, acortando la brecha entre los datos persistentes en IRIS y los consumidores de datos, como una aplicación front-end de Angular. ¡Pero las clases de IRIS son mucho más que una simple definición para cargar y guardar registros individuales! Este artículo tiene como objetivo destacar algunas maneras de aprovechar el poder de IRIS en tus aplicaciones REST.  Usando la aplicación de ejemplo Phone.Contact, veremos el soporte de consultas incluido en AppS.REST, el uso de consultas de clase y finalmente los métodos ObjectScript. ## Preparación Puedes encontrar el [paquete AppS.REST en Open Exchange](http://openexchange.intersystems.com/package/apps-rest). Este artículo utilizará ejemplos de la [aplicación Sample Phonebook (disponible en github)](https://github.com/intersystems/apps-rest/blob/master/docs/sample-phonebook.md).  Ambos paquetes se pueden instalar fácilmente con Objectscript Package Manager, que se puede encontrar [aquí](https://openexchange.intersystems.com/package/ObjectScript-Package-Manager-2%C2%A0%C2%A0).  Una vez instalados, la aplicación Sample Phonebook generará algunos datos ficticios y configurará la aplicación web necesaria. Esa aplicación web reenviará todas las solicitudes a la clase Sample.Phonebook.REST.Handler. Como estoy haciendo las pruebas en mi equipo local, todas mis solicitudes http estarán en `http://localhost:52773/csp/USER/phonebook-sample/api/`, pero tu servidor y tu puerto pueden ser diferentes a los míos. Utilicé la extensión Talend API Tester de Google Chrome para probar todas estas llamadas REST, pero hay muchas buenas herramientas API disponibles. ## Cómo habilitar solicitudes simples Echemos un vistazo a la clase Model.Person, que está habilitada para REST al extender de AppS.REST.Model.Adaptor. Vemos que se muestra como el recurso "contact", como se define en el parámetro RESOURCENAME. Class Sample.Phonebook.Model.Person Extends (%Persistent, %Populate, %JSON.Adaptor, AppS.REST.Model.Adaptor) { Parameter RESOURCENAME = "contact"; Ten en cuenta que aunque la clase IRIS se llama "Person", estamos mostrando la clase como el recurso "contact" a nuestros consumidores de REST. Empezaremos con una solicitud básica de GET para demostrar como un consumidor de datos puede interactuar con este recurso:   `http://localhost:52773/csp/USER/phonebook-sample/api/contact/2` { "_id": "2", "name": "Harrison,Angela C.", "phones":[ {"_id": "2||15","number": "499-388-2049","type": "Office"}, {"_id": "2||32","number": "227-915-3954","type": "Mobile"} ]} Genial, ¡ya estamos consumiendo datos de IRIS a través de REST!  ## Consultas pre-construidas listas para usar Lo anterior es bueno para cargar una instancia particular conocida de un recurso, pero ¿qué sucede si quieres aprovechar las funcionalidades de consulta de datos de IRIS? El *framework* AppS.REST nos da algunas capacidades de consulta listas para usarse directamente. Por ejemplo, la siguiente solicitud GET busca todos los contactos: `http://localhost:52773/csp/USER/phonebook-sample/api/contact` Fíjate que tiene el mismo aspecto que la anterior, sólo que sin especificar el ID del contacto. Para solicitar contactos con un nombre determinado, podemos añadir un parámetro URL: `http://localhost:52773/csp/USER/phonebook-sample/api/contact?name[eq]=Harrison,Angela C.` [{ "_id": "2", "name": "Harrison,Angela C.", "phones":[{"_id": "2||15", "number": "499-388-2049", "type": "Office"…] }] Podemos imaginar una aplicación que permita al usuario buscar contactos por medio de las primeras letras de su apellido. Podemos conseguirlo con otro operador: `http://localhost:52773/csp/USER/phonebook-sample/api/contact?name[stwith]=Harri` Ahora recuperamos todos los contactos cuyos nombres empiezan con "Harri": [{ "_id": "2", "name": "Harrison,Angela C.", "phones":[{"_id": "2||15", "number": "499-388-2049", "type": "Office"…] },{ "_id": "47", "name": "Harrison,Yan N.", "phones":[{"_id": "47||26", "number": "372-757-5547", "type": "Mobile" },…] }] AppS.REST admite 7 operadores que se traducen directamente a SQL:         "lte": " < "         "gte": " > "         "eq": " = "         "leq": " <= "         "geq": " >= "         "stwith": " %startswith "         "isnull": " es nulo" ## Cómo exponer consultas de clase como acciones REST ¿Qué pasa si queremos aprovechar consultas más complicadas o previamente existentes en nuestra aplicación REST?  ¡Podemos exponer cualquier consulta de clase como una acción REST! La clase Person tiene una consulta llamada FindByPhone, que utiliza la tabla `PhoneNumber` para buscar personas por un phoneFragment: Query FindByPhone(phoneFragment As %String) As %SQLQuery { select distinct Person from Sample_Phonebook_Model.PhoneNumber where $Translate(PhoneNumber,' -+()') [ $Translate(:phoneFragment,' -+()') } El *framework* AppS.REST generará automáticamente la representación de JSON apropiada a partir de los ID que devuelve la consulta. Para exponer la consulta como acción REST, añadimos una entrada en el bloque XData llamado ActionMap de la clase Person para definir un endpoint "find-by-phone" en el recurso contact: ``` XData ActionMap { } ``` Esa acción ahora se dirige a la consulta de clase de FindByPhone, y espera un argumento para phoneFragment como parámetro de la URL. Ahora, si hacemos una solicitud GET al endpoint $find-by-phone del recurso de contacto, obtendremos todos los contactos que tengan un número de teléfono que coincida:http://localhost:52773/csp/USER/phonebook-sample/api/contact/$find-by-phone?phoneFragment=641 [{ "_id": "19", "name": "Ipsen,Rob Z.", "phones":[{"_id": "19||211", "number": "641-489-2449", "type": "Home"}] },{ "_id": "86", "name": "Newton,Phil Y.", "phones":[{"_id": "86||108", "number": "380-846-4132", "type": "Mobile"}] }] Cómo exponer métodos de ObjectScript como acciones REST Mostrar consultas puede ser útil, pero ¿qué pasa si queremos mostrar el código de la aplicación que está escrito en ObjectScript? Echemos un vistazo al método AddPhoneNumber de Model.Person: Method AddPhoneNumber(phoneNumber As Sample.Phonebook.Model.PhoneNumber) As Sample.Phonebook.Model.Person {     Set phoneNumber.Person = $This     $$$ThrowOnError(phoneNumber.%Save())     Quit $This } Este método de instancia se llama para añadir un número de teléfono a una persona que ya existe en la base de datos, así que vamos a ver cómo se puede mostrar como una acción REST, al igual que el ejemplo de consulta de clase anterior. En el ActionMap de Model.Person, vemos otra entrada que llama al método AddByPhone: <action name="add-phone" target="instance" method="POST" call="AddPhoneNumber"> <argument name="phoneNumber" target="phoneNumber" source="body" /> </action> A diferencia de la acción find-by-phone, esta se dirige a una instancia de Model.Person, por lo que nuestra solicitud de URL deberá incluir un ID después del recurso: http://localhost:52773/csp/USER/phonebook-sample/api/contact/2/$add-phone El *framework* AppS.REST automáticamente creará una instancia de un objeto para la persona con ID=2, y ejecutará el método de instancia en él. El Action Map define esto como una acción POST, y espera un phoneNumber en la estructura de la solicitud. AppS.REST traducirá automáticamente la estructura JSON de la publicación en una instancia de la clase Phone, ya que ésta también se extiende a AppS.REST.Model.Adaptor. Poniendo todo junto, ejecutamos POST en http://localhost:52773/csp/USER/phonebook-sample/api/contact/2/$add-phone con la siguiente estructura: {"number":"123-456-7890","type":"Mobile"} Si la solicitud tiene éxito, el número de teléfono se añade a nuestro contacto y recibimos de vuelta el objeto Person completo, con el nuevo número de teléfono incluido: { "_id": "2", "name": "Harrison,Angela C.", "phones":[ {"_id": "2||301", "number": "123-456-7890", "type": "Mobile"}, {"_id": "2||15", "number": "499-388-2049", "type": "Office"}, {"_id": "2||32", "number": "227-915-3954", "type": "Mobile"} ]} Las acciones son muy flexibles, lo que te permite mostrar casi cualquier funcionalidad de las clases a tus consumidores REST. Conclusión Espero que esta rápido vistazo de la aplicación Sample.Phonebook haya demostrado las muchas maneras en que el *framework* AppS.REST facilita la exposición rápida y sencilla de las clases de IRIS a través de REST.  AppS.REST.Model.Adaptor permite habilitar operaciones CRUD sobre REST para tus clases IRIS con muy pocos pasos manuales. Mostrar las consultas de clase y los métodos de ObjectScript se puede hacer definiendo Acciones en el ActionMap de tu clase.
Artículo
Alberto Fuentes · 27 abr, 2023

Cómo mantener contenta a la API: limpieza de las utilidades SQL

Con IRIS 2021.1, realizamos una importante revisión de nuestra API de utilidades SQL en [`%SYSTEM.SQL`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL). Sí, eso fue hace algún tiempo, pero la semana pasada un cliente hizo unas preguntas sobre ello y entonces @Tom.Woodfin me empezó a presionar un poco ;-) para que describiera con más detalle en la Comunidad de Desarrolladores las razones de estos cambios. ¡Así que allá vamos! A principios de 2020, la API `%SYSTEM.SQL` pasó de ser un útil envoltorio de clases alrededor de unas rutinas clave a un gran número de puntos de acceso no tan coherentes para diversas funcionalidades relacionadas con SQL. Eso no debería sorprendernos, ya que el motor SQL de IRIS (y antes el motor SQL de Caché) creció mucho en funcionalidades y facilidad de uso. Como nos preocupamos mucho por la compatibilidad con las versiones anteriores, casi todos los cambios de la API fueron una incorporación neta, un nuevo método o una extensión de la estructura de un método. En algunos casos, este objetivo de compatibilidad con las versiones anteriores significó que el intento de simplificar las cosas mediante la eliminación de un argumento de métodos (para algo que ahora estaba automatizado) se diluyó al eliminar el argumento en la clase de referencia, pero dejándolo en su lugar para no alterar el código que lo utilizaba. En otras palabras, la API comenzó a parecerse a un famoso plato italiano que no es una pizza. _Si a estas alturas piensas: "¡Qué lío has hecho, eso nunca nos pasaría a nosotros!" - puedes dejar de leer aquí. _ _... Creo que aún sigues ahí :-)_ Probablemente sea justo decir que este tipo de crecimiento orgánico es inevitable y, en cierto modo, no necesariamente negativo, ya que significa que progresas y te preocupas por tus usuarios, ya que no destruyes su código al adaptar la API entre cada versión. Pero en algún momento hay que sacar la escoba y limpiar. Y eso es lo que hicimos en 2020.3 con las herramientas SQL de IRIS. ## Ordenando las cosas El primer problema que queríamos resolver era la gran cantidad de métodos, que eran demasiados para que una sola API fuera manejable. Así que empezamos por dividir la clase en una serie de clases API más pequeñas y más centradas en el paquete `%SYSTEM.SQL`, lo que significaba que podríamos conservar la agradable sintaxis abreviada de $SYSTEM. * [`SYSTEM.SQL.Functions`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Functions) – por si no lo sabes, `%SYSTEM.SQL` tiene equivalentes en ObjectScript para todas las funciones escalares simples de IRIS SQL, como `%SYSTEM.SQL.ABS()`. * [`SYSTEM.SQL.PTools`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.PTools) – las [Herramientas para analizar rendimiento](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQLOPT_querytoolkit) son un grupo de utilidades para analizar en profundidad el rendimiento de las consultas individuales. Este avanzado conjunto de herramientas lo utilizan principalmente el servicio de Soporte de InterSystems y los clientes más experimentados, pero es una API muy potente y bien documentada, por lo que merece la pena echarle un vistazo si necesitas resolver esa dichosa consulta * [`%SYSTEM.SQL.Schema`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Schema) – la verificación de la existencia de tablas, la importación o exportación de sentencias DDL y otras funciones para consultar o modificar objetos del esquema ahora están aquí. * [`%SYSTEM.SQL.Security`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Security) – agrupa los puntos de acceso ObjectScript para los comandos GRANT y REVOKE * [`%SYSTEM.SQL.Statement`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Statement) – como probablemente sabrás, InterSystems IRIS incluye una completa gestión y almacenamiento de sentencias SQL. Esta clase agrupa métodos para administrar el contenido del [Índice de sentencias](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQLOPT_sqlstmts), como importar y exportar planes, así como freezing y thawing, si se quiere forzar el uso de alguna en particular. * [`%SYSTEM.SQL.Stats.Table`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Stats.Table) – Aquí es donde puedes recopilar [estadísticas de las tablas](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQLOPT_opttable#GSQLOPT_opttable_tunetable) y, opcionalmente, anularlas, importarlas o exportarlas. Es un nivel más profundo que los demás, ya que hay otros elementos sobre los que nos gustaría recopilar y gestionar estadísticas en el futuro. * [`SYSTEM.SQL.Util`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL.Util) – No todo encaja en un cajón temático limpio, así que aquí es donde ponemos los restos: la funcionalidad que queríamos conservar de %SYSTEM.SQL, pero para la que no encontramos un lugar más adecuado. Solo un número muy reducido de tareas extremadamente comunes, como [`%SYSTEM.SQL.Explain()`](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.SQL#Explain), se quedaron en la clase de nivel superior. Por supuesto, todavía nos preocupamos por los usuarios con un código que llama a los puntos de acceso preexistentes, por lo que hemos dejado todos los métodos originales en la clase `%SYSTEM.SQL` y los hemos marcado como internos y obsoletos usando palabras clave para los métodos, dejando una nota sobre la nueva ubicación preferida de la función. Una de las ventajas de este enfoque con nuevas clases es que no tenemos que preocuparnos de que lo nuevo se interponga en el camino de lo que ya existe. En la mayoría de nuestras API REST (como /api/deepsee/ y /api/iknow/*), introdujimos un número de versión en la propia URL de la API (por ejemplo, /api/iknow/v1/namespace/domains) para tener en cuenta estos cambios en la API. También probamos brevemente esa idea aquí, pero pensamos que quedaría muy mal en el nombre de una clase y no invitaría a los usuarios a adoptarla en vez del punto de acceso base que ya existe y está obsoleto. Los nuevos puntos de acceso también son un poco más extensos, pero al menos el token extra tiene sentido con respecto a lo que intenta conseguir. De manera implícita, esto significa que suponemos con cierto optimismo que nunca volveremos a tener que cambiar estas API, así que vamos a echar un vistazo al otro cambio importante que hemos hecho: revisar las estructuras de los métodos. ## Limpieza profunda Como hemos descrito en la introducción, no solo veíamos un número desmesurado de métodos, sino que algunos tenían una estructura desmesurada en el mismo método. Algunos buenos ejemplos eran los métodos TuneTable() y Export(), a los que se les agregaron un gran número de indicadores a lo largo de los años. Normalmente, los argumentos más interesantes son los nuevos del final, y habitualmente veía incluso a los desarrolladores más experimentados contar las comas antes de poner ese 1 o 0 para anular un valor (esperamos que decente) predeterminado. Claramente era un área que podíamos mejorar, y así lo hicimos. La primera pregunta que debemos hacernos es, obviamente, si merecía la pena mantener cada argumento. Algunos han sido superados con el tiempo o por nuevas funcionalidades, y pueden omitirse sin problemas. Después, pusimos los más importantes y obligatorios al principio y agrupamos todos los indicadores opcionales en un nuevo argumento _qualifiers_, de naturaleza similar a los calificadores utilizados en varios métodos %SYSTEM.OBJ. Admitimos un objeto dinámico o un formato heredado. El primero se puede pasar como una instancia %DynamicObject, o en formato de cadena JSON, como { "silent": 1, "logFile": "/tmp/xyz.log" }. El último utiliza el mismo formato que en `%SYSTEM.OBJ` usando barras, en el que los calificadores anteriores podrían expresarse como "/silent /logFile=/tmp/xyz.log". Este mecanismo ha sido una excelente forma de ofrecer métodos API fáciles de usar, de documentar y de evolucionar. Además de cambiar la lista de argumentos, también hicimos otras cosas más sencillas, como un uso más estandarizado de los valores de retorno %Status y los parámetros ByRef. Y por último, pero no por ello menos importante, cambiar los nombres de los métodos para que sean más uniformes y autoexplicativos, a menudo facilitado por el paso a un subpaquete con nombre razonable. ¿Sabrías decir sin mirar si el antiguo método `%SYSTEM.SQL.Export()` exportaba datos o información del esquema? ¡No tengo nada más que decir! ## Pensando en el futuro ¿Ya terminamos? Definitivamente no. Estoy muy agradecido por el tiempo que el equipo ha dedicado a revisar este cambio de especificación, implementación y pruebas, pero cuando lo lanzamos con 2020.3, ya habíamos identificado algunos casos en los que podríamos haber ido un poco más lejos o haber simplificado aún más las cosas. Continuaremos buscando oportunidades de este tipo y, ahora que la mayor parte de esta primera gran limpieza se ha llevado a cabo, podemos impulsar de forma práctica cambios más pequeños que se ajusten a las normas descritas anteriormente. Si estás buscando una razón por la que antes podías llamar a `%SYSTEM.SQL.FreezePlans(1, 3, "MyTable")` y ahora se recomienda usar `%SYSTEM.SQL.Statement.FreezeRelation("MyTable")`, espero que esto te haya sido útil. Si buscabas consejos generales sobre la evolución de la API, espero que al menos esto no te haya parecido una pérdida de tiempo :-) y estaré encantado de escuchar vuestras opiniones sobre cómo lo hicimos y cómo lo haríais vosotros de otra manera. No hay absolutamente ninguna ciencia exacta en lo anterior. Adoptamos un enfoque pragmático, para intentar que las cosas fueran más fáciles de utilizar sin cambiar los métodos por el placer de hacerlo y tratando de economizar en tiempo de desarrollo y pruebas. Esperamos que, con el tiempo, también ahorre tiempo en el aprendizaje y el uso de la API, pero sobre todo, ¡que ahorre el tiempo dedicado a contar las comas!
Artículo
Eduardo Anglada · 7 sep, 2021

ObjectScript sobre ODBC

Este es un ejemplo de código que funciona en IRIS 2020.1 y en Caché 2018.1.3 No se mantendrá sincronizado con las nuevas versiones. Y NO cuenta con el servicio de soporte de InterSystems. De vez en cuando, puedes encontrarte una situación en la que, por diferentes razones, ODBC es la única opción para acceder a un sistema remoto. Lo cual es suficiente mientras necesites examinar o cambiar tablas. Pero no puedes ejecutar directamente algunos comandos o cambiar algunos globals. En este artículo vamos a ver 3 procedimientos SQL que permiten acceder a los globals usando ODBC. SQLprocedure Ping() devuelve Server::Namespace::$ZV. Permite verificar la conexión. SQLprocedure Xcmd(<commandline>,<resultvar>) ejecuta la línea de comandos que envías y devuelve el resultado en <resultvar>. SQLprocedure Gset(<global>,<subscript>,<value>,<$data>) te permite establecer o eliminar un global. <global> es un nodo del global perteneciente al *namespace* del servidor remoto. Hay que incluir el símbolo inicial, por ejemplo, '^MyGlobal' . <subscript> representa el subíndice completo incluyendo los paréntesis, por ejemplo: '(1,3,"something",3)' . <value> es el valor que queremos establecer. <$data> controla si se establece el Nodo global o se ejecuta un ZKILL en él, por ejemplo: 1, 11 para establecer el valor. 0,10 para borrar (ZKILL) el valor. El procedimiento Gset está diseñada para hacer uso del Global Scanning descrito en otro artículo. Gset permite copiar globals a través de cualquier conexión ODBC. Instalación: - En el sistema remoto necesitas la clase que se encuentra en OpenExchange.- Además necesitas definir los procedimientos como "Linked SQL Procedures", para ello emplea este asistente: SMP>System>SQL> Wizards>Link Procedure En los ejemplos se usa el namespace rccEX.- Si quieres ejecutar la copia de globals también necesitas instalar la clase Global Scanning desde OEX Ejemplos: USER>do $system.SQL.Shell() SQL Command Line Shell [SQL]USER>>select rccEX.Ping() Expression_1 cemper9::CACHE::IRIS for Windows (x86-64) 2020.1 (Build 215U) Mon Mar 30 2020 20:14:33 EDT Verifica la existencia del global ^rcc [SQL]USER>>select rccEX.Xcmd('set %y=$d(^rcc)','%y') ok: 10 Establece algún valor en ^rcc4(1,"demo",3,4) [SQL]USER>>select rccEX.Gset('^rcc4','(1,"demo",3,4)','this is a demo',1) Expression_1 ok: ^rcc4(1,"demo",3,4) Haz una copia de ^rcc2 a ^rcc4. Primero vemos el contenido de ^rcc2: USER>>select reference,value,"$DATA" from rcc_G.Scan where rcc_G.scan(^rcc2,4)=1 Reference Value $Data ^rcc2 10 (1) 1 1 (2) 2 11 (2,"xx") 10 (2,"xx",1) "XX1" 1 (2,"xx",10) "XX10" 1 (2,"xx",4) "XX4" 1 (2,"xx",7) "XX7" 1 (3) 3 1 (4) 4 11 (4,"xx") 10 (4,"xx",1) "XX1" 1 (4,"xx",10) "XX10" 1 (4,"xx",4) "XX4" 1 (4,"xx",7) "XX7" 1 (5) 5 1 16 Rows(s) Affected Ahora ejecuta la copia del contenido de ^rcc2 en ^rcc4: [SQL]USER>>select rccEX.Gset('^rcc4',reference,value,"$DATA") from rcc_G.Scan where rcc_G.scan('^rcc2',4)=1 Expression_1 ok: ^rcc4 ok: ^rcc4(1) ok: ^rcc4(2) ok: ^rcc4(2,"xx") ok: ^rcc4(2,"xx",1) ok: ^rcc4(2,"xx",10) ok: ^rcc4(2,"xx",4) ok: ^rcc4(2,"xx",7) ok: ^rcc4(3) ok: ^rcc4(4) ok: ^rcc4(4,"xx") ok: ^rcc4(4,"xx",1) ok: ^rcc4(4,"xx",10) ok: ^rcc4(4,"xx",4) ok: ^rcc4(4,"xx",7) ok: ^rcc4(5) 16 Rows(s) Affected Un agradecimiento especial para @Anna.Golitsyna por inspirarme a publicar esto.
Artículo
Alberto Fuentes · 13 ago, 2021

Generador de clases según definición OpenAPI en ObjectScript

InterSystems IRIS ofrece la posibilidad de crear interfaces REST con el enfoque *spec-first*, esto es, partiendo de las especificaciones de la API. Puedes echarle un vistazo a este artículo para más información al respecto : [https://es.community.intersystems.com/post/cómo-desarrollar-una-api-rest-con-un-enfoque-spec-first](https://es.community.intersystems.com/post/c%C3%B3mo-desarrollar-una-api-rest-con-un-enfoque-spec-first). Algo muy práctico que tiene este enfoque y la propia OpenAPI, es la definición de los **objetos** que se van a intercambiar. El comando `do ^%REST` crea las rutas y los métodos asociados, pero sin embargo no crea las definiciones de los objetos. Este es la salida del comando `do ^%REST` para el ejemplo de Petshop: ``` USER>do ^%REST REST Command Line Interface (CLI) helps you CREATE or DELETE a REST application. Enter an application name or (L)ist all REST applications (L): PetShop REST application not found: PetShop Do you want to create a new REST application? Y or N (Y): Y File path or absolute URL of a swagger document. If no document specified, then create an empty application. OpenAPI 2.0 swagger: https://petstore.swagger.io/v2/swagger.json OpenAPI 2.0 swagger document: https://petstore.swagger.io/v2/swagger.json Confirm operation, Y or N (Y): -----Creating REST application: PetShop----- CREATE PetShop.spec GENERATE PetShop.disp CREATE PetShop.impl REST application successfully created. Create a web application for the REST application? Y or N (Y): Specify a web application name beginning with a single '/'. Default is /csp/PetShop Web application name: /csp/petshop -----Deploying REST application: PetShop----- Application PetShop deployed to /csp/petshop ``` Como vemos en el ejemplo, se han generado 3 clases: * una clase disp (no accesible al usuario). * una clase impl (que contiene la implementación de la API). * una clase spec (que contiene la definición de OpenAPI en un bloque XData) ¿Y si además pudiésemos crear un paquete que contenga todas las definiciones de clases incluidas en la especificación? Eso es lo que vamos a hacer con esta aplicación de OpenExchange disponible con el gestor de paquetes ZPM. Para instalarlo con zpm: ``` USER>zpm zpm: USER>install objectscript-openapi-definition [objectscript-openapi-definition] Reload START [objectscript-openapi-definition] Reload SUCCESS [objectscript-openapi-definition] Module object refreshed. [objectscript-openapi-definition] Validate START [objectscript-openapi-definition] Validate SUCCESS [objectscript-openapi-definition] Compile START [objectscript-openapi-definition] Compile SUCCESS [objectscript-openapi-definition] Activate START [objectscript-openapi-definition] Configure START [objectscript-openapi-definition] Configure SUCCESS [objectscript-openapi-definition] Activate SUCCESS ``` Una vez instalado puedes utilizarlo de la siguiente forma: ``` zw ##class(Grongier.OpenApi.Definition).Process("PetShop.spec") Compilation started on 04/06/2020 15:40:49 with qualifiers '' Compiling 6 classes, using up to 8 worker jobs Compiling class PetShop.Definition.ApiResponse Compiling class PetShop.Definition.Category Compiling class PetShop.Definition.Tag Compiling class PetShop.Definition.Order Compiling class PetShop.Definition.Pet Compiling class PetShop.Definition.User Compiling routine PetShop.Definition.Tag.1 Compiling routine PetShop.Definition.Category.1 Compiling routine PetShop.Definition.Order.1 Compiling routine PetShop.Definition.ApiResponse.1 Compiling routine PetShop.Definition.User.1 Compiling routine PetShop.Definition.Pet.1 Compilation finished successfully in 0.183s. ``` Como ves, se han generado las clases incluidas en la especificación OpenAPI. Las clases generadas, incluyen: * campos requeridos * tipos * valores mínimos/máximos De esta forma, podrías convertir un cuerpo de un mensaje a un objeto de una de estas clases generadas: ``` ClassMethod addPet(body As %Stream.Object) As %Stream.Object { // Verify payload confirmity against definition Set tPaylaod = ##class(PetShop.Definition.Pet).%New() Set tSC = tPaylaod.%JSONImport(body) If ($$$ISERR(tSC)) { Do ..%ReportRESTError(500,tSC,1) Quit "" } Quit "" } ``` Puedes encontrar el código y más ejemplos en este repositorio: https://github.com/grongierisc/objectscript-openapi-definition
Artículo
Muhammad Waseem · 13 oct, 2021

División de mensajes ORU mediante ObjectScript y DTL

A lo largo de los años, me he encontrado con la necesidad de crear varios mensajes HL7 basados en un solo mensaje entrante. Por lo general, toman la forma de un pedido o son el resultado de un laboratorio. Cada vez que he afrontado el reto, he intentado empezar de cero, con la convicción de que el intento anterior podría haberse hecho mejor. Recientemente, volvió a surgir la necesidad y pude crear una solución de la que no me avergonzaba. Mi principal preocupación era que siempre me encontraría enterrado en un BPL, o usaría ObjectScript e intentaría editar mensajes usando el método SetValueAt para la clase de mensaje HL7. ProblemaCuando el Sistema A procesa múltiples pedidos para un solo paciente, el resultado vendrá en un solo mensaje con ORCgrp repetido con los segmentos OBR y OBX contenidos en este. El sistema B solo puede recibir un único OBR por mensaje. EstrategiaDesarrollar un proceso de ObjectScript que dividirá un solo mensaje en varios, en función del número de repeticiones de ORCgrp, y hacerlo manipulando únicamente el mensaje HL7 con una DTL. Ejecución El Código (parte 1)Para empezar, necesitamos una clase que extienda Ens.BusinessProcess y espere un mensaje HL7 bajo pedido: Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{ Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{ Quit $$$OK } } El siguiente paso es recorrer el mensaje en función del número de repeticiones de ORCgrp. Para ello, necesitaremos 2 cosas: El número de repeticiones Un bucle For Para obtener el número de repeticiones, podemos tomar el recuento del mensaje usando el siguiente código: Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)") Esto establecerá la variable "ORCCount" en el número de repeticiones. Poniendo esto junto con un bucle For y un seguimiento para ver algunos resultados, se ve así: Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{ Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)") For i=1:1:ORCCount { $$$TRACE("This is loop number: "_i) } Quit $$$OK } Pasar un mensaje con dos repeticiones ORCgrp a través de este proceso desde dentro de una producción nos da: Como puedes ver, obtenemos dos rastros. Ahora, desde aquí, necesitamos poder llamar a una transformación, y también poder decirle a esa transformación qué iteración del ORCgrp debería estar devolviendo. Para esto, usaremos el parámetro "aux" (que a veces es ignorado) para las clases de transformación. La transformación La transformación en sí puede ser muy simple. Todo lo que queremos para esto es: Copiar el encabezado MSH mientras haces que MessageControlID sea único para tu mensaje dividido Establecer el primer ORCgrp del mensaje de destino en la iteración en la que estamos desde la fuente Establecer el Target SetIDOBR en "1", ya que siempre será el primero en el mensaje de destino Para esto, nuestra transformación debería verse un poco así: Pero espera, ¿de dónde obtiene aux.StringValue los datos? Para averiguarlo, tenemos que volver al código... El Código (parte 2)Podemos pasar una clase a la transformación a través del parámetro aux, y para este escenario solo voy a usar un contenedor de cadenas. También vamos a enviar la salida de la transformación a un objetivo para que podamos ver los resultados: Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{ Property TargetConfigName As Ens.DataType.ConfigName; Parameter SETTINGS = "TargetConfigName"; Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{ Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)") For i=1:1:ORCCount { Set contextString = ##class(Ens.StringContainer).%New() Set contextString.StringValue = i $$$QuitOnError(##Class(Demo.Transformations.R01Split).Transform(pRequest,.SplitR01,.contextString)) $$$QuitOnError(..SendRequestAsync(..TargetConfigName,SplitR01,0)) } Quit $$$OK } } Luego, en la producción, establecemos un destino usando la nueva opción de configuración que creamos con la propiedad y el parámetro en la parte superior de la clase, y deberíamos ver algo como esto: Conclusión Como dije al principio, siempre me parece que desarrollo una solución para este tipo de problema y luego miro hacia atrás y pienso que podría hacerse mejor. Esta no es una excepción, pero ciertamente es mejor que las iteraciones anteriores. Para mejorar esto en el corto plazo, añadiré comentarios descriptivos al ObjectScript. A largo plazo, me gustaría poder añadir una configuración para la clase de transformación, para que pueda controlarse desde fuera de ObjectScript. En general, puedo ver esto como un enfoque que tomaré para desarrollos futuros, y no comenzaré completamente desde cero. (P.D.: Estoy feliz de compartir el código para esto, aunque solo puedo adjuntar un pdf a este artículo. Parece poco para ser algo para Open Exchange, pero si hay algún interés en que lo cargue allí o en otro lugar, decídmelo). Este artículo ha sido etiquetado como "Mejores prácticas" ("Best practices"). Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems.
Anuncio
Luis Angel Pérez Ramos · 21 oct, 2022

glsdb: Objetos JavaScript que son en realidad Objetos IRIS

Quiero anunciar el lanzamiento de algo muy interesante - y revolucionario, de hecho. Puede sonar exagerado, pero no creo que hayáis visto nada como esto, ¡ni si quiera imaginar que sería posible! Hemos sacado un nuevo módulo JavaScript/Node.js llamado glsdb del que podéis leer todo aquí: https://github.com/robtweed/glsdb No obstante, para el propósito de este anuncio, me voy a centrar en una parte de glsdb: sus APIs que abstraen las Clases de IRIS (o Cache) como Objetos JavaScript equivalentes. Con esto quiero decir que los Objetos de JavaScript serán en realidad ¡Objetos IRIS persistidos en la base de datos! Suponed que tengo instalados los datos SAMPLES del repositorio de InterSystems, como se describe aquí: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ASAMPLES Así que, para acceder a estos datos SAMPLES usando glsdb, primero tengo que abrir una conexión con IRIS (glsdb usa nuestro interfaz mg-dbx para hacer esa conexión), por ejemplo: const {glsDB} = await import('glsdb'); let glsdb = new glsDB('iris'); let options = { connection: 'network', host: '172.17.0.2', tcp_port: 7042, username: "_SYSTEM", password: "xxxxxx", namespace: "SAMPLES" } glsdb.open(options); Ahora, si deseo acceder a un registro de Empleado (Employee) en la base de datos SAMPLES, bastaría con lo siguiente: let Employee = glsdb.irisClass('Sample.Employee'); let employee = Employee(101); Por ahora, quizás, no os sorprenda. Pero aquí es donde empieza lo alucinante. Hemos creado un Objeto JavaScript llamado employee que parece representar la instancia Employee con un OID de 101, pero realmente es un Objeto JavaScript bastante especial: es un Objeto Proxy de JavaScript que está directamente mapeado a la instancia física de la Clase IRIS en la base de datos. Tenemos acceso directo a todas las propiedades y métodos de la Clase IRIS así que, cuando manipulamos el Objeto Proxy, ¡estamos realmente manipulando la instancia de la Clase IRIS en la base de datos! Por lo tanto, podremos hacer cosas como esta: let salary = employee.Salary; console.log('employee.Salary = ' + salary); let company = employee.Company; let name = company.Name; console.log('Company: ' + name); Lo importante que hay que tener en cuenta aquí es que el objeto employee no es una copia de JavaScript almacenada en memoria de una instancia de la Clase IRIS - es la instancia de la Clase IRIS real, en la base de datos! Como podéis ver aquí debajo, podemos acceder a atributos de subclases a través de la relación entre el Empleado (Employee) y la Compañía (Company) como se haría en ObjectScript, pudiendo hacerlo de una sóla vez: console.log(employee.Company.Name); De tal forma podremos acceder, a través de las propiedades, directamente a la Clase física de IRIS en la base de datos. También podremos guardar los cambios de los objetos - para ello utilizaremos un método especial llamado _save(), por ejemplo: employee._save() Para crear una nueva instancia de un empleado (sin especificar el OID), por ejemplo: let newEmployee = Employee(); glsdb proporciona un método más rápido para definir y guardar un nuevo registro de una sola vez, este método será _set(), por ejemplo: let ok = newEmployee._set({ Title: 'Manager' Salary: 60000 }) Podréis incluso examinar los métodos y propiedades disponibles para la Clase: console.log(employee._properties); console.log(employee._methods); Esto ha sido un vistazo rápido a glsdb: ¡espero que os haya permitido conocer un poco lo fantástico que es! Es posible que os estéis preguntando cómo se pueden hacer este tipo de cosas. A modo de resumen, todo se reduce a una funcionalidad relativamente nueva de JavaScript: Objetos Proxy. Los Objetos Proxy son objetos especiales que actúan, como su nombre indica, como proxy de otro objeto real. Pero su verdadero poder está en el hecho de que puedes configurar pasarelas para acceder o cambiar las propiedades del Objeto Proxy y (esta es la parte importante) puedes aplicar lógica personalizada a esas pasarelas. En el caso de glsdb, esa lógica personalizada utiliza las APIs mg-dbx por detrás para llevar a cabo el acceso y los cambios equivalentes que sucedan sobre el Objeto Proxy en la clase IRIS correspondiente. De todas formas, esto es un vistazo rápido al increible mundo de posibilidades con glsdb. Lo que he publicado es una primera versión, por lo que estoy seguro de que hay aspectos del proxy de IRIS que irán siendo modificados. Probadlo y ved lo que puede hacer, si encontráis algo que no funciona correctamente no dudéis en poneros en contacto conmigo. Y por cierto, comprobad las otras APIs que glsdb proporciona - ¡son igual de interesantes y alucinantes! Por último, glsdb es software Open Source, así que podéis probar el código y postearlo en el repositorio de Github si queréis echar una mano con los desarrollos futuros.
Artículo
Luis Angel Pérez Ramos · 20 feb, 2023

Conexión a base de datos MySQL mediante JDBC desde un Business Service

Estimados miembros de la Comunidad. Una problemática muy común en muchos usuario es el uso de una base de datos externa como entrada de datos a una producción de IRIS. Como ya sabréis tenemos dos métodos de conexión directas a bases de datos externas desde IRIS, la primera es mediante ODBC y la segunda es recurriendo a una conexión vía JDBC. En nuestro ejemplo procederemos a realizar una conexión mediante JDBC y para ello montaremos un pequeño proyecto en Docker para que podáis jugar con ello cuanto queráis. Tenéis el código disponible en esta url: https://github.com/intersystems-ib/workshop-sql-jgw En nuestro docker-compose.yml configuraremos las imágenes que vamos a necesitar: version: "2.2" services: # mysql mysql: build: context: mysql container_name: mysql restart: always command: --default-authentication-plugin=mysql_native_password environment: MYSQL_ROOT_PASSWORD: SYS MYSQL_USER: testuser MYSQL_PASSWORD: testpassword volumes: - ./mysql/sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql adminer: container_name: adminer image: adminer restart: always depends_on: - mysql ports: - 8080:8080 # java gateway jgw: build: context: java dockerfile: Dockerfile depends_on: - mysql container_name: jgw restart: always ports: - 44444:44444 environment: - PORT=44444 # iris iris: init: true container_name: iris build: context: . dockerfile: iris/Dockerfile depends_on: - 'jgw' ports: - 52773:52773 - 51773:51773 command: --check-caps false MySQL: nuestro motor de base de datos. Adminer: aplicación web que nos permitirá administrar nuestra instancia de MySQL. IRIS: con la versión community más reciente de IRIS. JGW: el Java Gateway que nos permitirá conectarnos vía JDBC a nuestra base de datos. Para desplegar los contenedores deberemos ejecutar los siguientes comandos: docker-compose build ... docker-compose up -d ... docker-compose start Una vez arrancados los contenedores podréis acceder al administrador de base de datos en la siguiente url con las siguientes valores: Motor de base de datos: MySQL Servidor: mysql Usuario: root Contraseña: SYS Podréis ver que se encuentra configurada una base de datos llamada test dentro de la cual hay una tabla llamada patient con dos registros. Será sobre esa tabla sobre la que lanzaremos la consulta desde el Business Service. Finalmente, accediendo al portal de gestión de la instancia de IRIS, podremos revisar la producción en el Namespace USER que se carga por defecto, Test.Production: Analizaremos los diferentes elementos que la componen: EnsLib.JavaGateway.Service: este business service será el responsable de permitir la conectividad vía JDBC a nuestra base de datos, para este ejemplo ha sido necesario desplegar un container (JGW) en el que ubicaremos nuestro JDK y que se encontrará escuchando en el puerto 44444. Si queréis configurarlo en un IRIS instalado en Windows o Linux sólo necesitaréis añadir el business services, definir la ruta al JAVA HOME y la ubicación de la librería de java que nos permitan la conexión a la base de datos, el puerto por defecto en ese caso será el 55555. EnsLib.SQL.Service.GenericService: mediante este servicio deberemos configurar una serie de opciones. DSN: url de conexión a nuestra base de datos (jdbc:mysql://mysql:3306/test). Credentials: las credenciales necesarias para la conexión (root/SYS). Target config names: a donde vamos a enviar el objeto obtenido tras la lectura de la base de datos. Query: consulta que vamos a lanzar periódicamente a nuestra base de datos para obtener los últimos registros añadidos. Message Class: objeto que se creará con los datos obtenidos a partir de la consulta (sus propiedades deben coincidir con el nombre utilizado en los campos de la consulta). Java Gateway Service: el nombre en nuestra producción del JavaGateway service. JDBC Driver: el nombre del driver de conexión a nuestra base de datos (en nuestro caso será el de MySQL, com.mysql.jdbc.Driver). Test.PatientToString: business process de ejemplo en el que se capturan los datos del objeto definido en el Message Class anterior y se envían a un business operation. EnsLib.File.PassthroughOperation: business operation estándar que nos escribirá el resultado en un fichero de salida. Si vemos en más detalle el objeto que generaremos por cada nuevo registro en la base de datos podremos comprobar que sus propiedades coinciden con las usadas en la consulta: Class Test.Patient Extends (%Persistent, %JSON.Adaptor, %XML.Adaptor, Ens.Request) { Property Id As %Integer; Property Name As %String; Property Lastname As %String; } Este objeto nos permitirá mapear automáticamente nuestra query a un objeto, sin necesidad de realizar ninguna transformación farragosa. ¡Si tenéis cualquier pregunta o sugerencia no dudéis en añadir un comentario!
Artículo
Luis Angel Pérez Ramos · 25 mayo, 2023

Instalando Apache Server y HealthShare HealthConnect en Ubuntu Linux

Es posible que ya hayáis oído que, a partir de las versiones de IRIS y HealthShare HealthConnect 2023.2, se va a eliminar de la instalación por defecto el Apache Server interno por lo que será necesario contar con un servidor de aplicaciones externo como puede ser Apache Server o NGINX. En este artículo voy a proceder a realizar la instalación de un HealthShare HealthConnect 2023.1 para que funcione con un Apache Server pre-instalado. Para ello utilizaré una máquina virtual sobre la que he instalado un Ubuntu 22.04. Instalando Apache Server Como hemos indicado deberemos instalar previamente nuestro Apache Server y lo haremos siguiendo los pasos que nos indica su propia web sudo apt update sudo apt install apache2 Apache Server instalado, prosigamos con la instalación de HealthConnect. Instalando HealthConnect Revisemos lo que nos indica la documentación oficial de InterSystems. Si consultáis la documentación nos indica cómo descomprimir el archivo que hemos descargado de WRC. En mi caso tendré que hacer unos pequeños retoques ya que tengo el fuente de HealthConnect en una carpeta compartida con mi máquina virtual. mkdir /tmp/iriskit chmod og+rx /tmp/iriskit umask 022 gunzip -c /mnt/hgfs/shared/HealthConnect-2023.1.0.229.0-lnxubuntu2204x64.tar.gz | ( cd /tmp/iriskit ; tar xf - ) Repasemos que estamos haciendo con estos comandos. Creamos el directorio donde vamos a descomprimir nuestro fuente de HealthConnect. Damos permisos de lectura y ejecución del directorio creado tanto al propietario del archivo como a todos los usuarios del grupo del propietario para poder descomprimir el archivo en el directorio indicado. Damos permisos de lectura, escritura y ejecución sobre todos los archivos y directorios que se vayan a crear. Descomprimimos el archivo gz y accediendo a la ruta donde lo hemos descomprimido descomprimimos el archivo tar. Veamos como ha quedado nuestro directorio temporal. Perfecto, aquí tenemos el código de nuestro HealthShare HealthConnect. Siguiente paso, crear un usuario que será el propietario de la instalación de HealthConnect, lo llamaremos irisusr y a continuación desde nuestro directorio temporal ejecutaremos el comando de instalación: sudo useradd irisusr sudo sh irisinstall Al ejecutar la instalación se nos mostrarán una serie de opciones que deberemos ir configurando con los valores que deseemos. En este caso vamos a realizar una instalación CUSTOM para poder configurar el WebGateway con el servidor web de Apache Y continuamos... En este punto deberemos indicar que deseamos configurar el Web Gateway con el Apache Web Server ya desplegado en nuestro servidor y a continuar definir la ruta de instalación del mismo. Configuración de Apache Server La instalación configurará automáticamente el Web Gateway para funcionar con el Apache Server y nuestra instancia APACHETEST de HealthConnect. En este ejemplo, al ser Ubuntu 22.0.4, el fichero de configuración de Apache se encuentran en la ruta /etc/apache2/apache2.conf , para otras distribuciones de Linux podéis consultar la documentación. Si abrimos el archivo apache2.conf y bajamos hasta el final del mismo podremos comprobar los cambios introducidos por la instalación de Web Gateway: Esta configuración está redirigiendo todas las llamadas que reciba el puerto 80 (puerto en el que Apache Server escucha por defecto) con la ruta /csp a nuestro Web Gateway, el cual a su vez enviará la llamada a nuestra instancia de HealthConnect. Por defecto el parámetro CSPFileTypes está configurada únicamente para redireccionar los archivos de tipo "csp cls zen cxw", para poder trabajar con el portal de gestión sin inconveniente lo hemos modificado para aceptar todos los tipos "*". El cambio de CSPFileTypes exige reiniciar Apache Server. Acceso al portal de gestión Muy bien, tenemos nuestro Apache escuchando en el puerto 80, nuestro Web Gateway configurado y la instancia de HealthConnect arrancada. Probemos el acceso al portal de gestión utilizando el puerto 80 de Apache. En mi caso la URL de acceso será http://192.168.31.214/csp/sys/%25CSP.Portal.Home.zen , al ser el puerto 80 el puerto por defecto no será necesario incluirlo en la URL. Aquí tenemos nuestro portal de gestión plenamente operativo. Introduzcamos el usuario y la contraseña que definimos durante la instalación y abramos la configuración del Web Gateway desde la opción System Administrator --> Configuration --> Web Gateway Management. Esta vez el usuario de acceso será CSPSystem. Recordemos que el Web Gateway está configurado para funcionar con el Apache Web Server que hemos instalado previamente. Accedamos a la opción de Server Access para comprobar la configuración de nuestra instancia de HealthConnect en el Web Gateway: Ahí tenemos nuestra instancia. Comprobemos como el Web Gateway gestiona las llamadas recibidas desde el Apache Server abriendo la opción de Application Access. Comprobemos que hace con las URL que empiecen por /csp Ahí está nuestra instancia configurada por defecto para recibir las llamadas que lleguen a nuestro Apache Server. Ya tendríamos nuestra instancia de HealthShare HealthConnect configurada para trabajar con un Apache Server externo y el Web Gateway. Si tenéis cualquier pregunta o sugerencia no dudéis en enviarnos un comentario.
Artículo
Bernardo Linarez · 1 jul, 2019

Dominando JDBC con SQL Gateway

Como todos sabemos, IRIS Database / Caché es un motor de base de datos que efectúa muchas tareas dentro de sí misma. Sin embargo, ¿qué puede hacer cuando necesita tener acceso a una base de datos externa? Una opción es utilizar el SQL Gateway en Caché mediante un conector JDBC. En este artículo, mi objetivo es responder las siguientes preguntas con el fin de ayudarle a que se familiarice con la tecnología y que resuelva algunos de los más problemas comunes.Resumen general¿Cuáles son los parámetros de conexión que necesita para conectarse a una base de datos remota?¿Cuál es la diferencia de JDBC Gateway en comparación con el servicio que ofrece Java Gateway en la capa de interoperabilidad?¿Cuáles son las herramientas y métodos que están disponibles para resolver problemas?¿Cuáles son los tipos de problemas más comunes y los enfoques para resolverlos?Antes de profundizar en estas preguntas, hablemos rápidamente de la arquitectura de la Conectividad con las bases de datos de Java (JDBC) en SQL Gateway. Para que esto sea más sencillo, puede pensar en la arquitectura como una aplicación en IRIS Database / Cache realiza una conexión TCP con alguno de los procesos de Java, dicho proceso se llama Puerta de enlace. Entonces, el proceso Puerta de enlace se conecta con una base de datos remota, como Caché, Oracle o SQL Server, mediante el controlador que se especificó para esa base de datos. Para obtener más información sobre la arquitectura de SQL Gateway, consulte los documentos de apoyo sobre el uso de SQL Gateway en Caché.Parámetros de conexiónCuando se conecte con una base de datos remota, debe proporcionar los siguientes parámetros:Nombre de usuarioContraseñaNombre del controladorURLRuta para la claseCómo conectarse con una base de datos de CachéPor ejemplo, si necesita conectarse con una instancia de Caché, que utilice SQL Gateway mediante la JDBC, necesita navegar hacia [System Administration] -> [Configuration] -> [Connectivity] -> [SQL Gateway Connections] en el portal de Administración del sistema (SMP). A continuación, haga clic en "Crear una nueva conexión" y especifique "JDBC" como el tipo de conexión.Cuando se conecte a un sistema IRIS Database / Caché, el nombre del controlador siempre debe ser com.intersys.jdbc.CacheDriver, como se muestra en la captura de pantalla. Si se conecta con una base de datos de terceros, entonces deberá utilizar un nombre diferente para el controlador (consulte la sección Cómo conectarse con las bases de datos de terceros que se encuentra más abajo).Cuando se conecte con las bases de datos de Caché, no es necesario que especifique una ruta para la clase, porque el archivo JAR se carga automáticamente.El parámetro URL también variará dependiendo de la base de datos a la que se esté conectando. Para las bases de datos de Caché, debe utilizar una URL en el formulario jdbc:Cache://[server_address]:[superserver_port]/[namespace]Cómo conectarse con las bases de datos de tercerosUna base de datos común de terceros es Oracle. A continuación, se muestra un ejemplo de su configuración.Como puede ver, el nombre del controlador y la URL tienen patrones diferentes a los que utilizamos para la conexión anterior. Además, especifiqué una ruta para la clase en este ejemplo, porque necesito utilizar el controlador de Oracle para conectarme a su base de datos.Como puede imaginarse, SQL Server utiliza diferentes URL y patrones para el nombre del controlador.Puede probar si los valores son válidos al hacer clic en el botón "Test Connection". Para establecer la conexión, haga clic en "Save".JDBC Gateway contra el Servicio empresarial de Java GatewayEn primer lugar, los servicios de JDBC Gateway y Java Gateway son completamente independientes uno del otro. JDBC Gateway puede utilizarse en todos los sistemas que se basan en Caché, mientras que el Servicio empresarial de Java Gateway únicamente existe como parte de la IRIS Interoperabilidad / Ensemble. Además, el servicio de Java Gateway utiliza un proceso diferente en comparación al que utiliza JDBC Gateway. Para obtener más información sobre el Servicio empresarial de Java Gateway, consulte El Servicio empresarial de Java Gateway.Herramientas y métodosA continuación, se muestran 5 herramientas y métodos comunes que se utilizan para resolver problemas con JDBC SQL Gateway. Mi intención primero es discutir sobre estas herramientas y mostrarle algunos ejemplos de cuándo se utilizan en la siguiente sección.1. RegistrosA. El registro del controlador contra el registro de la Puerta de enlaceCuando se utiliza JDBC Gateway, el registro correspondiente es el registro JDBC de SQL Gateway. Como comentamos anteriormente, el JDBC Gateway se utiliza cuando Caché necesita acceder a las bases de datos externas, lo cual significa que Caché es el cliente. El registro del controlador, sin embargo, corresponde al que utiliza el controlador JDBC de InterSystems para acceder a una base de datos de Caché desde una aplicación externa, lo que significa que Caché es el servidor. Si tiene una conexión desde una base de datos de Caché con otra base de datos de Caché, ambos tipos de registro pueden ser útiles.En nuestros documentos de apoyo, la sección sobre la habilitación del registro del controlador se denomina "Habilitación del registro para la JDBC", y la sección sobre la habilitación del registro para la puerta de enlace se denomina "Habilitación del registro para la JDBC con SQL Gateway".Aunque en ambos registros se incluye la palabra "JDBC", estos son completamente independientes. El objetivo de este artículo es conocer más sobre JDBC Gateway, de modo que analizaré con más detalle el registro para la puerta de enlace. Para obtener más información sobre el registro del controlador, consulte la Habilitación del registro del controlador .B. Habilitación del registro para la Puerta de enlaceSi utiliza el SQL Gateway en Caché mediante la JDBC, entonces debe hacer lo siguiente para habilitar el registro: en el Portal de administración, vaya a [System Administration] > [Configuration] > [Connectivity] > [JDBC Gateway Settings]. Especifique un valor para el registro de JDBC Gateway. Esta debe ser la ruta completa y el nombre de un archivo de registro (por ejemplo, /tmp/jdbcGateway.log). Sino existe, el archivo se creará automáticamente pero el directorio no existirá. Caché iniciará con el registro de la JDBC con SQL Gateway por usted.Si utiliza el Servicio empresarial de Java Gateway en Ensemble, consulte Habilitación del registro para Java Gateway para obtener más información sobre cómo habilitar el registro.C. Análisis del registro para la Puerta de enlaceAhora que recopiló un registro para la puerta de enlace, quizás se pregunte: ¿cuál es la estructura del registro y cómo puedo leerlo? ¡Excelentes preguntas! Aquí le proporcionaré la información básica para que pueda comenzar. Desafortunadamente, no siempre es posible interpretar completamente el registro sin acceder al código fuente, así que para este tipo de situaciones complejas, ¡no dude en ponerse en contacto con el Centro Mundial de Respuesta de Intersystems (WRC)!Para desmitificar la estructura del registro, recuerde que siempre son segmentos grandes de datos seguidos por una descripción de lo que hacen. Por ejemplo, observe esta imagen en la que se resalta a algunos elementos básicos de sintaxis:Para entender lo que el término Received significa aquí, es necesario recordar que en el registro de la puerta de enlace se documentan las interacciones entre la puerta de enlace y el flujo descendente de la base de datos. Por lo tanto, el término Received significa que la información proveniente de IRIS Database/Caché/Ensemble se recibió en la puerta de enlace. En el ejemplo anterior, la puerta de enlace recibió el texto de una consulta para SELECT . El significado de los diferentes valores de msgId se encuentran en el código interno. El 33 que vemos aquí significa "Preparar una sentencia".El registro por sí mismo también proporciona información sobre el controlador, la cual se puede verificar cuando se depuran los errores. Por ejemplo:Como podemos ver, el Driver Name es com.intersys.jdbc.CacheDriver, que es el nombre del controlador que se utilizó para conectarse con el proceso para la puerta de enlace. El Jar File Name es cachejdbc.jar, que es el nombre del archivo jar que se encuentra en <cache_install_directory>cache_install_directory>\lib\.2. Cómo encontrar el proceso para la puerta de enlacePara encontrar el proceso para la puerta de enlace, puede ejecutar el comando ps . Por ejemplo:ps -ef | grep javaCon el comando ps se despliega la información sobre el proceso de Java, que incluye el número de puerto, el archivo jar, el archivo de registro, el ID del proceso Java y el comando que comenzó el proceso de Java.Este es un ejemplo del resultado del comando:mlimbpr15:~ mli$ ps -ef | grep java 17182 45402 26852 0 12:12PM ?? 0:00.00 sh -c java -Xrs -classpath /Applications/Cache20151/lib/cachegateway.jar:/Applications/Cache20151/lib/cachejdbc.jar com.intersys.gateway.JavaGateway 62972 /Applications/Cache20151/mgr/JDBC.log 2>&1 17182 45403 45402 0 12:12PM ?? 0:00.22 /usr/bin/java -Xrs -classpath /Applications/Cache20151/lib/cachegateway.jar:/Applications/Cache20151/lib/cachejdbc.jar com.intersys.gateway.JavaGateway 62972 /Applications/Cache20151/mgr/JDBC.log 502 45412 45365 0 12:12PM ttys000 0:00.00 grep javaEn Windows, puede verificar el administrador de tareas para encontrar más información sobre el proceso para la puerta de enlace.3. Cómo iniciar y detener la puerta de enlaceExisten dos formas para iniciar y detener la puerta de enlace:Mediante el SMPUtilizando el TerminalA. Mediante el SMPPuede iniciar y detener la puerta de enlace en el SMP al ingresar en [System Administration] -> [Configuration] -> [Connectivity] -> [JDBC Gateway Server].B. Utilizando el TerminalEn equipos que cuentan con el sistema operativo Unix, también puede iniciar la puerta de enlace desde el terminal. Como comentamos en la sección anterior, el resultado de ps -ef | grep java contiene el comando que inició el proceso para Java, que en el ejemplo anterior es:java -Xrs -classpath /Applications/Cache20151/lib/cachegateway.jar:/Applications/Cache20151/lib/cachejdbc.jar com.intersys.gateway.JavaGateway 62972 /Applications/Cache20151/mgr/JDBC.logPara detener la puerta de enlace desde el terminal, puede finalizar el proceso. El ID del proceso en Java es el segundo número de la línea que contiene el comando anterior, que en el ejemplo anterior es 45402. Por lo tanto, para detener la puerta de enlace, puede ejecutar:kill 454024. Cómo escribir un programa en JavaPara ejecutar un programa en Java debe conectarse con una base de datos de flujo descendente, ya que es una excelente manera de probar la conexión, verificar las consultas y aislar la causa de un problema determinado. Adjunté un ejemplo de un programa de Java, el cual establece una conexión con SQL Server e imprime una lista de todas las tablas. En la siguiente sección explicaré por qué esto puede ser útil.import java.sql.*; import java.sql.Date; import java.util.*; import java.lang.reflect.Method; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.math.BigDecimal; import javax.sql.*; // Autor: Vicky Li// Este programa establece una conexión con SQL Server y recupera todas las tablas. El resultado es una lista de tablas. public class TestConnection { public static void main(String[] args) { try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //reemplace la URL, el nombre de usuario y la contraseña con los parámetros correctos Connection conn = DriverManager.getConnection(url,username,password); System.out.println("connected"); DatabaseMetaData meta = conn.getMetaData(); ResultSet res = meta.getTables(null, null, null, new String[] {"TABLE"}); System.out.println("List of tables: "); while (res.next()) { System.out.println( " " + res.getString("TABLE_CAT") + ", " + res.getString("TABLE_SCHEM") + ", " + res.getString("TABLE_NAME") + ", " + res.getString("TABLE_TYPE") ); } catch (Exception e) { e.printStackTrace(); }Para ejecutar este programa de Java (o cualquier otro programa de Java), primero debe compilar el archivo .java , que en nuestro caso se llama TestConnection.java. A continuación, se generará un archivo nuevo en la misma ubicación, el cual podrá ejecutar con el siguiente comando en un sistema UNIX:java -cp "<path to driver>/sqljdbc4.jar:lib/*:." TestConnectionEn Windows, puede ejecutar el siguiente comando:java -cp "<path to driver>/sqljdbc4.jar;lib/*;." TestConnection5. Cómo realizar un seguimiento del jstackComo su nombre lo indica, jstack imprime un registro de seguimiento (stack traces) de Java en los subprocesos de Java. Esta herramienta puede ser útil cuando se necesita comprender mejor lo que está haciendo alguno de los procesos de Java. Por ejemplo, si observa que el proceso para la puerta de enlace reproduce un mensaje determinado en su registro, es posible que desee recopilar un seguimiento del jstack. Quiero señalar que jstack es una herramienta de bajo nivel que solo debería utilizarse cuando otros métodos, como el análisis del registro para la puerta de enlace, no resuelven el problema.Antes de que recopile un seguimiento del jstack, debe asegurarse de que el Java Development Kit (JDK) está instalado. Este es el comando para recopilar un seguimiento del jstack:jstack -F <pid> > /<path to file>/jstack.txtdonde pid es el ID del proceso para la puerta de enlace, el cual puede obtenerse cuando se ejecuta el comando ps, por ejemplo, en ps -ef | grep java. Para obtener más información sobre cómo encontrar el pid, consulte de nuevo Cómo iniciar y detener la puerta de enlace.Ahora bien, aquí puede consultar algunas consideraciones especiales para los equipos que cuentan con Red Hat. En el pasado, hubo problemas para adjuntar el jstack en el proceso de JDBC Gateway (al igual que en el proceso del Servicio empresarial de Java Gateway que comenzó en Ensemble) con algunas versiones de Red Hat, por lo que la mejor manera para recopilar un seguimiento del jstack en Red Hat es iniciar el proceso para la puerta de enlace de forma manual. Para obtener las instrucciones, consulte la sección Cómo recopilar un seguimiento del jstack en Red Hat.Los tipos de problemas más comunes y los enfoques para resolverlos1. Problema: Java no se instaló correctamenteEn esta situación, verifique la versión de Java y las variables de entorno.Para verificar la versión de Java, puede ejecutar la siguiente línea de comando desde un terminal:java -versionSi recibe el error java: Command not found, entonces el proceso de Cache no puede encontrar la ubicación de los ejecutables de Java. Esto normalmente se puede arreglar con la instalación de los ejecutables de Java en PATH. Si tiene problemas para realizar esto, no dude en ponerse en contacto con el Centro Mundial de Respuesta de Intersystems (WRC).2. Problema: Hubo un fallo en la conexiónUn buen método para diagnosticar los fallos en la conexión es verificar si el proceso para la puerta de enlace se inicia correctamente. Puede hacerlo, ya sea, al verificar el registro para la puerta de enlace o el proceso para la puerta de enlace. En las versiones modernas, también puede ir al SMP y consultar [System Administration] -> [Configuration] -> [Connectivity] -> [JDBC Gateway Server], para comprobar si en la página se muestra el mensaje "JDBC Gateway está en ejecución".Si el proceso para la puerta de enlace no está en ejecución, es posible que Java no se instaló correctamente o que utiliza el puerto equivocado, si el proceso para la puerta de enlace está en ejecución, entonces es probable que los parámetros de conexión no sean los correctos.Si ocurre la primera situación, consulte la sección anterior y verifique nuevamente el número del puerto. Aquí analizaré con más detalle esta última situación.Es responsabilidad del cliente utilizar los parámetros de conexión correctos:nombre de usuariocontraseñanombre del controladorURLruta de la clasePuede verificar si tiene los parámetros correctos mediante cualquiera de las siguientes tres formas:Utilice el botón "Test Connection" después de seleccionar un nombre para la conexión en [System Administration] -> [Configuration] -> [Connectivity] -> [SQL Gateway Connections].Nota: en los sistemas modernos, el procedimiento "Test Connection" proporciona mensajes de error que son útiles, en los sistemas más antiguos es necesario el registro de JDBC Gateway para encontrar más información sobre el fallo.Ejecute la siguiente línea de comandos desde un terminal de Caché para probar la conexión: d $SYSTEM.SQLGateway.TestConnection(<connection name>)Ejecute un programa de Java para establecer una conexión. El programa que escriba puede ser similar al ejemplo que discutimos anteriormente.3. Problema: falta de correspondencia entre la forma en que Caché interpreta al JDBC y la forma en que la base de datos remota interpreta al JDBC, como en:los problemas con el tipo de datosel procedimiento para almacenar con parámetros de salidalas transmisionesPara esta categoría, con frecuencia es más útil trabajar con ayuda del Centro Mundial de Respuesta de Intersystems (WRC). Este es el procedimiento que realizamos con frecuencia para determinar si el problema está dentro de nuestro código interno o en la base de datos remota (o con el controlador):Se revisan los registros y se analiza lo que se envióSe reproduce el problema que se encuentra fuera del Caché al escribir un programa en Java.Notas al pie de páginaEl Bussiness Service JavaGatewayEl nombre de la clase del Servicio empresarial en IRIS Interoperabilidad/Ensemble es EnsLib.JavaGateway.Service, y la clase del adaptador es EnsLib.JavaGateway.ServiceAdapter. En la sesión de Ensemble primero se crea una conexión con Java Gateway Server, el cual es un proceso de Java. La arquitectura es similar a la que se encuentra en la JDBC con SQL Gateway, excepto que el proceso de Java lo administra la Actividad empresarial. Para obtener más detalles, consulte los documentos de apoyo.Cómo habilitar el registro del controladorPara habilitar el registro del controlador, debe añadir un nombre de archivo para el registro al final de la cadena de conexión de la JDBC. Por ejemplo, si la cadena de conexión original tiene el siguiente aspecto:jdbc:Cache://127.0.0.1:1972/USERPara habilitar el registro, agregue un archivo (jdbc.log) al final de la cadena de conexión, de modo que se vea como lo siguiente:jdbc:Cache://127.0.0.1:1972/USER/jdbc.logEl archivo de registro se guardará en el directorio de trabajo de la aplicación Java.Habilitación del registro de Java Gateway en EnsembleSi utiliza el Business Service (BS) de Java Gateway en Ensemble para ingresar a otra base de datos, entonces para habilitar el registro debe especificar la ruta y el nombre de un archivo de registro (por ejemplo, /tmp/javaGateway.log) en el campo "Log File" del servicio Java Gateway. Tenga en cuenta que la ruta ya debe existir.Recuerde, la conexión de Java Gateway que se utiliza en la producción de Ensemble es independiente de las conexiones que utilizan las tablas vinculadas u otras producciones. Por lo tanto, si utiliza Ensemble, debe recopilar el registro en el servicio de Java Gateway. El código que inicia el servicio Java Gateway utiliza el parámetro "Log File" en Ensemble, y no utiliza la configuración del Caché SQL Gateway en el SMP como se describió anteriormente.Cómo recopilar un seguimiento del jstack en Red HatLa clave aquí es iniciar el proceso para la puerta de enlace manualmente, y el comando para iniciar la puerta de enlace se puede obtener con la ejecución de ps -ef | grep java. Más adelante se muestran todos los pasos que deben seguirse cuando se recopila un seguimiento del jstack en Red Hat para ejecutar el JDBC Gateway o el Servicio empresarial de Java Gateway.Asegúrese de que el JDK está instalado.En un terminal, ejecute el comando ps -ef | grep java. Obtenga las siguientes dos piezas de información a partir del resultado:Copie el comando que inició la puerta de enlace. Debería verse algo como esto: java -Xrs -classpath /Applications/Cache20151/lib/cachegateway.jar:/Applications/Cache20151/lib/cachejdbc.jar com.intersys.gateway.JavaGateway 62972 /Applications/Cache20151/mgr/JDBC2.logObtenga el ID del proceso de Java (pid), es decir, el segundo número de la línea que contiene el comando anteriorFinalice el proceso con kill <pid>.Ejecute el comando que copió del Paso 2.a. para comenzar un proceso para la puerta de enlace de forma manual.Eche un vistazo al registro para la puerta de enlace (en nuestro ejemplo, se encuentra en /Applications/Cache20151/mgr/JDBC2.log) y asegúrese de ver una entrada parecida a >> LOAD_JAVA_CLASS: com.intersys.jdbc.CacheDriver. Este paso es solo para verificar que la llamada para la puerta de enlace se realizó correctamente.En un terminal nuevo, ejecute el comando ps -ef | grep java para obtener el pid del proceso para la puerta de enlace.Recopile un seguimiento del jstack: jstack -F <pid> > /tmp/jstack.txt
Artículo
Joel Espinoza · 27 feb, 2020

Crear OID's SNMP personalizados

Hola Comunidad: Esta publicación está dedicada a la tarea de supervisar una instancia de Caché usando SNMP. Algunos usuarios de Caché probablemente ya lo hacen de una u otra forma. Hace ya mucho que el paquete de Caché estándar soporta la supervisión por SNMP, pero no todos los parámetros necesarios vienen "listos para usar". Por ejemplo, sería bueno poder supervisar el número de sesiones CSP, obtener información detallada sobre el uso de la licencia, KPI particulares del sistema en uso, etc. Después de leer este artículo, sabrás cómo agregar tus parámetros a la supervisión de Caché mediante SNMP. Lo que ya tenemos Es posible supervisar Caché con SNMP. En los archivos de <Install_dir>/SNMP/ puedes encontrar una lista completa de todo lo soportado. Aquí encontrarás 2 archivos: ISC-CACHE.mib y ISC-ENSEMBLE.mib. Nos interesa el archivo destinado a Caché — ISC-CACHE.mib. En particular, quisiéramos saber qué información podemos obtener sobre licencias y sesiones. La tabla contiene los OID correspondientes, siempre que la jerarquía comience desde la raíz de InterSystems: 1.3.6.1.4.1.1656 OID Nombre Descripción Tipo de dato .1.1.1.1.10 cacheSysLicenseUsed El número actual de licencias en uso en esta instancia de Caché ENTERO .1.1.1.1.11 cacheSysLicenseHigh La cota máxima para las licencias usadas en esta instancia de Caché ENTERO .1.2.16 cacheLicenseExceed Una solicitud de licencia ha superado las licencias disponibles o permitidas Mensaje trap .1.1.1.1.6 cacheSysCurUser Número actual de usuarios en esta instancia de Caché ENTERO Al paquete le faltan muchos parámetros importantes, como el número de sesiones CSP, información de licencia y, por supuesto, no tiene KPI específicos por aplicación. Esto es un ejemplo de lo que nos gustaría saber: El número de usuarios CSP Limitations of our license in terms of the user count; License expiry date. Podemos añadir también unos pocos parámetros para el análisis de desempeño. Los parámetros en sí están en el paquete, pero queremos conocer su incremento por minuto. Por ejemplo: El aumento del número de referencias “globales” por minuto La cantidad de comandos de ejecución por minuto La cantidad de llamadas de rutina por minuto Cómo agregar “tus” parámetros Puedes consultar el documento “Monitoring Caché using SNMP”. La versión de Caché de nuestra instancia de prueba (CACHE2016) es 2016.2.0.721.0. El sistema operativo es Linux Fedora 24 (Workstation Edition). La instalación de Caché en Linux OS se describe al detalle aquí. Este es nuestro plan: Crear una clase para recolectar métricas Registrar y activar una nueva clase en Caché usando ^%SYSMONMGR Crear una MIB de usuario usando métodos de la clase MonitorTools.SNMP. Usaremos 99990 como PEN (Private Enterprise Number) temporal, pero necesitaremos registrarnos con IANA después. Este proceso es gratuito, lleva una semana o dos y requiere un intercambio de correos electrónicos acerca de “¿Para qué necesita su propio PEN?” Comenzar un servicio de supervisión con un subagente de Caché conectado Usar snmpwalk para asegurarse de tener acceso a todos los OID recién creados Agregar los OID a un sistema de supervisión de otro proveedor. Usemos por ejemplo Zabbix. La documentación de Zabbix está aquí. Asegurémonos de que la supervisión esté funcionando Agregar el inicio de la supervisión del sistema en el namespace TEST a la lista de inicio del sistema Sigamos ahora el plan, punto por punto: 1. Crear una clase para recolectar métricas La clase de recolección de métricas extiende %Monitor.Adaptor. En el terminal, cambiamos al namespace %SYS y exportamos la clase oculta Monitor.Sample: %SYS>do $system.OBJ.Export("Monitor.Sample.cls","/tmp/Monitor_Sample.xml") Exporting to XML started on 02/07/2017 21:39:56 Exporting class: Monitor.Sample Export finished successfully. Vamos a asumir que el namespace TEST es nuestra área de trabajo. Vamos a pasar a ella e importar aquí la clase Monitor.Sample. Ahora crearemos una clase que describa la implementación de un mecanismo de supervisión para las 6 métricas descritas en la sección "Lo que ya tenemos". Class monitoring.snmp.Metrics Extends %Monitor.Adaptor { /// Give the application a name. This allows you to group different /// classes together under the same application level in the SNMP MIB. /// The default is the same as the Package name. Parameter APPLICATION = "Monitoring"; /// CSP sessions count Property Sessions As %Monitor.Integer; /// License user limit Property KeyLicenseUnits As %Monitor.Integer; /// License key expiration date Property KeyExpirationDate As %Monitor.String; /// Global references speed Property GloRefSpeed As %Monitor.Integer; /// Number of commands executed Property ExecutedSpeed As %Monitor.Integer; /// Number of routine loads/save Property RoutineLoadSpeed As %Monitor.Integer; /// The method is REQUIRED. It is where the Application Monitor /// calls to collect data samples, which then get picked up by the /// ^SNMP server process when requested. Method GetSample() As %Status { set ..Sessions = ..getSessions() set ..KeyLicenseUnits = ..getKeyLicenseUnits() set ..KeyExpirationDate = ..getKeyExpirationDate() set perfList = ..getPerformance() set ..GloRefSpeed = $listget(perfList,1) set ..ExecutedSpeed = $listget(perfList,2) set ..RoutineLoadSpeed = $listget(perfList,3) quit $$$OK } /// Get CSP sessions count Method getSessions() As %Integer { // This method will only work if we don't use WebAddon: // quit $system.License.CSPUsers() // // This will work even if we use WebAddon: set csp = "" try { set cn = $NAMESPACE znspace "%SYS" set db = ##class(SYS.Stats.Dashboard).Sample() set csp = db.CSPSessions znspace cn } catch e { set csp = "0" } quit csp } /// Get license user's power Method getKeyLicenseUnits() As %Integer { quit $system.License.KeyLicenseUnits() } /// Get license expiration date in human-readable format Method getKeyExpirationDate() As %String { quit $zdate($system.License.KeyExpirationDate(),3) } /// Get performance metrics (gloref, rourines etc.) Method getPerformance(param As %String) As %Integer { set cn = $NAMESPACE znspace "%SYS" set m = ##class(SYS.Monitor.SystemSensors).%New() do m.GetSensors() znspace cn quit $listbuild(m.SensorReading("GlobalRefsPerMin"), m.SensorReading("RoutineCommandsPerMin"), m.SensorReading("RoutineLoadsPerMin")) } } Asegúrate de que el método GetSample() realmente recupere los datos necesarios: TEST>set metrics = ##class(monitoring.snmp.Metrics).%New() TEST>write metrics.GetSample() 1 TEST>zwrite metrics metrics=<OBJECT REFERENCE>[2@monitoring.snmp.Metrics] +----------------- general information --------------- | oref value: 2 | class name: monitoring.snmp.Metrics | reference count: 2 +----------------- attribute values ------------------ | ExecutedSpeed = 431584 | GloRefSpeed = 881 | KeyExpirationDate = "2017-02-28" | KeyLicenseUnits = 100 | RoutineLoadSpeed = 0 | Sessions = 1 +----------------------------------------------------- 2. Registrar y activar la nueva clase en Caché usando ^%SYSMONMGR Abre el terminal y pasa al namespace TEST: # csession cache2016 -U test Node: server, Instance: CACHE2016 TEST>do ^%SYSMONMGR 1. Select item 5, Manage Application Monitor. 2. Select item 2, Manage Monitor Classes. 3. Select item 3, Register Monitor System Classes. Exporting to XML started on 02/09/2017 11:22:57 Exporting class: Monitor.Sample Export finished successfully. Load started on 02/09/2017 11:22:57 Loading file /opt/intersystems/cache2016/mgr/Temp/Mb7nvq5xuovdHQ.stream as xml Imported class: Monitor.Sample Compiling class Monitor.Sample Compiling table Monitor.Sample Compiling routine Monitor.Sample.1 Load finished successfully. 4. Select item 1, Activate/Deactivate Monitor Class Class?? Num MetricsClassName Activated 1) %Monitor.System.AuditCount N … 15) monitoring.snmp.Metrics N Class? 15 monitoring.snmp.Metrics Activate class? Yes => Yes 5. Select item 6, Exit 6. Select item 6 again, Exit 7. Select item 1, Start/Stop System Monitor 8. Select item 2, Stop System Monitor Stopping System Monitor… System Monitor not running! 9. Select item 1, Start System Monitor Starting System Monitor… System Monitor started 10. Select item 3, Exit 11. Select item 4, View System Monitor State Component State System Monitor OK %SYS.Monitor.AppMonSensor OK 12. Select item 7, Exit 3. Crear una MIB de usuario Para crear una MIB de usuario, se usan métodos de clase de MonitorTools.SNMP. Para este ejemplo, usaremos un PEN (Private Enterprise Number) falso, 99990, pero será necesario registrar el PEN con IANA después. Aquí puedes ver los números registrados. Por ejemplo, el PEN de InterSystems es 16563. 16563 InterSystems Robert Davis rdavis&intersystems.com Usaremos la clase MonitorTools.SNMP y su método CreateMIB() para crear un archivo MIB. Este método lleva 10 argumentos: Nombre y tipo de argumento Descripción Valor AppName As %String nombre de la aplicación Valor del parámetro APPLICATION de la clase metrics.snmp.Metrics — Monitoring Namespace As %String nuestro namespace TEST EntID As %Integer PEN de la empresa 99990 (ficticio) AppID As %Integer OID de aplicación dentro de la empresa 42 Company As %String nombre de la empresa (mayúsculas) ficticio Prefix As %String prefijo de todos los objetos SNMP que creemos ficticio CompanyShort As %String prefijo corto de la empresa (mayúsculas) fict MIBname As %String nombre del archivo MIB ISC-TEST Contact As %String información de contacto (en particular, dirección) Dejemos el valor predeterminado: Earth, Russia, Somewhere in the forests, Subject: ISC-TEST.mib List As %Boolean equivalente a verbose. Mostrar progreso de tarea para el archivo MIB 1 Ahora llega la creación del archivo MIB: %SYS>d ##class(MonitorTools.SNMP).CreateMIB("Monitoring","TEST",99990,42,"fiction","fict","fiction","ISC-TEST",,1) Create SNMP structure for Application - Monitoring Group - Metrics ExecutedSpeed = Integer GloRefSpeed = Integer KeyExpirationDate = String KeyLicenseUnits = Integer RoutineLoadSpeed = Integer Sessions = Integer Create MIB file for Monitoring Generate table Metrics Add object ExecutedSpeed, Type = Integer Add object GloRefSpeed, Type = Integer Add object KeyExpirationDate, Type = String Add object KeyLicenseUnits, Type = Integer Add object RoutineLoadSpeed, Type = Integer Add object Sessions, Type = Integer MIB done. Ahora hay una nueva MIB ISC-TEST.mib en la carpeta <Install_dir>/mgr/TEST. 4. Comenzar el servicio de supervisión con el subagente de Caché conectado Abramos System Administration -> Security -> Services -> %Service_Monitor (click) -> Service Enabled (check). También especificaremos que queremos iniciar el subagente SNMP cuando se inicie Caché (clic en Configure Monitor Settings): En Linux usamos el paquete net-snmp para supervisión SNMP. Así que lo instalamos, lo configuramos para usar con subagentes y especificamos el puerto 705 como predeterminado para que el agente maestro hable con los subagentes. # grep -i agentx /etc/services agentx 705/tcp # AgentX agentx 705/udp # AgentX Puede ver un breve artículo sobre el archivo de configuración snmpd.conf que complementa al manual en cyberciti. Aquí están los ajustes finales # yum install net-snmp # grep '^[^#]' /etc/snmp/snmpd.conf master agentx agentXSocket TCP:localhost:705 com2sec local localhost public group MyRWGroup v1 local group MyRWGroup v2c local group MyRWGroup usm local view all included .1 80 view system included .iso.org.dod access MyROGroup "" any noauth exact all none none access MyRWGroup "" any noauth exact all all none syslocation server (edit /etc/snmp/snmpd.conf) syscontact Root <root@localhost> (configure /etc/snmp/snmp.local.conf) dontLogTCPWrappersConnects yes Reiniciamos los daemons snmpd y snmptrapd en Linux. Después, iniciaremos el servicio SNMP para activar el subagente de Caché SNMP: %SYS>do start^SNMP %SYS>; Check SNMP subagent status %SYS>zwrite ^SYS("MONITOR") ^SYS("MONITOR","SNMP")="RUN" ^SYS("MONITOR","SNMP","NAMESPACE")="%SYS" ^SYS("MONITOR","SNMP","PID")=5516 ^SYS("MONITOR","SNMP","PORT")=705 ^SYS("MONITOR","SNMP","STARTUP")="SNMP agent started on port 705, timeout=20, winflag=0, Debug=0" ^SYS("MONITOR","SNMP","STATE")="Terminated - 01/27/2017 04:15:01.2833PM" ^SYS("MONITOR","SNMP","WINSTART")=0 5. Verifique que solo estén disponibles sus propios OID nuevos Para hacer esto, use snmpwalk. Mostraremos el OID que indica el número de sesiones CSP: # snmpwalk -On -v 2c -c public localhost 1.3.6.1.4.1.99990 # snmpwalk -On -v 2c -c public localhost 1.3.6.1.4.1.99990 .1.3.6.1.4.1.99990.42.1.1.1.1.9.67.65.67.72.69.50.48.49.54 = INTEGER: 559851 .1.3.6.1.4.1.99990.42.1.1.1.2.9.67.65.67.72.69.50.48.49.54 = INTEGER: 973 .1.3.6.1.4.1.99990.42.1.1.1.3.9.67.65.67.72.69.50.48.49.54 = STRING: "2017-02-28" .1.3.6.1.4.1.99990.42.1.1.1.4.9.67.65.67.72.69.50.48.49.54 = INTEGER: 100 .1.3.6.1.4.1.99990.42.1.1.1.5.9.67.65.67.72.69.50.48.49.54 = INTEGER: 0 .1.3.6.1.4.1.99990.42.1.1.1.6.9.67.65.67.72.69.50.48.49.54 = INTEGER: 2 # If you get such result # .1.3.6.1.4.1.99990 = No Such Object available on this agent at this OID # try to restart SNMP subagent in Caché in this way: # do stop^SNMP # do start^SNMP El archivo ISC-TEST.mib contiene la secuencia de nuestros OID’s: FictMetricsR ::= SEQUENCE { fictExecutedSpeed Integer32, fictGloRefSpeed Integer32, fictKeyExpirationDate DisplayString, fictKeyLicenseUnits Integer32, fictRoutineLoadSpeed Integer32, fictSessions Integer32 } Por tanto, el número de sesiones, por ejemplo, es el último OID 1.3.6.1.4.1.99990.42.1.1.1.6. Puede compararlo con el número de sesiones que se muestra en el tablero SMP: 6. Agregar nuestros OID a un sistema de supervisión externo Usaremos Zabbix. La documentación de Zabbix está aquí. Puede encontrar una guía detallada de instalación y configuración de Zabbix en Linux aquí. Se eligió Zabbix como un sistema que no solo le permite trazar gráficas, sino también supervisar texto simple (en nuestro caso, fecha de vencimiento de licencia y unidades de licencia). Después de agregar nuestras 6 métricas a los elementos de nuestro host local (escriba: SNMPv2 agent) y crear 4 gráficas y 2 parámetros de texto simple (como elementos de pantalla), deberíamos ver la siguiente imagen: Arriba podrá encontrar la información de vencimiento de licencia y número de licencias disponibles. Las gráficas hablan solas. 7. Agregar el inicio del supervisor de sistema a la lista de inicio de nuestro namespace TEST. Hay un documento bastante bueno sobre las rutinas de usuario ejecutadas cuando Caché se inicia y se detiene. Se llaman %ZSTART y %ZSTOP, respectivamente. Lo que nos interesa es que el supervisor del sistema (^%SYSMONMGR) se inicie en el namespace TEST durante el inicio del sistema. De forma predeterminada, este supervisor solo se inicia en el namespace %SYS. Por lo tanto, solo miraremos al programa ^%ZSTART. La fuente está en %ZSTART.mac (créelo y guárdelo al namespace %SYS). %ZSTART; User startup routine. SYSTEM; ; Cache starting do $zu(9,"","Starting System Monitor in TEST namespace by ^%ZSTART...Begin") znspace "TEST" set sc = ##class(%SYS.Monitor).Start() do $system.OBJ.DisplayError(sc) if (sc = 1) { do $zutil(9,"","Starting System Monitor in TEST namespace by ^%ZSTART...OK") } else { do $zutil(9,"","Starting System Monitor in TEST namespace by ^%ZSTART...ERROR") } ; Starting SNMP znspace "%SYS" do start^SNMP quit LOGIN; ; a user logs into Cache (user account or telnet) quit JOB; ; JOB'd process begins quit CALLIN; ; a process enters via CALLIN interface quit Otra forma de hacer lo mismo es usar ^%SYSMONMGR: %SYS>do ^%SYSMONMGR 1. Select item 3, Configure System Monitor Classes. 2. Select item 2, Configure Startup Namespaces. 3. Select item 2, Add Namespace. Namespace? TEST 4. Select item 1, List Start Namespaces. Option? 1 TEST 5. Select item 4, Exit. 6. Select item 3, Exit. 7. Select item 8, Exit. Reiniciamos ahora Caché (de ser posible) para asegurarnos de que las estadísticas SNMP se sigan recolectando después de un reinicio. Y terminamos. Quizás algunos cuestionen mi elección de parámetros supervisados o código, pero la tarea era mostrar la mera posibilidad de implementar dicha supervisión. Más tarde puede agregar parámetros extra o refactorizar su código. ¡Gracias por vuestra atención! Muy buen articulo. Utilizo SNMP personalizado con Zabbix y el artículo cubrió completamente la configuración. En mi experiencia, una desventaja que encontré es el mantenimiento. Tuve muchos problemas al principio. Recomiendo este protocolo para aquellos que tienen pocos indicadores personalizados para crear y actualizar. Hola Joel, Buenas tardes, Te saluda desde Argentina Fernando Iglesias Primero que nada muchas gracias por compartir esta información hace tiempo que estoy tratando de monitorear  varios clientes que utilizan HSFOUNDATION2014 con zabbix. Tengo problemas para crear la clase ¿Me podrás ayudar? Hola @Fernando.Iglesias de forma independiente a lo que responda Joel, creo que si creas un post con el problema concreto y todos los datos será más fácil que la comunidad pueda tener más información al respecto Hola @Fernando.Iglesias, Encantado de poder ayudarte! de todos modos, lo que menciona @David.Reche es lo ideal, si publicas tu post acá es mucho más probable que puedas conseguir respuestas más rápido. Te dejo mi mail joel.espinoza@intersystems.com para ir avanzando. Saludos! Joel