Sie sind auf Seite 1von 291

Desarrollo de software

Este texto está pensado para


aquellos que, teniendo
conocimientos de la
tecnología ASP, deseen
aumentar la potencia y
escalabilidad de sus
aplicaciones mediante el uso
de componentes compilados.

La primera parte del libro


consiste en un recordatorio
de las características más
importantes de ASP,
mientras que la segunda se
centra en el diseño y uso de
componentes.

Se tratan temas como la


creación de librerías de
enlace dinámico (DLLs), uso
de componentes desde
páginas ASP, modelo COM+,
Servicios de Componentes,
componentes de acceso a
datos, componentes
transaccionales, MSMQ y
ADSI.

Se requieren unos
conocimientos básicos de
Windows, fundamentos de
Internet/Intranet, lenguaje
HTML y tecnología ASP, así
como ciertas nociones de
programación con Visual
Basic.

DESARROLLO DE
APLICACIONES COM+
PARA INTERNET/INTRANET CON ASP 3
VÍCTOR ARRONDO, ÁNGEL ESTEBAN
ADVERTENCIA LEGAL
Todos los derechos de esta obra están reservados a Grupo EIDOS Consultoría y Documentación
Informática, S.L.

El editor prohíbe cualquier tipo de fijación, reproducción, transformación, distribución, ya sea mediante
venta y/o alquiler y/o préstamo y/o cualquier otra forma de cesión de uso, y/o comunicación pública de la
misma, total o parcialmente, por cualquier sistema o en cualquier soporte, ya sea por fotocopia, medio
mecánico o electrónico, incluido el tratamiento informático de la misma, en cualquier lugar del universo.

El almacenamiento o archivo de esta obra en un ordenador diferente al inicial está expresamente


prohibido, así como cualquier otra forma de descarga (downloading), transmisión o puesta a disposición
(aún en sistema streaming).

La vulneración de cualesquiera de estos derechos podrá ser considerada como una actividad penal
tipificada en los artículos 270 y siguientes del Código Penal.

La protección de esta obra se extiende al universo, de acuerdo con las leyes y convenios internacionales.

Esta obra está destinada exclusivamente para el uso particular del usuario, quedando expresamente
prohibido su uso profesional en empresas, centros docentes o cualquier otro, incluyendo a sus empleados
de cualquier tipo, colaboradores y/o alumnos.

Si Vd. desea autorización para el uso profesional, puede obtenerla enviando un e-mail fmarin@eidos.es o
al fax (34)-91-5017824.

Si piensa o tiene alguna duda sobre la legalidad de la autorización de la obra, o que la misma ha llegado
hasta Vd. vulnerando lo anterior, le agradeceremos que nos lo comunique al e-mail fmarin@eidos.es o al
fax (34)-91-5017824). Esta comunicación será absolutamente confidencial.

Colabore contra el fraude. Si usted piensa que esta obra le ha sido de utilidad, pero no se han abonado los
derechos correspondientes, no podremos hacer más obras como ésta.

© Victor Arrondo y Ángel Esteban, 2000


© Grupo EIDOS Consultaría y Documentación Informática, S.L., 2000

ISBN 84-88457-20-0

Desarrollo de Aplicaciones COM+ para Internet/Intranet con


ASP 3
Victor Arrondo y Ángel Esteban
Responsable editorial Coordinación de la edición
Paco Marín (fmarin@eidos.es) Antonio Quirós (aquiros@eidos.es)
Autoedición
Magdalena Marín (mmarin@eidos.es)
Victor Arrondo (varrondo@eidos.es)
Ángel Esteban (aesteban@eidos.es)
Grupo EIDOS
C/ Téllez 30 Oficina 2
28007-Madrid (España)
Tel: 91 5013234 Fax: 91 (34) 5017824
www.grupoeidos.com/www.eidos.es
www.LaLibreriaDigital.com
Índice

ÍNDICE................................................................................................................................................... 5
INTRODUCCIÓN A ASP................................................................................................................... 11
ANTECEDENTES DE ASP: LA ESPECIFICACIÓN CGI .......................................................................... 11
DEFINICIÓN DE ASP........................................................................................................................... 12
APLICACIONES ASP ........................................................................................................................... 13
APORTACIONES DE ASP..................................................................................................................... 14
SINTAXIS DE ASP............................................................................................................................... 15
OBJETOS INTEGRADOS EN ASP 3.0.................................................................................................... 17
COMPONENTES DE SERVIDOR ............................................................................................................ 18
NOVEDADES DE ASP 3.0 ................................................................................................................... 19
Mejoras generales en ASP 3.0....................................................................................................... 19
El objeto Response......................................................................................................................... 20
El objeto Server ............................................................................................................................. 20
El objeto ASPError........................................................................................................................ 24
Componente de registro de IIS (Logging Utility) .......................................................................... 26
Aplicaciones ASP con IIS 5.0 ........................................................................................................ 30
Otros cambios................................................................................................................................ 32
MODELO DE OBJETOS DE ASP. PARTE I .................................................................................. 35
INTRODUCCIÓN .................................................................................................................................. 35
EL OBJETO RESPONSE ........................................................................................................................ 36
Colecciones del objeto Response................................................................................................... 37
Propiedades del objeto Response .................................................................................................. 39
Métodos del objeto Response......................................................................................................... 41
EL OBJETO REQUEST .......................................................................................................................... 44
Colecciones del objeto Request ..................................................................................................... 44
MODELO DE OBJETOS DE ASP. PARTE II ................................................................................ 49
EL OBJETO APPLICATION ................................................................................................................... 49
Colecciones del objeto Application ............................................................................................... 51
Métodos del objeto Application ..................................................................................................... 52
Eventos del objeto Application. El GLOBAL.ASA......................................................................... 53
EL OBJETO SESSION ........................................................................................................................... 56
Colecciones del objeto Session...................................................................................................... 56
Propiedades del objeto Session ..................................................................................................... 58
Métodos del objeto Session............................................................................................................ 60
Eventos del objeto Session............................................................................................................. 60
EL OBJETO SERVER ............................................................................................................................ 61
Propiedades del objeto Server....................................................................................................... 61
Métodos del objeto Server ............................................................................................................. 62
EL OBJETO OBJECTCONTEXT ............................................................................................................. 64
Métodos del objeto ObjectContext................................................................................................. 66
EVENTOS DEL OBJETO OBJECTCONTEXT ........................................................................................... 66
EL OBJETO ASPERROR ...................................................................................................................... 67
Propiedades del objeto ASPError ................................................................................................. 67
COMPONENTES DE SERVIDOR ................................................................................................... 69
INTRODUCCIÓN .................................................................................................................................. 69
COMPONENTE ADROTATOR .............................................................................................................. 71
COMPONENTE FUNCIONES DEL NAVEGADOR .................................................................................... 74
COMPONENTE NEXTLINK................................................................................................................... 77
COMPONENTE CONTENT ROTATOR ................................................................................................... 80
COMPONENTE PAGECOUNTER ........................................................................................................... 83
COMPONENTE COUNTERS .................................................................................................................. 86
COMPONENTE MYINFO ...................................................................................................................... 87
COMPONENTE TOOLS ......................................................................................................................... 88
COMPONENTE PERMISSIONCHECKER ................................................................................................ 91
COMPONENTES DE ACCESO A DATOS. ADO .......................................................................... 93
INTRODUCCIÓN .................................................................................................................................. 93
MODELO DE OBJETOS DE ADO .......................................................................................................... 96
EL OBJETO CONNECTION ................................................................................................................... 97
Abrir una conexión ........................................................................................................................ 98
Ejecutar comandos sobre una conexión ...................................................................................... 102
Cerrar la conexión....................................................................................................................... 104
EL OBJETO COMMAND ..................................................................................................................... 104
Crear un objeto Command .......................................................................................................... 105
La colección Parameters ............................................................................................................. 107
Ejecutar un comando................................................................................................................... 109
EL OBJETO RECORDSET ................................................................................................................... 111
Creación y apertura de un objeto Recordset ............................................................................... 114
CDONTS Y ASP ................................................................................................................................ 119
INTRODUCCIÓN ................................................................................................................................ 119
MODELO DE OBJETOS DE CDONTS................................................................................................. 121
EL OBJETO NEWMAIL ....................................................................................................................... 122
EL OBJETO SESSION ......................................................................................................................... 127
EL OBJETO FOLDER .......................................................................................................................... 130
EL OBJETO MESSAGE ....................................................................................................................... 133
EL MODELO COM.......................................................................................................................... 143

6
INTRODUCCIÓN A LOS COMPONENTES ............................................................................................. 143
CONCEPTOS ...................................................................................................................................... 144
ESPECIFICACIÓN COM..................................................................................................................... 144
VENTAJAS DEL USO DE COMPONENTES............................................................................................ 145
LOS INTERFACES .............................................................................................................................. 146
IDENTIFICADORES ÚNICOS GLOBALES (GUIDS)............................................................................. 147
ENLACE TEMPRANO Y TARDÍO ......................................................................................................... 148
ESPACIO DE PROCESO DE UN COMPONENTE ..................................................................................... 149
EL INTERFAZ IUNKNOWN ................................................................................................................ 150
WINDOWS DNA ............................................................................................................................... 150
COMPONENTES EN VISUAL BASIC .......................................................................................... 153
INTRODUCCIÓN ................................................................................................................................ 153
COMPONENTES EN VISUAL BASIC Y VISUAL C++........................................................................... 153
UN PRIMER EJEMPLO DE COMPONENTE............................................................................................ 154
Fases en el diseño del componente.............................................................................................. 154
Creación del proyecto ActiveX DLL............................................................................................ 155
Diseño del interfaz....................................................................................................................... 156
Generación de la DLL y registro del componente....................................................................... 159
Rediseño del componente ............................................................................................................ 161
Reutilización del componente ...................................................................................................... 164
TIPOS DE COMPONENTES.................................................................................................................. 167
MODELOS DE APLICACIONES CLIENTE/SERVIDOR.......................................................... 171
INTRODUCCIÓN ................................................................................................................................ 171
ARQUITECTURA CLIENTE/SERVIDOR EN DOS CAPAS........................................................................ 171
ARQUITECTURA CLIENTE/SERVIDOR EN TRES CAPAS ...................................................................... 172
EJEMPLO DE UNA APLICACIÓN ASP EN TRES CAPAS ....................................................................... 174
COMPONENTIZACIÓN DE LA CAPA INTERMEDIA .............................................................................. 175
Diseño de la capa intermedia con un componente ...................................................................... 175
Rediseño del componente de la capa intermedia......................................................................... 178
Un segundo rediseño del componente de la capa intermedia ..................................................... 178
ARQUITECTURA CLIENTE/SERVIDOR EN N CAPAS ............................................................................ 180
DISEÑO DE COMPONENTES PARA ASP................................................................................... 181
INTRODUCCIÓN ................................................................................................................................ 181
TIPOS VARIANT ................................................................................................................................ 181
ACCESO AL MODELO DE OBJETOS DE ASP DESDE UN COMPONENTE .............................................. 185
Acceso al objeto Application ....................................................................................................... 185
Acceso a los objetos Request y Response .................................................................................... 187
Recompilación de componentes con enlace temprano ................................................................ 192
IMPLEMENTACIÓN DE INTERFACES EN VISUAL BASIC .................................................................... 193
SERVICIOS DE COMPONENTES ................................................................................................ 197
INTRODUCCIÓN ................................................................................................................................ 197
CARACTERÍSTICAS DEL MTS (MICROSOFT TRANSACTION SERVER).............................................. 197
SERVICIOS QUE APORTA EL MTS..................................................................................................... 198
Transacciones para componentes................................................................................................ 199
Comercio de objetos .................................................................................................................... 199
Independencia entre procesos ..................................................................................................... 199
Fondo común de recursos............................................................................................................ 199
Activación en el momento (Just-In-Time).................................................................................... 199
Seguridad..................................................................................................................................... 200
CONCEPTOS PRINCIPALES DEL MTS ................................................................................................ 200
La intercepción ............................................................................................................................ 200

7
Propiedades declarativas. El catálogo del MTS.......................................................................... 200
FUNCIONAMIENTO DEL MTS ........................................................................................................... 201
El objeto Context Wrapper .......................................................................................................... 201
El objeto de contexto ................................................................................................................... 201
El interfaz ObjectContext ............................................................................................................ 202
El interfaz ObjectControl ............................................................................................................ 203
INSTANCIAR UN OBJETO A PARTIR DE UN COMPONENTE MTS ........................................................ 204
Instanciar un objeto desde otro objeto de Visual Basic .............................................................. 204
Instanciar un objeto desde una página ASP con VBScript.......................................................... 204
INTRODUCCIÓN A LAS TRANSACCIONES .......................................................................................... 205
Duración y resultado de una transacción.................................................................................... 206
ACTIVACIÓN EN EL MOMENTO ......................................................................................................... 206
ALMACENAR EL ESTADO DEL OBJETO ............................................................................................. 208
ADMINISTRADOR DE PROPIEDADES COMPARTIDAS (SPM)............................................................. 208
Obtener una referencia al SPM ................................................................................................... 209
Crear un grupo de propiedades compartidas.............................................................................. 210
Crear una propiedad compartida................................................................................................ 212
SERVICIOS DE COMPONENTES Y COM+ .......................................................................................... 213
La intercepción en COM+........................................................................................................... 215
DISEÑO DE COMPONENTES COM+ .......................................................................................... 219
EL INTERFAZ OBJECTCONTROL ....................................................................................................... 219
ACTIVACIÓN JUST-IN-TIME (JIT) .................................................................................................... 227
INICIALIZACIÓN DEL COMPONENTE ................................................................................................. 229
ESTADO DE UN COMPONENTE .......................................................................................................... 232
CADENA DEL CONSTRUCTOR ........................................................................................................... 238
COMPONENTES TRANSACCIONALES..................................................................................... 241
INTRODUCCIÓN ................................................................................................................................ 241
EL CONTEXTO Y LAS TRANSACCIONES ............................................................................................ 242
PAUTAS PARA EL DESARROLLO DE COMPONENTES TRANSACCIONALES ......................................... 242
CASOS PRÁCTICOS ........................................................................................................................... 243
La página contiene todo el código transaccional........................................................................ 243
PARTE DEL CÓDIGO TRANSACCIONAL EN UN COMPONENTE ............................................................ 245
VOTO TRANSACCIONAL DE UN COMPONENTE ................................................................................. 249
DOS COMPONENTES TRANSACCIONALES INVOCADOS DESDE ASP.................................................. 251
UN COMPONENTE TRANSACCIONAL INVOCA A OTRO ...................................................................... 253
ACCESO A MSMQ DESDE ASP .................................................................................................... 257
SERVICIOS DE MENSAJERÍA ENTRE APLICACIONES .......................................................................... 257
MSMQ (MESSAGE QUEUING SERVER)............................................................................................ 258
FUNCIONAMIENTO DEL MSMQ ....................................................................................................... 259
Creación de una cola................................................................................................................... 260
Modelo de objetos de MSMQ....................................................................................................... 261
Envío de mensajes........................................................................................................................ 262
Prioridad de los mensajes ........................................................................................................... 265
Recepción de mensajes ................................................................................................................ 266
Colas de diario ............................................................................................................................ 269
ENVÍO DE UN COMPONENTE COM+................................................................................................. 271
ACCESO A ADSI DESDE ASP ....................................................................................................... 277
DIRECTORIOS Y SERVICIOS DE DIRECTORIO .................................................................................... 277
ACTIVE DIRECTORY DE MICROSOFT ............................................................................................... 278
ADSI (ACTIVE DIRECTORY SERVICES INTERFACE) ........................................................................ 279
ESTRUCTURA DE ACTIVE DIRECTORY ............................................................................................. 280

8
PROVEEDOR ADSI PARA WINNT .................................................................................................... 281
CONTENEDORES ............................................................................................................................... 283
PROPIEDADES DE LOS OBJETOS DEL DIRECTORIO ............................................................................ 285
PROPIEDADES CON VALORES MÚLTIPLES ........................................................................................ 287
MODIFICACIÓN DE PROPIEDADES .................................................................................................... 288

9
Introducción a ASP

Antecedentes de ASP: La especificación CGI


ASP no es una idea realmente nueva, encontramos un antecedente muy importante y muy utilizado en
Internet denominado comúnmente scritps CGI.

Las siglas CGI se corresponden en inglés a Common Gateway Interface, es decir, interfaz de pasarela
común. Vamos a ir viendo paso a paso que significan cada unas de estas palabras, que realmente son
las que definen el concepto de CGI.

La especificación Common Gateway Interface permite a los servidores Web ejecutar y comunicarse
con otros programas, llamados programas CGI, e incorporar la salida de los mismos a los gráficos,
texto o audio enviados a un navegador Web.

La programación en CGI implica diseñar programas que se ejecutarán en el entorno de Internet, y más
concretamente en el entorno World Wide Web.

El programa CGI se ejecutará dentro del entorno ofrecido por el servidor Web que lo contiene. El
servidor Web creará una información especial para el CGI cuando pasa a ejecutarlo, y el servidor
esperará una respuesta del programa CGI como resultado de su ejecución. Es esta comunicación o
interacción entre el servidor Web y el programa CGI es lo que define realmente la especificación CGI.

Los programas CGI también se suelen denominar scripts CGI, esto es debido a que los primeros
programas CGI fueron escritos utilizando scripts de la shell de UNIX y Perl.

Antes de que el programa CGI se ejecute, el servidor Web que lo contiene se encargará de crear un
entorno con el que podrá interactuar el programa CGI. Este entorno comprende la traducción de
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

cabeceras de peticiones del protocolo HTTP (HyperText Transfer Protocol) en variables de entorno a
las que podrá acceder nuestro programa CGI. Estas variables de entorno contendrán una información
muy variada acerca del cliente que ha realizado la petición o del propio servidor Web en el que se
ejecuta el programa CGI.

Una vez que el servidor ha iniciado la ejecución del programa CGI esperará un resultado de la
ejecución del mismo. Este resultado suele ser una serie de encabezados de respuesta del protocolo
HTTP y código HTML. Estos encabezados y código HTML serán recogidos por el servidor Web y
enviados al cliente que realizó la petición, es decir, al navegador o cliente Web.

Después de ver esta pequeña introducción podemos definir un programa CGI como un programa que
se encuentra en un servidor Web y que recibe peticiones desde un cliente Web través del servidor
Web. Y gracias al entorno que le ofrece el servidor Web el programa CGI puede obtener información
sobre la petición realizada, además de otra información útil, que le permitirá procesar la petición. La
respuesta a esta petición será generada por el programa CGI en forma de cabeceras de respuesta del
protocolo HTTP y etiquetas del lenguaje HTML (HyperText Markup Language), que serán enviadas
por el servidor Web al navegador Web que realizó la petición.

CGI no es un lenguaje de programación sino que es una especificación. La especificación CGI va a


realizar la función de interfaz o pasarela entre el servidor Web y los programas CGI, haciendo uso del
protocolo HTTP y el lenguaje de hipertexto HTML.

Un programa CGI será aquel que cumpla la especificación CGI, es decir, interactuará con el servidor
atendiendo a unos principios establecidos por la especificación CGI.

CGI ya lleva siendo utilizado muchos años en la red y todavía se sigue utilizando en muchos sitios
Web a la hora de acceder a datos o construir páginas dinámicas, pero cada vez más los sitios Web van
adoptando la utilización de Active Server Pages.

Active Server Pages (ASP) es el nombre que reciben las páginas activas de servidor, es decir, las
páginas que se ejecutan en el servidor. ASP se basa en la especificación CGI, podemos considerar que
ASP es una evolución de la especificación CGI.

Definición de ASP
La filosofía de ASP resulta muy sencilla, en pocas palabras se puede definir de la siguiente forma: las
páginas ASP, también llamadas páginas activas, son páginas que contienen código HTML, script de
cliente y un script que se ejecuta en el servidor, dando como resultado código HTML. Por lo tanto al
cargar una página ASP en nuestro navegador, en realidad no estamos cargando la página ASP como
tal, sino el resultado de la ejecución de la página ASP, es decir la salida de la página ASP, y como se
ha apuntado anteriormente se trata de código HTML. Es decir, son páginas que se ejecutan en el
servidor enviando como resultado al cliente código HTML.

Antes de seguir vamos a definir de forma sencilla lo que se considera un lenguaje de script o de
secuencia de comandos. Un lenguaje de script es un subconjunto de otro lenguaje más general y que se
utiliza para un entorno muy determinado, en este caso el entorno es la Web.

Una página ASP podrá contener los siguientes elementos: texto, componentes ActiveX, código HTML
y comandos de script. Este script puede ser de dos tipos: script de cliente o script de servidor. El script
de servidor es la nueva idea que introduce ASP, se debe tener en cuenta que en el script de servidor se
tiene acceso a diferentes objetos y no está orientado a eventos.

12
© Grupo EIDOS 1. Introducción a ASP

El script de servidor utilizado en ASP utiliza la misma sintaxis que el script de cliente, la diferencia
está en que con ASP el script de servidor es compilado y procesado por el servidor Web antes de que
la página sea enviada al navegador.

ASP no es un lenguaje de script, ASP ofrece un entorno para procesar scripts que se incorporan dentro
de páginas HTML, es decir, un entorno de procesamiento de scripts de servidor.

La propia Microsoft define ASP de la siguiente manera: "...es un entorno de secuencias de comandos
en el lado del servidor que puede utilizar para crear y ejecutar aplicaciones de servidor Web
dinámicas, interactivas y de alto rendimiento...".

Realmente, ASP es un componente (asp.dll) que se instala en un servidor Web y cuya misión es la de
procesar ficheros que terminan con la extensión .asp y transmitir el resultado al cliente que solicitó la
página ASP.

El script de servidor incluido en una página ASP empieza a ejecutarse cuando un navegador solicita el
archivo .asp al servidor Web. El servidor Web llama entonces a ASP, el cual lee el archivo solicitado
de arriba a abajo, ejecuta los comandos y envía una página HTML al explorador. ASP incluye un
motor de interpretación de scripts del lado del servidor.

Las páginas ASP son ficheros con la extensión asp. Crear un fichero .asp resulta muy sencillo, se
puede crear a partir de una página HTML existente, simplemente renombrando el fichero .html o .htm
a un fichero .asp. Para hacer esta página ASP disponible para los usuarios de la Web, el fichero .asp se
debe almacenar en un directorio de publicación en Internet, se debe tener en cuenta que el directorio
virtual asociado debe tener permisos de ejecución de secuencias de comandos.

La última versión de la tecnología ASP es la versión 3.0. Esta versión es muy similar a su predecesora,
y todas las nuevas características que presenta se deben a que se utiliza una nueva versión del servidor
Web (Internet Information Services 5.0), recordemos que las páginas ASP son procesadas por el
servidor.

En el tema siguiente se ofrece una comparativa de ASP 2.0 con ASP 3.0 comentando brevemente
todas sus novedades, se recomienda la lectura del segundo capítulo sobretodo a los alumnos que ya
conozcan ASP 2.0.

Aplicaciones ASP
Una aplicación basada en ASP consta de un directorio virtual en un servidor Web y de todos los
subdirectorios y archivos contenidos en él. Una aplicación puede ser una página principal sencilla, o
bien puede estar formada por un conjunto completo de páginas interrelacionadas entre sí.

Al usar aplicaciones en ASP es posible mantener un estado, es decir, se tiene la capacidad de mantener
información. Dentro de una aplicación ASP se pueden mantener dos tipos de estado:

• Estado de la aplicación, en la que toda la información relativa a una aplicación está disponible
para todos los usuarios de la misma.

• Estado de sesión, en la que la información sólo está disponible para un usuario o sesión
específicos. Una sesión por lo tanto, pertenece a un solo usuario.

Un ejemplo práctico de una aplicación ASP puede ser este mismo sitio Web. Almagesto está
completamente realizado con páginas ASP constituyendo por lo tanto una aplicación ASP, este sitio

13
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Web demuestra los diferentes usos que puede tener la tecnología ASP y las necesidades que puede
cubrir.

Las aplicaciones ASP no son aplicaciones al uso, ya que en realidad no se dispone de un ejecutable
sino de un conjunto de páginas, imágenes y recursos, por lo tanto se trata de aplicaciones muy
particulares que requieren para su ejecución de un servidor Web que soporte las páginas ASP.

Aportaciones de ASP
En este apartado se comentan las aportaciones que ofrece ASP desde su primera versión, es decir, se
trata de aportaciones muy genéricas de la tecnología ASP.

Para entender las aportaciones que ofrecen las páginas ASP se deben tener en cuenta una serie de
características del protocolo HTTP (HyperText Transfer Protocol). Se dice que le protocolo HTTP es
un protocolo sin estado, es decir, no se puede mantener un estado entre diferentes peticiones. El
protocolo HTTP se basa en el paradigma cliente/servidor o petición/respuesta.

Se deben tener en cuenta un par de puntos a la hora de establecer la comunicación entre clientes
(navegadores Web) y servidores (servidores Web) del protocolo HTTP:

• Después de realizar una petición el cliente se desconecta del servidor y espera una respuesta.
El servidor debe restablecer la conexión después de que haya procesado la petición.

• El servidor y el cliente sólo se tienen en cuenta durante la conexión, después, se olvidan el uno
del otro. Por esta razón, ni el cliente ni el servidor pueden retener información entre diferentes
peticiones o a través de diferentes páginas Web. Sin embargo, ASP permite al servidor
almacenar información, o mantener el estado, entre las diferentes peticiones del cliente.

El cliente y el servidor Web se comunican utilizando cabeceras HTTP, estas cabeceras son colecciones
de datos que intercambian el cliente y el servidor para asegurar que la transacción es coherente y
completa. Como petición del usuario se envía una cabecera y el servidor interpreta esta cabecera y
envía una respuesta HTTP cuyo cuerpo sería el contenido del recurso demandado por el cliente.

ASP permite al desarrollador intervenir en todo el proceso de comunicación del protocolo HTTP. Los
objetos integrados dentro de ASP Request y Response interactúan con las peticiones y respuestas del
protocolo HTTP, respectivamente.

Dentro de los objetos integrados de ASP podemos encontrar la forma de acceder al servidor, obtener
información del mismo, así como del usuario. Y también se permite, como se había comentado
anteriormente, mantener el estado entre diferentes peticiones del cliente.

Se puede considerar ASP como una nueva (aunque ya no tan nueva) aproximación a la creación de
páginas web complejas que pueden acceder a bases de datos o a otros objetos del servidor. Ofrece lo
siguiente:

• Independencia del navegador, ASP puede ejecutar complejas operaciones en el servidor y


enviar solamente los resultados al cliente.

• Construcción de páginas basadas en bases de datos que permiten realizar operaciones sobre las
bases de datos del servidor de forma bastante sencilla.

• Es una de las soluciones más versátiles para el desarrollo de aplicaciones en el entorno de


Internet/Intranet.

14
© Grupo EIDOS 1. Introducción a ASP

• Desarrollo de complejas aplicaciones Web.

• Facilidad de uso de componentes de terceras partes ejecutándose en el servidor, es decir, se


pueden utilizar componentes para liberarnos de realizar tareas complejas. Estos componentes
se deben registrar en el servidor y podrán ser utilizados desde el script correspondiente. Estos
componentes se denominan componentes ActiveX de servidor.

• Posibilidad de definir páginas ASP transaccionales para realizar todas las operaciones
contenidas en la misma dentro de una transacción.

• Una tecnología en constante evolución y mejora.

A lo largo del curso se profundizará más en todos estos puntos, aquí se han comentado simplemente
los más evidentes y también para poseer una visión general de lo que supone la tecnología ASP.

Sintaxis de ASP
Como se ha comentado anteriormente ASP no es un lenguaje de script, sino que ofrece un entorno
para la ejecución de estos lenguajes que se encuentran dentro de páginas ASP. ASP posee una sintaxis
para poder distinguir cada uno de los elementos que nos podemos encontrar dentro de una página
ASP.

Encerrado dentro de los delimitadores <%%> se va a encontrar todo el código de script de servidor, de
esta forma el comando <%nombre="Pepe"%> asigna el valor Pepe a la variable nombre; y dentro
de los delimitadores <%=%> se encuentran expresiones de salida, así por ejemplo la expresión
<%=nombre%> enviará al navegador el valor Pepe, es decir, el valor actual de la variable, más
adelante se verá una equivalencia de estos delimitadores con un método de un objeto integrado de
ASP.

Entre los delimitadores <%%> se puede y debe incluir varias sentencias en distintas líneas de código
del lenguaje de secuencias de comandos, sin embargo los delimitadores <%=%> sólo podemos
encerrar una sentencia por línea.

Entre los delimitadores de ASP se puede incluir cualquier tipo de expresión válida en el lenguaje de
script principal. Por ejemplo el Código fuente 1 genera un texto que contiene la hora actual del
servidor.

Esta página se actualizó a las <%=Now%>

Código fuente 1

En este caso el servidor Web devuelve al navegador el valor de la función Now de VBScript junto con
el texto. Dentro de los delimitadores de script de servidor se pueden encontrar también instrucciones
del lenguaje de script correspondiente, así por ejemplo puede aparecer una instrucción If...Then...Else
del lenguaje VBScript como se puede apreciar en el Código fuente 2.

<%
If nombre="" Then
variable="Nombre desconocido"

15
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Else
variable="Hola amigo "&nombre
End If
%>
<FONT COLOR="GREEN">
<%=variable%>
</FONT>

Código fuente 2

En el código anterior se comprueba si la variable nombre tiene algún valor, si lo tiene saludamos con
el valor de la variable, mostrando el saludo en color verde.

También se puede incluir código HTML entre las instrucciones del script. Por ejemplo la secuencia de
comandos del Código fuente 3 mezcla HTML con una instrucción condicional y produce el mismo
resultado que la secuencia anterior.

<FONT COLOR="GREEN">
<% If nombre="" Then%>
Nombre desconocido
<%Else%>
Hola amigo <%=nombre%>
<%End If%>
</FONT>

Código fuente 3

Para poder realizar una lectura más sencilla del código ASP se recomienda utilizar los delimitadores
de script de servidor encerrando varias líneas de código en lugar de un par de delimitadores por cada
línea. Así, en lugar de escribir lo que se muestra en el Código fuente 4, se debería escribir lo que se
muestra en el Código fuente 5.

<%strNombre=Session("nombre")%>
<%strApellidos=Session("apellidos")%>
<%strEdad=Session("edad")%>

Código fuente 4

<%
strNombre=Session("nombre")
strApellidos=Session("apellidos")
strEdad=Session("edad")
%>

Código fuente 5

16
© Grupo EIDOS 1. Introducción a ASP

Objetos integrados en ASP 3.0


ASP en su versión 3.0 contiene siete objetos integrados que liberan al programador de la realización
de tareas complejas. Estos seis objetos no requieren que sean instanciados siempre se encuentran
disponibles en nuestras páginas ASP.

Estos objetos son los siguientes: Application, Session, Request, Response, Server, ASPError y
ObjectContext.

Cada uno de estos objetos posee una serie de métodos y propiedades para poder ser utilizados por el
script de servidor, además cada objeto posee una función determinada, básicamente estas funciones
son las siguientes:

• Request: obtención de información del cliente.

• Response: envío de información al cliente.

• Server: acceso a los recursos del servidor, como puede ser la creación de componentes .

• Session: almacena información sobre la sesión de un usuario.

• Application: almacena información común para todos los usuarios de la aplicación ASP.

• ObjectContext: gestión de transacciones en páginas ASP.

• ASPError: contiene información detallada acerca del último error que se ha producido.

Cada uno de estos objetos se explicarán con una mayor profundidad más adelante.

La sintaxis utilizada para poder acceder a los métodos y propiedades de los objetos depende del
lenguaje de script que estemos utilizando. Debido que el lenguaje de script por defecto de ASP es
VBScript (subconjunto de Visual Basic) en este curso nos vamos a centrar en este script.

Los objetos Request y Response contienen colecciones. Una colección es un conjunto de elementos de
información relacionados y que se accede a ellos de una misma forma.

Se puede acceder a cada elemento de una colección mediante el bucle For...Each. La utilización de
colecciones se verá en detenimiento en los capítulos dedicados a estos dos objetos integrados.

Un método es un procedimiento que actúa sobre un objeto, la sintaxis para poder invocar un método
de un objeto es la del Código fuente 6.

Objeto.metodo parametros

Código fuente 6

Donde el tipo de parametros dependerá del método invocado.

Una propiedad es un atributo de un objeto. Las propiedades son características de un objeto que
describen su estado, así por ejemplo un objeto podría tener las características tamaño, nombre, color,
etc. Para obtener el valor de una propiedad utilizamos la sintaxis del Código fuente 7.

17
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Objeto.propiedad

Código fuente 7

Y para asignarle un valor a una propiedad de un objeto debemos utilizar la sintaxis del Código fuente
8.

Objeto.propiedad=valor

Código fuente 8

Donde valor depende de la propiedad del objeto.

Componentes de servidor
ASP incluye una serie de componentes ActiveX de servidor (o componentes de servidor), llamados
componentes ActiveX Server, anteriormente conocidos como servidores de Automatización. Estos
componentes están diseñados para ejecutarse en un servidor Web y contienen una serie de funciones
bastante útiles para que el programador no tenga que construirlas, una de estas funciones puede ser el
acceso a bases de datos. Estos componentes los invocaremos desde nuestras páginas ASP.

No se deben confundir los componentes de servidor con los objetos integrados en ASP.

Para poder tener acceso a alguno de los componentes ActiveX de servidor primero se deberá crear una
instancia del componente correspondiente. Una vez creada la instancia, se pueden usar los métodos
asociados al componente o establecer y leer sus propiedades.

Los componentes ActiveX Server que incluye ASP en su versión 3.0 son los siguientes:

• Componente de acceso a bases de datos, ADO (ActiveX Data Objects). A través de la


utilización de este componente se puede ofrecer acceso a bases de datos desde una página
ASP, así por ejemplo, se puede mostrar el contenido de una tabla, permitir que los usuarios
realicen consultas y otras operaciones sobre una base de datos.

• Componente Ad Rotator. Este componente permite mostrar una serie de imágenes alternativas
con un vínculo a otra dirección desde la imagen presentada. Este componente se suele utilizar
para mostrar diferentes anuncios de forma alternativa dentro de una página ASP.

• Componente Funciones del explorador. A través de este componentes podemos recuperar


datos acerca del tipo de navegador del cliente y que capacidades o funciones tiene.

• Componente vínculo de contenidos. Facilita el desplazamiento lógico entre las diferentes


páginas ASP de una aplicación ASP.

• Componente Content Rotator (rotador de contenidos). Este componente permite hacer


rotaciones de cadenas de contenido HTML en una página.

18
© Grupo EIDOS 1. Introducción a ASP

• Componente Page Counter (contador de páginas). Permite llevar una cuenta del número de
veces que se ha accedido a una página determinada dentro de nuestro sitio Web.

• Componente Counters. A través de este componente podremos almacenar, crear, incrementar


y consultar cualquier contador.

• Componente MyInfo. Nos permite almacenar información personal que será ofrecida por el
administrador del sitio Web.

• Componente Tools. Es el denominado componente de utilidades. Ofrece una serie de


funciones diversas, como la generación de números aleatorios o la comprobación de la
existencia de un fichero en el servidor.

• Componente Permission Checker. A través de este componente podremos determinar si a un


usuario se le ha dado permisos para acceder a un fichero determinado.

• Componente Status. Este componente, de momento, únicamente está disponible para el


servidor Personal Web Server en plataformas Macintosh. Resulta extraño pero es así. Nos
ofrece una información variada acerca del estado del servidor Web.

• Componente de registro de IIS. Mediante este componente tenemos acceso a la información y


manipulación de los ficheros de registro (log) generados por el servidor Web IIS 5.0.

Además de todos estos componentes, el programador puede crear sus propios componentes ActiveX
Server. Estos componentes se pueden desarrollar en lenguajes de programación como Visual Basic,
Java o C++, una vez creado el componente se transforma a una DLL que se registrará en el servidor.

Todos los componentes de servidor que no es encuentran incluidos en ASP deben ser registrados. Una
vez registrado el componente en el servidor Web lo podemos instanciar desde el lenguaje de
secuencias de comandos de una página ASP, al igual que hacíamos con los componentes que vienen
por defecto con ASP.

También se puede adquirir estos componentes a terceros, existen empresas que se dedican al diseño de
componentes para que sean utilizados desde páginas ASP.

Novedades de ASP 3.0


Aquí se pretende mostrar de forma muy general las mejoras y novedades que aporta ASP 3.0 sobre la
versión anterior de las páginas activas ASP 2.0, no se va a entrar en detalles y se supone que el lector
tiene ya algún conocimiento de ASP 2.0.

A continuación vamos a ir comentando en cada apartado cada uno de los cambios y mejoras que
aporta ASP 3.0, para ello se apoya en Internet Information Server 5.0.

Mejoras generales en ASP 3.0


En este epígrafe se va a reunir una serie de cambios y novedades que ofrece ASP 3.0, que son de
carácter general y que afectan al rendimiento y funcionamiento de las aplicaciones ASP.

En esta nueva versión de ASP se ofrece un mejor funcionamiento y escalabilidad de la tecnología


ASP, basándose en las nuevas características y mejoras de Internet Information Server 5.0.

19
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Se ha producido una mejora en el procesamiento de las páginas ASP por parte de la librería
ASP.DLL.

• Se ofrece lo que se denomina ajuste automático, que consiste en detectar cuándo una petición
está bloqueada por recursos externos, en ese caso se proporcionan automáticamente más
subprocesos para ejecutar peticiones adicionales y continuar de esta forma con el
procesamiento normal de forma simultánea.

• Los objetos COM se liberan más rápidamente y por defecto los componentes COM se
ejecutan out-of-process, es decir, en un espacio de memoria distinto al del servidor Web.

• Con ASP 3.0 se ofrecen los objetos COM que se ofrecían con ASP 2.0 (componentes de
servidor, como Content Rotator) pero con su rendimiento mejorado, es decir, aparecen
versiones mejoradas de los componentes anteriores.

• El servidor transaccional Microsoft Transaction Server (MTS) ya no existe como una entidad
separada en Windows 2000, y pasa a formar parte de Servicios de componentes (Microsoft
Component Services). IIS 5.0 y Servicios de componentes funcionan conjuntamente para
formar la arquitectura básica para la creación de aplicaciones Web.

El objeto Response
Los únicos objetos integrados dentro de ASP que han sufrido alguna modificación han sido el objeto
Response, que vemos en este apartado, y el objeto Server.

Por defecto la propiedad Buffer del objeto Response tiene el valor True (verdadero), en ASP 2.0 y 1.0
esta propiedad del objeto Response tenía por defecto el valor de False (falso). Debido a esto, en ASP
3.0 el resultado de la ejecución de una página ASP únicamente es enviado al cliente cuando se
termina de procesar la página ASP correspondiente, o bien cuando se utilizan los métodos Flush o End
del objeto Response.

Por lo tanto, a no ser que se indique otra cosa, de forma predeterminada el resultado de la ejecución de
la página ASP se enviará al búfer. Según afirma Microsoft la técnica del búfer ofrece una entrega de
páginas más eficiente al cliente.

En el objeto Response también cambia la forma de utilizar la propiedad IsClientConnected, mediante


esta propiedad podemos consultar si un cliente se encuentra todavía conectado a nuestro servidor o por
el contrario si ha finalizado su sesión con el mismo. En ASP 2.0 podíamos consultar esta propiedad
sólo si antes habíamos enviado ya alguna salida o contenido al cliente, ahora con ASP 3.0 podemos
utilizar IsClientConnected antes de enviar cualquier contenido al navegador.

El objeto Server
Este es otro de los objetos de ASP que ha experimentado cambios. Presenta dos nuevos métodos:
Transfer y Execute, que permiten controlar el control de flujo del programa, ampliando las
capacidades de control de flujo de las páginas ASP, anteriormente sólo se disponía del método
Redirect del objeto Response.

En ASP 2.0 si queríamos transferir la ejecución a otra página ASP teníamos que utilizar el método
Redirect del objeto Response, pero esto suponía enviar una respuesta al cliente para indicarle la carga
de una nueva página, que es la página a la que pasamos la ejecución.

20
© Grupo EIDOS 1. Introducción a ASP

La utilización del método Redirect es bastante costosa y problemática ya supone un envío de


información más del servidor al cliente para indicarle mediante una cabecera HTTP de redirección que
la página ha cambiado de localización, siendo la nueva localización la página que deseamos cargar.
Esto es problemático ya que en algunos navegadores como Netscape Communicator aparace un
mensaje del tipo El objeto requerido se ha movido y se puede encontrar aquí, esto también ocurre
cuando la conexión la realiza el cliente a través de proxy.

Pero ahora con ASP 3.0 podemos evitar esta redirección, que como hemos visto, tiene lugar en el
cliente, mediante los métodos Execute y Transfer del objeto Server que permiten que la redirección
tenga lugar en el servidor, quedando el cliente completamente ajeno. Ambos métodos reciben como
parámetro la ruta de la página a la que queremos redirigir al cliente.

La utilización del método Execute es muy similar a realizar una llamada a un procedimiento o función.
Cuando lanzamos el método Execute se empieza a ejecutar la página que indicamos por parámetro, y
cuando termina la ejecución de esta nueva página, el control pasa a la siguiente sentencia después de la
llamada al método Execute en la página inicial, siguiendo a partir de aquí con la ejecución de la
página, es decir, el navegador del cliente recibe una salida formada por la combinación de la ejecución
de ambas páginas.

El método Transfer se comporta de distinto modo, al lanzar este método se pasa la ejecución a la
nueva página, pero una vez que finaliza la ejecución de la misma no se vuelve a la página inicial,
como ocurría con el método Execute.

En ambos métodos se mantiene el contexto de la página inicial, es decir, en la nueva página tenemos
acceso a las variables, objetos y a todos los objetos intrínsecos de ASP (Request, Session, Response...)
de la página inicial o página de origen. También se mantienen las transacciones entre distintas páginas,
siempre que proceda, atendiendo a la directiva @TRANSACTION.

De esta forma como la redirección entre páginas se produce en el servidor, el navegador cree que sigue
recibiendo todavía la página original que habia demandado, incluso en la barra de direcciones del
navegador sigue apareciendo la misma URL y los botones Atrás y Adelante funcionan correctamente.

Vamos a ofrecer un sencillo código de una página ASP que utiliza los métodos Transfer y Execute
para ejecutar otra página, y así se puede ver más claramente la utilización de estos dos nuevos métodos
del objeto Server.

Nuestra página, llamada PaginaInicial.asp, va a constar de un formulario con dos botones, y según el
botón que se pulse se lanzará el método Execute o Transfer para ejecutar la página OtraPagina.asp. El
código de de la página PaginaInicial.asp se muestra en el Código fuente 9, y el de OtraPagina.asp en el
Código fuente 10.

<%If Request.Form("Execute")<>"" Then


Response.Write "Se está ejecutando la página "_
& Request.ServerVariables("SCRIPT_NAME") & "<br>"
Server.Execute "OtraPagina.asp"
Response.Write "Se está ejecutando de nuevo la página "_
& Request.ServerVariables("SCRIPT_NAME") & "<br>"
ElseIf Request.Form("Transfer")<>"" Then
Response.Write "Se está ejecutando la página "_
& Request.ServerVariables("SCRIPT_NAME") & "<br>"
Server.Transfer "OtraPagina.asp"
Response.Write "Se está ejecutando de nuevo la página "_
& Request.ServerVariables("SCRIPT_NAME") & "<br>"
End if%>
<FORM ACTION="PaginaInicial.asp" METHOD="POST">
<input type="submit" name="Execute" value="Lanza Server.Execute"><br>

21
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<input type="submit" name="Transfer" value="Lanza Server.Transfer">


</FORM>

Código fuente 9

<hr>

Se está ejecutando la página OtraPagina.asp<br>


Esta página se ha cargado con el método

<%If Request.Form("Execute")<>"" Then%>


<b>EXECUTE</b>
<%ElseIf Request.Form("Transfer")<>"" Then%>
<b>TRANSFER</b>
<%End If%>
<br>La variable Request.ServerVariables("SCRIPT_NAME") sigue teniendo el valor:
<%=Request.ServerVariables("SCRIPT_NAME")%><br>

Termina la ejecución de OtraPagina.asp<br>

<hr>

Código fuente 10

Si ejecutamos la página PAGINAINICIAL.ASP y pulsamos cada uno de sus botones, vemos el


distinto comportamiento de los método Execute y Transfer, en el primer caso se intercala el resultado
ejecución de ambas páginas, y en el segundo paso una vez que se ha terminado de ejecutar la segunda
página finaliza también la ejecución de la secuencia de comandos, sin retornar a la página inicial.

Las siguientes figuras muestran estas dos situaciones.

La Figura 1 muestra la página PAGINAINICIO.ASP cuando todavía no se ha pulsado ningún botón.


En la Figura 2 se muestra cuando se ha pulsado el botón Execute. Y en la Figura 3 cuando se ha
pulsado el botón Transfer.

Figura 1

22
© Grupo EIDOS 1. Introducción a ASP

Figura 2

Figura 3

Otro nuevo método que ofrece el objeto Server, y que está relacionado con el tratamiento de errores,
es el método GetLastError. Mediante el uso del método GetLastError podemos tener acceso a toda la
información referente al último error que se ha producido en la página ASP actual. Pero es necesario
aclarar que su utilización no es similar al tratamiento de errores que realizábamos con la sentencia On
Error Resume Next y el objeto Err de VBScritp, que preguntábamos por la propiedad Number del
objeto Err para averiguar si se había producido algún error, el método GetLastError se puede utilizar

23
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

únicamente dentro de una página de error personalizada, es decir, cuando el error ya se ha producido y
lo ha detectado el servidor Web.

Mediante Internet Information Services 5 podemos indicar las páginas de error personalizadas y es en
estas páginas dónde podemos hacer uso de este método.

El método GetLastError devuelve un nuevo objeto de ASP llamado ASPError, son las propiedades de
este nuevo objeto las que nos permiten acceder de forma detallada a toda la información referente al
error que se ha producido. Este nuevo objeto lo trataremos con más detalle en el siguiente apartado.

El objeto ASPError
Como ya hemos visto en el apartado anterior, este es un nuevo objeto del modelo de objetos incluido
dentro de ASP 3.0. Tendremos acceso al objeto ASPError a través de la llamada al método
GetLastError del objeto Server. La función de este objeto es la de ofrecer de forma detallada toda la
información disponible del último error que se ha producido.

El objeto ASPError únicamente dispone de una serie de propiedades de sólo lectura, que contienen la
información relativa al último error que se ha producido. Estas propiedades se comentan de forma
breve en la Tabla 1.

Propiedad Descripción

ASPCode Un entero generado por IIS.

ASPDescription Una cadena que es una descripción detallada del error si está relacionado con
ASP.

Category Cadena que indica si se trata de una error interno de ASP, del lenguaje de
secuencia de comandos o de un objeto.

Column Entero que indica la posición de la columna del archivo ASP que generó el
error.

Description Breve descripción del error.

File Nombre del archivo ASP que se estaba procesando cuando se produjo el error.

Line Línea del archivo ASP que generó el error.

Number Código de error estándar de COM.

Source Devuelve el código fuente real, si está disponible, de la línea que causó el error.

Tabla 1

24
© Grupo EIDOS 1. Introducción a ASP

En el Código fuente 11 se muestra un sencillo código de ejemplo que hace uso del objeto ASPError, y
que podría pertenecer a una página de error personalizada de IIS 5.0. Primero se obtiene una referencia
al objeto ASPError mediante el método GetLastError del objeto Server, y a continuación se muestra
los valores de las propiedades del objeto ASPError para informar al cliente acerca del error que se ha
producido.

<%@ language="VBScript" %>


<html>
<body>
<head>
<title>The page cannot be displayed</title>
</head>
<%Set objASPError=Server.GetLastError%>
<b>Detalles del error que se ha producido</b><br>
Código de error ASP: <i><b><%=objASPError.ASPCode%></b></i><br>
Número de error: <i><b><%=objASPError.Number%></b></i><br>
Código fuente que lo ha producido:
<i><b><%=Server.HTMLEncode(objASPError.Source)%></b></i><br>
Categoría del error: <i><b><%=objASPError.Category%></b></i><br>
Fichero en el que se producido el error: <i><b><%=objASPError.File%></b></i><br>
Línea y columna en la que se ha producido el error:
<i><b><%=objASPError.Line%>, <%=objASPError.Column%></b></i><br>
Descripción del error: <i><b><%=objASPError.Description%></b></i><br>
</body>
</html>

Código fuente 11

Un ejemplo de la ejecución del código anterior se puede ver en la Figura 4, y se produce cuando hay
un error de ASP, es decir un error interno de servidor del tipo 500;100.

Figura 4

25
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Componente de registro de IIS (Logging Utility)


Otras de las novedades de ASP 3.0, abandonado ya los objetos integrados en ASP, es que ofrece un
nuevo componente de servidor (componente ActiveX de servidor). El nuevo componente de servidor
es denominado componente de registro o programa de registro de IIS. Mediante este componente
tenemos acceso a la información y manipulación de los ficheros de registro (log) generados por el
servidor Web IIS 5.0.

Este componente, al igual que todos los existentes en ASP 2.0, se instala conjuntamente con el
servidor Web Internet Information Server 5.0. El fichero DLL que contiene a este nuevo componente
es LOGSCRPT.DLL.

Para instanciar un componente de registro debemos utilizar la sentencia que muestra el Código fuente
12.

Set objRegistro=Server.CreateObject("MSWC.IISLog")

Código fuente 12

Es importante señalar que el usuario que tiene acceso a la secuencia de comandos ASP que crea la
instancia del componente de registro debe autenticarse como Administrador u Operador en el servidor
donde se ejecuta IIS, si es un usuario anónimo, el componente de registro de IIS no funcionará
correctamente.

Para manipular los ficheros de registro de IIS el componente IISLog ofrece una serie de métodos que
se muestran en la Tabla 2.

Método Descripción

AtEndOfLog Indica si se leyeron o no todos los registros del archivo.

CloseLogFiles Cierra todos los archivos de registro abiertos.

OpenLogFile Abre un archivo de registro para lectura o escritura.

ReadFilter Filtra los registros del archivo según la fecha y la hora.

ReadLogRecord Lee el siguiente registro disponible del archivo de registro actual.

WriteLogRecord Escribe un registro en el archivo actual.

Tabla 2

26
© Grupo EIDOS 1. Introducción a ASP

Para obtener la información del registro actual el componente IISLog ofrece veinte propiedades de
sólo lectura, como se muestra en la Tabla 3, que se corresponden con los distintos campos de un
registro de un archivo de registro.

Propiedad Descripción

BytesReceived Número de bytes recibidos del navegador como una petición.

BytesSent Número de bytes enviados al navegador como una respuesta.

ClientIP Dirección IP del cliente.

Cookie Indica los contenidos de cualquier cookie enviada en la petición.

CustomFields Un vector de cabeceras personalizadas que se añadieron a la petición.

DateTime La fecha y hora de la petición en formato GMT.

Method El tipo de operación, tal como puede ser GET o POST.

ProtocolStatus El mensaje de estado devuelto al cliente, por ejemplo 200 OK.

ProtocolVersion Una cadena con la versión del protocolo utilizado, por ejemplo HTTP/1.1.

Referer La URL de la página que contiene el enlace que inició la petición, si está disponible.

ServerIP La dirección IP del servidor Web.

ServerName El nombre del servidor Web.

ServerPort El número de puerto por el que se recibió la petición.

ServiceName Nombre del servicio, como puede ser el servicio FTP (MSFTPSVC) o Web
(W3SVC).

TimeTaken El tiempo de procesamiento total para devolver y crear la página devuelta.

URIQuery Cualquier parámetro añadido a la cadena de consulta (QueryString) de la URL en la


petición.

27
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

URIStem La URL que demandó el cliente.

UserAgent La cadena de agente de usuario (tipo de navegador) enviada por el cliente.

UserName Nombre de inicio de sesión del usuario si no ha accedido de forma anónima.

Win32Status Código de estado Win32 después de haber procesado la petición.

Tabla 3

Se puede configurar el tipo de registro que queremos en nuestro servidor a través de IIS 5.0, de esta
forma podremos añadir o eliminar de nuestro fichero de registro los campos descritos anteriormente.
Para ello acudiremos a las propiedades del sitio Web y en la pestaña sitio Web pulsaremos el botón
Propiedades contenido en el epígrafe de Habilitar registro, como se ve en la Figura 5. Se debe
seleccionar uno de los formatos de registro que se corresponden con un fichero de registro, por lo tanto
la opción registro ODBC no sería válida.

Figura 5

28
© Grupo EIDOS 1. Introducción a ASP

En el Código fuente 13 se muestra la utilización de este nuevo objeto ActiveX de servidor. En este
sencillo código se utiliza el componente de registro para mostrar algunos de los campos contenidos en
el fichero de registro.

<html>
<head>
<!--METADATA TYPE="TypeLib" FILE="c:\winnt\system32\inetsrv\logscrpt.dll"-->
</head>
<body>
<%Set objRegistro=Server.CreateObject("MSWC.IISLog")
objRegistro.OpenLogFile "c:\winnt\system32\logfiles\w3svc1\ex000517.log"_
,ForReading,"W3SVC",1,0
objRegistro.ReadFilter DateAdd("d",-1,Now),Now%>
<table align="center" border="0" cellspacing="2" cellpadding="5">
<tr>
<th>Fecha/Hora</th>
<th>IP del cliente</th>
<th>Método</th>
<th>URL</th>
</tr>
<%While Not objRegistro.AtEndOfLog
objRegistro.ReadLogRecord%>
<tr>
<td><%=objRegistro.DateTime%></td>
<td><%=objRegistro.ClientIP%></td>
<td><%=objRegistro.Method%></td>
<td><%=objRegistro.URIStem%></td>
</tr>
<%Wend
objRegistro.CloseLogFiles(ForReading)%>
</body>
</html>

Código fuente 13

Se ha utilizado un filtro para recuperar la información del fichero de registro referente al servicio Web
y únicamente de las últimas 24 horas.

También se puede observar que se utiliza una directiva METADATA, más tarde comentaremos su
utilidad y sintaxis, de momento diremos únicamente que nos permite incluir las constantes definidas
en la librería que contiene al componente de registro.

La información que se va a mostrar del fichero de registro va a ser la fecha y hora de la petición, la
dirección IP del cliente que ha realizado la petición, el método que se ha utilizado y la URL
correspondiente. En la Figura 6 se puede ver un ejemplo de ejecución de la página anterior.

El nombre del fichero de registro variará según sea nuestra configuración del registro en el sitio Web
correspondiente, la ubicación de estos ficheros de registro suele ser el directorio C:\WINNT
\SYSTEM32\LOGFILES\W3SVc1 para el servicio Web.

Esta página ASP que utiliza el componente de registro se puede utilizar únicamente restringiendo el
acceso anónimo a la propia página o al directorio que la contiene a nivel de permisos de NTFS, en
caso contrario no podremos acceder al fichero de registro, ya sea para leer o escribir datos.

29
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 6

Aplicaciones ASP con IIS 5.0


En IIS 4.0 ya podíamos definir aplicaciones ASP y su directorio de inicio, también podíamos indicar a
través de la configuración de los directorios de un sitio Web si la aplicación se ejecutaba en otro
espacio de memoria como un proceso aislado.

Con IIS 5.0 el concepto de aplicación ASP no ha variado, es decir, una aplicación es un conjunto de
páginas ASP que se ejecutan en un conjunto de directorios definidos dentro de un sitio Web, tampoco
ha variado excesivamente la forma de configurar las aplicaciones ASP. Lo más destacable que ofrece
IIS 5.0 con respecto a las aplicaciones ASP es la posibilidad de definir tres niveles de protección
distintos para dichas aplicaciones.

En IIS 4.0 podíamos indicar que nuestra aplicación se ejecutara en el mismo espacio de memoria que
el servidor Web o que se ejecutara en un proceso aislado, pero con IIS 5.0 tenemos otra posibilidad
intermedia, que consiste en que la aplicación se ejecuta en un proceso agrupado con el resto de las
aplicaciones ASP.

Los tres grados de protección que ofrece IIS 5.0 para las aplicaciones ASP se denominan bajo, medio
y alto, y se muestran en la Tabla 4.

Protección Descripción

Baja (proceso IIS) Las aplicaciones ASP se ejecutan todas en el mismo espacio de memoria que el
servidor Web IIS 5.0. Si una aplicación ASP falla afectará a todo el servidor
Web, poniendo en peligro la ejecución de la aplicacion InetInfo.exe, que es el
ejecutable del servidor Web. Ofrece la ejecución más rápida y eficiente de las
aplicaciones ASP, pero tambiénes la que ofrece más riesgos.

30
© Grupo EIDOS 1. Introducción a ASP

Media (agrupada) Esta es la protección por defecto, todas las aplicaciones ASP se ejecutan
agrupadas en un espacio de memoria distinto que el del servidor Web, en este
caso todas las aplicaciones ASP del servidor Web utilizan una instancia
compartida del ejecutable DLLHost.exe. De esta forma se proteje al ejecutable
InetInfo.exe de los posibles fallos de las aplicaciones ASP. Si se produce un
fallo en una aplicación ASP no afecta al servidor Web, pero sí a resto de las
aplicaciones ASP.

Alta (aislada) Cada aplicación ASP se ejecuta en un espacio de memoria distinto, es decir,
cada aplicación se ejecuta en una instancia distinta y exclusiva del ejecutable
DLLHost.exe. De esta forma si una aplicación falla no afectará al resto de las
aplicaciones ASP ni tampoco al servidor Web, ya que se ejecuta en su propio
espacio de memoria. Microsoft recomienda que por cada servidor Web no se
definan más de diez aplicaciones aisladas. Este tipo de protección es
recomendable para aplicaciones ASP de alto riesgo o críticas.

Tabla 4

Por defecto el sitio Web predeterminado se define como una aplicación ASP agrupada o con grado de
protección medio, este es el modo de protección de la aplicación más usual y recomendable, ya que
ofrece una buena relación en lo que a rendimiento y seguridad se refiere, con el grado de protección
alto comprometemos el rendimiento y con el grado de protección bajo se compromete la seguridad del
funcionamiento del servidor Web. En la Figura 7 se puede ver la forma de configurar el grado de
protección de una aplicación.

Figura 7

31
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Otros cambios
En la versión anterior de ASP si necesitábamos utilizar las constantes definidas en una librería de
componentes, como puede ser ADO, teníamos que incluir un fichero con la definición de dichas
constantes mediante la también conocida directiva INCLUDE de ASP, en el caso de ADO se trataba
del famoso fichero ADOVBS.INC. Pero con ASP 3.0 esta situación ha cambiado, podemos incluir las
constantes definidas en una librería de forma directa desde la propia librería, sin tener que crear un
fichero de definición de constantes diferenciado.

Para incluir una referencia a una librería un componentes utilizamos la nueva directiva METADATA,
cuya sintaxis se muestra en el Código fuente 14.

<!-- METADATA TYPE="TypeLib"


FILE="camino y nombre del fichero"
UUID="identificador de la librería"
VERSION="numVersionMayor.numVersionMenor"
LCID="identificador de localización"
-->

Código fuente 14

Las propiedades que son obligatorias son FILE o UUID, siempre deberemos indicar uno u otro para
identificar la librería, el resto de las propiedades son de tipo opcional De esta forma para incluir las
constantes de ADO y poder utilizarlas, escribiremos lo que indica el Código fuente 15.

<!-- METADATA TYPE="TypeLib"


FILE="c:\Archivos de Programa\Archivos
comunes\System\ado\msado15.dll"-->

Código fuente 15

Se debe señalar que o bien podemos utilizar la directiva METADATA en cada una de las páginas ASP
en las que necesitemos incluir las constantes o también se puede incluir en el fichero GLOBAL.ASA y
de esta forma estar disponible la definición de constantes y la referencia a la librería para todas las
páginas de la aplicación ASP. Como curiosidad hago notar que le nombre de la librería de ADO sigue
siendo MSADO15.DLL cuando lógicamente debería ser MSADO25.DLL, ya que ya nos encontramos
en la versión 2.5 de ADO, aunque de todas formas esta librería contiene la última versión de ADO.

Para incluir archivos en nuestras páginas ASP ya hemos visto que utilizamos la directiva INCLUDE,
pero en ASP 3.0 hay otra alternativa que es la utilización de la etiqueta <SCRIPT> como muestra el
Código fuente 16.

<SCRIPT RUNAT="SERVER" SRC="ruta relativa, física o virtual al fichero de


scrip"></SCRIPT>

Código fuente 16

32
© Grupo EIDOS 1. Introducción a ASP

El fichero que incluimos, a diferencia de la directiva INCLUDE, únicamente puede contener


secuencias de comandos, no puede contener texto ni código HTML, además no debe existir ningún
elemento entre las etiquetas <SCRIPT></SCRIPT>.

Si un usuario accede a un sitio Web indicando únicamente el nombre del mismo, sin indicar ninguna
página, se enviará al usuario el documento o página por defecto. Sin embargo, si se añadía una cadena
de consulta (QueryString) a esta URL en la versión anterior de ASP esta cadena era ignorada. Pero
ahora con ASP 3.0 y combinación con IIS 5 la cadena de consulta si es considerada por la página
predeterminada. De esta forma en ASP 3.0 escribir http://www.eidos.es/?prueba=true es equivalente a
escribir la URL http://www.eidos.es/default.asp?prueba=true, siendo default.asp la página prede-
terminada del sitio Web del Grupo EIDOS.

33
Modelo de objetos de ASP. Parte I

Introducción
Este tema pretende ser un simple recordatorio de los objetos integrados de ASP, y sus principales
colecciones, métodos y propiedades. Sólo se entrará en detalle en aquéllos que sean más utilizados.

ASP nos proporciona una serie de objetos integrados, a los que siempre tenemos acceso sin necesidad
de instanciarlos, son objetos que constituyen lo que se denomina el modelo de objetos de ASP. Estos
objetos son bastante interesantes ya que gran parte de la programación de aplicaciones ASP se basa en
la utilización de los mismos.

Estos objetos ponen a disposición del programador una serie de métodos y propiedades que pueden ser
utilizados desde el script de servidor, es decir, son directamente accesibles y manipulables desde
VBScript. Cada uno de estos objetos cubre unas funciones determinadas.

Antes de comenzar a explicar cada unos de los objetos integrados se considera necesario repasar el
concepto de objeto, método y propiedad dentro del entorno de la programación orientada a objetos y
desde el punto de vista que nos interesa, es decir desde las páginas activas de servidor.

Un objeto es un componente que posee una serie de comportamientos y que tiene un estado. El estado
de un objeto se encuentra definido por sus propiedades y sus comportamientos a través de los
métodos. Un objeto puede contener otros objetos.

Cada objeto tiene unas propiedades que definen sus atributos. Las propiedades de un objeto nos
indican algo sobre el objeto o sus contenidos. Las propiedades diferencian un objeto de otro y
determinan su estado y características. Los métodos que poseen los objetos definen su comportamiento
interno y frente a otros objetos.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Los objetos integrados además de poseer métodos y propiedades, también ofrecen colecciones. Una
colección es un grupo de objetos del mismo tipo. Un objeto y una colección son ambos contenedores,
pero de distinto tipo. Un objeto contendrá cero o más colecciones de diferente tipo, mientras que una
colección contendrá cero o más objetos de naturaleza similar.

Por ejemplo, ASP ofrece el objeto Request que contiene diferentes propiedades y colecciones de
distinto tipo. Una de estas colecciones es la colección Form, esta colección contiene información sobre
los elementos del formulario que se ha enviado. Aquí se puede observar la diferencia entre objetos y
colecciones en ASP, un objeto puede siempre contener otro nivel de información variada pero las
colecciones no.

La mayoría de los objetos integrados de ASP proporcionan colecciones. Una colección es una
estructura de datos, similar a una matriz, que almacena cadenas, números, objetos y otros valores. A
diferencia de las matrices, las colecciones se amplían y reducen automáticamente al recuperar o
almacenar elementos. La posición de un elemento también cambia al modificar la colección. Es
posible tener acceso a un elemento de una colección por su clave de cadena única, por su índice
(posición) en la colección o si se iteran todos los elementos de la colección.

El acceso a las colecciones, su sintaxis y utilización la veremos según vayamos avanzando en el


temario con distintos ejemplos.

Todas las colecciones ofrecen una serie de métodos comunes a todas ellas y son los siguientes:

• Count: devuelve número de elementos contenidos en la colección.

• Item: devuelve el elemento que se corresponde con el índice o cadena clave que se pasa por
parámetro.

• Key: devuelve el nombre de una clave dado su índice.

Las colecciones que no son sólo de lectura, sino también de escritura, permiten además los métodos:

• Remove: elimina un elemento determinado de la colección, del que indicamos su clave o


índice. La colección debe ser de escritura.

• RemoveAll: elimina todos los elementos presentes en una colección. Al igual que el método
anterior requiere que la colección sea de escritura.

Las colecciones comienzan en el índice 1, a diferencia de los arrays que empiezan en el índice cero.

Antes de poder utilizar un objeto se debe instanciar, es decir, crear el objeto y asignárselo a una
variable que va a representar una instancia de ese objeto, en realidad, hasta que no se instancia un
objeto, el objeto no existe. Pero en el caso de los objetos integrados no hay que instanciarlos, se puede
considerar que son creados al iniciar la ejecución la aplicación ASP.

El objeto Response
Este objeto integrado en el modelo de objetos de ASP tiene la función de enviar datos al cliente, es
decir, al navegador que ha cargado la página ASP. A través de este objeto podremos escribir en la
página que visualizará el usuario e incluso podremos redirigir al usuario a otra dirección en Internet.
También a partir de este objeto podremos definir cookies para el usuario y asignarles un valor. En
definitiva, representa la respuesta que el servidor web devuelve al navegador del cliente como
resultado de su petición.

36
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

Colecciones del objeto Response


El objeto Response posee una única colección llamada Cookies, que le permite crear y asignar valores
a una cookie. Una cookie, físicamente, es un fichero que se escribe en la máquina local del cliente que
se conecta a un sitio Web y que contiene información relativa a la conexión.

Una cookie es utilizada para mantener información entre diferentes conexiones HTTP. Se debe
recordar que el protocolo HTTP es un protocolo sin estado, es decir, no se retiene información entre
las diferentes conexiones que se realicen. Por esta razón, ni el cliente ni el servidor pueden mantener
información entre diferentes peticiones o a través de diferentes páginas Web.

Este mecanismo para mantener información entre diferentes conexiones HTTP fue propuesto e
implementado en un principio por la compañía Netscape.

Existen varios usos prácticos de las cookies, a continuación se van a comentar los más destacados:

• Para almacenar información acerca de las preferencias del cliente que se conecta a nuestro
sitio Web, por ejemplo el color seleccionado de la página, el tipo de letra, etc.

• Para conservar información personal, no sensible, del usuario, como puede ser el nombre, el
país de origen, código postal, el número de veces que ha accedido a nuestro sitio Web, etc.

Por lo tanto el uso de cookies nos puede permite personalizar las páginas ASP según el cliente que se
haya conectado atendiendo a sus preferencias y datos personales. Por ejemplo podemos saludar al
usuario con su nombre y asignar al color de fondo de la página su color favorito o también podremos
indicarle el número de veces que ha accedido a nuestro sitio Web.

De esta forma podemos evitar preguntar al usuario sus preferencias o datos personales cada vez que
entra en nuestro sitio Web.

Siempre debe haber una primera ocasión en la que el cliente conectado especifique el valor de la
cookie, una vez especificado este valor ya puede ser utilizada la cookie en las diferentes conexiones
que realice ese cliente, ya que la información ha quedado almacenada en la cookie. Esta información
se almacena físicamente en un fichero del disco duro local del cliente. En el caso del navegador
Internet Explorer de Microsoft cada cookie se corresponde con un fichero.txt que se encuentra en el
directorio Documents and Settings (en el caso de Windows 2000) y en el subdirectorio Cookies, y en
el caso del Communicator de Netscape las cookies se almacenan todas en un fichero llamado
cookies.txt en el subdirectorio Netscape\User\usuario.

Hay una serie de consideraciones que se deben tener en cuenta a la hora de utilizar cookies en nuestra
aplicación ASP:

• Las cookies se pueden perder, por lo tanto nunca se debe depender de las cookies para
almacenar una información que no se pueda volver a generar. Este tipo de información se
debería almacenar en una base de datos del servidor. No se debe olvidar que las cookies se
almacenan en el disco local del cliente en ficheros, y por lo tanto estos ficheros se pueden
dañar, ser borrados o sobreescritos.

• Las cookies pueden ser modificadas por el cliente, por lo tanto nunca se debe considerar que la
información ofrecida por una cookie es auténtica. Si estamos usando una cookie para
determinar la fecha de la última vez que un usuario visitó nuestro sitio Web podemos
considerar como auténtica esta información sin ningún problema, pero sin embargo no es nada
recomendable considerar auténtica una cookie que posee el número de cuenta de un usuario.
Como regla general nunca se debe utilizar una cookie para almacenar información

37
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

confidencial, este tipo de información se debería almacenar en una base de datos en el servidor
Web.

• Las cookies pueden ser copiadas sin el consentimiento del propietario, nunca se debería
utilizar una cookie para identificar a un usuario, una solución mejor es utilizar una contraseña
y validarla con una base de datos del servidor.

• Algunos navegadores no reconocen las cookies. Incluso los más populares como el Internet
Explorer y el Communicator, se pueden configurar para no utilizar cookies.

Se debe señalar que ASP para mantener estados, es decir, valores de variables, objetos..., entre
diferentes páginas de una aplicación ASP utiliza cookies desde los objetos integrados Session y
Application.

El uso de estas cookies por parte de ASP, se oculta completamente al programador para que se
abstraiga de la utilización lógica de las cookies, todo ello gracias a los objetos Session y Application
que las gestionan internamente (estos dos objetos los trataremos en los capítulos correspondientes).
Así por ejemplo, cuando se inicia una sesión se crea la cookie ASPSSESIONID, aunque esta cookie
no se grabará en el disco duro del cliente, sino que permanecerá en memoria, por lo tanto podemos
asegurar al usuario que no vamos a acceder a su disco.

De esta forma si un navegador no acepta cookies, la aplicación ASP no podrá mantener el estado entre
diferentes páginas. Si tenemos configurado nuestro navegador para que nos avise cuando recibe una
cookie, al iniciar una sesión con una aplicación ASP aparecerá una ventana similar a la que se muestra
en la Figura 8.

Figura 8

La colección Cookies también aparece en el objeto Request, pero en el objeto Request la colección es
sólo de lectura, mientras que en el objeto Response es sólo de escritura. De esta forma en el objeto
Response la colección Cookies será utilizada para crear las cookies o para modificar su valor, y en el
objeto Request para recuperar el valor de las cookies.

38
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

Para crear una cookie se utiliza la sintaxis del Código fuente 17.

Response.Cookies(NombredelaCookie)=ValordelaCookie

Código fuente 17

Si la cookie ya existe modifica su valor.

La sintaxis general es Response.Cookies(cookie)[(clave)|atributo]=valor, donde


cookie, es el nombre de la cookie a la que hacemos referencia, clave es un parámetro opcional que se
utiliza cuando la cookie es un diccionario, es decir puede contener diferentes datos, y atributo
especifica una propiedad de la cookie. Las propiedades que poseen las cookies son:

• Expires: sólo de escritura, indica la fecha en la que caduca la cookie. Si no se especifica


ningún valor, por defecto caduca cuando termina la sesión.

• Domain: sólo de escritura, especifica a que dominio es enviada la cookie. Por defecto será el
nombre del dominio del servidor del que proviene la cookie.

• Path: sólo de escritura, indica la ruta del servidor de la que proviene la cookie. Si no se
especifica, por defecto se toma la ruta de la página ASP que generó la cookie.

• Secure: sólo de escritura, para indicar si la cookie es segura.

• HasKeys: sólo de lectura, especifica si la cookie tiene claves, es decir, si es un diccionario.

Si queremos crear una cookie que tenga claves, por ejemplo que contenga los datos nombre, apellidos
y edad, y que caduque a finales del 2001 se debería escribir lo que indica el Código fuente 18.

<%
Response.Cookies("datosPersonales").Expires= #December 31, 2001#
Response.Cookies("datosPersonales")("nombre")="Angel"
Response.Cookies("datosPersonales")("apellidos")="Esteban Núñez"
Response.Cookies("datosPersonales")("edad")=25
%>

Código fuente 18

En el caso de que se quiera eliminar una cookie que ya exista, basta con cambiar el valor de su
propiedad Expires, asignándole una fecha anterior a la actual.

Las cookies se envían desde el servidor al navegador del cliente como un encabezado del protocolo
HTTP.

Propiedades del objeto Response


En este apartado vamos a comentar cada una de las propiedades del objeto Response, y como podemos
utilizar las más comunes.

39
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

El objeto Response posee las siguientes propiedades:

• Buffer: indica si los datos de la página se almacenan en un búfer.

• ContentType: especifica el tipo de contenido HTTP de la respuesta.

• Expires: especifica el intervalo de tiempo que debe transcurrir para que caduque una página
almacenada en la caché del navegador.

• ExpiresAbsolute: tiene el mismo sentido que la propiedad anterior, pero aquí se especificar la
hora y la fecha en la que caducará la página en la caché del navegador.

• Status: valor de la línea de estado que devuelve el servidor.

• CacheControl: determinará si los servidores proxy van a almacenar o no en su caché la salida


generada por una página ASP.

• CharSet: indicará el juego de caracteres que está utilizando la página ASP.

• PICS: mediante esta propiedad indicaremos el tipo de contenido que posee nuestra página
ASP.

• IsClientConnected: propiedad de sólo lectura con la que podremos averiguar si el cliente se


ha desconectado del servidor.

Si utilizamos la propiedad Buffer del objeto Response asignándole el valor True, conseguiremos que
ASP procese todo el script en el servidor antes de enviar algo al usuario, por defecto esta propiedad
tiene el valor True. El contenido del búfer no es enviado al navegador hasta que no se haya terminado
de procesar la página o hasta que los métodos End o Flush del objeto Response no son invocados.

Una vez activado el almacenamiento en búfer, es decir, la propiedad Buffer posee el valor True, y una
vez que ya se ha enviado contenido al navegador del cliente, no se puede modificar su valor, tampoco
se puede modificar ningún valor de las propiedades del objeto Response.Así por ejemplo el Código
fuente 19 generaría un error.

<%Response.Buffer=False%>
<HTML>
<HEAD>
<%Response.Expires=10%>

Código fuente 19

El error devuelto por el navegador sería similar a “Los encabezados HTTP ya están
escritos en el explorador cliente”.

Cualquier cambio en el encabezado HTTP se debe hacer antes de escribir el contenido de la página.

En las versiones anteriores de ASP la propiedad Buffer tenía por defecto la propiedad False, de esta
forma según se iba procesando la página ASP se enviaba el resultado de la ejecución al cliente. Según
Microsoft con este cambio se obtiene un mejor rendimiento en la ejecución de páginas ASP. En
realidad lo que se tiene también es un mayor control sobre la salida que va a recibir el usuario.

40
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

El uso de este búfer puede ser útil en el caso de que sea necesario procesar el contenido de una página
ASP antes de enviar ningún contenido previo al navegador del cliente. De esta forma se puede
redireccionar al navegador hacia otra página con el método Redirect del objeto Response, este método
se comentará en su apartado correspondiente, pero se puede adelantar que una llamada a este método
producirá un error en el caso de que se haya enviado anteriormente algún contenido al usuario.

La propiedad Buffer también puede ser utilizada para verificar errores antes de enviar información al
navegador, de esta forma si se detecta un error se puede detener el proceso de la página y redireccionar
al navegador del cliente hacia una página determinada.

Cuando la propiedad Buffer, tiene su valor por defecto, es decir, el valor True se pueden utilizar una
serie de métodos del objeto Response, estos métodos, cuya función se verá en el apartado
correspondiente, son: Clear, End y Flush y básicamente lo que permiten es controlar el resultado o
salida que se le envía al cliente.

Métodos del objeto Response


A continuación, de la misma forma que se ha hecho con las propiedades del objeto Response, se va a
hacer una breve descripción de los métodos que nos ofrece este objeto.

• AddHeader: agrega un nuevo encabezado HTML a la respuesta. Este método no es muy


utilizado. Permite definir nuevos encabezados. Su sintaxis es: Response.AddHeader
nombreEncabezado, valorEncabezado.

• AppendToLog: agrega una cadena al final de la entrada de registro del servidor Web para la
petición. IIS se puede configurar para registrar las peticiones de los clientes en una base de
datos o en un fichero de log. Este método permite agregar una cadena a la información que
será almacenada por el usuario acerca de una petición de un cliente.

• BinaryWrite: este método es utilizado para escribir información directamente en la salida


HTTP, es decir, el navegador del cliente, sin ningún tipo de interpretación. Este método es útil
cuando se necesita enviar pequeños gráficos, multimedia u otros componentes que no son
texto.

• Clear: elimina el contenido del código HTML del búfer.

• End: detiene el procesamiento de la página ASP y devuelve los resultados obtenidos hasta la
llamada a End.

• Flush: envía al navegador el contenido del búfer antes de finalizar el procesamiento de la


página ASP.

• Redirect: se redirecciona al navegador Web del cliente a la dirección que se le pasa como
parámetro.

• Write: escribe en el navegador del cliente.

Los métodos Clear, Flush y End, deben ser utilizados cuando la propiedad Buffer del objeto Response
esté activada, es decir, tiene como valor True, ya sabemos que este es el valor por defecto de esta
propiedad.

Cuando está activado el almacenamiento en el búfer existen varias formas de enviar la información del
búfer al navegador del cliente:

41
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Llamando al método Flush.

• Llamando al método End. De esta forma también se detiene la ejecución de la página ASP.

• Cuando finaliza le ejecución de la página ASP, es decir, cuando se ejecuta la última línea de
las secuencias de comandos de servidor.

El método Clear se utiliza cuando se quiere eliminar todo el contenido que posee el búfer, pero esto no
implica que se borre el contenido que ya se ha enviado al cliente, a ese contenido ya no se tiene
acceso, así por ejemplo el Código fuente 20 produciría un error.

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<b>Esto es contenido</b>
<%Response.Flush
Response.Clear
Response.Redirect "pruebas.asp"%>
</BODY>
</HTML>

Código fuente 20

La utilización de estos métodos junto con la propiedad Buffer nos permite controlar el procesamiento
de la página ASP de una forma muy sencilla y eficaz. Para mostrar el uso de estos métodos junto con
el almacenamiento en búfer vamos a ver un sencillo ejemplo, en el Código fuente 21, que va
mostrando poco a poco un texto en pantalla.

<%strTexto="Texto que se va mostrando poco a poco en el navegador"


For i=1 to Len(strTexto)
'se realiza una pausa
For j=1 To 100000
Next

Response.Write Mid(strTexto,i,1)
'se envía el contenido del búfer
Response.Flush
Next%>

Código fuente 21

El efecto de este código es que se va enviando el texto caracter a caracter mediante el método Flush,
para que se produzca una pausa se realiza un bucle For adicional. Si situamos la sentencia
Response.Flush al final del código, aparecerá todo el texto de una sola vez.

A lo largo de los distintos ejemplos que hemos visto hasta ahora en el curso, en alguno de ellos hemos
utilizado la sentencia Response.Write, por lo tanto ya sabemos exactamente la función de este método
del objeto Response.

Pero también para escribir se han utilizado los delimitadores <%=%>. Estos delimitadores son
equivalentes a escribir Response.Write, se pueden considerar como su abreviatura. El método

42
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

Response.Write se utilizará dentro de una bloque de script de servidor, es decir, un bloque de código
encerrado por los delimitadores <%%>, y el uso de los delimitadores <%=%> será adecuado cuando
se necesite intercalar una línea aislada entre código HTML:

Como ya se ha visto, el método Write se puede utilizar para escribir texto en el navegador. Las
llamadas a métodos y las variables dentro del método Write serán evaluadas y sólo el resultado se
escribirá en el navegador del cliente. Por ejemplo, el Código fuente 22 mostrará el valor de la variable
contador en el navegador del cliente.

<%Response.Write("La variable contador contiene el valor: " & contador)%>

Código fuente 22

El uso de paréntesis en el método Write es opcional. Si tenemos en cuenta los delimitadores <%=%>,
de forma equivalente podríamos haber escrito lo que se muestra en el Código fuente 23 o en el Código
fuente 24.

<%="La variable contador contiene el valor: "&contador%>

Código fuente 23

La variable contador contiene el valor: <%=contador%>

Código fuente 24

Si en nuestro código tenemos diferentes líneas con los delimitadores <%=%> intercaladas dentro de
código HTML, para una mayor eficiencia es recomendable sustituirlos por una única línea mediante
Response.Write. Además se consigue un código más legible, se debe tender a bloques de HTML y de
script de servidor bien diferenciados, siempre que sea posible.

El método Redirect recibe como parámetro una cadena de texto en forma de URL que especifica la
nueva dirección a la que se va a redirigir el navegador del usuario. La URL puede ser absoluta, del
tipo www.almagesto.com o del tipo /directorio/pagina.asp, o relativa del tipo pagina.asp.

Al lanzar el método Redirect en realidad lo que estamos haciendo es enviar una cabecera HTTP al
navegador Web del cliente para indicarle que la página se ha movido y le indicamos la nueva
localización, que es la URL que hemos indicado al método Redirect. La información que se envía al
navegador es HTTP/1.1 Object Moved Location /directorio/pagina.asp.

Como se puede observar la redirección se realiza en el cliente, indicándole que la página se ha movido
a una nueva dirección, que en realidad es otra página completamente distinta.

Aquí nos hemos centrado en redireccionar el navegador a otra página (HTML o ASP), pero se puede
generalizar diciendo que podemos redireccionar al navegador a cualquier recurso, como puede ser una
imagen, un fichero zip, un documento, etc.

43
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

El objeto Request
Este objeto integrado es utilizado para obtener información del usuario, por lo tanto realiza las
funciones contrarias al objeto Response.

Cuando un navegador cliente se comunica con el servidor Web a través del protocolo HTTP, el
navegador manda un gran número de información además de la página que se quiere cargar, esta
información puede ser: variables de entorno, información sobre certificados, cookies, formularios, etc.
Toda esta información es codificada y enviada a través de cabeceras del protocolo HTTP y en muchos
casos no es sencilla de extraer. ASP realiza las funciones de extraer, decodificar y organizar toda esta
información a través del objeto Request y sus diferentes colecciones.

Esta información enviada por el navegador, se puede utilizar para: validar un usuario, obtener
información sobre el usuario el navegador, almacenar información para su posterior uso, enviar
información al servidor a través de formularios, etc.

ASP almacena toda esta información en colecciones del objeto Request. Mediante llamadas del tipo
Request.colección se puede obtener de forma sencilla la información que sea necesaria. El objeto
Request presenta cinco colecciones que veremos en el siguiente apartado

Colecciones del objeto Request


Las cinco colecciones que ofrece el el objeto Request del modelo de objetos de ASP contienen toda la
información enviada por el navegador cliente al servidor Web. Estas colecciones se pasan a comentar
de forma breve:

• ClientCertificate: contiene los valores de los campos de certificación (especificados en el


estándar X.509) de la petición emitida por el navegador.

• Cookies: valores de las cookies del cliente.

• Form: valores de elementos de un formulario HTML.

• QueryString: valores de las variables de la cadena de consulta HTTP enviada.

• ServerVariables: esta colección almacena información sobre el navegador, el servidor y el


usuario.

Para tener acceso a la información de las colecciones del objeto Request se puede utilizar la sintaxis
general Request.NombreColeccion(variable), donde NombreColeccion es el nombre de la
colección que se quiere consultar y variable es el nombre de la variable de la colección a la que se
quiere tener acceso y recuperar su valor.

Las variables contenidas en las colecciones del objeto Request son únicamente de lectura.

También se puede acceder a una variable de una colección sin especificar el nombre de la colección
(Request(variable)). En este caso se realizará una búsqueda por todas las colecciones del objeto
Request hasta que se obtenga la variable deseada. El orden de búsqueda entre las colecciones es el
siguiente: QueryString, Form, Cookies, ServerVariables y por último ClientCertificate. Se obtendrá la
primera variable que coincida con el parámetro variable. Esto no es muy recomendable ya que es más
lento y pueden existir variables con el mismo nombre en distintas colecciones.

44
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

Para obtener todos los valores de las variables de una colección se puede utilizar la instrucción For
Each...in..., el Código fuente 25 muestra como se obtendría el nombre y el valor de cada variable de la
colección ServerVariables:

<%For each variable in Request.ServerVariables


Response.Write("La variable: "&variable&" tiene el valor: "&_
Request.ServerVariables(variable)&"<br>")
Next%>

Código fuente 25

El objeto Request mediante su colección Form permite acceder a los datos enviados a través de un
formulario mediante el método POST.

Para obtener los datos de un formulario, que por ejemplo tuviera los campos Nombre y Edad, se
debería escribir el Código fuente 26 dentro de la página ASP llamada PROCESAINFO.ASP.

Nombre usuario: <%=Request.Form("nombre")%><br>


Edad usuario: <%=Request.Form("edad")%>

Código fuente 26

Si se quiere obtener los datos sin decodificar bastaría con escribir el Código fuente 27.

Datos: <%=Request.Form%>

Código fuente 27

En el navegador aparecería lo siguiente: Datos: nombre=Pepe&edad=30&boton=Enviar. Se


puede observar que además de los campos de texto del formulario también se envía el botón de Submit
si a éste se le ha asignado un nombre en la etiqueta de HTML.

En algunos casos los elementos de un formulario pueden ser un array, basta con, por ejemplo, dar a
varias cajas de texto el mismo nombre, de esta forma para acceder a la información se deberá utilizar
un índice. Por ejemplo si tenemos el formulario del Código fuente 28.

<form action="procesaInfo.asp" method="POST">

Nombre: <input type="Text" name="datosPersonales" value="">


Apellidos: <input type="Text" name="datosPersonales" value="">
Edad: <input type="Text" name="datosPersonales" value="">
<input type="Submit" name="boton" value="Enviar">

</form>

Código fuente 28

45
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Para poder recuperar la información enviada se debería tratar como un array, y se realizaría de la
forma que se indica en el Código fuente 29.

<%For i=1 To Request.QueryString("datosPersonales").Count


Response.write(Request.Form("datosPersonales")(i)&"<br>")
Next%>

Código fuente 29

Como se puede observar los índices comienzan en 1, y para saber el número de elementos del array se
utiliza el método Count que devuelve el número de elementos de un array.

Las páginas ASP se pueden utilizar para recoger y procesar valores de formularios HTML de varias
maneras:

• Un archivo htm o html estático puede contener un formulario que envíe sus valores a una
página ASP, como ha sido el caso de los ejemplos anteriores.

• Una página ASP puede contener un formulario que envíe información a otra página ASP.

• Una página ASP puede contener un formulario que envíe información así misma, es decir, a la
página ASP que contiene el formulario.

La última opción es la más potente y la más recomendable, de esta forma una página ASP se llama así
misma. Para poder utilizar esta estrategia lo que se debe tener en cuenta es si la página ASP se carga
por primera vez o se carga de nuevo porque ha sido llamada por sí misma. Esto se puede conseguir
preguntando si existe alguno de los campos del formulario, si existe es que se ha realizado la llamada
desde la misma página y por lo tanto se validarán o mostrarán los datos; y si no existe, la página se
está cargando por primera vez y se mostrará el formulario. En el Código fuente 30 de ejemplo se
puede observar esta técnica.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">


<HTML>
<HEAD>
<TITLE>Misma página ASP</TITLE>
</HEAD>
<BODY>
<%If Request.Form("botonEnviar")<>"" Then%>
<u>Datos enviados</u><br>
Nombre: <%=Request.Form("nombre")%><br>
Edad: <%=Request.Form("edad")%>
<%Else%>
<form action="mismaPagina.asp" method="POST">
Nombre: <input type="Text" name="nombre" value="">
Edad: <input type="Text" name="edad" value="">
<input type="Submit" name="botonEnviar" value="Enviar">
</form>
<%End If%>
</BODY>
</HTML>

Código fuente 30

46
© Grupo EIDOS 2. Modelo de objetos de ASP. Parte I

En este caso se pregunta por el valor del botón de envío, ya que siempre que se envíe el formularío se
enviará también el botón de tipo submit. Además se supone el código mostrado pertenece a una página
ASP llamada mismaPagina.asp tal como se indica en la propiedad ACTION de la etiqueta <FORM>.

Si cargamos por primera vez esta página pasaremos por la rama de la instrucción Else y veríamos el
formulario en el navegador. Una vez cargada la página, si rellenamos los campos de texto y pulsamos
sobre el botón etiquetado como Enviar, pasaríamos por el primer bloque del If, ya que la página ASP
se volvería a cargar así misma y la variable botonEnviar de la colección Form ya contendría un valor.

Como se puede observar da la impresión que son dos páginas distintas, pero en realidad es la misma
página ASP que genera diferente código HTML según la ejecución del script de servidor.

Para obtener los valores de un formulario que se ha enviado con el método GET o para recuperar los
valores de una cadena de consulta de un enlace, utilizaremos esta otra colección del objeto Request:
QueryString.

Todo lo visto para la colección Form es válido para la colección QueryString. La única diferencia es
que los datos aparecen visibles en la barra de direcciones del navegador y existe un tamaño máximo de
1024K.

La decisión de utilizar una colección u otra depende del tamaño de los datos a enviar y del número de
ellos. Cuando el tamaño es mayor de 1024K sólo tendremos la opción de usar la colección Form.

Yo personalmente sigo la siguiente regla, para los formularios de HTML siempre utilizao el método de
envío POST y por lo tanto para recuperar los valores de los campos utilizo Form, y la colección
QueryString la suelo utilizar para recuperar los valores de los datos facilitados desde un enlace.

Se debe tener en cuenta que el usuario puede manipular la cadena de consulta enviada mediante un
enlace, por lo tanto habrá algunos datos que no podremos enviar desde una cadena de consulta, pero
hay algunos casos en que no representa ningún problema que el usuario modifique la cadena de
consulta.

47
Modelo de objetos de ASP. Parte II

El objeto Application
Un objeto Application representa una aplicación ASP. Una aplicación basada en ASP consta de un
directorio en un servidor Web y de todos los subdirectorios y archivos contenidos en él. Una
aplicación puede ser una página principal sencilla, o bien puede estar formada por un conjunto
completo de páginas interrelacionadas entre sí.

Se debe recordar que el protocolo HTTP es un protocolo sin estado, es decir, no se puede almacenar
información entre diferentes conexiones HTTP. No se puede mantener el estado entre diferentes
páginas Web a través del protocolo HTTP, sino que se deben utilizar otros mecanismos como las
cookies. Pero el objeto Application junto con el objeto Session (que se comentará en su capítulo
correspondiente) nos permite de forma sencilla y directa almacenar información abstrayéndonos del
uso de cookies y de encabezados HTTP.

La información almacenada en los objetos Session y Application difieren en el ámbito de la misma,


una información tendrá el ámbito de la sesión de un usuario concreto y la otra el ámbito de la
aplicación general, respectivamente.

Las variables almacenadas dentro del objeto Application son visibles para todos los usuarios que están
utilizando la misma aplicación ASP, es decir son compartidas por varios usuarios. En contraposición
al objeto Session, cuyas variables son para cada uno de los usuarios conectados, es decir, no se
comparten y son propias de cada sesión. Podremos acceder a una variable a nivel de aplicación en
cualquiera de las páginas ASP contenidas en la aplicación ASP actual.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En este punto se debe señalar que una variable en una aplicación ASP puede tener cuatro ámbitos
diferentes, se va a ir del más global al más particular. La creación de una variable con un ámbito u otro
dependerá del uso que queramos hacer de ella.

Ámbito de aplicación: esta variable la podrán manipular todos los usuarios de la aplicación ASP, es
común a todos ellos. Se almacena dentro del objeto Application.

• Ámbito de sesión: las variables con este ámbito son propias de cada uno de los usuarios de la
aplicación, es decir, cada usuario tendrá acceso a las variables de su sesión. Estas variables se
almacenan dentro del objeto integrado Session.

• Ámbito de página: estas variables son las que se crean dentro del script de servidor de una
página ASP, sólo tienen vigencia dentro de la página en la que se declararon, al abandonar la
página se destruirán.

• Ámbito de procedimiento: a este ámbito pertenecen las variables declaradas dentro de un


procedimiento (Sub o Function). Estas variables sólo existirán dentro del procedimiento en el
que son declaradas, se dice que son variables locales al procedimiento.

Para almacenar una variable dentro de un objeto Application, es decir, crear una variable cuyo ámbito
sea la aplicación se debe utilizar la sintaxis del Código fuente 31.

Application("NombreVariable")=valorVariable

Código fuente 31

También se pueden almacenar objetos dentro del objeto Application.

Hay una serie de consideraciones acerca de las variables que se pueden almacenar en el objeto
Application y que pasamos a comentar a continuación.

En cuanto a tipos de variable se refiere, una variable de aplicación puede contener cualquier tipo de
valor Variant, y en cuanto a objetos, puede contener cualquier tipo de objeto o componente que se
encuentre preparado para tener ámbito de aplicación, a excepción de los objetos del modelo de objetos
de ASP (ASPError, Request, Server, Response, Application, Session, ObjectConext) de esta forma la
instrucción del Código fuente 32 generaría un error.

<%Set Application("objeto")=Request%>

Código fuente 32

Si almacenamos una variable array a nivel de aplicación, para modificar sus valores no podremos
hacerlo directamente, sino que se debe hacer a través de una variable auxiliar, que será la que se
modificará y luego asignará a la variable de aplicación correspondiente. En el Código fuente 33 se
muestra la utilización de una variable de aplicación que es un array de cadenas.

<%Dim vector(3)
vector(0)="Primer valor"
vector(1)="Segundo"

50
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

vector(2)="Tercero"
vector(3)="Ultimo Valor"
Application("valores")=vector
'----
'----
'Para modificar los valores recuperamos la variable
vectorAux=Application("valores")
vectorAux(2)="Elemento modificado"
Application("valores")=vectorAux
For i=0 to UBound(Application("valores"))
Response.Write
"Application('valores')("&i&")="&Application("valores")(i)&"<br>"
Next%>

Código fuente 33

Sin embargo los valores de cada elemento de array si que se pueden recuperar directamente de la
variable de aplicación.

No es recomendable almacenar objetos a nivel de aplicación, ya que esto supone que el objeto va a
existir durante toda la vida de la aplicación ASP y puede ser accedido por distintos usuarios de manera
concurrente. Lo ideal para almacenar en variables de aplicación son variables de tipo entero, booleano
o cadenas de caracteres.

Colecciones del objeto Application


En la versión 1.0 de ASP el objeto Application no poseía ninguna colección, sin embargo en ASP 2.0
se añadieron dos colecciones, y son:

• Contents: contiene las variables a nivel de aplicación a excepción de los objetos creados con la
etiqueta <OBJECT>

• StaticObjects: contiene únicamente los objetos a nivel de aplicación creados con la etiqueta
<OBJECT>.

Ambas colecciones nos van a permitir recuperar o acceder a las variables y objetos creados a nivel de
aplicación, aunque ya hemos visto en el apartado anterior que podemos acceder directamente a ellos.

La colección Contents contiene todos los objetos y variables creados a nivel de aplicación, excluyendo
aquellos objetos que han sido creados utilizando la etiqueta de HTML <OBJECT>. Esta etiqueta se
utiliza para la creación de objetos, de todas formas veremos con detalle el uso de la etiqueta
<OBJECT> en este mismo apartado a la hora de comentar la colección StaticObjects.

En esta colección se incluirán, por tanto, los objetos creados a nivel de aplicación con la sentencia
Server.CreateObject y las variables creadas con Application("variable")=valor.

La colección Contents del objeto Application permite utilizar un par de métodos que son comunes a
todas las colecciones de escritura y que son los siguientes:

• RemoveAll: elimina todas las variables de aplicación, a excepción de las creadas con la
etiqueta <OBJECT>. Este método carece de parámetros.

• Remove: elimina una variable de aplicación determinada que esté contenida en la colección
Contents. Este método recibe por parámetro el índice o nombre de la variable correspondiente.

51
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

La colección StaticObjects contiene todos los objetos a nivel de aplicación que se han creado mediante
la etiqueta <OBJECT>, por lo tanto la colección StaticObjects únicamente contendrá variables que son
referencias a objetos.

De la misma forma que veíamos en el ejemplo anterior, podremos recorrer la colección StaticObjects
para acceder a los objetos estáticos creados a nivel de aplicación.

La etiqueta <OBJECT> es una etiqueta de HTML que se suele utilizar para instanciar y crear objetos
ActiveX en el navegador del cliente, sin embargo aquí su uso es muy distinto, ya que se utiliza para
instanciar componentes del servidor, podemos decir que es equivalente a la sentencia
Server.CreateObject. Pero esta etiqueta únicamente se puede utilizar en el fichero especial global.asa.

La colección StaticObjects no ofrece ningún método como los de la colección Contents, únicamente
ofrece los métodos que son comunes a todas las colecciones de lectura, que son Count, Key e Item.
Veamos un ejemplo, suponiendo que tememos creados los objetos del ejemplo anterior.

Métodos del objeto Application


Las variables almacenadas en el objeto Application son compartidas por todos los usuarios de la
aplicación ASP. Debido a esto se pueden dar problemas de concurrencia, es decir, cuando dos usuarios
acceden a la misma información y la quieren modificar al mismo tiempo, por lo tanto se debe tener un
acceso exclusivo a las variables del objeto Application cuando se quiera modificar su valor.

Aquí entendemos por usuarios a sentencias del lenguaje de secuencias de comandos, es decir, serían
dos sentencias en distintas páginas que intentaran modificar la misma variable de aplicación al mismo
tiempo.

Para implementar este mecanismo de exclusión mutua el objeto Application ofrece dos métodos: Lock
y UnLock. Cuando se llama al método Lock, éste impide que otros usuarios modifiquen el contenido
de las variables de la aplicación. Y cuando se llama al método UnLock, se permite el acceso a las
variables a todos los usuarios de la aplicación. Es un método de exclusión mutua, sólo un usuario
puede a la vez estar utilizando las variables del objeto Application, las variables quedarán libres para
el resto de los usuarios cuando se lance sobre el objeto Application el método UnLock. El Código
fuente 34 muestra un ejemplo de utilización de estos dos métodos.

<%Application.Lock
Application("NumVisitas")=Application("NumVisitas")+1
Application.UnLock%>

Código fuente 34

Este mecanismo puede ser ineficiente, ya que se bloquean todas las variables del objeto Application
cuando, como en el ejemplo anterior, necesitamos que solamente se proteja una variable.

No se debe utilizar demasiadas sentencias entre una llamada al método Lock y la correspondiente
llamada a UnLock, ya que las variables del objeto Application podrían quedar bloqueadas demasiado
tiempo afectando de esta forma al correcto funcionamiento de la aplicación ASP correspondiente.

Lock y UnLock se utilizarán normalmente en el momento en el que se vaya a modificar el valor de


una variable de aplicación.

52
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

Nunca se debe olvidar el corresponder cada llamada Lock con una llamada UnLock, ya que podríamos
caer en el error de dejar bloqueada las variables de la aplicación de forma indefinida.

Eventos del objeto Application. El GLOBAL.ASA


El objeto Application posee dos eventos, cuando comienza la ejecución de la aplicación ASP y cuando
finaliza la ejecución de la aplicación ASP, estos dos eventos se llaman, respectivamente
Application_OnStart y Application_OnEnd.

Estos dos eventos se corresponden con dos procedimientos del mismo nombre definidos en el fichero
global.asa. Ya empezamos a ver una de las principales funciones del ya mencionado fichero
global.asa.

Dentro de estos eventos se puede situar un script que realice las tareas que consideremos necesarias. El
script que se debe ejecutar en cada uno de estos eventos se deberá indicar el fichero especial llamado
GLOBAL.ASA (ASA, Active Server Application).

El evento Application_OnStart se produce cuando entra el primer usuario en la aplicación, es decir


antes de producirse el primer inicio de sesión (Session_OnStart), por ejemplo, cuando el primer
usuario carga una página ASP perteneciente a una aplicación. Dentro de este evento se deberá indicar
el código de inicialización de la aplicación ASP.

Y el evento Application_OnEnd se produce cuando se apaga el servidor Web, se descarga la


aplicación ASP, finaliza la última de las sesiones de un usuario o se modifica el fichero
GLOBAL.ASA, estas son las distintas formas que tiene de finalizar una sesión. Dentro de este evento
se deberá escribir el script que se desee que se ejecute antes de destruir la aplicación.

En los eventos Application_OnEnd y Application_OnStart únicamente se tiene acceso a dos de los


objetos integrados de ASP, se trata de los objetos Server y Application, si intentamos utilizar algún
otro objeto del modelo de objetos de ASP se producirá un error.

El fichero GLOBAL.ASA (ASA, Active Server Application, aplicación activa de servidor o mejor
aplicación ASP), que tanto hemos mencionado hasta ahora en este capítulo, es un fichero opcional que
se encuentra en el directorio raíz de la aplicación ASP y que está relacionado de forma directa con los
objetos integrados Application y Session. Este fichero, que debe ser único para cada aplicación ASP,
tiene varias funciones:

• Definir como son tratados los eventos de los objetos Session y Application. Como hemos
comentado en el apartado anterior.

• Permitir crear objetos con el ámbito de aplicación y sesión.

• Inicializar y crear variables en el ámbito de la sesión y aplicación.

• Información acerca del tratamiento de los eventos de los objetos Session y Application.

• Declaraciones de objetos mediante la etiqueta <OBJECT>.

Este fichero no es un fichero cuyo contenido se muestre al usuario, si un usuario intenta cargar el
fichero global.asa en su navegador recibirá un mensaje de error:.

La estructura general del GLOBAL.ASA es la del Código fuente 35.

53
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<SCRIPT LANGUAJE=VBScript RUNAT=Server>


SUB Application_OnStart
......
END SUB

SUB Session_OnStart
......
END SUB

SUB Session_OnEnd
......
END SUB

SUB Application_OnEnd
......
END SUB
</SCRIPT>

Código fuente 35

Si se escribe script fuera de las etiquetas <SCRIPT></SCRIPT> o se declara un objeto que no tiene
ámbito de sesión o de aplicación se producirá un error.

Tampoco se pueden incluir ficheros de ningún tipo mediante el uso de la directiva INCLUDE.

El lenguaje de secuencias de comandos a utilizar se indica en el atributo LANGUAGE de la etiqueta


<SCRIPT>, y también se debe indicar en que lugar se ejecutará el script, esto se consigue a través del
atributo RUNAT de la etiqueta <SCRIPT>, en realidad este atributo sólo tiene un valor posible:
Server, es decir, el script se ejecutará en el servidor.

Para declarar objetos mediante la etiqueta <OBJECT>, para que tengan el ámbito de sesión o
aplicación se deberá seguir la sintaxis del Código fuente 36.

<OBJECT RUNAT=Server SCOPE=Ambito ID=Identificador


{PROGID="IDprog"|CLASSID="IDclase"}>
........
</OBJECT>

Código fuente 36

La declaración de los objetos deberá ir fuera de las etiquetas de script. Los objetos declarados de esta
forma no se crearán hasta que el servidor procese una secuencia de comandos en el que se haga
referencia a ellos. Así se ahorran recursos, al crearse sólo los objetos que son necesarios.

Estos objetos pasaran a formar parte de la colección StaticObjects del objeto Application.

El parámetro Ambito especifica el ámbito del objeto, podrá tener los valores Session o Application;
Identificador especifica un nombre para la instancia del objeto; IDprog e IDclase para identificar la
clase del objeto.

La propiedad SCOPE de la etiqueta <OBJECT> de HTML, tiene un valor de ámbito más, además de
los de sesión y de aplicación, y se trata del ámbito de página SCOPE="PAGE". El ámbito de página
indica que el objeto que se instancie sólo existirá en el página actual, pero este ámbito no se puede

54
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

utilizar en el fichero global.asa sino que se puede utilizar en cualquier página ASP. Sin embargo en las
páginas ASP no se puede utilizar la etiqueta <OBJECT> con el ámbito de aplicación o de sesión,
únicamente se pueden instanciar objetos con ámbito de página.

El Código fuente 37 dentro del fichero global.asa crearía un objeto Connection de ADO con ámbito de
sesión.

<OBJECT RUNAT=Server SCOPE=Session ID=conexion PROGID="ADODB.Connection">


</OBJECT>

Código fuente 37

Para utilizar este objeto posteriormente dentro de una página ASP, por ejemplo, para ejecutar una
sentencia SQL sobre la conexión con la base de datos, bastaría con escribir la línea del Código fuente
38.

<%conexion.Execute "DELETE FROM Usuarios WHERE id<10"%>

Código fuente 38

Es decir, accedemos directamente al nombre del objeto, que será el indicado en la propiedad ID de la
etiqueta <OBJECT>.

El objeto Application se crea cuando el primer usuario se conecta a una aplicación ASP y pide una
sesión, es decir, carga la primera página de la aplicación ASP. Cuando se crea el objeto Application, el
servidor busca el fichero GLOBAL.ASA en el directorio raíz de esa aplicación ASP, si el fichero
existe se ejecuta el script del evento Application_OnStart.

A continuación se crea el objeto Session. La creación del objeto Session ejecuta el script que se
encuentra en el evento Session_OnStart. El script asociado al tratamiento de este evento se ejecutará
antes de cargar la página indicada por la petición del navegador cliente.

Cuando el objeto Session caduca o se lanza el método Session.Abandon, se ejecutará el script que se
corresponde con el evento Session_OnEnd, el código de este evento se procesará antes de destruir la
sesión.

Cuando finaliza la última sesión de los usuarios, es decir, se ejecuta el código del último evento evento
Session_OnEnd, se lanza el evento Application_OnEnd antes de que se destruya el objeto Application.

Si se modifica el fichero GLOBAL.ASA, el servidor lo recompilará, para ello el servidor deberá


destruir el objeto Application actual y los objetos Session actuales. Primero, el servidor procesa todas
las peticiones activas, el servidor no procesará más peticiones hasta que no se procese el evento
Application_OnEnd. Los usuarios que se intenten conectar durante este proceso recibirán un mensaje
que le indica que la petición no puede ser procesada mientras la aplicación es reiniciada. Durante este
proceso se dan los siguientes pasos:

• Las sesiones activas se destruyen, dando lugar al procesamiento del evento Session_OnEnd.

• La aplicación se destruye, produciéndose el evento Application_OnEnd.

55
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• La primera petición reiniciará el objeto Application y creará un nuevo objeto Session, es decir,
se darán los eventos Application_OnStart y Session_OnStart, respectivamente.

También se ejecutará la el evento Application_OnEnd cuando se descargue la aplicación ASP desde el


Administrador de servicios de Internet o cuando se apague el servidor Web.

El objeto Session
Al igual que ocurría con el objeto Application, el objeto Session nos va a permitir almacenar
información entre diferentes páginas ASP incluidas en una misma aplicación ASP. La diferencia con
el objeto Application se encuentra en el ámbito de las variables, cada variable del objeto Session es
particular a una sesión de un usuario determinado, no a toda la aplicación. De esta forma, cada usuario
tendrá sus variables y sus valores, sin dar lugar a problemas de concurrencia, tampoco se podrá
acceder a distintas variables de sesión, cada usuario tiene su espacio de almacenamiento.

Las variables de aplicación son valores globales y comunes a toda la aplicación, y las variables de
sesión son particulares para cada usuario de la aplicación.

El servidor Web crea automáticamente un objeto Session, cuando un usuario que aún no estableció
una sesión solicita una página ASP perteneciente a la aplicación ASP actual. El servidor Web sabe si
un navegador tiene ya una sesión debido a que la cookie ASPSESSIONID es enviada junto con la
petición de la página ASP.

Un uso común del objeto Session es almacenar las preferencias del usuario o información personal. Se
debe señalar que se podrán almacenar variables dentro del objeto Session si el navegador acepta
cookies, ya que el objeto Session para almacenar información se basa en la cookie ASPSESSIONID.
Aunque la utilización de esta cookie es completamente transparente para el programador.

Para una aplicación ASP tendremos tantas sesiones como usuarios conectados a la aplicación ASP.

Las recomendaciones realizadas para el objeto Application en cuanto a utilización de objetos y


variables de tipo array, son también válidas para el objeto Session.

La sintaxis para manipular variables del objeto Session es la misma que en el objeto Application. El
Código fuente 39 crea una variable del objeto Session con el nombre que el usuario facilita en un
formulario.

<%Session("nombreUsuario")=Request.Form("nombre")%>

Código fuente 39

Colecciones del objeto Session


En la versión 1.0 de ASP el objeto Session ,al igual que ocurría con el objeto Application tampoco
poseía ninguna colección, apareciendo en la versión de 2.0 ASP con dos nuevas colecciones con las
mismas funciones que las del objeto Application pero a nivel de sesión claro:

• Contents: contiene todas las variables a nivel de sesión menos las creadas con al etiqueta
<OBJECT>.

56
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

• StaticObjects: contiene las variables de la sesión creadas con la etiqueta <OBJECT>.

Ambas colecciones nos van a permitir recuperar o acceder a las variables y objetos creados, en este
caso, a nivel de sesión. La colección Contents contiene todos los objetos y variables creados a nivel de
sesión, excluyendo a los objetos que han sido creados utilizando la etiqueta <OBJECT>.

En esta colección se incluirán, por tanto, los objetos creados a nivel de sesión con Server.CreateObject
y las variables creadas con las sentencias del tipo Session("variable"). La colección StaticObjects
contiene todos los objetos que se han creado mediante la etiqueta <OBJECT> dentro del alcance de la
sesión.

Si suponemos que tenemos el fichero GLOBAL.ASA que aparece en el Código fuente 40.

<SCRIPT LANGUAGE="VBScript" RUNAT="Server">


Sub Session_OnStart
Session("variableUno")="valor"
Session("variableDos")=2
Session("variableTres")=True
End Sub
</SCRIPT>
<OBJECT RUNAT="Server" SCOPE="Session" ID="conex" PROGID="ADODB.Connection">
</OBJECT>
<OBJECT RUNAT="Server" SCOPE="Session" ID="recordset" PROGID="ADODB.RecordSet">
</OBJECT>

Código fuente 40

Si ahora se ejecuta una página con el Código fuente 41.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
Número de elementos de la colección Contents:
<b><%=Session.Contents.Count%></b><br>
<%For Each variable in Session.Contents
Response.Write variable & " = "&Session.Contents(variable)&"<br>"
Next%>
<br>
Número de elementos de la colección StaticObjects:
<b><%=Session.StaticObjects.Count%></b><br>
<%For Each objeto in Session.StaticObjects
Response.Write objeto&"<br>"
Next%>
<br>
<%'accedemos la variable de aplicación definida con <OBJECT>
conex.Open "DSN=BD;UID=sa;PWD="
Response.Write "Base de datos predeterminada: "&conex.DefaultDatabase
conex.Close%>
</BODY>
</HTML>

Código fuente 41

57
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Se producirá este resultado:

Número de elementos de la colección Contents: 3


variableUno = valor
variableDos = 2
variableTres = Verdadero

Número de elementos de la colección StaticObjects: 2


conex
recordset

Base de datos predeterminada: Cursos

Propiedades del objeto Session


El objeto Session posee cuatro propiedades:

• SessionID: contiene la identificación de la sesión para el usuario. Cada sesión tiene un


identificador único que genera el servidor al crearla. No se debe utilizar esta propiedad como
clave de una tabla de una base de datos, ya que, al reiniciar el servidor Web, algunos de los
valores de SessionID pueden coincidir con los generados antes de que se apagase el servidor.
Es únicamente de lectura.

• Timeout: la propiedad Timeout especifica el intervalo de inactividad para el objeto Session en


minutos. Si el usuario no actualiza o solicita una página durante ese intervalo, la sesión
termina. El valor por defecto de esta propiedad es de 20 minutos, es decir, por defecto la
sesión permanecerá inactiva 20 minutos. Una sesión se dice que está inactiva mientras el
navegador cliente no realice una petición. El valor de esta propiedad se puede modificar
dinámicamente a lo largo de la ejecución de la aplicación ASP.

• CodePage: indica la página de códigos de caracteres que va a ser utilizado. A esta propiedad
se le puede asignar un valor entero par especificar la página de códigos que se va a utilizar.
Así si queremos utilizar el juego de caracteres del alfabeto turco le asignaremos a esta
propiedad el valor 1254 ( <% Session.CodePage = 1254 %>). Esta propiedad es también de
lectura/escritura.

• LCID (Locale Identifier): propiedad de lectura/escritura, es una abreviatura estándar e


internacional que identifica de forma única la localización de los sistemas. Esta localización
determina la forma en la que se le da formato a las horas y fechas, como se tratan las cadenas
de caracteres y los diferentes elementos del alfabeto. Así si queremos establecer la propiedad
LCID para la región de Rusia, le debemos asignar a esta propiedad el valor 1049 ( <%
Session.LCID = 1049 %> ).

Las últimas dos propiedades han sido incluidas con la versión 2.0 de ASP.

Se puede configurar si queremos utilizar el estado de sesión o no, mediante el Administrador de


servicios de Internet.

Pulsamos sobre la aplicación con el botón derecho del ratón y seleccionamos la opción de menú
propiedades, aparecerán las hojas de propiedades del directorio como se ve en la Figura 9.

58
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

Figura 9

En el epígrafe denominado configuración de aplicación podemos observar un botón etiquetado como


Configuración, si lo pulsamos aparece una nueva ventana de la que elegiremos la pestaña etiquetada
como opciones de aplicación. Ver Figura 10. Desde esta hoja de propiedades podemos configurar
algunos parámetros de nuestra aplicación ASP, entre ellos el que nos interesa, es decir, la posibilidad
de habilitar o deshabilitar el estado de la sesión.

Figura 10

59
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Además podemos configurar la propiedad Timeout de la sesión y el lenguaje del intérprete de


comandos por defecto. También podemos habilitar o deshabilitar el almacenamiento en búfer. Los
valores que especifiquemos en esta pantalla se pueden sobreescribir desde el código ASP indicando
los valores de las propiedades o directivas de procesamiento correspondientes

Métodos del objeto Session


Este objeto posee un único método: el método Abandon. Al lanzar sobre el objeto Session el método
Abandon, se destruyen todas las variables de la sesión y se liberan sus recursos, finalizando la misma.
Si no se llama explícitamente al método Abandon, el servidor destruirá los objetos cuando la sesión
caduque, atendiendo al valor de la propiedad Timeout.

La destrucción del objeto Session no se hará efectiva hasta que el servidor no haya terminado de
ejecutar la página ASP. Por lo tanto las variables del objeto Session existirán mientras no se cargue
otra página.

El servidor creará un nuevo objeto Session al abrir una nueva página ASP después de abandonar la
sesión.

Eventos del objeto Session


Al igual que ocurría en el objeto Application, el objeto Session objeto posee dos eventos: el inicio de
sesión, Session_OnStart, y el fin de sesión Session_OnEnd.

El inicio de sesión se produce cuando el usuario carga la primera página se una aplicación ASP.
Dentro de este evento se deberán indicar las acciones a llevar a cabo antes de que se cargue la primera
página de una aplicación ASP.

Este evento se suele utilizar para inicializar las variables para toda la sesión, así por ejemplo, si la
aplicación ASP va a manipular una serie de tablas de una base de datos, se podrá crear una conexión
con la base de datos en el evento de inicio de sesión y almacenarla en el objeto Session para que esté
disponible para ese usuario mientras su sesión permanezca activa.

También se puede utilizar para cargar una serie de variables que definan el perfil del usuario que se
acaba de conectar.

El fin de la sesión, es decir, el evento Session_OnEnd, se puede producir porque se haya caducado la
sesión al permanecer inactiva el tiempo indicado por su propiedad Timeout, o bien, se puede forzar
mediante a una llamada al método Abandon.

Cuando el usuario cierra el navegador no se ejecuta el evento Session_OnEnd de forma inmediata,


sino que se ejecutará cuando se cumpla el Timeout correspondiente, para la aplicación ASP es igual
que el usuario se encuentre con la página cargada en el navegador sin realizar ninguna petición o que
el usuario haya cerrado su sesión del navegador, lo que se tiene en cuenta es el tiempo de inactividad
de la sesión del usuario.

Sin embargo una llamada a Session.Abandon lanza el evento Session_OnEnd de manera inmediata.

Al igual que ocurría con el objeto Application, los procedimientos para el tratamiento de estos eventos
se deben incluir en el ya conocido fichero de aplicación GLOBAL.ASA.

60
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

En el evento Session_OnEnd sólo están disponibles los objetos Application, Server y Session, y
tampoco se puede utilizar el método MapPath del objeto Server. En el evento Session_OnStart se tiene
acceso a todos los objetos integrados de ASP.

El objeto Server
El objeto Server nos permite ampliar las capacidades de las páginas ASP mediante la posibilidad de la
creación y utilización de objetos externos y componentes en el lenguaje de secuencias de comandos.

El objeto Server está diseñado para realizar tareas específicas en el servidor. Además de la posibilidad
de instanciar componentes el objeto Server ofrece una serie de métodos muy útiles como pueden se los
que permiten dar formato URL o HTML a cadenas de caracteres, los que modifican la línea de
ejecución de un script de una página con la posibilidad de ejecutar distintas páginas, y también existe
un método utilizado para el tratamiento de errores dentro de ASP, etc. Todos estos métodos los
veremos en el presente capítulo.

En algunas definiciones del objeto Server se suele indicar también que nos permite acceder a los
recursos del servidor, en cierto modo esto es correcto ya que para poder instanciar un componente
debe encontrarse registrado en el servidor Web.

Propiedades del objeto Server


El objeto Server posee una única propiedad ScriptTimeout que es de lectura/escritura. La propiedad
ScriptTimeOut expresa en segundos el periodo de tiempo durante el que puede ejecutarse una
secuencia de comandos (script) antes de que termine su intervalo de espera.

El intervalo de espera de la secuencia de comandos no tendrá efecto mientras haya un componente del
servidor en proceso.

Las modificaciones sobre la propiedad ScriptTimeOut del objeto Server se aplican únicamente a la
página actual. Puede ser interesante aumentar el valor de esta propiedad en el caso de páginas que
tarden mucho tiempo en ejecutarse debido a su complejidad, ya que si las secuencias de comandos de
una página tardan en ejecutarse más tiempo que el especificado en la propiedad ScriptTomeOut se
producirá un error.

La propiedad ScriptTimeOut es una forma de evitar que el servidor Web se sobrecargue son páginas
ASP que presentan tiempo de ejecución excesivo.

El valor por defecto que posee esta propiedad es de 90 segundos, el valor por defecto se puede
modificar a través del Administrador de servicios de Internet para que sea aplicable a todas las páginas
ASP que componen una aplicación ASP determinada, es decir, se puede definir un valor por defecto
de la propiedad ScriptTimeout para cada aplicación ASP.

La forma de modificar el valor por defecto de la propiedad ScriptTimeout es acudiendo a las


propiedades de la carpeta que contiene la aplicación ASP dentro del Administrador de servicios de
Internet. Pulsamos el botón de Configuración y accedemos a la pestaña Opciones de la aplicación. En
esta ventana podemos ver un parámetro denominado Tiempo de espera de archivo de comandos ASP,
este será el parámetro que defina el valor predeterminado de la propiedad ScriptTimeout.

61
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Métodos del objeto Server


Tiene los siguientes métodos:

• CreateObject: método por excelencia del objeto Server, crea una instancia de un componente.
Este componente debe estar registrado en el servidor Web.

• HTMLEncode: devuelve una cadena codificada en HTML a partir de la cadena que se le pasa
como parámetro.

• MapPath: devuelve la ruta física de una ruta virtual dada a través de una cadena en formato
URL.

• URLEncode: devuelve una cadena a la que se le ha aplicado la codificación URL


correspondiente a las cadenas de consulta (QueryString).

• URLPathEncode: devuelve una cadena a la que se le ha aplicado la codificación URL


correspondiente a las cadenas de rutas.

• Execute: para la ejecución de la página actual y transfiere la ejecución a la página indicada.


Una vez que ha finalizado la ejecución de la nueva página, la ejecución continúa en la página
inicial.

• Transfer: finaliza la ejecución de la página actual y transfiere la ejecución a la página indicada


por parámetro. Sin volver en ningún caso a la página original.

• GetLastError: devuelve una referencia al objeto ASPError que contiene la información


detallada del último error que se ha producido.

Los cuatro últimos métodos han sido añadidos en la versión 3.0 de ASP.

El método más utilizado del objeto Server es, posiblemente, el método CreateObject. Este método
permite la instanciación de componentes de servidor, para que a continuación sean manipulados por
las secuencias de comandos de nuestras páginas ASP. Para crear una instancia de un componente de
servidor se debe utilizar la sintaxis general del Código fuente 42.

Set nombreObjeto=Server.CreateObject(ProgID)

Código fuente 42

La palabra reservada Set siempre se debe utilizar en la instanciación de un objeto y ProgID es el


nombre con el que está registrado el objeto en el servidor, el formato para ProgID es:
[Fabricante.]Componente[.Versión].

Así por ejemplo el ProgID de el objeto Recordset del modelo de objetos de acceso a datos ActiveX
Data Objects (ADO) es ADODB.Recordset.

La variable nombreObjeto debe tener un nombre distinto de cualquiera de los objetos integrados de
ASP. Se puede destruir un objeto asignándole Nothing, con el Código fuente 43.De esta forma se
libera la memoria ocupada por el objeto.

62
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

<%Set nombreObjeto=Nothing%>

Código fuente 43

Las instancias de un objeto, por defecto, se destruyen cuando la página ASP termina de ser procesada,
aunque es más seguro destruirlas mediante la palabra reservada Nothing.

Para crear un objeto con ámbito de sesión o de aplicación se debe almacenar el objeto en una variable
de sesión o de aplicación, o bien utilizando la etiqueta de HTML <OBJECT> en el fichero global.asa y
asignando al parámetro SCOPE los valores Session o Application, como vimos en el capítulo anterior.
De esta forma el objeto se destruirá cuando haya finalizado la sesión o la aplicación.

No es demasiado recomendable utilizar objetos a nivel de sesión o aplicación, y si se utilizan se deben


destruir cuanto antes, ya que pueden suponer una gran carga para la memoria del servidor Web. Es
preferible crear y destruir un objeto varias veces que llevar su referencia almacenada en una variable
de sesión o de aplicación.

El método MapPath devuelve la ruta física que se corresponde con el la ruta virtual que se le pasa por
parámetro. El objeto Server posee información sobre los directorios físicos, virtuales y relativos de la
máquina servidor y sabe como debe realizar la traducción de rutas virtuales a físicas.

Se puede necesitar obtener rutas físicas cuando se quieran crear directorios o manipular ficheros en el
servidor. La forma más frecuente de utilizar este método es la del Código fuente 44, que devuelve la
ruta física de la carpeta en que se encuentra la páigna ASP en ejecución.

nombreVariable=Server.MapPath(rutaVirtual)

Código fuente 44

Se recomienda por eficiencia evitar el uso del método MapPath, cada llamada a este método supone
una nueva conexión al servidor Web para que IIS devuelva la ruta actual del servidor. En su lugar es
más rápido utilizar la ruta literal, si se conoce.

Cuando tratamos el objeto Response en su capítulo correspondiente vimos que presentaba un método
llamado Redirect que nos permitía pasar a ejecutar una página distinta, pero esto suponía enviar una
respuesta al cliente para indicarle la carga de una nueva página, que es la página a la que pasamos la
ejecución.

La utilización del método Redirect es bastante costosa y problemática ya supone un envío de


información más del servidor al cliente para indicarle mediante una cabecera HTTP de redirección que
la página ha cambiado de localización, siendo la nueva localización la página que deseamos cargar.
Esto es problemático ya que en algunos navegadores como Netscape Communicator aparace un
mensaje del tipo El objeto requerido se ha movido y se puede encontrar aquí, esto también ocurre
cuando la conexión la realiza el cliente a través de proxy.

Pero mediante los métodos Execute y Transfer podemos evitar esta redirección, que como hemos
visto, tiene lugar en el cliente. Estos dos métodos permiten que la redirección tenga lugar en el
servidor, quedando el cliente completamente ajeno. Ambos métodos reciben como parámetro la ruta
de la página a la que queremos redirigir al cliente.

63
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

La utilización del método Execute es muy similar a realizar una llamada a un procedimiento o función.
Cuando lanzamos el método Execute se empieza a ejecutar la página que indicamos por parámetro, y
cuando termina la ejecución de esta nueva página, el control pasa a la siguiente sentencia después de la
llamada al método Execute en la página inicial, siguiendo a partir de aquí con la ejecución de la
página, es decir, el navegador del cliente recibe una salida formada por la combinación de la ejecución
de ambas páginas.

El cliente ni siquiera observa la URL correspondiente a la segunda página, ya que la redirección entre
páginas se produce en el servidor. El navegador cree que sigue recibiendo todavía la página original
que habia demandado, incluso en la barra de direcciones del navegador sigue apareciendo la misma
URL y los botones Atrás y Adelante funcionan correctamente.

En ambos métodos se mantiene el contexto de la página inicial, es decir, en la nueva página tenemos
acceso a los mismos objetos intrínsecos de ASP (Request, Session, Response...) de la página inicial o
página de origen. Es decir, la colección Request.Form de la segunda página coincide con la de la
primera.

El método Transfer también permite la redirección entre páginas en el servidor, pero este método se
comporta de distinto modo al método Execute, al lanzar este método se pasa la ejecución a la nueva
página, pero una vez que finaliza la ejecución de la misma no se vuelve a la página inicial, como
ocurría con el método Execute. El método Transfer es bastante más parecido al método Redirect que
lo era el método Execute, ya que con Transfer al igual que con Redirect no se regresa a la página
original.

Con el método Transfer se sigue manteniendo el contexto de la página inicial entre las distintas
páginas. La única diferencia es que la ejecución finaliza cuando termina la ejecución de la segunda
página, y no se vuelve a la inicial.

Mediante el uso del método GetLastError podemos tener acceso a toda la información referente al
último error que se ha producido en la página ASP actual. Pero es necesario aclarar que su utilización
no es similar al tratamiento de errores que realizábamos con la sentencia On Error Resume Next y el
objeto Err de VBScritp, que preguntábamos por la propiedad Number del objeto Err para averiguar si
se había producido algún error.

El método GetLastError se puede utilizar únicamente dentro de una página de error personalizada, es
decir, cuando el error ya se ha producido y lo ha detectado el servidor Web.

Mediante el servidor Web Internet Information Server 5 podemos indicar las páginas de error
personalizadas y es en estas páginas dónde podemos hacer uso de este método.

El método GetLastError devuelve un nuevo objeto del modelo de objetos de ASP llamado ASPError,
son las propiedades de este nuevo objeto las que nos permiten acceder de forma detallada a toda la
información referente al error que se ha producido.

El objeto ObjectContext
Este objeto integrado se incluyó en la versión 2.0 de las páginas ASP. Este objeto se ofrece gracias a la
integración que presentaban dentro de Windows NT el servidor Web IIS4 (Internet Information Server
4) con el servidor de transacciones MTS 2.0 (Microsoft Transaction Server).

A través del objeto integrado ObjectConext podremos tratar y gestionar las transacciones que se
realicen en nuestras páginas ASP, pudiendo construir, por tanto, páginas ASP transaccionales.

64
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

Esta integración entre el servidor Web y el servidor de transacciones sigue existiendo, pero ahora el
servidor Web se encuentra en su nueva versión IIS5 y el servidor de transacciones ya no se denomina
MTS sino que se encuentra integrado en lo que en Windows 2000 se denomina Servicios de
componentes, se puede decir que IIS y los Servicios de componentes funcionan conjuntamente para
formar la arquitectura básica para la creación de aplicaciones Web y para coordinar el proceso de
transacciones para las aplicaciones ASP transaccionales.

A través del objeto ObjectConext podremos deshacer o llevar a cabo las transacciones gestionadas por
los Servicios de componentes.

Recordemos que una transacción es una operación que se debe realizar de forma completa, es decir, si
falla una parte de la transacción se deben deshacer todos los cambios. Un ejemplo típico de una
transacción es la transferencia de una cuenta bancaria a otra, las operaciones de que implican restar el
capital de una cuenta y añadirlo a otra se deben ejecutar dentro de una transacción, si falla alguna de
las dos se debe devolver al sistema a su estado inicial. Las transacciones ofrecen una serie de
características comunes conocidas como propiedades ACID (Atomicity, Consistency, Isolation,
Durability).

Para indicar la naturaleza transaccional de una página ASP debemos incluir, como primera línea de la
página, la directiva de procesamiento TRANSACTION, como en el Código fuente 45.

<%@ TRANSACTION = valor %>

Código fuente 45

Donde el argumento valor define el comportamiento transaccional de la página ASP actual y puede
tomar uno de los siguientes valores:

• Requires_New: se requiere una nueva transacción, por lo que siempre se iniciará una nueva
transacción para las páginas ASP con este valor. Esta configuración es recomendable para
páginas que debe realizar transacciones pero que siempre deben estar separadas del resto.

• Required: se requiere una transacción, si no hay una iniciada, se iniciará una nueva. La página
ASP siempre se ejecutará en una transacción ya sea iniciada por ella misma o bien
aprovechando una transacción ya existente.

• Supported: soporta transacciones, pero no iniciará ninguna en el caso de que no exista. En


algunos casos la página ASP se ejecutará en una transacción y en otros no, ya que sólo utiliza
transacciones existentes.

• Not_Supported: no soporta transacciones, es el valor por defecto. La página ASP nunca


participará en ningún tipo de transacción.

• Disabled: se ignorarán los requerimientos transaccionales de la página ASP. La diferencia con


el caso anterior, es que con Not_Supported la página ASP siempre se ejecutará en un nuevo
contexto, y con Disabled, si existe un contexto se utilizará el mismo.

A través del objeto ObjectContext podremos construir páginas ASP transaccionales, pero
evidentemente, cuando se realicen operaciones que puedan tener una naturaleza transaccional, como
puede ser el acceso a bases de datos. Además, una transacción no puede abarcar varias páginas ASP, a
no ser que hagamos uso de los métodos Transfer o Execute del objeto Server, ya comentamos que con

65
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

estos métodos de ejecución entre páginas se conserva el contexto de la página inicial, en este contexto
se encuentra incluido las transacciones.

Hay que tener en cuenta, además, que la mayoría de las aplicaciones ASP sólo requieren hacer uso de
un contexto transaccional en determinadas operaciones. Así por ejemplo, una aplicación de una Línea
de Autobuses podría hacer uso de páginas ASP transaccionales para la venta de billetes y la reserva de
asientos, dejando el resto de tareas fuera del contexto transaccional.

Métodos del objeto ObjectContext


Para gestionar la transacción de una página ASP y poder indicar si se debe llevar a cabo la misma
(commit) o bien se deben deshacer los cambios y volver al estado anterior (rollback) debemos hacer
uso de los dos métodos del objeto ObjectContext.

En algún momento debemos determinar si las operaciones que debía realizar una página ASP se han
realizado con éxito, y por tanto, se puede llevar a cabo la transacción correspondiente. Igualmente
debemos disponer de algún método que nos permita anular una transacción en el caso de que falle
alguna de las operaciones implicadas en la misma. Para ello disponemos de dos métodos del objeto
ObjectContext:

• SetComplete: indica que se puede llevar a cabo la transacción correspondiente al realizarse con
éxito las operaciones que debía llevar a cabo la página ASP. Es decir, una llamada a este
método equivaldría a realizar un commit dentro de SQL Server o del gestor de bases de datos
correspondiente.

• SetAbort: indica que algo ha fallado en las operaciones implicadas en la transacción, una
llamada a este método cancela la transacción y deshace los cambios que se hayan podido
realizar. Sería equivalente a realizar una llamada a RollBack en SQL Server.

En el caso de que finalice el procesamiento de la página ASP y no se haya realizado ninguna llamada a
alguno de los dos métodos anteriores, se considerará que no se ha producido ningún problema en el
procesamiento de la página y, por lo tanto, se llamará automáticamente al método SetComplete.

Eventos del objeto ObjectContext


El objeto ObjectContext ofrece dos eventos que indican la forma en la que ha finalizado la
transacción. Esto es bastante útil para obrar en consecuencia, es decir, si se ha abortado una
transacción o se ha realizado con éxito se deberán hacer unas operaciones u otras. O si lo queremos
podemos no utilizar estos eventos.

Los eventos del objeto ObjectContext se describen a continuación:

• OnTransactionCommit: este evento se dispara cuando se llevó a cabo de una transacción de


manera satisfactoria con la llamada al método SetComplete.

• OnTransactionAbort: este evento se disparará tras la cancelación de una transacción con el


método SetAbort.

El uso de estos dos eventos, nos permitirá realizar diferentes tratamientos según se haya finalizado la
transacción, es decir, si se ha producido con éxito , o por el contrario se han producido errores y se ha
tenido que deshacer.

66
© Grupo EIDOS 3. Modelo de objetos de ASP. Parte II

El objeto ASPError
Este objeto es le nuevo objeto integrado que aparece en ASP 3.0. La función del objeto ASPError es la
de ofrecer de forma detallada toda la información relativa al último error que se ha producido dentro
de una aplicación ASP, describe el error que se ha producido, la naturaleza y fuente del mismo, y si es
posible el código fuente que causó el error. Para ello el objeto ASPError consta de nueve propiedades
de sólo lectura, y no ofrece ningún método.

Una referencia al objeto ASPError la obtenemos a través de un nuevo método del objeto Server, el
método GetLastError que ya comentamos en el capítulo correspondiente, la sintaxis de este método es
la del Código fuente 46.

Set objASPError=Server.GetLastError()

Código fuente 46

Ahora bien, el método GetLastError del objeto Server no se puede utilizar de forma indiscriminada en
cualquier lugar de nuestro código para consultar si se ha producido un error, únicamente se puede
utilizar de forma satisfactoria dentro de una página ASP de error personalizado.

Si desactivamos el tratamiento de errores predeterminado que presenta ASP mediante la sentencia On


Error Resume Next, al igual que se hacia anteriormente para consultar la propiedad Number del objeto
Error, e intentamos lanzar el método GetLastError para obtener una referencia al objeto ASPError,
esta llamada fallará y el método no podrá acceder a los detalles del error que se ha producido.

Propiedades del objeto ASPError


El objeto ASPError no ofrece ni eventos ni métodos únicamente ofrece propiedades y todas de lectura.
Estas propiedades contienen la información detallada relativa al último error de ASP que se ha
producido en una aplicación.

Las propiedades del objeto ASPError las consultaremos cuando se haya producido un error, cada una
de ellas tiene un significado determinado que se pasa a exponer a continuación:

• ASPCode: un entero generado por IIS (Internet Information Server) y que representa un
código de error de ASP.

• ASPDescription: una cadena que es una descripción detallada del error si está relacionado con
ASP.

• Category: cadena que indica si se trata de una error interno de ASP, del lenguaje de secuencia
de comandos o de un objeto.

• Column: entero que indica la posición de la columna del archivo ASP que generó el error.

• Description: cadena que contiene una breve descripción del error. Tiene el mismo significado
que la propiedad Description del objeto Err.

• File: cadena que contiene el nombre del archivo ASP que se estaba procesando cuando se
produjo el error.

67
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Line: entero que se corresponde con el número de línea del archivo ASP que generó el error.

• Number: entero que representa un código de error estándar de COM. Tiene el mismo
significado que la propiedad Number del objeto Err.

• Source: cadena que contiene el código fuente real, si está disponible, de la línea que causó el
error.

Como se puede observar podemos obtener una información bastante más precisa que con el objeto Err
de VBScript.

68
Componentes de Servidor

Introducción
Hemos visto los componentes integrados dentro de ASP y que forman parte del modelo de objetos de
ASP y los componentes que ofrece el motor de secuencias de comandos de ASP contiene una serie de
componentes que ofrecen una serie de funciones que liberan al programador de implementarlas, estas
funciones pueden ser tareas comunes que se deban realizar dentro de un desarrollo en el entorno Web.
Estos componentes están diseñados para ejecutarse en el servidor Web como parte de una aplicación
basada en ASP.

Estos componentes se denominan componentes ActiveX Server o componentes de servidor, y


anteriormente eran conocidos como servidores de automatización. Los componentes ActiveX Server
se invocan desde páginas ASP (en nuestro caso), pero también pueden ser llamados desde otros
orígenes, como por ejemplo aplicaciones ISAPI, desde otro componente de servidor o desde otros
lenguajes OLE compatibles.

Los componentes de servidor son ficheros DLL que se ejecutan en el mismo espacio de direcciones de
memoria que las páginas ASP y el servidor Web Internet Information Server.

Los componentes ActiveX Server que se encuentran incluidos dentro de ASP en su versión 3.0 y que
se van a comentar en este tema son:

• Componente de acceso a bases de datos, ADO (ActiveX Data Objects). A través de la


utilización de este componente se puede ofrecer acceso a bases de datos desde una página
ASP, así por ejemplo, se puede mostrar el contenido de una tabla, permitir que los usuarios
realicen consultas y otras operaciones sobre una base de datos.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Componente Ad Rotator. Este componente permite mostrar una serie de imágenes alternativas
con un vínculo a otra dirección desde la imagen presentada. Este componente se suele utilizar
para mostrar diferentes anuncios de forma alternativa dentro de una página ASP.

• Componente Funciones del explorador. A través de este componentes podemos recuperar


datos acerca del tipo de navegador del cliente y que capacidades o funciones tiene.

• Componente vínculo de contenidos. Facilita el desplazamiento lógico entre las diferentes


páginas ASP de una aplicación ASP.

• Componente Content Rotator (rotador de contenidos). Este componente permite hacer


rotaciones de cadenas de contenido HTML en una página.

• Componente Page Counter (contador de páginas). Permite llevar una cuenta del número de
veces que se ha accedido a una página determinada dentro de nuestro sitio Web.

• Componente Counters. A través de este componente podremos almacenar, crear, incrementar


y consultar cualquier contador.

• Componente MyInfo. Nos permite almacenar información personal que será ofrecida por el
administrador del sitio Web.

• Componente Tools. Es el denominado componente de utilidades. Ofrece una serie de


funciones diversas, como la generación de números aleatorios o la comprobación de la
existencia de un fichero en el servidor.

• Componente Permission Checker. A través de este componente podremos determinar si a un


usuario se le ha dado permisos para acceder a un fichero determinado.

• Componente Status. Este componente, de momento, únicamente está disponible para el


servidor Personal Web Server en plataformas Macintosh, resulta extraño pero es así. Nos
ofrece una información variada acerca del estado del servidor Web.

• Componente de registro de IIS. Mediante este componente tenemos acceso a la información y


manipulación de los ficheros de registro (log) generados por el servidor Web IIS 5.0.

Para poder utilizar un componente de servidor dentro de una página ASP debemos instanciarlo, para
ello deberemos utilizar el método CreateObject del objeto Server de la misma forma que lo hacíamos
con los componentes de VBScript.

También se puede crear una instancia de un componente de servidor a través de la etiqueta


<OBJECT> de HTML, como ya vimos en el tema anterior

La diferencia entre los dos mecanismos es desde el punto de vista de la eficiencia. Cuando se instancia
un objeto con el método Server.CreateObject se crea inmediatamente el objeto, aunque no lo
lleguemos a utilizar, sin embargo, si instanciamos el objeto con la etiqueta <OBJECT> el objeto sólo
se creará en el momento que lo vayamos a utilizar por primera vez.

A lo largo de los siguientes apartados del presente capítulo vamos a ir comentando los distintos
componentes de servidor que se ofrecen junto con la instalación de ASP.

70
© Grupo EIDOS 4. Componentes de Servidor

Componente AdRotator
Este componente de servidor se encarga de rotar una serie de anuncios (imágenes) dentro de una
página ASP. Cada vez que un usuario vuelva a cargar la página el componente AdRotator mostrará
una imagen con una URL asociada, atendiendo a un fichero especial denominado fichero de
planificación.

Una de las características de HTML es que permite la creación de imágenes con vínculos. La etiqueta
HTML que permite visualizar una imagen en una página es la etiqueta <IMG>. El componente
AdRotator genera una etiqueta <IMG> a la que le asigna también una URL, al pulsar sobre la imagen
se redireccionará al usuario hacia la URL asociada a esa imagen. Para llevar a cabo este proceso el
componente AdRotator se sirve de dos ficheros: el fichero de redireccionamiento y el de planificación.

El fichero de redireccionamiento es una página ASP que debemos crear si queremos utilizar este
componente. Este fichero suele incluir secuencias de comandos para analizar la cadena de consulta
enviada por el componente AdRotator y para redirigir al usuario a al dirección URL asociada con el
anuncio sobre el que se ha pulsado. En el Código fuente 47,el fichero de redirección simplemente
redirige al usuario a la página del anunciante.

<%Response.Redirect(Request.QueryString("url"))%>

Código fuente 47

Dentro de la colección QueryString, en este caso, podemos encontrar dos variables: url, que indica la
URL que tiene asociada la imagen e image, que indica el nombre de la imagen sobre la que se ha
pulsado.

Como ya se había comentado con anterioridad, dentro del fichero de redireccionamiento también
podemos realizar otras tareas además de redireccionar al navegador a una dirección determinada. Con
el Código fuente 48 que se muestra a continuación se consigue saber el número de usuarios que pulsan
sobre un anuncio determinado. En este código aparece un par de componentes de VBScript como son
el objeto FileSystemObject y TextStream, que nos permiten leer y escribir en un fichero del servidor
en el que vamos a almacenar el número de usuarios que han pulsado sobre el anuncio número dos.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\scrrun.dll"-->


<%'Cuenta el número de usuarios que han pulsado sobre un anuncio determinado
'el fichero de pulsaciones está en este caso en el mismo directorio
'que la página ASP de redirección
nombreFichero=Server.MapPath("pulsaciones.txt")
if Request.QueryString("image")="/cursoASP30/images/Anuncio2.gif" Then
Set objFSO= Server.CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(nombreFichero) Then
'Se abre el fichero para su lectura
Set objTextStream=objFSO.OpenTextFile(nombreFichero)
pulsaciones=CInt(objTextStream.ReadLine)
pulsaciones=pulsaciones+1
objTextStream.Close
Else
pulsaciones=1
End If
Set objTextStream=objFSO.OpenTextFile(nombreFichero,ForWriting,True)
objTextStream.WriteLine pulsaciones
objTextStream.Close
Set objFSO=Nothing

71
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

End if
Response.Redirect(Request.QueryString("url"))%>

Código fuente 48

En la sentencia if...Then... se consulta la variable image de la colección QueryString del objeto


Request, para comprobar si contiene el nombre del fichero de imagen del segundo anuncio. Si es el
anuncio número dos, se lee del fichero el número actual de usuarios que lo han pulsado y se actualiza
este número incrementándolo en uno.

Este es un ejemplo muy básico, por que si dos usuarios pulsan a la vez sobre el anuncio número dos
uno de ellos no se contará. Para evitar este problema se debería guardar el objeto que representa al
fichero dentro de una variable del objeto Application, y al ir a utilizar esta variable lanzar sobre el
objeto Application los métodos Lock y UnLock.

El fichero de planificación contiene información utilizada por el componente AdRotator para


administrar y mostrar las diversas imágenes de los anuncios. En él se especifican los detalles de los
anuncios como puede ser tamaño, imagen a utilizar, URL asociada, frecuencia de aparición de cada
imagen.

Este fichero tiene un formato determinado que se pasa a comentar a continuación. Se encuentra
dividido en dos secciones mediante un asterisco (*). La primera sección establece los parámetros
aplicables a todas la imágenes de los anuncios existentes, y la segunda sección especifica la
información de cada imagen individual y el porcentaje de tiempo de presentación de cada anuncio.

En la primera sección hay cuatro parámetros opcionales y globales, si no se especifica ninguno de


estos parámetros la primera línea del archivo debería empezar por un asterisco. El formato general del
fichero de planificación es el siguiente:

REDIRECT url
WIDTH ancho
HEIGHT alto
BORDER borde
*
Fichero que contiene la imagen
Url asociada
Texto alternativo
Número de impactos

La primera línea, REDIRECT url, indica el fichero (página ASP) que implementa el
redireccionamiento, es decir, indica cual es el fichero de redireccionamiento. Las tres líneas siguientes,
pertenecientes a la primera sección del fichero de planificación, hacen referencia al aspecto de la
imagen: dimensiones y borde.

En la segunda sección tenemos la ubicación del fichero gráfico que representa la imagen a mostrar, a
continuación está la URL a la que será enviado el navegador cuando el usuario pulse sobre la imagen,
esta URL será la dirección del anunciante, por ejemplo; si el anuncio no tiene ningún vinculo asociado
se deberá escribir un guión (-).

La siguiente línea contiene el texto alternativo que aparecerá cuando el navegador no esté habilitado
para cargar imágenes o bien el texto que aparecerá cuando el cursor del ratón se sitúe sobre la imagen,
el equivalente a la propiedad ALT de la etiqueta <IMG>. Por último se debe indicar el número relativo
de apariciones del anuncio.

72
© Grupo EIDOS 4. Componentes de Servidor

En el Código fuente 49 se va a mostrar y comentar un ejemplo de un fichero de planificación para un


componente Ad Rotator.

Redirect redirecciona.asp
BORDER 0
*
/cursoASP30/images/Anuncio1.gif
http://www.almagesto.com
Campus Virtual Almagesto
40
/cursoASP30/images/Anuncio2.gif
http://www.eidos.es
Sitio Web del Grupo EIDOS
30
/cursoASP30/images/Anuncio3.gif
http://www.eidos.es/algoritmo
Revista Algoritmo
30

Código fuente 49

En este ejemplo el fichero encargado de redireccionar al navegador a la dirección asociada a la imagen


que se ha pulsado es la página ASP redirecciona.asp. Al no especificarse las dimensiones de la
imagen, se tomarán los valores por defecto: WIDTH=440 y HEIGHT=60. Se han utilizado tres
imágenes, cuya frecuencia de aparición será: para la primera un 40% de las veces que se cargue la
página, para la segunda un 30% y para la tercera otro 30%.

El componente AdRotator además de estos dos ficheros especiales que ya se han comentado, posee
también tres propiedades y un método. Sus propiedades son las siguientes:

• Border: indica el tamaño del borde que rodea a la imagen del anuncio, cumple la misma
función que la línea BORDER del fichero de planificación.

• Clickable: indica si el anuncio es un hipervínculo o no, tendrá los valores True o False,
respectivamente.

• TargetFrame: esta propiedad especifica el marco de destino en el que se debe cargar el


vínculo. Esta propiedad realiza la misma función que el parámetro TARGET de una
instrucción HTML de creación de vínculos.

El único método que posee este componente ActiveX de servidor es el método GetAdvertisement. A
este método se le debe pasar como parámetro el nombre del fichero de planificación, y la función de
este método es la de obtener las especificaciones, a partir del fichero de planificación, para el siguiente
anuncio y le da formato de código HTML.

Para crear un componente AdRotator cuyo fichero de planificación se llama planificacion.txt se deberá
escribir el Código fuente 50.

<%Set anuncios=Server.CreateObject("MSWC.AdRotator")%>
<%=anuncios.GetAdvertisement("planificacion.txt")%>

Código fuente 50

73
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Con el método GetAdvertisement siempre debemos utilizar los delimitadores <%=%> o bien
Response.Write, para enviar al navegador el código HTML encargado de mostrar la imagen
correspondiente con su vínculo asociado.

Si tenemos en cuenta el código anterior y su fichero de planificación, una posible generación de


código HTML por parte del método GetAdvertisement, podría ser la que se muestra en el Código
fuente 51.

<A HREF="redirecciona.asp?url=http://www.eidos.es&image=images/Anuncio2.gif">
<IMG SRC="/cursoASP30/images/Anuncio2.gif" ALT="Sitio Web del Grupo EIDOS"
WIDTH=440 HEIGHT=60 BORDER=0></A>

Código fuente 51

En la Figura 11 se puede observar como se relaciona el componente AdRotator con todos los ficheros
que utiliza.

Figura 11

Componente Funciones del navegador


El componente de funciones del navegador (Browser Capabilities) permite consultar las funciones que
posee el navegador Web que ha cargado la página ASP.

Cuando un navegador se conecta a un servidor Web, le envía automáticamente un encabezado HTTP


llamado User Agent. Este encabezado es una cadena que identifica el explorador y su número de
versión.

El componente encargado de comprobar las funciones del navegador compara el encabezado User
Agent con las entradas de un fichero especial llamado Browscap.ini. Para crear una instancia de este
componente deberemos escribir lo que se muestra en el Código fuente 52.

74
© Grupo EIDOS 4. Componentes de Servidor

<%Set navegador=Server.CreateObject("MSWC.BrowserType")%>

Código fuente 52

El fichero Browscap.ini es un fichero de texto que asigna las propiedades del navegador atendiendo al
encabezado HTTP User Agent, este fichero se debe encontrar en el mismo directorio que la librería
que contiene al componente, es decir, el fichero BROWSCAP.DLL.

Si se encuentra coincidencia entre el encabezado User Agent y una entrada del fichero
BROWSCAP.INI, el componente asumirá las propiedades del navegador de la lista que coincida con
el el encabezado User Agent.

Si el componente no encuentra una coincidencia entre el encabezado User Agent y una entrada del
fichero BROWSCAP.INI, toma las propiedades predeterminadas del navegador Web. Pero si dentro
de BROWSCAP.INI no se han especificado estas propiedades predeterminadas se establecerá la
cadena UNKNOWN para las propiedades del navegador.

Dentro del fichero BROWSCAP.INI se pueden realizar definiciones de propiedades para diferentes
navegadores, también se pueden establecer valores predeterminados para utilizarlos si el navegador no
pertenece a ninguna de las definiciones existentes.

Para cada definición de navegador se debe proporcionar un valor del encabezado User Agent y las
propiedades y valores que se desea asociar a ese encabezado. El formato general de este fichero es el
siguiente:

;comentarios
[EncabezadoHTTPUserAgent]
parent = DefiniciónExplorador
propiedad1 = valor1

propiedadN = valorN
[Default Browser Capability Settings]
PropiedadPredeterminada1 = ValorPredeterminado1

PropiedadPredeterminadaN = ValorPredeterminadoN

Dentro de este fichero los comentarios se indicarán con punto y coma. El encabezado User Agent
puede contener comodines utilizando el carácter * para reemplazar cero o más caracteres. Por ejemplo
si escribimos el siguiente valor para el encabezado User Agent:

[Mozilla/2.0 (compatible; MSIE 3.0;* Windows 95)]

Coincidiría con los siguientes encabezados User Agent:

[Mozilla/2.0 (compatible; MSIE 3.0; Windows 95)]


[Mozilla/2.0 (compatible; MSIE 3.0; AK; Windows 95)]
[Mozilla/2.0 (compatible; MSIE 3.0; SK; Windows 95)]
[Mozilla/2.0 (compatible; MSIE 3.0; AOL; Windows 95)]

Si se dan varias coincidencias, el componente devolverá las propiedades de la primera definición


coincidente.

Mediante la propiedad parent, podemos utilizar dentro de una definición de un navegador todos los
valores de las propiedades de otro navegador cuyo nombre se indica en DefinicionExplorador. La

75
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

definición del navegador actual heredará todas las propiedades declaradas en la definición del
navegador principal. Esto es útil para definir propiedades de una nueva versión de un navegador, ya
que las nuevas versiones suelen conservar la mayoría de las propiedades de la versión anterior. Estos
valores de propiedades heredados se pueden sobreescribir estableciendo un nuevo valor para esa
propiedad.

El número de propiedades a definir puede ser cualquiera, las únicas restricciones es que el nombre de
la propiedad debe comenzar por un carácter alfabético y no puede tener más de 255 caracteres.
Normalmente para cada tipo de navegador solamente se definirán las propiedades que vayan a ser
utilizadas. El valor que se le asignará a la propiedad por defecto es una cadena, si se quiere asignar un
entero se deberá utilizar el signo de número (#) y para especificar un valor booleano se utilizará TRUE
o FALSE.

Con el valor de User Agent [Default Browser Capability Settings] se especifican las propiedades por
defecto de un navegador cuya cabecera User Agent no ha coincidido con ninguna de las especificadas
en el fichero BROWSCAP.INI.

Se debe realizar un mantenimiento del fichero BROWSCAP.INI para tenerlo actualizado con las
nuevas versiones de navegadores Web que van apareciendo. Existen un par de direcciones que ofrecen
la última versión de este fichero: http://www.cyscape.com/browscap y http://www.asptracker.com.

En el Código fuente 53 se crea un componente defunciones del navegador y se muestran por pantalla
algunas de las características del navegador del cliente. Si en el servidor Web tenemos el fichero
BROWSCAP.INI como se ve en el Código fuente 54.

<%Response.Write("La Cabecera USER_AGENT enviada por el navegador es: "&_


Request.ServerVariables("HTTP_USER_AGENT")&"<br>")
Set navegador=Server.CreateObject("MSWC.BrowserType")
if navegador.vbscript=TRUE Then Response.Write("Soporta VBScript<br>")
if navegador.cookies=TRUE Then Response.Write("Soporta Cookies<br>")
if navegador.beta=FALSE Then Response.Write("No es una beta<br>")
if navegador.javaapplets=TRUE Then Response.Write("Soporta Java<br>")
Response.Write("Plataforma: "&navegador.platform&"<br>")
Response.Write("Navegador: "&navegador.browser&navegador.version&"<br>")%>

Código fuente 53

;Internet Explorer 4.0


[IE 4.0]
browser=IE
Version=4.0
cookies=TRUE
vbscript=TRUE
javascript=TRUE
javaapplets=TRUE
ActiveXControls=TRUE
Win16=False
beta=False

[Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)]


parent=IE 4.0
Version=4.01
platform=Win95

Código fuente 54

76
© Grupo EIDOS 4. Componentes de Servidor

Si el navegador que se conecta es el Internet Explorer y envía la cabecera User Agent: Mozilla/4.0
(compatible; MSIE 4.01; Windows 95), el resultado de la ejecución del Código fuente 53 sería:

La Cabecera USER_AGENT enviada por el navegador es:


Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)
Soporta VBScript
Soporta Cookies
No es una beta
Soporta Java
Plataforma: Win95
Navegador: IE4.01

Pero si el navegador que se conecta envía otra cabecera diferente, el navegador no será reconocido y
por lo tanto todas las propiedades se establecerán como UNKNOWN, y la salida que devolverá la
ejecución del ejemplo anterior sería:

La Cabecera USER_AGENT enviada por el navegador es: Mozilla/4.03


[es] (Win95; I)
Plataforma: Unknown
Navegador: UnknownUnknown

Componente Nextlink
Este componente llamado también componente de vinculación de contenido, tiene como misión
gestionar un conjunto de direcciones URL para poder utilizarlas dentro de páginas ASP de forma
sencilla, permite tratar las páginas como las de un libro, es decir, ofrece la gestión de una navegación
entre las diferentes páginas ASP de una forma sencilla.

Se debe tener en cuenta que el componente de Vinculación de Contenido trata un sitio Web como si
fuera un libro que debe ser leído desde el principio al final. Sin embargo este no es el comportamiento
natural de un sitio Web, ya que su distribución no es lineal, por lo tanto esta herramienta puede ser
necesaria para guiar al usuario a través de un sitio Web, es decir es una herramienta lineal para un
espacio no lineal.

El componente Nextlink hace referencia a un fichero especial llamado lista de vínculos, este fichero
contiene una lista de las direcciones de las páginas vinculadas, este fichero se debe encontrar en el
servidor Web.

El fichero de lista de vínculos contiene una línea de texto por cada dirección URL de la lista. Cada
línea termina con un retorno de carro y cada elemento de una línea se separa mediante un tabulador.
Los vínculos a las páginas que aparece en este fichero deben estar en el orden deseado, como si fuera
un libro. El formato de este fichero es el siguiente:

URL-Página-Web descripción

No se admiten URLs absolutas, es decir, las que comienzan con "http:","//" o \\. Las URLs utilizadas
deben ser relativas con el formato nombreFichero o directorio/nombreFichero.

Cuando se debe modificar el orden o el número de las páginas de contenido, sólo es necesario
actualizar la lista de URLs que se encuentra dentro del fichero lista de vínculos y no se tiene que
actualizar los vínculos de exploración de cada página. Una ejemplo de un fichero de lista de vínculos
podría ser el siguiente:

77
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

intro.asp Introducción a ASP


cap1.asp VBScript
cap2.asp Objetos Integrados
cap3.asp Componentes ActiveX Server

Para tener acceso a las URLs que aparecen especificadas dentro del fichero lista de vínculos, el objeto
Nextlink ofrece una serie de métodos:

• GetListCount(fichero): devuelve el número de vínculos del fichero lista de vínculos.

• GetNextURL(fichero): obtiene la siguiente URL teniendo en cuenta la página actual. Si la


página actual no está especificada en dicho fichero, GetNextURL devuelve la dirección URL
de la última página de la lista.

• GetPreviousDescription(fichero): este método devuelve la descripción del elemento anterior


del fichero lista de vínculos, atendiendo a la página actual. Si la página actual no está en el
fichero, se devolverá la descripción del primer elemento de la lista.

• GetListIndex: devuelve el índice que corresponde a la página actual dentro del fichero lista de
vínculos. La primera página tendrá el índice 1. Si la página actual no existe dentro de la lista
de vínculos se devolverá 0.

• GetNthDescription(fichero, indice): devuelve la descripción correspondiente al elemento de la


lista de vínculos cuyo índice coincide con el índice que se le pasa como parámetro a este
método.

• GetPreviousURL(fichero): obtiene la URL anterior teniendo en cuenta ja página actual. Si la


página actual no está especificada en dicho fichero, GetPreviousURL devuelve la dirección
URL de la primera página de la lista de vínculos.

• GetNextDescription(fichero): este método devuelve la descripción del siguiente elemento del


fichero lista de vínculos, atendiendo a la página actual. Si la página actual no está en el
fichero, se devolverá la descripción del último elemento de la lista de vínculos.

• GetNthUTL(fichero, indice): devuelve la dirección URL correspondiente al elemento de la


lista de vínculos cuyo índice coincide con el índice que se le pasa como parámetro a este
método.

Todos estos métodos poseen un parámetro común que indica la ubicación del fichero lista de vínculos
con el que debe trabajar un componente de vinculación de contenido.

En el Código fuente 55 se muestra como se utilizaría este componente para indicar en un página un
enlace a la página siguiente, anterior y para volver a la primera página:

<div align="center">
<hr>
<%Set vinculos=Server.CreateObject("MSWC.NextLink")
listaVinculos="vinculos.txt"
If (vinculos.GetListIndex(listaVinculos) > 1) Then %>
<a href="<%=vinculos.GetPreviousURL(listaVinculos)%>">Página
anterior</a>&nbsp;
<%End If%>
<%If (vinculos.GetListIndex(listaVinculos)<vinculos.GetListCount(listaVinculos))
Then%>
<a href="<%=vinculos.GetNextURL(listaVinculos)%>">Página siguiente</a>&nbsp;

78
© Grupo EIDOS 4. Componentes de Servidor

<%End If%>
<a href="<%=vinculos.GetNthURL(listaVinculos,1)%>">Volver Inicio</a>
</div>

Código fuente 55

Para comprobar si es necesario crear un enlace a la página anterior se verifica que la página actual no
es la primera, esta comprobación se realiza en el primer If...Then..., y en el segundo If se comprueba si
es necesario crear un enlace a la página siguiente, para ello se comprueba si la página actual es la
última o no comparando el valor devuelto por el método GetListIndex con el devuelto por
GetListCount.

Para realizar el enlace a la página anterior se utilizará el método GetPreviousURL, para crear el enlace
a la página siguiente se utiliza GetNextURL y para el enlace de la página de inicio se utiliza el método
GetNthURL pasándole como parámetro el índice con valor 1.

Este código lo podemos escribir en una página llamada pie.asp y realizar un INCLUDE en las páginas
correspondientes para que muestren en la parte inferior esta sencilla barra de navegación. El aspecto
sería el de la Figura 12.

Figura 12

En el capítulo de introducción de la lista de vínculos del ejemplo nos puede interesar mostrar un índice
de todo el curso. En el Código fuente 56 se muestra como se crearía una tabla de contenidos a partir
del fichero lista de vínculos de un componente Nextlink.

<%Set vinculos=Server.CreateObject("MSWC.NextLink")
listaVinculos="vinculos.txt"
num=vinculos.GetListCount(listaVinculos)
i=1
%>
Índice:
<ul>
<%While (i<=num)%>
<li>
<a href="<%=vinculos.GetNthURL(listaVinculos,i)%>">
<%=vinculos.GetNthDescription(listaVinculos,i)%></a>

79
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

</li>
<%i=i+1%>
<%Wend%>

Código fuente 56

Para recorrer el fichero lista de vínculos es necesario obtener el número de URLs que existen en el
fichero, es decir, el número de enlaces, para ello utilizamos el método GetListCount, para acceder a
cada elemento del fichero deberemos utilizar un bucle While. Dentro de este bucle se lanza el método
GetNthURL, para obtener la URL cuyo índice se corresponde con el valor del contador del bucle,
también se lanza el método GetNthDescription para obtener la descripción correspondiente.

El aspecto que se correspondería con el ejemplo de fichero de vínculos anterior sería el de la Figura
13.

Figura 13

Componente Content Rotator


Este es uno de los componentes de servidor que se incluyó con la versión 2.0 de ASP. Su finalidad es
similar al componente Ad Rotator (rotador de anuncios) que ya habíamos comentado en este capítulo.

Pero este nuevo componente no permite sólo alternar diferentes imágenes con enlaces atendiendo a
una determinada frecuencia, sino que permite mostrar cualquier contenido HTML que irá apareciendo
atendiendo a la frecuencia establecida en el fichero de planificación correspondiente, al igual que
ocurría con la frecuencia asignada a las imágenes del componente AdRotator.

80
© Grupo EIDOS 4. Componentes de Servidor

El fichero de planificación para este componente es un fichero de texto que debe estar disponible en
una ruta virtual del servidor Web y puede incluir cualquier número de entradas de cadenas de
contenido HTML, por ello también se le denomina fichero de contenido.

Cada entrada se compone de dos partes: una línea que empieza por dos signos de porcentaje (%%)
seguida, de manera opcional, por un número entre 0 y 65.535 que indica el peso relativo de la cadena
de contenido HTML y unos comentarios acerca de la entrada.

La segunda parte de la entrada contiene la cadena de contenido HTML propiamente dicha (texto,
imágenes, hipervínculos...).

La sintaxis de estas entradas sería:

%% [#Peso] [//Comentarios]
CadenaContenido

En el Código fuente 57 se ofrece un ejemplo de fichero de planificación para el componente Content


Rotator.

%% 2 // Esta es la primera línea del fichero de contenidos.


<A HREF = "http://www.eidos.es"><img src="images/eidosportada.jpg"></A>
%% 4 // Segunda Línea.
<B>Cursos - Internet</B>

<UL>

<LI>Programación de aplicaciones para Internet con ASP.


<LI>Lenguaje HTML.
<LI>La Especificación CGI.
<LI>JavaScript.

</UL>

Código fuente 57

La probabilidad de que el objeto presente una determinada cadena de contenido se expresa como el
peso asociado a dicha cadena dividido por la suma de todos los pesos de las distintas entradas del
fichero de contenido.

Así, para el fichero de ejemplo, el componente Content Rotator presentará la primera cadena de
contenido una tercera parte del tiempo, y la segunda las dos terceras partes del tiempo.

Un peso 0 hará que se pase por alto la entrada, y si no se especifica peso para una entrada determinada
se asume que su peso es 1.

El componente Content Rotator, para recuperar las cadenas de contenido del fichero de planificación,
dispone de dos métodos:

• ChooseContent(fichero): recupera una cadena de contenido HTML del archivo de contenidos


y la presenta en la página actual. Este método recuperará una nueva cadena de contenido cada
vez que se cargue la página.

• GetAllContent(fichero): recupera todas las cadenas de contenido del archivo de contenidos y


las escribe directamente en la página como una lista con una etiqueta <HR> después de cada
entrada.

81
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En el Código fuente 58 vamos a ver un ejemplo de cómo se recuperaría el contenido del fichero
CONTENIDO.TXT, haciendo uso del método ChooseContent().

<%Set objContenido = Server.CreateObject("MSWC.ContentRotator")%>

<%=objContenido.ChooseContent("contenido.txt")%>

Código fuente 58

Cada vez que el usuario ejecute la secuencia de comandos anterior, se visualizará una de las dos
cadenas de contenido HTML incluidas en el fichero de planificación CONTENIDO.TXT, como se ve
en la Figura 14.

Figura 14

En el Código fuente 59 podemos ver otro ejemplo haciendo uso del método GetAllContent para
representar todas las cadenas de contenido incluidas en el fichero CONTENIDO.TXT.

<B>Cadenas de contenido HTML incluidas en "contenido.txt":</B>

<%Set objContenido = Server.CreateObject("MSWC.ContentRotator")%>

<%=objContenido.GetAllContent("contenido.txt")%>

Código fuente 59

El resultado de la ejecución de la secuencia de comandos anterior sería el mostrado en la Figura 15.

82
© Grupo EIDOS 4. Componentes de Servidor

Figura 15

Componente Pagecounter
Este componente nos permite llevar la cuenta del número de veces que una página Web ha sido
abierta. El componente guarda el número de visitas que se han realizado en una página determinada y
lo almacena periódicamente en un archivo de texto.

Al guardarse la cuenta de visitas por página en un fichero de texto, si por ejemplo se resetea el
servidor los datos no se perderán. Este fichero de texto se llama HITCNT.CNT y se encuentra en el
directorio c:\winnt\system32\inetsrv\data, un ejemplo de contenido de este fichero es:

21 /cursoasp30/pagecounter.asp
5 /cursoasp30/adrot.asp
0 adrot.asp
12 /cursoasp30/page/visitas.asp
3 /cursoASP30/page/adrot.asp
2 /cursoASP30/page/pagecounter.asp
0 /cursoASP30/page/Rec.asp

El fichero HITCNT.CNT se creará o actualizará cuando se ejecute el evento Application_OnEnd del


fichero GLOBAL.ASA de la aplicación ASP correspondiente.

Este componente también fue incluido con la versión 2.0 de ASP.

El componente contador de páginas se instancia de la forma que indica el Código fuente 60.

83
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%Set ObjetoContador = Server.CreateObject("MSWC.PageCounter")%>

Código fuente 60

Los métodos de los que dispone el objeto Page Counter son los que se comentan a continuación:

• Hits(rutapagina): devuelve un valor LONG representado el número de veces que se ha abierto


una página Web determinada. Este método puede recibir como parámetro la ruta de la página
a la que se le va a llevar la cuenta. Si se omite este parámetro el método devolverá el número
de visitas de la página actual.

• PageHit(): Incrementa el número de visitas a la página actual. Sintaxis.

• Reset(rutapagina): Pone a 0 el número de visitas de la página cuya ruta se le pasa como


parámetro. Si no se especifica ninguna página en el parámetro, se pondrá a 0 el número de
visitas de la página actual.

En el Código fuente 61 un ejemplo del uso de este componente en el que nos vamos a fabricar un
contador de visitas para la página actual. Guardaremos la página como CONTADOR.ASP:

<% Set Contador = Server.CreateObject("MSWC.PageCounter")%>


<b>Número de visitas:<%=Contador.Hits%>
<%Contador.PageHit 'incrementamos el contador en uno cada vez que se cargue la
página%>

Código fuente 61

En cualquier momento, desde otra página podremos conocer el número de visitas de la página anterior.
Suponiendo que dicha página se encuentra en la ruta virtual "/CursoASP30/pagecounter.asp",
visualizaríamos su número de visitas como se indica en el Código fuente 62.

<b>El número de visitas de la página Contador.asp es:


<%=Contador.Hits("/CursoASP30/pagecounter.asp")%>

Código fuente 62

La ruta que se indica en los métodos Hits() y Reset() del objeto PageCounter debe iniciarse siempre
con /, es decir, debe partir desde la raíz del sitio Web. Si queremos registrar las visitas realizadas a
todas las páginas ASP mediante este componente, podemos construirnos la página ASP que se muestra
en el Código fuente 63, que se utilizaría como un INCLUDE en todas las páginas.

<%Set Contador=Server.CreateObject("MSWC.PageCounter")
Contador.PageHit
Set Contador=Nothing%>

Código fuente 63

84
© Grupo EIDOS 4. Componentes de Servidor

Siguiendo este ejemplo vemos que sería muy fácil realizar una página ASP que contenga una tabla
informativa de las visitas que han recibido todas las páginas del Web con contadores de un sitio Web
determinado.

En este nuevo ejemplo del objeto Page Counter vamos a mostrar el número de visitas que han tenido
las páginas contenidas en un directorio determinado de nuestro sitio Web. Para ello vamos hacer uso
de los ya conocidos componentes de acceso al sistema de ficheros. Veamos el Código fuente 64 de
este página.

<%strDirectorio="/cursoASP30/page"
'creamos un objeto PageCounter
Set Contador = Server.CreateObject("MSWC.PageCounter")
'en esta mismo página también se registran sus visitas
Contador.PageHit
Set objFSO=Server.CreateObject("Scripting.FileSystemObject")
'obtnemos una refencia a la carpeta dónde están las páginas
Set objFolder=objFSO.GetFolder(Server.MapPath(strDirectorio))
'obtenemos los ficheros de la carpeta
Set colFiles=objFolder.Files
'se recorren los ficheros
For each objFile in colFiles
'se recupera el nombre para obtener después el nº de visitas
strNombreFichero=objFile.Name
Response.Write strNombreFichero&" --número de visitas: "&_
Contador.Hits(strdirectorio&"/"&strNombreFichero)&"<br>"
Next%>

Código fuente 64

Se supone que todas las páginas mostradas deben registrar mediante el método PageHit() que un
usuario a cargado la página, sino parecerá siempre cero en su número de visitas. En la Figura 16 se
puede observar la ejecución del código anterior.

Figura 16

85
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Componente Counters
Este componente nos permite crear, manipular y almacenar cualquier número de contadores
individuales a través de una instancia del mismo.

Sólo necesitamos crear un único objeto Counters en el sitio Web, a través del cual podremos crear los
contadores individuales que queramos. Para ello debemos crear el objeto Counters en el archivo
especial de aplicación ASP llamado GLOBAL.ASA, como se ve en el Código fuente 65, y guardarlo
en una variable de aplicación. Con lo cual dicho objeto persistirá durante toda la vida de la aplicación.

Sub Application_OnStart
Set ObjContador = Server.CreateObject("MSWC.Counters")
Set Application("Contador") = ObjContador
End Sub

Código fuente 65

También podemos crearlo con la etiqueta OBJECT, según puede verse en el Código fuente 66.

<object id="objContador" runat="server" scope="application"


progid="MSWC.Counters">

Código fuente 66

Los contadores que creemos a partir de este objeto Counters no se incrementan automáticamente al
cargar la página asp en la que han sido incluidos, sino que debemos hacerlo con el método
correspondiente, que en este caso es Increment.

Todos los contadores se almacenan en un archivo de texto del servidor Web y se utilizan con valores
enteros. Este fichero de texto se llama COUNTERS.TXT y se encuentra en el directorio
c:\winnt\system32\inetsrv\Data, un ejemplo de contenido de este fichero es:

contadorUno:16
contadorDos:0
Contador1:31
Contador2:1

Podremos manipular los contadores con los siguientes métodos que ofrece el objeto Counters:

• Get: Devuelve el valor actual del contador cuyo nombre se le pasa como parámetro. Si el
contador no existe, el método lo crea y lo pone a 0.

• Increment: Incrementa en un 1 el valor actual del contador cuyo nombre se le pasa como
parámetro, y devuelve el nuevo valor del contador. Si el contador no existe, el método lo crea
y lo pone a 1.

• Set: Recibe dos parámetros, el nombre del contador y un entero, asignando al contador el
valor entero y devolviendo el nuevo valor. Si el contador no existe, el método lo crea y le
asigna el valor entero.

86
© Grupo EIDOS 4. Componentes de Servidor

• Remove: Elimina de la colección Counters y el contador cuyo nombre se le pasa como


parámetro.

Veamos un ejemplo del uso de este componente en el que vamos a crear dos contadores "Contador1"
y "Contador2" a partir del objeto de aplicación Counters que anteriormente hemos creado en el archivo
GLOBAL.ASA mediante la etiqueta <OBJECT>. Ambos contadores se incrementarán cada vez que se
actualice la página. En el momento que el segundo contador alcance el valor 10 lo volvemos a poner a
0. Todo esto puede verse en el Código fuente 67.

<b>"Contador1": <%= objCounter.Increment("Contador1") %>


<br><b>"Contador2":<%=objCounter.Increment("Contador2")%>
<%If CInt(objCounter.Get("Contador2"))=10 Then
objCounter.Set "Contador2",0
End If%>

Código fuente 67

En cualquier momento, desde otra página ASP podremos conocer el valor de ambos contadores como
puede verse en el Código fuente 68.

<b>Valor del "Contador1": <%=objCounter.Get("Contador1") %>


<br><b>Valor del "Contador2":<%=objCounter.Get("Contador2")%>

Código fuente 68

Componente MyInfo
Este componente, que también fue novedad en la versión 2.0 de ASP, nos permite almacenar
información personal que será ofrecida por el administrador del sitio Web.

Esta información se cargará en las propiedades correspondientes del objeto MyInfo y podrá ser
recogida a través de secuencias de script de la forma: <%=MyInfo.Propiedad%>. Todas las
propiedades del objeto MyInfo devuelven una cadena. Con el componente MyInfo al igual que ocurría
con Counters, únicamente se suele crear una instancia a nivel de aplicación en el fichero
GLOBAL.ASA, como vemos en el Código fuente 69, o también como en el Código fuente 70.

Sub Application_OnStart
Set objMyInfo=Server.CreateObject("MSWC.MyInfo")
Set Application("objMyInfo")=objMyInfo
End Sub

Código fuente 69

<object id="objMyInfo" runat="server" scope="application" progid="MSWC.MyInfo">

Código fuente 70

87
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En cualquier momento podremos crear nuevas propiedades para el objeto MyInfo y asignarles un
valor. Por ejemplo, suponiendo que hemos creado el objeto con la etiqueta OBJECT, podríamos
utilizar el Código fuente 71.

<%objMyInfo.Nombre = "Angel"
objMyInfo.Apellidos = "Esteban Núñez"
objMyInfo.Correo = "aesteban@eidos.es"%>
Nombre: <%=objMyInfo.Nombre%><br>
Apellidos: <%=objMyInfo.Apellidos%><br>
Correo: <%=objMyInfo.Correo%>

Código fuente 71

Las tres nuevas propiedades que acabamos de crear se almacenarán de forma persistente junto con las
demás propiedades de MyInfo. Por lo que podemos hacer uso de las propiedades de este objeto para
almacenar valores que permanezcan constantes en todo el sitio Web.

Este objeto no tiene métodos, y las propiedades son las que le indiquemos nosotros.

La información que contiene el objeto MyInfo almacena en un fichero XML llamado myinfo.xml y se
encuentra en el directorio c:\winnt\system32\inetsrv\data. Un ejemplo de contenido de este fichero
podría ser el Código fuente 72.

<XML>
<Nombre>Angel</>
<Apellidos>Esteban Núñez</>
<Correo>aesteban@eidos.es</>
</XML>

Código fuente 72

Componente Tools
Este componente, componente de herramientas, proporciona una serie de utilidades a través de sus
métodos. Estos métodos son: FileExists, ProcessForm, Random, Owner y PluginExists. Hay que tener
en cuenta que los dos últimos métodos (Owner y PluginExists) únicamente han sido implementados en
Personal Web Server para Macintosh.

Este componente, también fue novedad en la versión 2.0 de ASP.

Vamos a describir los métodos del componente Tools:

• FileExists: Este método permite comprobar si el archivo cuya URL se le pasa como parámetro
está publicado en el sitio Web. Por tanto, las URLs que recibe como parámetro son relativas y
no absolutas. Si la URL existe dentro del directorio de publicación devolverá True, en caso
contrario devolverá False.

• ProcessForm: Este método es el más complejo del objeto Tools y procesa el contenido de un
formulario enviado por un visitante del sitio Web. Su sintaxis es:

88
© Grupo EIDOS 4. Componentes de Servidor

ObjetoTools.ProcessForm(URL_Archivo_Resultados,
URL_Archivo_Plantilla, [PuntoInserción])

Donde: URL_Archivo_Resultados: es la URL del archivo de salida en el que se han escrito los
datos procesados; URL_Archivo_Plantilla: es la URL del archivo que contiene las
instrucciones para procesar los datos y PuntoInserción: es un parámetro opcional que indicará
en qué lugar del archivo del resultados se han de insertar los datos procesados. El fichero
de salida puede ser una página ASP que se ejecutará en un navegador, el fichero de plantilla
puede ser también un página ASP pero no se ejecutará, sino que es copiado su código ASP al
fichero de salida, pero si utilizamos la sintaxis <%% %%>, al realizar la copia al fichero se
salida si se ejecutará el código ASP correspondiente.

• Random: Este método devuelve un número entero aleatorio entre -32768 y 32767.

• Owner: Este método devuelve True si el nombre y la contraseña enviados en el encabezado de


la petición coinciden con los del Administrador establecidos en la interfaz de Personal Web
Server. En caso contrario devuelve False. Hay que tener en cuenta que este método
únicamente han sido implementado en Personal Web Server para Macintosh.

• PluginExists: Comprueba la existencia de un complemento de servidor. Devuelve True si el


complemento está registrado y False en caso contrario. Igual que el método anterior, sólo ha
sido implementado en Personal Web Server para Macintosh.

Los métodos Random() y FileExists() son muy sencillos y los podemos observar en el Código fuente
73.

<%Set objTools=Server.CreateObject("MSWC.Tools")%>
<%=objTools.FileExists("versiones.asp")%><br>
<%=objTools.Random()%>

Código fuente 73

El método ProcessForm es algo más complicado, si tenemos el fichero de plantilla mostrado en el


Código fuente 74.

Nombre: <%%=Request.Form("nombre")%%><br>
Apellidos: <%%=Request.Form("apellidos")%%><br>
Edad: <%%=Request.Form("edad")%%><br>
Generado en: <%%=Now%%>

Código fuente 74

Al pasarle los datos de un formulario generará este otro fichero de salida:

Nombre: Angel<br>
Apellidos: Esteban Núñez<br>
Edad: 25<br>
Generado en: 05/06/2000 17:18:01

Veámoslo con un sencillo ejemplo. Al pulsar el botón del formulario se ejecutará el método
ProcessForm y escribirá los datos en el fichero de salida atendiendo al fichero que funciona como

89
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

plantilla. Además, en esta página a través de un objeto TextStream se muestra el contenido del fichero
de salida una vez ejecutado el método ProcessForm. Ver el Código fuente 75.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%Set objTools=Server.CreateObject("MSWC.Tools")%>
<form method="post" action="tools.asp">
Nombre:<input type="text" name="nombre" value="" size="20"><br>
Apellidos:<input type="text" name="apellidos" value="" size="40"><br>
Edad:<input type="text" name="edad" value="" size="2"><br>
<input type="submit" name="enviar" value="Enviar"><br>
</form>
<%If Request.Form("enviar")<>"" Then
objTools.ProcessForm "ficheroSalida.asp","plantilla.asp"%>
Se ha generado el siguiente fichero de salida:<br>
<%Set objFSO=Server.CreateObject("Scripting.FileSystemObject")
Set objTextStream=objFSO.OpenTextFile(Server.MapPath("ficheroSalida.asp"))
Response.Write(objTextStream.ReadAll)
objTextStream.Close
End if%>
</BODY>
</HTML>

Código fuente 75

Y en la Figura 17 se puede ver un ejemplo de ejecución.

Figura 17

90
© Grupo EIDOS 4. Componentes de Servidor

Si especificamos el parámetro punto de inserción se buscará la cadena especificada en el fichero de


plantilla y añadirá los datos a partir de esa coincidencia.

Componente PermissionChecker
Este componente nos va a permitir determinar si a un usuario se le ha dado permisos para acceder a un
fichero determinado. Este componente se basa en los mecanismos de autenticación de usuario de IIS,
es decir, usuario anónimo, autenticación básica y autenticación desafío/respuesta de Windows NT.

En el caso de que se encuentre activada la autenticación anónima, todos los usuarios iniciarán sus
sesiones con la cuenta de usuario anónimo de IIS. En este caso, al compartir todos los usuarios la
misma cuenta, El componente PermissionChecker no podrá autenticar usuarios de forma individual.

En el caso de aplicaciones en las que todos los usuarios tengan cuentas individuales, como en los sitios
Web dentro de una Intranet, es recomendable la desactivación de la autenticación anónima para que
PermissionChecker pueda identificar a cada usuario de forma individual.

Para denegar el acceso anónimo a una página Web, podemos usar uno de estos tres métodos:

• En la lista de control de acceso al archivo, excluir la cuenta del usuario anónimo a nivel de
NTFS.

• Desde el Administrador de servicios de Internet, en la hoja de propiedades de la seguridad de


directorio se desactiva el acceso anónimo. En el capítulo siguiente veremos en detenimiento la
configuración y administración del servidor Web Internet Information Server.

• Mediante un script se servidor similar al del Código fuente 76.

<%If Request.ServerVariables("LOGON_USER") = "" Then


Response.Redirect("NoAutorizado.htm")
End If%>

Código fuente 76

En este script comprobamos si la variable del servidor LOGON_USER de la colección


ServerVariables está vacía, en tal caso se tratará de la cuenta de usuario anónimo y le redireccionamos
a una página que le indica al usuario que no está autorizado para el acceso.

En el caso de sitios Web mixtos de Internet e Intranet, es recomendable activar la autenticación


anónima y al menos uno de los dos métodos de autenticación por contraseña, autenticación básica o
autenticación desafío/respuesta de Windows NT. De esta forma, si se deniega a un usuario el acceso
anónimo a una página, el servidor intentará autenticar al usuario mediante alguno de dos métodos
antes comentados.

A través de este componente podremos, incluso, controlar la navegación entre páginas. Es decir, para
un hiperenlace determinado podríamos comprobar si el usuario tiene permisos de acceso a la página
direccionada, y en caso negativo desactivar el hiperenlace.

La sintaxis de creación de este componente es:

91
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Set Objeto = Server.CreateObject("MSWC.PermissionChecker")

Este componente dispone de un solo método, llamado HasAccess, a través del cual vamos a poder
realizar el chequeo del acceso al un archivo especificado. Su sintaxis es:

objPermissionChecker.HasAccess("rutaArchivo")

Donde rutaArchivo podrá ser una ruta física o virtual a un archivo determinado. HasAccess()
devolverá un valor booleano indicando si el usuario Web tiene acceso al archivo. En el caso de que el
archivo no exista, devolverá False.

Veamos en el Código fuente 77 un ejemplo de utilización del componente, en el que chequeamos el


acceso del usuario a un archivo del Web y en caso afirmativo le proporcionamos un enlace para que
acceda al mismo.

<%Set objPermission=Server.CreateObject("MSWC.PermissionChecker")
If objPermission.HasAccess("/MiWeb/Privado.htm") Then%>
<A HREF= "/MiWeb/Privado.html"> Area Privada </A>
<%Else%>
Enlace no disponible.
<%End If%>

Código fuente 77

Si el usuario no se ha autenticado para acceder a la página ASP del ejemplo, nunca tendrá acceso al
enlace, aunque el usuario anónimo de Internet esté autorizado el método HasAccess() devolverá False.

92
Componentes de acceso a datos. ADO

Introducción
Los componentes de acceso a bases de datos son en realidad un conjunto de objetos que se agrupan
dentro de la denominación ActiveX Data Objects (ADO).

Mediante el conjunto de objetos ActiveX Data Objects vamos a tener acceso a bases de datos, es decir,
nos va a permitir realizar conexiones a bases de datos, ejecutar sobre ellas sentencias SQL,
procedimientos almacenados, obtener resultados, etc. Es decir, nos va a ofrecer desde nuestras páginas
ASP todas las operaciones que se suelen realizar sobre una base de datos.

Antes en entrar en los detalles de ADO vamos a comentar el API (Application Program Interface) en
el que se basa ADO. ActiveX Data Objects (ADO) nos permite desarrollar aplicaciones ASP para
acceder y manipular bases de datos a través de un proveedor OLE DB (Object Linking and Embedding
para bases de datos). ADO es la primera tecnología de Microsoft basada en OLE DB.

OLE DB es una especificación basada en un API construido con C++, por lo tanto se encuentra
orientado a objetos. OLE DB consiste en consumidores de datos y proveedores de datos. Los
consumidores toman los datos desde interfaces OLE DB, los proveedores ofrecen estos interfaces OLE
DB.

En algunos casos OLE DB puede acceder a los datos de forma más rápida que DAO y RDO, esto es
así debido a que DAO y RDO deben pasar a través de la capa ODBC y OLE DB se puede conectar
directamente a fuentes de datos relacionales con el proveedor correspondiente.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 18

OLE DB puede ser utilizado para extender la funcionalidad de proveedores de datos sencillos. Estos
objetos más especializados y sofisticados se denominan proveedores de servicio, permiten abstraer al
consumidor de datos del tipo de datos al que se accede y su localización.

Los proveedores de servicio son consumidores y proveedores, es decir, un proveedor de servicio puede
consumir interfaces OLE DB y construir una tabla sencilla para ese entrada, y entonces puede ofrecer
interfaces OLE DB para un aplicación cliente para que construya su salida en HTML, este ejemplo
sería nuestro caso, es decir una página ASP que usa ADO.

Debemos tener en cuenta que OLE DB es una especificación de bajo nivel. ADO encapsula el API de
OLE DB en un API de más alto nivel que proporciona dentro de su tecnología de componentes un
lenguaje neutral, que permite aprovechar todas las ventajas y características de OLE DB sin realizar
una programación a bajo nivel.

Ya hemos introducido el concepto de proveedor y consumidor en el que se basa OLE DB, el


consumidor es simplemente el que consume (utiliza) los datos y el proveedor el que ofrece esos datos.

Para acceder a una base de datos determinada debemos tener el proveedor OLE DB correspondiente.
De esta forma el acceso a una base de datos determinada viene condicionado por la existencia del
proveedor OLE DB correspondiente.

Para las bases de datos que no exista o no dispongamos de su proveedor OLE DB podemos utilizar el
proveedor OLE DB para ODBC, ya que ODBC se encuentra más difundido y es posible que exista el
driver ODBC correspondiente.

Es importante que no confundamos los proveedores con los drivers. En la Figura 19 se muestra un
esquema dónde se pueden distinguir el lugar que ocupan los drivers y los proveedores OLE DB.

Como se puede observar los proveedores se encuentran en la capa OLE DB y los drivers en la capa
ODBC. ADO 2.5 ofrece varios proveedores OLE DB por defecto. De todas formas existen más
proveedores OLE DB que son ofrecidos por otras empresas.

ADO permite crear aplicaciones ASP para acceder y manipular datos en una base de datos a través de
un proveedor OLE DB. Las principales ventajas de ADO son su velocidad, facilidad de uso, baja carga
de memoria y requieren poco espacio en el disco duro.

94
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

Figura 19

Una ventaja de ADO es que si ya lo hemos utilizado en Visual Basic 6, veremos que será
prácticamente igual utilizarlo en ASP, aunque eso sí, teniendo siempre en cuenta el entorno tan
particular en el que nos encontramos.

Las características que posee ADO para construir aplicaciones cliente/servidor y aplicaciones ASP son
las siguientes:

• Sus objetos se pueden crear de forma independiente fuera de su jerarquía o modelo de objetos.
No es necesario navegar a través de una jerarquía para crear un objeto, la mayoría de los
objetos ADO se pueden instanciar de forma independiente. Esto nos permite crear únicamente
los objetos que necesitamos.

• Ofrece soporte para realizar llamadas a procedimientos almacenados con parámetros de


entrada/salida.

• Diferentes tipos de cursores.

• Soporta resultados múltiples devueltos desde procedimientos almacenados.

Se debe tener en cuenta que aunque ADO aporte todas estas características, los proveedores y drivers a
los que llama ADO pueden no contemplar estas características. Por lo tanto antes debemos consultar la
documentación de los proveedores OLE DB que vamos a utilizar para informarnos de las
características que ofrecen.

ADO utiliza la característica pooling de conexiones de ODBC 3.0 para efectuar el acceso a las bases
de datos de forma más eficiente. Esta característica consiste en mantener abiertas conexiones de bases
de datos y administrar la compartición de conexiones entre diferentes solicitudes de usuarios para
mantener el rendimiento y reducir el número de conexiones inactivas.

En cada solicitud de conexión se determina si del grupo de conexiones hay una conexión inactiva, si la
hay, el grupo de conexiones devuelve esa conexión en lugar de efectuar una nueva conexión a la base
de datos.

Por defecto el pooling de conexiones está activado en ASP. Para desactivar esta característica se debe
establecer a cero la entrada de registro llamada StartConnectionPool.

95
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Esta característica es bastante útil para ASP debido al entorno en el que nos encontramos. Las
aplicaciones tradicionales de bases de datos suelen crear una única conexión con la base de datos que
se utiliza durante toda la duración de la aplicación. Sin embargo, debido a que en el entorno Web no se
puede mantener la información entre diferentes peticiones de páginas, una aplicación de bases de datos
en el entorno Web debe abrir y cerrar una conexión en cada página.

Además no es recomendable almacenar referencias a objetos de ADO a nivel de variables del objeto
Session o Application.

Modelo de objetos de ADO


Como se ha dicho anteriormente ADO es más complejo que el resto de los componentes ActiveX
Server que se incluyen con ASP. ADO presenta un completo modelo de objetos que se muestra en la
Figura 20.

Figura 20

Como se puede observar en la figura hay tres objetos que se diferencian del resto, es decir, los objetos:
Connection, Command y Recordset. Estos objetos son los principales dentro de ADO, y se pueden
crear fuera de la jerarquía de la figura, y se pueden manejar sin tener que interactuar con un
intermediario. Estos tres objetos se crearán mediante el método CreateObject del objeto Server, al
igual que hacíamos con el resto de los componentes de servidor, el resto de los objetos de ADO se
crean a partir de estos tres.

Desde el punto de vista práctico el objeto Recordset es el más importante, ya que es le que va a
permitir acceder a los registros de la base de datos, pero los objetos Connection y Command permiten
la creación de un objeto Recordset.

ASP 3.0 incluye la última versión de ADO, la versión 2.5, que incluye dos nuevos objetos: el objeto
Record y el objeto Stream. De todas formas ya adelantamos que desde ASP 3.0 todavía no se
encuentra implementada de forma satisfactoria estos dos objetos.

96
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

El objeto Connection
Un objeto Connection representa una conexión a una base de datos. Los objetos Connection se pueden
crear de forma independiente sin tener en cuenta ningún objeto definido previamente. Este objeto, al
igual que los objetos Command y Recordset, es creado mediante el método CreateObject del objeto
Server.

Las actividades que se pueden realizar con un objeto Connection son: creación de conexiones con
bases de datos, obtención de objetos Recordset sencillos y tratamiento de errores.

A continuación se van a enumerar y comentar de forma breve las diferentes propiedades, métodos y
colecciones que posee este objeto. Después se verán con más detalle las propiedades, métodos y
colecciones más importantes.

Lo que se ofrece a continuación no es una descripción detallada del objeto Connection, sino que es una
referencia rápida del mismo.

Las propiedades del objeto Connection son:

• Attributes: indica distintas características de la conexión por ejemplo de que forma se puede
iniciar una nueva transacción.

• ConnectionTimeout: tiempo de espera máximo para la realización de una conexión antes de


que se produzca un error.

• ConnectionString: cadena de conexión que identifica la base de datos a la que nos queremos
conectar.

• CommandTimeout: tiempo máximo de espera en la ejecución de un comando SQL que se


ejecuta de forma directa sobre la conexión.

• CursorLocation: indica la localización de los cursores que se van a crear a partir de la


conexión. pueden situarse en el cliente o en el servidor.

• DefaultDatabase: indica la base de datos por defecto con la que realizar la conexión.

• IsolationLevel: propiedad de sólo lectura que indica el nivel de aislamiento de la conexión.

• Mode: modo de acceso a la conexión.

• Provider: proveedor OLE DB utilizado para establecer la conexión.

• Version: propiedad de sólo lectura, indica la versión de ADO.

• State: propiedad de sólo lectura que indica el estado de la conexión, es decir, si se encuentra
abierta o cerrada.

Los métodos del objeto Connection no son tan numerosos y son los siguientes:

• BeginTrans: indica el inicio de un transacción.

• CommitTrans: almacena todos los cambios y finaliza la transacción actual, representa el


commit de una transacción.

97
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• RollbackTrans: cancela todos los cambios realizados durante la transacción actual y finaliza la
transacción, representa el rollback de una transacción.

• Close: cierra la conexión.

• Execute: ejecuta un comando SQL.

• Open: abre la conexión.

• Cancel: cancela la ejecución de una operación asíncrona de ejecución de una sentencia SQL o
de una apertura de una conexión.

• OpenSchema: obtiene un objeto Recordset con el esquema de la base de datos.

El objeto Connection posee dos colecciones:

• Errors: colección de errores generados a través del uso de ADO.

• Properties: una colección de objetos Property referentes al objeto Connection.

La función principal de un objeto Connection es la de especificar el origen de los datos, aunque


también se puede utilizar a veces para ejecutar sentencias SQL sencillas.

Abrir una conexión


Una vez creado un objeto Connection mediante la siguiente línea, podemos utilizarlo para establecer
conexiones con un proveedor de datos. Decimos proveedor de datos porque la conexión no tiene que
ser necesariamente a una base de datos, como vimos en el capítulo anterior ADO forma parte de la
estrategia universal de acceso a datos UDA, que permite acceder a diferentes tipos de datos.

Para realizar la conexión utilizaremos el método Open del objeto Connection, al que le podemos pasar
como parámetro la cadena de conexión de la base de datos (o de forma más general, del proveedor de
datos u origen de los datos). También es posible especificar la información de la conexión que se va a
realizar a través de la propiedad ConnectionString.

La sintaxis general del método Open es la siguiente:

objConexion.Open [CadenaConexion], [IDusuario], [contraseña],


[opciones]

Donde CadenaConexion es la cadena de conexión del origen de los datos, IDusuario es el nombre del
usuario que va a realizar la conexión, contraseña es la contraseña de ese usuario y opciones permite
indicar información adicional acerca de la conexión. Vamos comentar estos parámetros de forma
detallada.

Como se puede observar en la sintaxis del método Open todos los parámetros del mismo son
opcionales, ya que esta misma información puede ser facilitado por la propiedad ConnectionString.
Esta situación la veremos en muchos casos al trabajar con ADO, es decir, los parámetros de los
métodos se corresponden con propiedades del objeto sobre el que se lanzan.

El parámetro CadenaConexion puede contener los siguientes parámetros para la conexión:

• Provider: proveedor OLE DB que se va a utilizar.

98
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

• Data Source: el nombre del servidor dónde se encuentra la base de datos o del fichero fuente
de datos.

• Initial Catalog: el nombre de la base de datos.

• User id: el nombre del usuario que va a establecer la conexión.

• Password: contraseña del usuario.

• File Name: nombre del fichero que contiene la información del proveedor de la conexión.

• URL: la URL absoluta que identifica la carpeta o fichero que se va a utilizar como origen de
datos.

• DSN: nombre de la fuente de datos de ODBC que se va a utilizar.

Estos parámetros son exactos a los que puede contener la propiedad ConnectionString del objeto
Connection. Además se puede observar que el usuario y contraseña pueden estar contenidos en esta
cadena de conexión.

En el parámetro opciones se puede indicar una constante de ADO que aporta información adicional de
la conexión, esta información indica si la conexión va a ser síncrona o asíncrona, el valor de este
parámetro es adASyncConnect, que indicará que la conexión es asíncrona, sino se indica nada la
conexión será síncrona. En el entorno de ASP no tiene sentido declarar una conexión como asíncrona,
ya que ASP no puede recibir los eventos que genera ADO, por lo tanto en nuestras páginas ASP el
método Open nunca llevará el parámetro opciones.

Para utilizar las constantes de ADO se debe incluir el fichero de constantes correspondiente.

El fichero de constantes de ADO se llamada ADOVBS.INC y se encuentra en la ruta c:\Archivos de


programa\archivos comunes\system\ado. Para incluir el fichero de constantes en nuestras páginas
podemos copiarlo a nuestro sitio Web y realizar una referencia al mismo mediante la directiva
INCLUDE, como se puede observar en el Código fuente 78.

<!--#INCLUDE FILE="ADOVBS.INC"-->

Código fuente 78

Esta sentencia la deberemos repetir en todas las páginas en las que queramos utilizar las constantes de
ADO. Una alternativa más sencilla es hacer una referencia a la librería de tipos de ADO mediante la
etiqueta METADATA dentro del fichero GLOBAL.ASA, como se ve en el Código fuente 79.

De esta forma las constantes se encontrarán accesibles desde cualquier página ASP de la aplicación.

<!-- METADATA TYPE="typelib" FILE="c:\Archivos de programa\archivos


comunes\system\ado\msado15.dll"-->

Código fuente 79

99
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Aunque el nombre de la librería de tipos termine en 15, pertenece a la versión 2.5 de ADO. A
continuación se va a mostrar una serie de ejemplos que realizan conexiones de distintas formas.

En este primer ejemplo se realiza una conexión con el proveedor OLE DB de SQL Server. Además de
realizar la conexión se muestra el nombre del proveedor utilizado a través de la colección Properties y
la versión de ADO que se se ha utilizado para establecer la conexión. El código necesario es el que
aparece en el Código fuente 80.

<%Set Conex=Server.CreateObject("ADODB.Connection")
Conex.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"%>
<b>Versión de ADO: </b> <i><%=Conex.Version%></i><br>
<b>Proveedor: </b><i><%=Conex.Properties("Provider Friendly Name")%>
(<%=Conex.Properties("Provider Name")%>)</i><br>

Código fuente 80

Mediante esta cadena de conexión lo que se consigue es establecer una conexión con un servidor
llamado aesteban2 que tiene instalado SQL Server, el usuario conectado es sa y se conecta a la base de
datos pubs.

El resultado que aparece en el navegador es:

Versión de ADO: 2.5


Proveedor: Proveedor de Microsoft OLE DB para SQL Server
(sqloledb.dll)

Como se puede comprobar se muestra el nombre completo del proveedor (o nombre "amigable") y la
librería en la que se encuentra. Si no indicamos ningún proveedor OLE DB a la hora de establecer la
conexión por defecto se utiliza el proveedor de ODBC.

La conexión del ejemplo anterior la podríamos haber realizado de esta otra forma, como indica el
Código fuente 81. Y otra forma más aparece en el Código fuente 82.

Set Conex=Server.CreateObject("ADODB.Connection")
Conex.ConnectionString="Provider=SQLOLEDB;Data Source=aesteban2;"&_
"Initial Catalog=pubs;User id=sa"
Conex.Open

Código fuente 81

Conex.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs","sa"

Código fuente 82

También podemos hacer uso de otras propiedades del objeto Connection, como se puede ver en el
Código fuente 83, que no es nada más que otra versión distinta de nuestro ejemplo.

Conex.Provider="SQLOLEDB"

100
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

Conex.ConnectionString="Data Source=aesteban2;User id=sa"


Conex.Open
Conex.DefaultDatabase="pubs"

Código fuente 83

La propiedad DefaultDatabase, que indica la base de datos por defecto a la que nos conectamos, se
puede utilizar una vez que ya está establecida la conexión.

Como se puede comprobar, a la vista de las numerosas versiones de este ejemplo, el establecimiento
de una conexión con ADO es muy flexible.

Ahora se va a realizar una conexión utilizando el proveedor OLE DB de ODBC, como ya hemos dicho
este es el proveedor por defecto que se utiliza para el objeto Connection. Se va actuar de la misma
forma que para la conexión anterior, es decir, se van a mostrar distintas formas de establecer una
misma conexión.

Si suponemos que tenemos una fuente de datos ODBC (DSN de ODBC) de sistema definida en el
servidor Web, podemos emplear el Código fuente 84 para establecer la conexión.

<%Set Conex=Server.CreateObject("ADODB.Connection")
Conex.ConnectionString="DSN=FuenteCursos;User id=cursos;Password=xxx"
Conex.Open%>
<b>Versión de ADO: </b> <i><%=Conex.Version%></i><br>
<b>Proveedor: </b><i><%=Conex.Properties("Provider Friendly Name")%>
(<%=Conex.Properties("Provider Name")%>)</i><br>

Código fuente 84

En esta caso nos conectamos a una DSN de ODBC de sistema llamada FuenteCursos con el usuario
cursos que tiene la contraseña xxx. El resultado que se obtiene ahora es el siguiente:

Versión de ADO: 2.5


Proveedor: Microsoft OLE DB Provider for ODBC Drivers (MSDASQL.DLL)

También disponemos de diversas formas de establecer una misma conexión, vamos a ver unas cuantas
en el Código fuente 85.

Set Conex=Server.CreateObject("ADODB.Connection")
Conex.Open "DSN=FuenteCursos;User id=cursos;Password=xxx"
Conex.Open "DSN=FuenteCursos;UID=cursos;PWD=xxx"
Conex.Open "DSN=FuenteCursos","cursos","xxx"

Código fuente 85

Como se puede observar podemos utilizar indistintamente el parámetro PWD o Password para indicar
la contraseña, y el parámetro User id y UID para indicar el usuario.

Incluso se puede establecer la conexión a través de el proveedor OLE DB de ODBC sin necesidad de
crear la fuente de datos de ODBC en el servidor Web, es decir, se realiza la conexión con ODBC sin
DSN. Veámoslo en el ejemplo del Código fuente 86.

101
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Set Conex=Server.CreateObject("ADODB.Connection")
Conex.Open "driver=SQL Server;server=aesteban2;uid=cursos;pwd=xxx;database=Cursos"

Código fuente 86

En este caso se ha establecido una conexión a la base de datos Cursos que se encuentra en el servidor
aesteban2 y se ha utilizado el driver ODBC para SQL Server, ya que nuestra base de datos se
encuentra en un servidor SQL Server. Como se puede comprobar debemos utlizar un parámetro driver
para indicar el driver ODBC que se va a utilizar para establecer la conexión.

Ejecutar comandos sobre una conexión


Una vez establecida la conexión con un origen de datos determinados, ya podemos ejecutar consultas
o sentencias sobre la conexión. Aunque ya hemos comentado en el tema anterior y en este mismo, que
ADO permite manipular datos de cualquier tipo, vamos a centrarnos en el acceso a datos a un sistema
gestor de bases de datos relacional, en mi caso he utilizado SQL Server 7, pero el lector puede utilizar
el que estime conveniente.

Esta no es la labor principal del objeto Connection, aunque se suelen ejecutar sentencias SQL sencillas
directamente sobre la conexión para una mayor simplicidad del código o por comodidad del
programador.

Una vez establecida la conexión, podremos ejecutar sobre ella comandos SQL (sentencias SQL,
procedimientos almacenados, nombre de una tabla...), para ello utilizaremos el método Execute, que
posee la siguiente sintaxis general:

Set resultado = conexion.Execute(CommandText[, RegistrosAfectados,


Opciones])
connection.Execute CommandText[, RegistrosAfectados, Opciones]

Como se puede observar existen dos formas distintas de utilizar el método Execute. En la primera
línea se muestra de que forma se llamaría al método Execute cuando necesitamos almacenar su
resultado en un objeto Recordset y en la segunda línea cuando no es necesario almacenar su resultado.

El parámetro CommandText es una cadena que representa el comando SQL que se debe ejecutar, es
decir, esta cadena contendrá: la sentencia SQL o procedimiento almacenado a ejecutar o el nombre de
la tabla que se quiere recuperar.

En el parámetro opcional RegistrosAfectados se devolverá el número de registros a los que ha


afectado la operación. En el parámetro opcional Opciones se puede indicar mediante una constante de
ADO el tipo de comando que se encuentra en el parámetro CommandText, es recomendable utilizar
este parámetro para optimizar la evaluación y ejecución del comando correspondiente. Los valores que
puede tomar este parámetro aparecen en la Tabla 5.

Constante Descripción

adCmdText Evalúa el comando como una sentencia SQL.

102
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

adCmdTable Evalúa el comando como el nombre de una tabla.

adCmdStoredProc Evalúa el comando como un procedimiento almacenado.

adCmdUnkown El tipo del comando SQL es desconocido.

adCmdTableDirect Se evalúa como el nombre de una tabla

Se trata como un fichero que contiene un objeto Recordset que se


adCmdFile
había almacenado.

adExecuteNoRecords Se utiliza cuando los comandos no devuelven registros

Tabla 5

Por lo tanto, a través del método Execute podremos ejecutar diferentes tipo de comandos SQL, a
continuación se comentan mediante ejemplos algunos de los usos del método Execute.

En el Código fuente 87 se muestra como se almacenaría el contenido de una tabla en un objeto


Recordset, indicando de forma explícita que el comando SQL es el nombre de una tabla.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
Set objRecordset=objConexion.Execute("provincias",,adCmdTable)%>

Código fuente 87

Un resultado equivalente lo obtendríamos mediante la ejecución de una sentencia SQL, como se


muestra en el Código fuente 88.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
Set objRecordset=objConexion.Execute("Select * from provincias",,adCmdText)%>

Código fuente 88

Para ejecutar un procedimiento almacenado llamado borraDatos, haríamos lo del Código fuente 89.

<%Set objConexion=Server.CreateObject("ADODB.Connection")

objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
objConexion.Execute "borraDatos",,adCmdStoredProc%>

Código fuente 89

103
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En el Código fuente 90 se borran todas las provincias cuyo código empiece por '8', el número de
provincias borradas se recoge en el parámetro registrosBorrados:

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
objConexion.Execute "DELETE FROM Provincias WHERE IDProvin LIKE '8%'"_

,,registrosBorrados,adCmdText
Response.write("Provincias borradas:"&registrosBorrados) %>

Código fuente 90

Cerrar la conexión
Una vez que hayamos terminado de trabajar con la conexión la deberemos cerrar mediante el método
Close. La llamada a Close liberará los recursos del sistema asociados a la conexión, pero no eliminará
el objeto de la memoria, para eliminarlo de la memoria se deberá asignar al objeto el valor Nothing.

Cerrar una conexión mientras que existe una transacción en progreso producirá un error. Al lanzar el
método Close sobre una conexión también se cerrarán todos los objetos Recordset asociados a la
conexión, y también se vaciarán las colecciones Parameters de los objetos Command asociados a la
conexión y su propiedad ActiveConnection tendrá el valor Nothing.

El objeto Command
Este es el segundo objeto que se podía crear de forma independiente a la jerarquía de objetos ADO, y
además era la segunda forma de obtener un objeto Recordset. Un objeto Command permite realizar la
ejecución de un procedimiento almacenado de una base de datos, por lo tanto para poder utilizar el
objeto Command de esta forma, el proveedor de datos debe soportar procedimientos almacenados. A
través de este objeto podremos pasar parámetros a los procedimientos almacenados.

Pero este objeto no sólo permite realizar la ejecución de procedimientos almacenados, también se
puede utilizar para ejecutar sentencias SQL optimizadas. El objeto Command se va a comentar de la
misma forma que el objeto Connection, primero se enumerarán todas su propiedades, métodos y
colecciones y luego detallaremos los más importantes según vayamos avanzando en la explicación de
este objeto de ADO.

Las propiedades que posee este objeto son las que se enumeran a continuación.

• ActiveConnection: conexión a la que se encuentra asociado el objeto Command.

• CommanText: comando que va a contener el objeto Command, puede ser una sentencia SQL,
el nombre de una tabla o un procedimiento almacenado.

• CommandTimeout: tiempo máximo de espera para la finalización de la ejecución de un objeto


Command. Indica cuanto tiempo se esperará mientras se ejecuta un objeto Command antes de
terminar su ejecución y generar un error. Se expresa en segundos, su valor por defecto es de
30 segundos.

• CommandType: indica el tipo del objeto Command.

104
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

• Prepared: indica si se va a crear un sentencia "preparada" (prepared statement), es decir, una


sentencia precompilada, a partir del objeto Command antes de la ejecución del mismo.

• State: propiedad de sólo lectura que indica la situación actual del comando, si se está
ejecutando, si está cerrado o abierto.

• Name: permite identificar un objeto Command para luego ejecutarlo directamente desde el
objeto Connection asociado.

Los métodos del objeto Command son:

• CreateParameter: mediante este método crearemos un parámetro para el comando SQL a


ejecutar.

• Execute: ejecuta el objeto Command.

• Cancel: cancela la ejecución asíncrona de un comando. No es posible utilizarlo en páginas


ASP ya que no está permitida la ejecución asíncrona.

A continuación las colecciones del objeto Command:

• Parameters: esta colección contiene objetos Parameter que son cada uno de los parámetros que
va a tener el objeto Command.

• Properties: colección de propiedades, objetos Property. Tiene la misma función que en el


objeto Connection.

Crear un objeto Command


Como se ha dicho anteriormente, un objeto Command se puede crear de forma independiente, pero
debemos indicarle sobre que conexión se va a realizar la ejecución de ese comando. Para ello
utilizaremos la propiedad ActiveConnection, el valor asignado a esta propiedad puede ser un objeto
Connection ya creado o una cadena de conexión. En el Código fuente 91 vemos varios ejemplos con
las dos posibilidades.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion%>

<%Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=pepe;PWD=xxx"%>

Código fuente 91

Como se puede observar indicamos que el objeto Command va a utilizar una DSN de ODBC como
origen de los datos, ya sea a través de un objeto Connection ya creado y abierto o bien a través de la
cadena de conexión correspondiente.

En el Código fuente 92 se ofrece la creación de un objeto Command pero que se conecta a través del
proveedor OLE DB de SQL Server.

105
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion %>

<%Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="Provider=SQLOLEDB;Data Source=aesteban2;"&_
"Initial Catalog=pubs;User id=sa"

Código fuente 92

Lo más recomendable es utilizar en la propiedad ActiveConnection un objeto Connection, de esta


forma distintos objetos Command pueden compartir una misma conexión. Si utilizamos una cadena de
conexión en la propiedad ActiveConnection, cada objeto Command tendrá su propia conexión (objeto
Connection), lo que supondrá una mayor carga para el servidor.

Una vez que hemos indicado al objeto Command la conexión a la base de datos, le debemos indicar el
comando que deseamos que contenga a través de la propiedad CommandText, además, podemos
indicar el tipo de comando que se va a ejecutar a través de la propiedad CommandType, los valores de
esta propiedad son las constantes que ya vimos dentro del parámetro CommandText del método
Execute del objeto Connection.

De esta forma si queremos crear un comando que ejecute una sentencia SQL INSERT deberemos
escribir el Código fuente 93.

<%Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=pepe;PWD=xxx"
objComando.CommandType=adCmdText
objComando.CommandText="INSERT INTO Provincias VALUES ('aa','Soria')"%>

Código fuente 93

El tiempo máximo que vamos a esperar a que un comando finalice su ejecución se indica en segundos
a través de la propiedad CommandTimeout, cuando la ejecución de un comando supera este tiempo, se
abortará su ejecución y se producirá un error. El valor de CommandTimeout dependerá del tráfico de
la red, si es alto tendremos que aumentar el valor de esta propiedad. Esta propiedad del objeto
Command permite sobreescribir el valor de la propiedad del objeto Connection para un objeto
Command determinado.

Mediante la propiedad Prepared indicaremos si el objeto Command contiene una sentencia SQL
precompilada. Si esta propiedad tiene el valor True, antes de ejecutar el comando se enviará a la base
de datos la sentencia para que sea compilada (sentencia preparada), esto podría ralentizar la primera
ejecución del objeto Command, pero una vez que el proveedor de datos a compilado el contenido del
comando, el proveedor utilizará la versión compilada del comando en las sucesivas ejecuciones, por lo
tanto ganaremos en eficiencia. Es recomendable asignar a Prepared el valor True cuando tengamos
que ejecutar varias veces el objeto Command con una misma sentencia SQL.

106
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

La colección Parameters
Como se ha comentado al comienzo de este capítulo, las funciones del objeto Command eran permitir
la ejecución de procedimientos almacenados y ejecución se sentencias SQL optimizadas
(precompiladas o preparadas), por lo tanto debe permitir realizar el paso de parámetros a la sentencia o
procedimiento SQL que se vaya a ejecutar.

Un objeto Command almacena los parámetros que va a necesitar para su ejecución en una colección
llamada Parameters, esta colección está constituida por objetos Parameter. Por lo tanto antes de
ejecutar un comando deberemos crear sus parámetros y añadírselos a la colección Parameters.

Para crear un parámetro utilizaremos el método CreateParameter del objeto Command, al lanzar este
método obtenemos un objeto Parameter, la sintaxis general de este método es la siguiente:

Set parametro=objComando.CreateParameter([nombre][, tipo][,


direccion][, tamaño][, valor])

Donde parametro es un objeto Parameter en el que se va a almacenar el parámetro creado como


resultado de lanzar este método, objComando es el objeto Command al que se le ha asociado ese
parámetro, los siguientes argumentos son todos opcionales: nombre es una cadena que va a representar
el nombre del parámetro (a través de este nombre se podrá hacer referencia al parámetro dentro de la
colección Parameters), tipo indica el tipo de dato del parámetro, direccion indica si el parámetro es de
entrada o de salida, tamaño indica el tamaño máximo del parámetro y valor es el valor para ese objeto
Parameter creado.

Como se ha podido comprobar, podremos crear un objeto Parameter con todas sus propiedades
especificadas en el método CreateParameter, pero también podremos lanzar el método
CreateParameter sin especificar ninguna de estas propiedades (se debe recordar que todos los
argumentos del método CreateParameter eran opcionales) y luego, a partir del objeto Parameter creado
podremos asignar valores a sus propiedades de forma directa.

Las propiedades que posee el objeto Parameter son las siguientes:

• Name: nombre con el que se identifica al parámetro dentro de la colección Parameters. Este
nombre no tiene porque coincidir con el nombre del parámetro correspondiente en el
procedimiento almacenado de la base de datos.

• Type: tipo de dato del parámetro.

• Direction: indica la naturaleza (dirección) del parámetro.

• Size: es el tamaño del parámetro.

• Value: valor que se asigna al parámetro.

• NumericScale: indica el número de decimales soportado por el parámetro.

• Precision: número máximo de dígitos del parámetro.

• Attributes: es una combinación de ciertas constantes que indican otras características del
parámetro. Estas constantes son adParamNullable (el parámetro acepta valores nulos),
adParamSigned (el parámetro acepta valores con signo) y adParamLong (el parámetro acepta
valores de datos grandes).

107
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En el Código fuente 94 se muestran las dos posibilidades, es decir, se indican las características del
parámetro a través del método CreateParameter y por otro lado se utilizan las propiedades del objeto
Parameter para asignarles los valores correspondientes. En el ejemplo se pretende crear un parámetro
de entrada llamado nombre de tipo char, de tamaño máximo 20 y cuyo valor es "Pepe".

<%Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=jose;PWD=xxx"
objComando.CommandType=adCmdText
objComando.CommandText="INSERT INTO Usuarios VALUES (?)"
Set parametro=objComando.CreateParameter("nombre",adChar,adParamInput,20,"Pepe")%>

<%Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=jose;PWD=xxx"
objComando.CommandType=adCmdText
objComando.CommandText="INSERT INTO Usuarios VALUES (?)"
Set parametro=objComando.CreateParameter
parametro.Name="nombre"
parametro.Type=adChar
parametro.Direction=adParamInput
parametro.Size=20
parametro.Value="Pepe" %>

Código fuente 94

Como se puede observar la primera forma de crear un parámetro y establecer sus propiedades es
mucho más cómoda. También podemos apreciar que en la sentencia SQL que contiene el objeto
Command, el parámetro se corresponde con el signo de interrogación (?), este es el caracter especial
que indica en que lugar se va a situar el parámetro.

Una vez creado el parámetro y establecidas sus propiedades lo deberemos añadir a la colección
Parameters del objeto Command correspondiente, para ello esta colección ofrece el método Append.
El orden en que se añaden los objeto Parameter a la colección Parameters es significativo, se irán
sustituyendo los signos de interrogación (?) de la sentencia o los parámetros del procedimiento
almacenado según se vayan añadiendo los parámetros a la colección. La sintaxis del método Append
es:

objCommand.Parameters.Append objParametro

La colección Parameters, además del método Append ofrece dos métodos más: Delete y Refresh. El
método Delete elimina el objeto Parameter de la colección Parameters, cuyo índice o nombre se pasa
como argumento, la sintaxis de este método es:

objCommand.Parameters.Delete {índice|nombre}

El método Delete se suele utilizar para una vez ejecutado un objeto Command, eliminar sus
parámetros para volver a utilizarlo con otros valores.

El método Refresh realiza una llamada al origen de los datos para obtener todos los datos
correspondientes a los parámetros que necesita un objeto Command determinado. El método Refresh
obtiene los nombres, tipos de datos, naturaleza y longitud de los parámetros y con esta información
rellena la colección Parameters por nosotros, únicamente deberemos indicar los valores de los
parámetros, no deberemos utilizar el método Append.

Sin embargo es recomendable construir la colección Parameters de forma manual, sin realizar la
llamada al método Refesh, ya que esto último resulta más costoso para el origen de los datos.

108
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

La colección Parameters posee dos propiedades:

• Count: devuelve el número de objetos Parameters presentes en la colección Parameters.

• Item: es la propiedad por defecto de la colección Parameters y devuelve el objeto Parameters


cuyo nombre o índice se le pasa como argumento.

Ejecutar un comando
Cuando se hayan facilitado al objeto Command todos sus parámetros ya estaremos en disposición de
ejecutar el comando mediante el método Execute. Una vez ejecutado el objeto Command, si deseamos
volver a utilizarlo con otros parámetros será necesario eliminar los parámetros existentes dentro de la
colección Parameters, para ello se utiliza el método Delete (como ya comentamos en el apartado
anterior).

Como se ha dicho con anterioridad para ejecutar un objeto Command se utiliza el método Execute,
que presenta la siguiente sintaxis:

Set objRecordset=objComando.Execute(RegistrosAfectados,
Parámetros, Opciones)
objComando.Execute RegistrosAfectados, Parámetros, Opciones

En el caso de que la ejecución del objeto Command devuelva un conjunto de filas y columnas
(resultado de una SELECT) lo podremos almacenar dentro de un objeto Recordset que podremos
recorrer para mostrar la información. Como ya se comentó anteriormente, el objeto Command ofrecía
el segundo mecanismo (el primer mecanismo lo ofrecía el objeto Connection) para obtener un objeto
Recordset.

Los argumentos son todos opcionales, RegistrosAfectados es un entero que se devuelve y que indicará
el número de registros que se han visto afectados por la ejecución del comando, Parametros es un
array de con los valores de los parámetros, se utiliza en el caso de que no hayamos añadido a la
colección Parameters los parámetros correspondientes, Opciones indica que tipo de comando se quiere
ejecutar, se utilizará si ya no lo hemos indicado en la propiedad CommandType del objeto Command,
además este parámetro si se especifica sobreescribe al valor indicado en la propiedad CommandType.

En el Código fuente 95 se muestra como se ejecutaría una sentencia SQL que realiza una actualización
sobre una tabla a partir de los parámetros que se le pasen.

<!--#INCLUDE VIRTUAL=/ADO/ADOVBS.INC-->
<%
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=pepe;PWD=xxx"
objComando.CommandType=adCmdText
objComando.CommandText="UPDATE Provincias SET Provincia=?"&_
"WHERE IDProvin=?"
Set
parametro1=objComando.CreateParameter("nombre",adChar,adParamInput,20,"Cáceres")
Set parametro2=objComando.CreateParameter("clave",adChar,adParamInput,2,"10")
objComando.Parameters.Append parametro1
objComando.Parameters.Append parametro2
objComando.Execute
%>

Código fuente 95

109
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En el Código fuente 96 se realiza una llamada a un procedimiento almacenado que realiza un alta
sobre la tabla de provincias.

<!--#INCLUDE VIRTUAL=/ADO/ADOVBS.INC-->
<%
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection="DSN=FuenteBD;UID=pepe;PWD=xxx"
objComando.CommandType=adCmdStoredProc
objComando.CommandText="altaProvincia"
Set parametro1=objComando.CreateParameter("clave",adChar,adParamInput,2,"90")
Set
parametro2=objComando.CreateParameter("nombre",adChar,adParamInput,20,"Santander")
objComando.Parameters.Append parametro1
objComando.Parameters.Append parametro2
objComando.Execute
%>

Código fuente 96

Como se puede apreciar, en el caso de realizar la ejecución de un procedimiento almacenado, no se


indican los parámetros con signos de interrogación, únicamente se debe proporcionar el nombre del
procedimiento almacenado que queremos utilizar, y los parámetros se situarán según esté definido el
procedimiento en la base de datos. Es responsabilidad del programador conocer la definición del
procedimiento almacenado y por lo tanto proporcionar los parámetros de forma adecuada.

La forma de obtener un objeto Recordset a partir de la ejecución de un objeto Command es muy


sencilla. A la hora de ejecutar el objeto Command con el método Execute podremos guardar su
resultado en un objeto Recordset, de la misma forma que ocurría al ejecutar un sentencia SQL sobre
un objeto Connection.

El Código fuente 97 trata de una página ASP que realiza una SELECT sobre la tabla de provincias y
guarda el resultado en un objeto Recordset para su futura inspección.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion
objComando.CommandType=adCmdText
objComando.CommandText="SELECT * FROM Usuarios"
Set objRecordset=objComando.Execute%>

Código fuente 97

En el Código fuente 98 se crea un Recordset con la información devuelta por un procedimiento


almacenado, en este caso, el procedimiento devuelveDomicilio devuelve una relación de los nombres
de usuario junto con su domicilio.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "DSN=FuenteBD;UID=pepe;PWD=xxx"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion
objComando.CommandType=adCmdStoredProc

110
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

objComando.CommandText="devuelveDomicilio"
Set objRecordset=objComando.Execute%>

Código fuente 98

El objeto Recordset
Este es el tercero de los objetos principales del modelo de objetos de ADO. Como se ha visto
anteriormente, un objeto de este tipo se obtiene de forma automática a partir de la ejecución de un
objeto Connection o un objeto Command. Pero el objeto Recordset que se obtiene es muy sencillo y
limitado, representa un cursor de sólo lectura y que únicamente permite un desplazamiento hacia
delante. Un objeto Recordset se puede crear de forma independiente, es decir, sin depender de ningún
objeto Connection o Command.

Un objeto Recordset es un objeto tabular que contiene datos. Los valores se encuentran en las filas y
los nombres de los campos en las columnas. Cada fila es un registro completo. Es importante destacar
que un objeto Recordset es una representación de los datos, pero no son los datos almacenados. En un
objeto Recordset se deben determinar tres cosas: el lugar en el que se encuentra, es decir, la fuente de
los registros que contiene, las capacidades de navegación y la conexión con los datos. Un cursor
determina el comportamiento de un objeto Recordset.

Por lo tanto, si queremos disponer de un cursor más potente, deberemos crear un objeto Recordset. al
igual que creábamos un objeto Connection o Command, y a través de sus propiedades definir
exactamente las características del Recorset. A continuación se pasa a mostrar de forma resumida
todas las propiedades, métodos y colecciones que posee este objeto.

El objeto Recordset posee un gran número de propiedades:

• PageSize: número de registros del Recordset que se van a encontrar dentro de una página
lógica.

• AbsolutePage: número de página del registro actual. Para movernos a una página determinada
le asignaremos a esta propiedad el número de página correspondiente.

• AbsolutePosition: especifica la posición ordinal del registro actual dentro de un Recordset.

• PageCount: indica el número de páginas lógicas que posee un objeto Recordset.

• ActiveConnection: indica la conexión a la que esta asociado el objeto Recordset. Esta


propiedad es sólo de lectura en el caso de que la propiedad Source tenga un valor válido.

• ActiveCommand: indica el objeto Command asociado con el objeto Recordset, si es que se ha


utilizado uno para crear el Recordset. Esta propiedad es de sólo lectura.

• Source: indica la procedencia de los datos que contiene el Recordset, puede ser un objeto
Command, una sentencia SQL, un nombre de una tabla , un procedimiento almacenado, una
dirección de Internet, etc. Esta propiedad es de lectura/escritura si el Recordset está cerrado, y
sólo de lectura si está abierto.

• DataMember: especifica el nombre de un miembro de datos del que obtener datos, este
miembro de datos pertenece al origen de datos especificado en la propiedad DataSource.

111
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• DataSource: especifica un objeto que contiene datos que pueden ser representados como un
objeto Recordset.

• Index: devuelve en una cadena el nombre del índice que se está utilizando actualmente.

• CursorLocation: indica la localización del motor del cursor, puede encontrarse en el cliente
(adUseClient) o en el servidor (adUseServer).

• MarshalOptions: indica los registros que deben ser enviados al servidor.

• Sort: especifica el nombre del campo o campos por el que se encuentra ordenado el objeto
Recordset, así como el orden.

• State: indica el estado del Recordset, si se encuentra abierto (adStateOpen) o cerrado


(adStateClosed).

• LockType: indica el tipo de bloqueo que se aplicará al objeto Recordset.

• CursorType: indica el tipo de cursor que se utilizará en el Recordset.

• Bookmark: guarda una posición determinada dentro de un Recordset para volver a ella en otro
momento.

• Status: indica el estado del registro actual.

• Filter: indica que se va a realizar un filtro sobre el Recordset.

• CacheSize: indica el número de registros que se encuentran en la memoria.

• EditMode: indica el proceso de edición del registro actual. Es de sólo lectura.

• MaxRecords: indica el número máximo de registros que debe contener un Recordset como
resultado de una consulta. Se utiliza esta propiedad para limitar el número de registros
devueltos por el proveedor desde una fuente de datos.

• RecordCount: devuelve el número de registros de un objeto Recordset.

• BOF: indica si la posición actual se encuentra antes del primer registro de un Recordset.

• EOF: indica si la posición actual se encuentra después del último registro de un Recordset.

Los métodos de estos objeto también son numerosos, y son los que aparecen a continuación:

• Open: este método abre un cursor que va a representar los registros resultantes de la
realización de un comando SQL.

• Close: cierra el cursor, perdiendo todos los datos asociados.

• CompareBookmarks: compara dos Bookmark y devuelve el resultado de la comparación.

• Move: la posición actual se desplaza a la posición indicada.

• GetString: devuelve el Recordset completo dentro de una cadena.

112
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

• MoveNext: el siguiente registro en un objeto Recordset pasa a ser el actual.

• MovePrevious: el registro anterior pasa a ser el registro actual.

• MoveFirst: el primer registro dentro de un objeto Recordset especificado, pasa a ser el registro
actual.

• MoveLast: el último registro dentro de un objeto Recordset especificado, pasa a ser el registro
actual.

• NextRecordset: elimina el Recordset actual y se desplaza al siguiente. Esto tiene sentido


cuando el comando SQL que se ha ejecutado, y cuyo resultado contiene el objeto Recordset,
está compuesto de varios resultados.

• AddNew: crea un nuevo registro en un objeto Recordset actualizable.

• Delete: borra el registro actual o grupo de registros.

• Find: busca en el Recordset un registro que coincida con el criterio especificado.

• Update: almacena todos los cambios realizados sobre el registro actual.

• CancelUpdate: cancela los cambios realizados sobre el registro actual o sobre un nuevo
registro sobre el que todavía no se ha lanzado el método Update.

• UpdateBatch: almacena todos los cambios pendientes de diferentes registros.

• CancelBatch: cancela todos los cambios pendientes de diferentes registros.

• GetRows: devuelve los registros de un Recordset dentro de un array de dos dimensiones.

• Supports: indica si el objeto Recordset soporta una función determinada.

• Clone: crea una copia de un objeto Recordset existente.

• Requery: actualiza los datos de un Recordset volviendo a ejecutar el comando correspondiente


que creó el objeto.

• Resync: refresca los datos en el Recordset actual.

• Save: almacena el Recordset en un fichero.

• Seek: localiza un valor dentro del Recordset.

• Supports: indica si un objeto Recordset soporta la funcionalidad específica que se le pasa


como argumento a este método.

El objeto Recordset posee dos colecciones:

• Fields: esta colección está formada por objetos Field. Cada objeto Field representa una
columna del Recordset, es decir, un campo.

• Properties: esta colección es como la que poseían los objetos Connection y Command.

113
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Creación y apertura de un objeto Recordset


Debido a las enormes posibilidades de este objeto, sólo se comentarán aquí las operaciones básicas
para abrir y cargar con datos un objeto recordset.

Vamos a comenzar este apartado comentando el Código fuente 99.

<HTML>
<HEAD>
<TITLE>Ejemplo objeto Recordset</TITLE>
</HEAD>
<BODY>
<%Set objRecordset=Server.CreateObject("ADODB.Recordset")
objRecordset.Source="SELECT * FROM Usuarios"
objRecordset.CursorType=adOpenStatic
objRecordset.ActiveConnection="DSN=FuenteBD;UID=pepe;PWD=xxx"
objRecordset.Open%>
<center><strong>
Número de registros: <%=objRecordset.RecordCount%><br><br>
</strong></center>
<table border="1" align="center">
<tr>
<th>DNI</th>
<th>Nombre</th>
<th>Domicilio</th>
<th>Código Postal</th>
</tr>
<%while not objRecordset.EOF%>
<tr>
<td><%=objRecordset("DNI")%></td>
<td><%=objRecordset("Nombre")%></td>
<td><%=objRecordset("Domicilio")%></td>
<td align="right"><%=objRecordset("Codigo_Postal")%></td>
</tr>
<%objRecordset.MoveNext
Wend
objRecordset.Close
Set objRecordset=Nothing%>
</table>
</body>
</html>

Código fuente 99

En este ejemplo se ha creado un objeto Recordset y se han manipulado tres propiedades del mismo. A
la propiedad CursorType, es decir, la propiedad que indica el tipo de cursor que va a poseer el
Recordset, se le ha asignado el valor adOpenStatic. De esta forma podremos utilizar una serie de
características del Recordset. En este caso se ha utilizado la propiedad RecordCount que devuelve el
número de registros que existen dentro un Recordset, además, este tipo de cursor permite un
desplazamiento completamente libre sobre el Recordset.

La propiedad Source indica la procedencia de los datos del Recordset, el valor de esta propiedad es un
comando SQL válido, como puede ser el nombre de una tabla, una sentencia SQL o el nombre de un
procedimiento almacenado, también se le puede asignar un objeto Command. En este ejemplo a esta
propiedad se le ha asignado una cadena que representa un sentencia SQL, en este caso una SELECT.

En la propiedad ActiveConnection se le indica al Recordset la conexión a la que se encuentra


asociado. A esta propiedad se le puede pasar un objeto Connection ya creado, o bien, una cadena de

114
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

conexión, como ocurre en este ejemplo. Lo normal es asignarle un objeto Connection, ya que de esta
forma se puede reutilizar el mismo objeto Connection para distintos objetos Recordset, esto mismo
ocurría con la propiedad ActiveConnection del objeto Command, que vimos anteriormente.

Una vez definidas las propiedades del Recordset, nos disponemos a obtener los datos, para ello se
utiliza el método Open. Al lanzar este método el Recordset pasará a contener los datos indicados por la
propiedad Source.

En este caso el método Open se ha utilizado sin parámetros, ya que las características del objeto
Recordset ya las hemos definido a través de sus propiedades.

La sintaxis general del método Open es la siguiente:

ObjetoRecordset.Open OrigenDatos, Conexion, TipoCursor,


TipoBloqueo, Opciones

Todos los parámetros son opcionales y se deberán especificar si no hemos dado algún valor a alguna
de las propiedades del Recordset que representa cada uno de ellos. De esta forma, OrigenDatos es el
valor que se le asignaría a la propiedad Source, Conexion representa el valor de la propiedad
ActiveConnection, TipoCursor se corresponde con la propiedad CursorType, TipoBloqueo con la
propiedad LockType y Opciones indica la naturaleza del origen de los datos.

A continuación se ofrecen varias formas que tenemos de obtener un objeto Recordset, en todos los
casos se pretende que el contenido del objeto Recordset sea la tabla de autores (authors), y en todos los
casos se va utilizar un cursor Forwar-only/Read-only.

Lo podemos obtener a partir de la ejecución directa sobre un objeto Connection, como se ve en el


Código fuente 100.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"
Set objRecordSet=objConexion.Execute("Select * from authors",,adCmdText)%>

Código fuente 100

También a partir de la ejecución directa de un objeto Command, como en el Código fuente 101.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion
objComando.CommandText="devuelveAutores"
Set objRecordSet=objComando.Execute(,,adCmdStoredProc)%>

Código fuente 101

De momento no hemos utilizado el método Open, pero en el Código fuente 102 sí que lo vamos a
utilizar. Se trata de obtener el Recordset a partir del objeto Command, pero especificando nosotros
mismos las propiedades de nuestro Recordset.

115
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion
objComando.CommandText="devuelveAutores"
Set objRecordSet=Server.CreateObject("ADODB.Recordset")
objRecordSet.Open objComando ,,adOpenForwardOnly,adLockReadOnly,adCmdStoredProc%>

Código fuente 102

Como se puede observar en el parámetro del método Open correspondiente a la propiedad


ActiveConnection del objeto Recordset no hemos indicado ninguna conexión, ya que se toma la
conexión que se ha especificado para el objeto Command que utilizamos como origen de datos del
objeto Recordset.

También podemos utilizar el método Open especificando la tabla, como en el Código fuente 103.

<%Set objConexion=Server.CreateObject("ADODB.Connection")
objConexion.Open "Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User
id=sa"
Set objComando=Server.CreateObject("ADODB.Command")
objComando.ActiveConnection=objConexion
objComando.CommandText="devuelveAutores"
Set objRecordSet=Server.CreateObject("ADODB.Recordset")
objRecordSet.Open
"authors",objConexion,adOpenForwardOnly,adLockReadOnly,adCmdTable%>

Código fuente 103

Y por último podemos tener un objeto Recordset completamente independiente, que posea su propia
conexión y recupere la información de la tabla a través de una sentencia SQL. Ver el Código fuente
104.

<%strConexion="Provider=SQLOLEDB;Data Source=aesteban2;Initial Catalog=pubs;User


id=sa"
strConsulta="select * from authors"
Set objRecordSet=Server.CreateObject("ADODB.Recordset")
objRecordSet.Open
strConsulta,strConexion,adOpenForwardOnly,adLockReadOnly,adCmdText%>

Código fuente 104

Al igual que abrimos un Recordset, debmos cerrarlo con el método Close, de esta forma liberaremos
todos los recursos del sistema asociados a este objeto, pero no lo eliminaremos de la memoria, para
ello deberemos asignarle al objeto Recordset el valor Nothing.

En teoría, si cerramos el objeto Connection al que está asociado una serie de objetos Recordset, se
cerrarán todos estos objetos Recordset dependientes de forma automática, pero es mejor, para liberar
memoria y recursos de forma más fiable e inmediata, cerrar cada uno de los Recordset mediante estas
dos sencillas líneas de código, como se muestra en el Código fuente 105.

116
© Grupo EIDOS 5. Componentes de acceso a datos. ADO

<%objRecordset.Close
Set objRecordset=Nothing%>

Código fuente 105

117
CDONTS y ASP

Introducción
En capítulos anteriores hemos visto distintos tipos de componentes y objetos que se pueden utilizar en
ASP:

• Objetos pertenecientes al modelo de objetos de ASP (Request, Server, Application, etc.).

• Componentes incluidos en el intérprete de secuencias de comandos de VBScript (Dictionary,


FileSystemObject, TextStream, etc.).

• Componentes incluidos con la instalación de ASP (AdRotator, MyInfo, ContentRotator,


Tools, ADO, etc.).

En este capítulo vamos a ver un conjunto de componentes agrupados todos ellos bajo el nombre de
CDONTS (Collaboration Data Objects for NT Server). CDONTS es una librería de Microsoft que
ofrece la posibilidad de utilizar sistemas de mensajería, basándose en servidores de correo como
Microsoft Exchange Server o el servicio SMTP incluido en IIS 5.0.

Como el propio nombre de estos componentes indica, son componentes para NT Server, en nuestro
caso para 2000 Server.

CDONTS se instala como parte del servicio de correo SMTP. Podremos acceder de igual forma a
Microsoft Exchage Server 5.5 o superior y al servicio de correo SMTP incluido en IIS 5.0.

En el presente capítulo los ejemplos realizados con CDONTS han sido probados con el servicio de
correo de IIS 5.0, para averiguar si tenemos instalado este servicio de IIS 5.0 debemos utilizar el
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Administrador de servicios de Internet y comprobar si existe el nodo llamado Servidor virtual SMTP
predeterminado.

Figura 21. El servicio SMTP dentro de IIS 5.0

Si no se encuentra instalado este servicio acudiremos a la opción Agregar o quitar programas del Panel
de control. Dentro de esta opción seleccionaremos Agregar o quitar componentes de Windows y de los
componentes seleccionaremos Servicios de Internet Information Server (IIS). Si pulsamos el botón
etiquetado como Detalles veremos los subcomponentes de IIS y seleccionaremos el componente
Servicio SMTP.

Figura 22. Instalando el servicio SMTP

120
© Grupo EIDOS 6. CDONTS y ASP

A lo largo de este capítulo comentaremos cuando sea necesario algunos puntos relacionados con la
configuración del servicio SMTP de IIS 5.0.

Antes de seguir con los distintos apartados que exponen el modelo de objetos de CDONTS, vamos a
ver el Código fuente 106 que utiliza un objeto de CDONTS llamado NewMail y que nos permite de
manera muy sencilla enviar un mensaje de correo electrónico.

<%Set objMail=Server.CreateObject("CDONTS.NewMail")
objMail.Send "pepe@trola.com","aesteban@mail.angel.es","Asunto","Hola que tal te
va..."%>

Código fuente 106

Con este código ya podríamos enviar un mensaje de correo a través de nuestro servicio SMTP. En
siguientes apartados comentaremos detenidamente de nuevo este código.

Modelo de objetos de CDONTS


La librería CDONTS se encuentra formada por un modelo de objetos que vamos a comentar es este
capítulo. Veamos gráficamente este modelo con la Figura 23.

Figura 23. Modelo de objetos de CDONTS

Como se puede observar todos los objetos se encuentran relacionados a excepción del objeto NewMail
que aparece desconectado del resto. A continuación se ofrece una breve descripción de los objetos
incluidos en CDONTS.

• NewMail: mediante este objeto podremos enviar nuevos mensajes de correo.

121
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Session: este objeto contiene toda la información necesaria para manipular mensajes y
también las carpetas de entrada (inbox) y de salida de mensajes (outbox), es decir, podremos
leer los mensajes revibidos y enviar nuevos mensajes. Mediante este objeto podremos acceder
a los mensajes almacenados de un usuario determinado. El resto de los objetos de CDONTS
tienen como referencia el objeto Session.

• Folder: representa una carpeta o contenedor que contiene los mensajes almacenados
correspondientes a la sesión actual. Vamos a poder acceder a los mensajes recibidos y a los
mensajes pendientes de enviar.

• Message: se corresponde con un mensaje de un objeto Folder, el objeto Folder ofrece una
propiedad llamada Messages que contiene una colección de objetos Message.

• AddressEntry: este objeto contiene información relativa al remitente de un mensaje


determinado, se obtiene a partir de la propiedad Sender del objeto Message.

• Attachments: colección perteneciente al objeto Message que contiene objetos Attachment, y


que representa todos los adjuntos de un mensaje de correo determinado.

• Attachment: objeto que representa un fichero adjunto de un mensaje.

• Recipients: colección perteneciente al objeto Message que contiene objetos Recipient, y que
representa todos los destinatarios de un mensaje determinado.

• Recipient: objeto que representa un destinatario de un mensaje.

En los distintos apartados de este tema veremos cada uno de los objetos del modelo de objetos de
CDONTS.

El objeto Newmail
Como ya hemos dicho anteriormente, este objeto que se encuentra fuera de la jerarquía del objeto
Session nos va a permitir enviar mensajes de correo electrónico.

Los métodos que ofrece este objeto son:

• Send(remitente, destinatario, asunto, mensaje, importancia): mediante este método enviaremos


un mensaje al destinatario que indiquemos. Todos los parámetros son opcionales, pudiendo
aplicar el método Send() sin parámetros, ya que el objeto NewMail ofrece una serie de
propiedades que permiten configurar el mensaje antes de enviarlo. En el parámetro
destinatario podemos expresar distintos destinatarios separados con comas. Tanto destinatario
como remitente son direcciones de correo electrónico. En el asunto especificaremos el asunto
del mensaje y en mensaje el cuerpo del mensaje, es decir, el mensaje en sí. Y mediante el
parámetro importancia indicamos si es alta (cdoHigh), normal (cdoNormal) o baja (cdoLow),
por defecto este parámetro tiene el valor cdoNormal. Todos estos parámetros se corresponden
con propiedades del objeto NewMail que veremos a continuación.

• AttachFile(fuente, nombreFichero, codificación): permite adjuntar el fichero especificado en


el parámetro fuente a un mensaje de correo, se debe indicar una ruta física completa, el resto
de los parámetros son opcionales. En nombreFichero indicamos el nombre con el que aparece
el fichero adjunto en el cliente de correo, y en codificación indicamos el método de

122
© Grupo EIDOS 6. CDONTS y ASP

codificación empleado para enviar el fichero adjunto, esta parámetro tiene dos posibles valores
cdoEncodingUUencode y cdoEncodingBase64, por defecto presenta el primero de ellos.

En el ejemplo del Código fuente 107 se trata de enviar un correo a aesteban@mail.angel.es, que es de
prioridad baja y que posee dos ficheros adjuntos.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->


<%Set objMail=Server.CreateObject("CDONTS.NewMail")
objMail.AttachFile "c:\tmp\imagen.png","Figura"
objMail.AttachFile "c:\tmp\tema30.html","Tema en HTML"
objMail.Send "pepe@trola.com","aesteban@mail.angel.es"_
,"Asunto","Hola que tal te va...",cdoLow%>

Código fuente 107

Como se puede observar se ha realizado una referencia a la librería de CDONTS, para así poder
utilizar las constantes definidas en ella. En este caso se ha utilizado la constante para establecer la
prioridad el mensaje.

Si todo ha funcionado correctamente el mensaje habrá llegado a su destinatario. Si el destinatario abre


el mensaje de correo con el cliente de correo Microsoft Outlook Express tendrá el aspecto que muestra
la Figura 24.

Figura 24. El mensaje en Outlook Express

123
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Las propiedades que posee el objeto NewMail son las siguientes, y como ya hemos dicho algunas de
ellas se corresponden con los parámetros del método Send():

• From: en esta propiedad indicamos el remitente del mensaje de correo. Se asigna una cadena
que representa una dirección de correo, no es posible utilizar nombres.

• To: destinatario del mensaje, si existen varios destinatarios se deben separar mediante comas.

• CC: indicamos las direcciones de correo a las que deseamos enviar copia del mensaje.

• BCC: tiene la misma función que el caso anterior, pero en este caso se oculta la lista de
destinatarios que reciben copia.

• Importance: es la importancia o prioridad del mensaje, se le puede asignar los valores


cdoHigh(alta), cdoNormal(normal) o cdoLow(baja). Por defecto tiene el valor de prioridad
normal.

• Body: representa el cuerpo del mensaje, es decir, el texto del mensaje. Puede contener texto
plano o en HTML.

• BodyFormat: mediante este propiedad indicamos el formato del cuerpo del mensaje, puede
aceptar los valores cdoBodyFormatHTML, para indica que el formato del cuerpo del mensaje
es HTML o cdoBodyFormatText para indicar que el formato del cuerpo del mensaje es texto
plano, por defecto el valor de esta propiedad es cdoBodyFormatText. Para poder utilizar el
valor cdoBodyFormatHTML se debe utilizar el formato de correo MIME, indicándolo en la
propiedad MailFormat.

• MailFormat: indica el formato con el que se crean los mensajes de correo, puede tener dos
valores posibles, formato de texto plano (cdoMailFormatText), que es el valor por defecto, y
formato MIME (cdoMailFormatMIME). Con el formato MIME podemos enviar contenidos
más elaborados en nuestros mensajes de correo.

• Version: esta propiedad de sólo lectura devuelve la versión de la librería CDONTS que
estamos utilizando, la versión actual es la 1.2.

• ContentLocation: permite especificar un camino relativo o absoluto para todas las URLs
contenidas en el cuerpo del mensaje, esta propiedad tiene sentido cuando estamos enviando
mensajes en formato HTML.

• ContentBase: propiedad relacionada con la anterior, ya que nos permite especificar la ruta
base para todas URLs contendidas en el cuerpo del mensaje. Los valores de ContentLocation
se consideran relativos a los valores de ContentBase. Así si la propiedad ContentBase tiene el
valor "http://www.almagesto.com" y ContentLocation tiene el valor "images/", para las URLs
presentes en el cuerpo del mensaje se construirá la ruta http://www.almagesto.com/images/.
Esto sería equivalente a asignar a la propiedad ContentBase el valor
"http://www.almagesto.com/images/".

Veamos un ejemplo que muestra la utilización de las propiedades del objeto NewMail. En este caso se
definen algunas de las propiedades y luego se envía el correo mediante el método Send() sin
parámetros.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->

124
© Grupo EIDOS 6. CDONTS y ASP

<%Set objMail=Server.CreateObject("CDONTS.NewMail")
objMail.From="pepe@trola.com"
objMail.To="aesteban@mail.angel.es"
objMail.Importance=cdoLow
objMail.MailFormat=cdoMailFormatMIME
objMail.Subject="Asunto"
objMail.BodyFormat=cdoBodyFormatHTML
objMail.ContentBase="http://aesteban/images/"
strTexto="<html><body><b><i>Hola que tal te va...</i></b><br>"
strTexto=strTexto&"<div align='center'><img src='almagesto.gif'></div>"
strTexto=strTexto&"</body></html>"
objMail.Body=strTexto
objMail.Send%>

Código fuente 108

En este ejemplo se ha dado el formato MIME al mensaje y al cuerpo el formato HTML, el resultado de
visualizar este correo mediante el cliente de correo Outlook Express es el que muestra la Figura 25.

Figura 25. Mensaje de correo con formato

A través del objeto NewMail también podemos tener acceso y manipular las cabeceras del mensaje de
correo, que normalmente en los clientes de correo permanecen ocultas. Parte de la información
contenida en las cabeceras se genera a partir de las propiedades del objeto NewMail y otra parte las
genera el servidor de correo de forma automática al enviar el mensaje.

Estas cabeceras las podemos ver con el cliente de correo Outlook Express si en el menú de el mensaje
acudimos a la opción Archivo|Propiedades, y luego elegimos la pestaña Detalles.

Mediante la colección Value del objeto NewMail podemos añadir nuevas cabeceras a los mensajes o
modificar las existentes.

En el Código fuente 109 se añade una nueva cabecera y se modifica la cabecera Subject.

125
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->


<%Set objMail=Server.CreateObject("CDONTS.NewMail")
objMail.From="pepe@trola.com"
objMail.To="aesteban@mail.angel.es"
objMail.Importance=cdoLow
objMail.MailFormat=cdoMailFormatMIME
objMail.Subject="Asunto"
objMail.BodyFormat=cdoBodyFormatHTML
objMail.ContentBase="http://aesteban/images/"
strTexto="<html><body><b><i>Hola que tal te va...</i></b><br>"
strTexto=strTexto&"<div align='center'><img src='imagen.gif'></div>"
strTexto=strTexto&"</body></html>"
objMail.Body=strTexto
objMail.Value("Subject")="Modifico el asunto"
objMail.Value("NuevaCabecera")="Nuevo valor"
objMail.Send%>

Código fuente 109

Los detalles del mensaje en Outlook Express tienen el aspecto que nos muestra la

Figura 26. Cabeceras del mensaje

Hemos visto lo sencillo que resulta enviar un mensaje mediante el objeto NewMail de la libreía
CDONTS, ahora vamos a seguir viendo los distintos objetos que proporciona esta librería. Los
siguientes objetos permiten acceder a los mensajes que se encuentran almacenados en el servidor de
correo.

126
© Grupo EIDOS 6. CDONTS y ASP

El objeto Session
Del objeto Session dependen el resto de los objetos de la librería CDONTS, a través del objeto Session
iniciamos una sesión con el servidor de correo mediante un usuario determinado.

El servidor con el que iniciamos la sesión será el servidor de correo que se encuentre instalado en el
servidor Web en el que se ejecutan las páginas ASP, como ya hemos dicho este servidor puede ser el
servicio SMTP incluido en IIS 5.0 o Exchange Server 5.5 o superior.

Para realizar las pruebas con los distintos objetos de CDONTS se ha utilizado el servicio SMTP de IIS
5.0. Para indicar una dirección de correo dentro de este servidor utilizaremos la siguiente sintaxis:
cuenta@nombreServidor, siendo nombreServidor el nombre del dominio por defecto del servidor
SMTP. Aunque desde el Administrador de servicios de Internet podemos definir alias para el dominio
por defecto. Para ello acudiremos dentro del Administrador de servicios de Internet al nodo llamado
Servidor virtual SMTP predeterminado, y en la rama Dominios pulsando con el botón derecho del
ratón seleccionamos la opción Nuevo|Dominio, en ese momento se lanza el asistente para la creación
de nuevos dominios.

Dentro del asistente seleccionamos la opción de Alias y pulsamos el botón Siguiente. A continuación
se debe indicar el nombre que vamos a dar al alias de nuestro dominio, yo en mi caso le he llamado
mail.angel.es, siendo mi dominio por defecto aesteban, es decir, el nombre de mi servidor. Pulsamos el
botón Finalizar. Si todo ha sido correcto veremos una pantalla similar a la de la Figura 27.

Figura 27. Creando dominios alias

Después de este pequeño comentario acerca de la creación de dominios, volvamos al tema principal
que nos ocupa dentro de este apartado.

El objeto Session presenta los siguientes métodos:

• LogonSMTP(nombre, direccion): mediante este método se inicia una sesión con el servidor de
correo. En el parámetro nombre se indica el nombre que se desea que se muestre al usuario, y
en dirección la dirección completa del usuario que se desea conectar al servidor de correo.
Este método no realiza ningún tipo de validación, si la conexión no se ha podido establecer no
recibimos ningún mensaje de error. Cada usuario conectado tendrá acceso a su propio buzón,
pudiendo leer el correo de su "bandeja de entrada" (inbox) y enviar correo a través de su
"bandeja de salida" (outbox).

127
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• LogOff(): este método finalizará la sesión actual con el servidor de correo. Podremos reutilizar
el objeto Session si deseamos realizar una nueva sesión llamando de nuevo al método
LogonSMTP.

• GetDefaultFolder(tipoCarpeta): mediante este método se obtiene un objeto Folder que


representa la bandeja o carpeta de entrada o la de salida del usuario conectado. El parámetro
de este método puede tener dos valores cdoDefaultFolderInbox(1) para obtener la bandeja de
entrada y poder leer los mensajes recibidos o cdoDefaultFolderOutbox(2) para obtener la
bandeja de salida y poder enviar mensajes.

• SetLocaleID(codigo): este método permite establecer un código de localización que afectará al


entorno del lenguaje, formatos horarios, fechas, valores monetarios, etc. El parñametro tiene
los mismos valores que vimos para la propiedad LCID del objeto integrado Session de ASP.
Este método si se utiliza se debe lanzar antes de establecer la sesión con el servidor de correo
mediante el método LogonSMTP().

Ahora vamos a pasar a comentar las propiedades del objeto Session:

• Inbox: propiedad de sólo lectura que devuelve un objeto Folder que representa la bandeja de
entrada de la sesión actual.

• Outbox: propiedad de sólo lectura que devuelve un objeto Folder que representa la bandeja de
salida de la sesión actual.

• MessageFormat: propiedad de lectura y escritura que permite obtener e indicar el formato de


codificación de los mensajes. Tiene dos valores posibles CDOMime, para mensajes de tipo
MIME, y CDOText para mensajes de tipo texto plano.

• Name: propiedad de sólo lectura que contiene el nombre del usuario conectado y que se ha
especificado en el método LogonSMTP().

• Version: propiedad de sólo lectura que indica la versión de la librería CDONTS.

El objeto Session además de estas propiedades ofrece otras tres que son comunes a todos los objetos
de su jerarquía, y son las siguientes:

• Session: esta propiedad devuelve el objeto Session que representa la raíz de la jerarquía de un
objeto determinado.

• Class: esta propiedad devuelve un entero que nos indica el tipo de objeto: Session(0),
Folder(2), Message(3), Recipient(4), Attachment(5), AddressEntry(8), Messages(16),
Recipients(17) y Attachments(18).

• Parent: devuelve un objeto que representa el padre del objeto especificado. La propiedad
Parent del objeto Session devuelve Nothing y la del objeto AddressEntry devuelve un objeto
Message.

Una vez comentados los métodos y propiedades del objeto Session vamos a mostrar un ejemplo que
hace uso de algunos de ellos.

Este ejemplo consiste en un formulario que nos permite conectarnos al servicio SMTP de IIS 5.0. El
formulario permite especificar el nombre del usuario y la cuenta de correo del usuario que se va a
conectar. Una vez que se ha establecido la conexión con el servidor SMTP se muestran algunos de los
valores de las propiedades de la sesión actual. También existe la posibilidad de desconectarnos para

128
© Grupo EIDOS 6. CDONTS y ASP

iniciar una nueva sesión, la sesión se debe almacenar en una variable a nivel de sesión, ya que más
tarde deberemos utilizarla para realizar la desconexión. Veamos el Código fuente 110. Y en la Figura
28 se puede ver un ejemplo de la ejecución del código anterior.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%If Request.Form("Desconectar")<>"" Then
'se realiza la desconexión
Set objSession=Session("oSession")
objSession.LogOff
End if
If Request.Form("Conectar")="" Then%>
<form action="<%=Request.ServerVariables("SCRIPT_NAME")%>" method="post">
Nombre:<input type="text" name="nombre" size="20" value=""><br>
Cuenta de correo:<input type="text" name="cuenta" size="20"
value=""><br>
<input type="submit" name="conectar" value="Conectar">
</form>
<%Else
'se realiza la conexión
Set objSession=Server.CreateObject("CDONTS.Session")
objSession.LogonSMTP Request.Form("nombre"),Request.Form("cuenta")
Set Session("oSession")=objSession%>
Usuario conectado: <%=objSession.Name%><br>
<%If objSession.MessageFormat=CDOMime Then
strFormato="CDOMime"
Else
strFormato="CDOText"
End if%>
Formato de los mensajes: <%=strFormato%><br>
Versión de CDONTS: <%=objSession.Version%><br>
<form action="<%=Request.ServerVariables("SCRIPT_NAME")%>" method="post">
<input type="submit" name="desconectar" value="Desconectar">
</form>
<%End if%>
</BODY>
</HTML>

Código fuente 110

Figura 28. Utilizando el objeto Session de CDONTS

129
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

El objeto Folder
Este objeto representa una carpeta o contenedor que se va a corresponder con las bandejas de entrada y
de salida de los mensajes de correo de la sesión actual. Un objeto Folder lo obtenemos a partir de la
propiedades Inbox y Outbox de un objeto Session que representa una sesión ya establecida con el
servicio SMTP. También lo podemos obtener a través del método GetDefaultFolder().

El objeto Folder suele representar las siguientes carpetas estándar:

• Bandeja de entrada (inbox): es la localización que tiene el correo recibido.

• Bandeja de salida (outbox): es la localización temporal que tiene el correo antes de proceder a
su envío.

Mediante el objeto Folder vamos a tener acceso a los contenidos de la carpeta indicada. Aunque
tenemos acceso a los mensajes este acceso es algo restrictivo, podremos eliminar mensajes existentes,
leer mensajes existentes y enviar mensajes nuevos, sin embargo no podemos cambiar las propiedades
de los mensajes ya existentes.

En este punto se debe hacer una aclaración acerca del servidor SMTP de IIS y los objetos de la librería
CDONTS.

El servidor de correo SMTP de IIS 5.0 a diferencia del servidor Exchange Server, no posee buzones
individuales, es decir, no existe una bandeja para cada usuario sino que todos los mensajes recibidos se
almacenan en una misma localización y todos los mensajes enviados en otra.

Si hemos aceptado las configuraciones por defecto al instalar el servicio SMTP de IIS, la bandeja de
entrada se corresponde con el directorio c:\inetpub\mailroot\Drop y la de salida con el directorio
c:\inetpub\mailroot\Pickup.

Sin embargo aunque los buzones sean globales, cuando iniciamos la sesión con el servidor SMTP, a
través de los objetos de CDONTS únicamente tendremos acceso a los mensajes que se corresponden
con los del cliente que se a conectado, de esta forma se intenta simular los buzones personales.

Debido a esto para utilizar el servicio SMTP no es necesario configurar buzones ni definir cuentas de
correo de usuarios, de todas formas en los siguientes ejemplos todo esto se verá más claro.

Hecha esta aclaración volvemos a tratar el objeto Folder, empezando ahora a comentar las propiedades
que nos ofrece.

• Name: propiedad de sólo lectura que contiene el nombre de la carpeta a la que representa el
objeto Folder. Normalmente tiene los valores "Inbox" y "Outbox".

• Messages: esta propiedad devuelve una colección de objetos Message, que representará los
mensajes presentes en la carpeta.

Veamos el tratamiento que tiene la colección Messages.

La colección Messages posee las siguientes propiedades:

• Count: número de objetos Message contenidos en la misma.

• Item(indice): devuelve un objeto Message de la colección cuyo índice coincide con el


parámetro indicado. El primer índice es 1.

130
© Grupo EIDOS 6. CDONTS y ASP

Y los siguientes métodos:

• Add(asunto, texto, importancia): permite añadir un nuevo mensaje a la colección. Todos los
parámetros son opcionales, y cuando se ejecuta este método se devuelve un objeto Message
para poder manipularlo. Este método únicamente se puede aplicar a una colección Messages
que pertenece a la bandeja de salida.

• Delete(): elimina todos lo mensajes de la colección. Para borrar un mensaje en concreto


debemos utilizar el método Delete del objeto Message, pero eso lo veremos en el siguiente
apartado.

• GetFirst(): método que devuelve el primer objeto Message de la colección Messages. Si el


objeto no existe devuelve Nothing.

• GetLast(): método que devuelve el último objeto Message de la colección Messages. Si el


objeto no existe devuelve Nothing.

• GetNext(): método que devuelve el siguiente objeto Message de la colección Messages. Si el


objeto no existe devuelve Nothing.

• GetPrevious(): método que devuelve el anterior objeto Message de la colección Messages. Si


el objeto no existe devuelve Nothing.

Vamos a ampliar y modificar el ejemplo del apartado anterior que utilizaba el objeto Session de
CDONTS para establecer una conexión, para mostrar la utilización práctica del objeto Folder.

En este nuevo ejemplo vamos a establecer la sesión con el servicio SMTP con el mismo formulario y
vamos a indicar el número de mensajes que posee el usuario conectado en ese momento en cada una
de sus bandejas.

También aparece un nuevo botón que permite eliminar al usuario todos los mensajes de su bandeja de
entrada. El Código fuente 111.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%If Request.Form("Desconectar")<>"" AND Session("oSession")<>"" Then
'se realiza la desconexión
Set objSession=Session("oSession")
objSession.LogOff
Session("oSession")=""
Elseif Request.Form("EliminarInbox")<>"" Then
'se eliminan todos los mensajes de Inbox
Set objSession=Session("oSession")
Set objFolderInbox=objSession.Inbox
objFolderInbox.Messages.Delete
End if
If Request.Form("Conectar")="" AND Session("oSession")="" Then%>
<form action="<%=Request.ServerVariables("SCRIPT_NAME")%>" method="post">
Nombre:<input type="text" name="nombre" size="20" value=""><br>
Cuenta de correo:<input type="text" name="cuenta" size="20"
value=""><br>
<input type="submit" name="conectar" value="Conectar">
</form>

131
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%Elseif Request.Form("Conectar")<>"" Then


'se realiza la conexión
Set objSession=Server.CreateObject("CDONTS.Session")
objSession.LogonSMTP Request.Form("nombre"),Request.Form("cuenta")
'se guarda a nivel se sesión la referencia a la sesión establecida
'con el servicio SMTP
Set Session("oSession")=objSession
End if
If Session("oSession")<>"" Then
'se recupera la sesión con SMTP
Set objSession=Session("oSession")%>
Usuario conectado: <b><%=objSession.Name%></b><br>
<form action="<%=Request.ServerVariables("SCRIPT_NAME")%>" method="post">
<input type="submit" name="desconectar" value="Desconectar">
<input type="submit" name="EliminarInbox" value="Vaciar Inbox">
</form>
<%Set objFolderInbox=objSession.GetDefaultFolder(cdoDefaultFolderInbox)%>
El número de mensajes en <%=objFolderInbox.Name%> es
<%=objFolderInbox.Messages.Count%><br>
<%Set objFolderOutbox=objSession.Outbox%>
El número de mensajes en <%=objFolderOutbox.Name%> es
<%=objFolderOutbox.Messages.Count%>
<%End if%>
</BODY>
</HTML>

Código fuente 111

Y en la Figura 29 se muestra un ejemplo de un usuario conectado.

Figura 29. Utilizando el objeto Folder

En el siguiente apartado veremos como mostrar los datos de los mensajes contenidos en las bandejas y
también como eliminar mensajes determinados y enviar nuevos.

132
© Grupo EIDOS 6. CDONTS y ASP

El objeto Message
Este objeto representa un mensaje contenido en un objeto Folder, es decir, en una carpeta. Una
referencia a un objeto Message la obtenemos a través de la colección Messages del objeto Folder
correspondiente.

En este apartado además de comentar el objeto Message vamos a tratar las dos colecciones que
contiene: Recipients y Attachments, y también veremos el objeto AddressEntry, que permite
especificar los datos del remitente.

El objeto Message lo utilizaremos para manipular los mensajes de correo, y para ello este objeto
ofrece las siguientes propiedades:

• Attachments: una colección de objetos Attachment que contiene los ficheros adjuntos de un
mensaje.

• ContentBase: URL base para los contenidos del mensaje.

• ContentID: identificador del tipo de contenido MIME del mensaje.

• ContentLocation: URL relativa para los contenidos del mensaje.

• HTMLText: cuerpo del mensaje en formato HTML.

• Importance: prioridad del mensaje.

• MessageFormat: formato de codificación del mensaje.

• Recipients: colección de objetos Recipient que representa a cada uno de los destinatarios del
mensaje.

• Sender: devuelve un objeto AddressEntry que representa al remitente del mensaje.

• Size: tamaño en bytes del mensaje.

• Subject: asunto del mensaje.

• Text: cuerpo del mensaje en formato de texto plano.

• TimeReceived: fecha y hora de recepción del mensaje.

• TimeSent: fecha y hora de envío del mensaje.

Muchas de estas propiedades son comunes a las del objeto NewMail.

El objeto Message ofrece los siguientes métodos:

• Send(): este método envía el mensaje a los destinatarios indicados.

• Delete(): elimina el mensaje de forma permanente.

Vamos a seguir utilizando el mismo ejemplo que el de los apartados anteriores para mostrar la
utilización del objeto Message. En este caso se trata de mostrar el contenido de la bandeja de entrada

133
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

del usuario que se ha conectado. La información que se va a mostrar de cada mensaje es la prioridad,
remitente, asunto y fecha de recepción.

Para mostrar el contenido de la bandeja de entrada simplemente recorreremos la colección Messages


de objeto Folder que se corresponde con inbox. Y utilizaremos las propiedades correspondientes de
cada objeto Message para mostrar la información deseada.

El código que se debe añadir al ejemplo anterior, después de haber obtenido una referencia a la carpeta
Inbox, es el Código fuente 112.

<%If objFolderInbox.Messages.Count>0 Then%>


<table border="0">
<tr><th>Prioridad</td>
<th>De</th>
<th>Asunto</th>
<th>Recibido</th>
</tr>
<%For Each objMessage In objFolderInbox.Messages%>
<tr>
<td><%=Prioridad(objMessage.Importance)%></td>
<td><%=objMessage.Sender%></td>
<td><%=objMessage.Subject%></td>
<td><%=objMessage.TimeSent%></td>
</tr>
<%Next%>
</table>
<%End if%>

Código fuente 112

Se utiliza una función llamada Prioridad, cuya función es la de devolver una cadena con el texto que
representa a la prioridad correspondiente, su código es el Código fuente 113.

<%Function Prioridad(prio)
Select Case prio
Case cdoLow: Prioridad="Baja"
Case cdoNormal: Prioridad="Normal"
Case cdoHigh: Prioridad="Alta"
End Select
End Function%>

Código fuente 113

Y un ejemplo de la ejecución del Código fuente 113 lo tenemos en la Figura 30.

Como ya hemos comentado al principio el objeto Message ofrece dos colecciones a través de de sus
propiedades Recipients y Attachments.

La colección Recipients contiene objetos Recipient que representan a cada uno de los destinatarios de
un mensaje. Para añadir un nuevo destinatario utilizamos el método Add() sobre la colección
Recipients, que además nos devolverá un objeto Recipient por si deseamos manipularlo. La sintaxis
del método Add() es la siguiente:

Set objRecipient=colRecipients.Add(nombre, dirección, tipo)

134
© Grupo EIDOS 6. CDONTS y ASP

Figura 30. Mensajes de la bandeja de entrada

Todos los parámetros de este método son opcionales. En nombre indicamos el nombre del destinatario
que se va a mostrar, en dirección se indica la dirección de correo del destinatario y en tipo una
constante que indica si es un destinatario de copia o no. Los valores de tipo pueden ser CdoTO (es el
destinatario sin copia), CdoCC (se le envía copia) o CdoBCC (se le envía copia pero no aparece en la
lista de los demás destinatarios.

Los mensajes contenidos en la bandeja de entra no se pueden modificar, sólo son de lectura, por lo
tanto si lanzamos el método Add() sobre una colección Recipients de un objeto Message que
pertenezca al objeto Folder Inbox, se producirá un error.

El método Delete de la colección Recipients elimina todos los objetos Recipient de la colección.

Los parámetros que aparecían en el método Add() de la colección Recipients se corresponden con las
propiedades del objeto Recipient, y son las siguientes:

• Name: nombre del destinatario. Es el nombre que se va a mostrar.

• Address: dirección de correo del destinatario.

• Type: tipo de destinatario, puede tener uno de los siguientes valores: CdoTO, CdoCC,
CdoBCC.

La otra colección que podemos encontrar en el objeto Message es la colección Attachments que se
encuentra formada por objetos Attachment y que representa los ficheros adjuntos de un mensaje
determinado.

Mediante el método Delete() de la colección Attachments eliminamos todos los ficheros adjuntos de
un mensaje, y mediante el método Add() se añade un nuevo fichero adjunto.

135
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Al igual que ocurría con la colección Recipients, al método Add() devuelve un objeto Attachment para
poder manipularlo, la sintaxis de este método es:

Set objAttachment=colAttachments.Add(nombre, tipo, fuente, localización, base)

Todos los parámetros de este método son opcionales. El parámetro nombre indica el nombre con el
que se va a mostrar el adjunto, el tipo de adjunto puede ser cdoFileData(1) para indicar un fichero y
cdoEmbeddedMessage(4) para indicar que el adjunto es otro mensaje. En fuente se indica la ruta
completa del fichero adjunto o bien un objeto Message en el caso de ser el adjunto de tipo
cdoEmbeddedMessage. Localización y base son dos parámetros que especifican cabeceras para
ficheros adjuntos de tipo MIME.

El método Add() no se puede utilizar con mensajes que se encuentren en la bandeja de entrada, ya que
estos mensajes son únicamente de lectura.

El objeto Attachment ofrece una serie de propiedades que se corresponden con los parámetros del
método Add() de la colección Attachments, y son las siguientes:

• ContentBase: cabecera para un adjunto MIME que especifica la dirección URL base.

• ContentID: identificador único para un adjunto MIME.

• ContentLocation: URL relativa para un adjunto MIME.

• Name: nombre con el que se va a mostrar el fichero adjunto.

• Source: ruta o localización del adjunto.

• Type: tipo de adjunto, puede presentar las siguientes constantes cdoFileData o cdoEmbedded-
Message.

Además el objeto Attachment ofrece los siguientes métodos:

• Delete(): para eliminar un adjunto determinado.

• ReadFromFile(nombreFichero): carga el adjunto desde el fichero indicado por parámetro. Se


debe indicar la ruta completa del fichero.

• WriteToFile(nombreFichero): escribe en el fichero indicado el contenido del adjunto.

Relacionado con el objeto Message también encontramos el objeto AddressEntry. El objeto


AddressEntry se obtiene a través de la propiedad Sender del objeto Message y representa al remitente
del mensaje.

Este objeto ofrece las siguientes propiedades:

• Name: nombre del remitente o alias, es el nombre que se muestra.

• Address: dirección de correo electrónico del remitente.

• Type: tipo de dirección. Para CDONTS siempre es "SMTP".

Ahora vamos a mostrar un ejemplo que utiliza todos estos nuevos objetos. Para ello vamos a retomar
el ejemplo visto con el objeto NewMail, y lo que vamos a hacer es la misma operación, es decir,

136
© Grupo EIDOS 6. CDONTS y ASP

enviar un correo nuevo, pero utilizando algunos de los objetos vistos hasta ahora que dependen del
objeto Session inicial de la librería CDONTS.

<!--METADATA TYPE="typelib" FILE="C:\Winnt\system32\cdonts.dll"-->


<%'se realiza la conexión
Set objSession=Server.CreateObject("CDONTS.Session")
objSession.LogonSMTP "Angel","aesteban@mail.angel.es"
'se obtiene la bandeja de salida
Set objFolderOutbox=objSession.Outbox
'se obtiene la colección de mensajes
Set colMensajes=objFolderOutbox.Messages
strTexto="Mensaje enviado con los objetos de CDONTS"
'se añade un mensaje nuevo y se obtiene la referencia al mismo
Set objMensaje=colMensajes.Add("Prueba de CDONTS",strTexto,cdoNormal)
'añadimos un adjunto al mensaje
objMensaje.Attachments.Add "Fichero Adjunto",cdoFileData,"c:\tmp\imagen.png"
'añadimos un destinatario al mensaje
objMensaje.Recipients.Add "Jose María","chema@mail.angel.es",cdoTO
'añadimos un destinatario al que se envía una copia
objMensaje.Recipients.Add "Pepe","pepe@mail.angel.es",cdoCC
'se envía el mensaje
objMensaje.Send%>

Código fuente 114

Como podemos comprobar es mucho más sencillo y recomendable utilizar el objeto NewMail a la
hora de enviar correo. El mensaje se ha enviado con el remitente que corresponde a la sesión actual, en
este caso aesteban@mail.angel.es. Si abrimos el mensaje que hemos creado con el cliente de correo
Outlook Express, vemos que tiene el aspecto que nos muestra la Figura 31.

Figura 31. Mensaje leído con Outlook Express

137
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Y si queremos podemos ver los detalles del mismo(Figura 32).

Figura 32. Detalles del mensaje

Antes de terminar este tema vamos añadir una mayor funcionalidad al ejemplo en el que mostrábamos
el contenido de la bandeja de entrada de un usuario conectado a una sesión de nuestro servidor SMTP.

La nueva funcionalidad consiste en poder visualizar el contenido de cada uno de los mensajes que se
encuentran en la bandeja de entrada del usuario conectado actualmente. Para ello vamos a situar en un
enlace los asuntos de cada uno de los mensajes y para identificar cada uno de ellos utilizar una
variable del QueryString que va a contener un entero para cada mensaje.

En la página de lectura del mensaje además vamos a permitir la opción de borrar el mensaje que
estamos leyendo.

Veamos el Código fuente 115 que debemos modificar en la página que muestra el contenido de la
bandeja de entrada. Se debe añadir la creación de los enlaces para cada asunto en el bucle For Each
que recorre todos los mensajes.

<%i=0
For Each objMessage In objFolderInbox.Messages
i=i+1%>
<tr>
<td><%=Prioridad(objMessage.Importance)%></td>
<td><%=objMessage.Sender%></td>

138
© Grupo EIDOS 6. CDONTS y ASP

<td><a
href="LeerMensaje.asp?idmensaje=<%=i%>"><%=objMessage.Subject%></a></td>
<td><%=objMessage.TimeSent%></td>
</tr>
<%Next%>

Código fuente 115

Como se puede comprobar en los enlaces se hace referencia a una página llamada LeerMensaje.asp, es
en esta página dónde se encuentra el código necesario para mostrar el contenido del mensaje
solicidado.

La página de lectura de mensajes va a tener un área de texto para mostrar el contenido del mensaje, y
también dos botones: uno para volver a la página anterior, y otro para eliminar el mensaje que estamos
leyendo. La página encargada de borrar el mensaje no es la de lectura de los mimos, sino que el
mensaje será borrado en la página que muestra el contenido de al bandeja de entrada.

Antes de nada veamos el Código fuente 116 de la página de lectura de mensajes.

<%'recuperamos la sesión con el servidor SMTP


Set objSession=Session("oSession")
Set colMensajes=objSession.Inbox.Messages
'recuperamos el identificador del mensaje
idmensaje=Request.QueryString("idmensaje")
'se recupera el mensaje correspondiente de la colección
Set objMensaje=colMensajes.Item(idmensaje)%>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%'se obtiene el objeto que representa al remitente
Set objAddressEntry=objMensaje.Sender%>
<b>De:</b> <%=objAddressEntry.Name%> (<%=objAddressEntry.Address%>)<br>
<b>Asunto:</b> <%=objMensaje.Subject%><br>
<form action="ListadoMensajes.asp" method="post">
<textarea rows="10" cols="50">
<%=objMensaje.Text%>
</textarea><br>
<input type="submit" name="Volver" value="Volver">
<input type="hidden" name="idmensaje" value="<%=idmensaje%>">
<input type="submit" name="Eliminar" value="Eliminar">
</form>
<%'si existen adjuntos en el mensaje se muestran enlaces a los mismos
If objMensaje.Attachments.Count>0 then%>
<b>Adjuntos:</b><br>
<ul>
<%For Each objAdjunto in objMensaje.Attachments
'se almacena el adjunto en un directorio del servidor Web

objAdjunto.WriteToFile(Server.MapPath("/cursoasp30")&"\"&objAdjunto.Name)
'se construye el enlace al adjunto%>
<li><a href="<%=objAdjunto.Name%>"><%=objAdjunto.Name%></a><br></li>
<%Next%>
</ul>
<%End if%>
</BODY>
</HTML>

Código fuente 116

139
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Como se puede ver también se crean enlaces para los adjuntos del mensaje si los tiene. Los ficheros
adjuntos se graban en el servidor Web con el método WrietToFile() del objeto Attachment, para que
los pueda recupera el destinatario del mensaje. La forma de grabar el adjunto en una misma carpeta y
aprovechando el valor de su propiedad Name, no demasiado adecuado, ya que si existe un fichero
adjunto con ese mismo nombre se sobreescribirá.

Veamos, en la Figura 33, el aspecto que ofrece esta nueva página.

Figura 33. Página de lectura del mensaje

La página LISTADOMENSAJES.ASP le pasa a LEERMENSAJE.ASP a través del QueryString un


parámetro para identificar el mensaje que deseamos leer. Igualmente la página LEERMENSAJE.ASP
le devuelve a la página LISTADOMENSAJES.ASP este mismo identificador de mensaje para que
cuando sea demandando la página LISTADOMENSAJES.ASP pueda eliminar el mensaje deseado.
Para que la página LISTADOMENSAJES.ASP pueda eliminar el mensaje solicitado, deberemos
añadir el Código fuente 117 justo antes de empezar a recorrer la colección de mensajes presentes en la
bandeja de entrada..

<%If Request.Form("Eliminar")<>"" Then


'se obtiene el mensaje que se desea borrar
Response.Write "paso: "& Request.Form("idmensaje")

140
© Grupo EIDOS 6. CDONTS y ASP

Set objMensaje=objFolderInbox.Messages.Item(Request.Form("idmensaje"))
'se borra
objMensaje.Delete
End if%>

Código fuente 117

Simplemente se obtiene el objeto Message correspondiente de la colección Messages y se le lanza el


método Delete().

En el siguiente enlace se encuentra el código completo de este ejemplo.

141
El modelo COM

Introducción a los componentes


Los componentes de software son unidades de código ejecutable que proporcionan una funcionalidad
específica para una aplicación. A la aplicación que utiliza código de un componente, creando objetos y
llamando a sus propiedades y métodos, se denomina cliente o contenedora.

Las ventajas derivadas de la utilización de los componentes en el desarrollo de software pueden


compararse a las de la producción industrial en cadena: en vez de ser un único fabricante el que se
encargue, por ejemplo, de la construcción de todas y cada una de las piezas de un coche, existen varios
fabricantes, cada uno de ellos especializado en una parte concreta (motor, amortiguadores, motores
eléctricos, chapa, caja de cambios, ...), que tienen funcionalidades diferentes. Cada uno de estos
fabricantes trabaja de forma independiente a los demás, pero siguiendo unos estándares industriales
para que luego las piezas encajen entre sí para formar una única unidad funcional: el coche.

Del mismo modo, la utilización de los componentes en una aplicación informática permite que varias
personas distintas, de la misma o distinta empresa, trabajando con distintos lenguajes de
programación, puedan crear pequeños bloques de código con una funcionalidad específica. Cada uno
de estos componentes deben ajustarse a unos estándares o protocolos, para que luego puedan encajar
entre sí y formar una aplicación con funcionalidad completa.

Uno de estos protocolos para que los componentes puedan interaccionar entre sí es el modelo COM
que propone Microsoft. El modelo DCOM permite que componentes que se ejecutan en máquinas
distintas puedan comunicarse a través de la red.

Otro de estos protocolos es el modelo CORBA. Cada uno de estos modelos tiene sus ventajas y sus
inconvenientes. El modelo CORBA está respaldado por una coalición de varios cientos de empresas,
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

el OMG (Object Management Group), y lleva varios años de ventaja respecto al modelo COM/DCOM
de Microsoft.

Los pasos para el diseño de componentes son los siguientes:

• Establecer qué servicios debe proporcionar el componente.

• Determinar qué objetos son necesarios para dividir la funcionalidad del componente de una
manera lógica.

• Decidir cómo se ejecutarán mejor esos componentes en el mismo proceso o en un proceso


aparte.

Conceptos
• Objeto: Es la instancia de una clase.

• Clase: Plantilla que implementa los métodos y propiedades para un objeto. Un solo
componente COM puede contener varias clases. En tiempo de ejecución, se creará un objeto
creando una instancia de una clase.

• Interfaz: Grupos de funciones que exponen la funcionalidad del componente; a través de este
interfaz, los clientes y los componentes COM se comunican. Las interfaces proporcionan
acceso estandarizado a los métodos y propiedades (funcionalidad) disponibles en los
componentes. Es más, son el contrato entre el autor del componente y los desarrolladores de
clientes que aseguran un acceso fiable a la funcionalidad aportada.

• Componente: Colección de clases COM empaquetadas en una unidad ejecutable, como puede
ser una DLL o un EXE. Los componentes son independientes del lenguaje; es decir, no se
definen sobre la base del lenguaje con que se construyeron, sino por las interfaces que
soportan. El lenguaje usado para implementar un componente y sus clientes es irrelevante.

Especificación COM
Las principales características del modelo COM son:

• Compatibilidad binaria: El modelo COM es una especificación binaria. Esto significa que se
pueden construir componentes COM con cualquier herramienta de desarrollo que soporte
COM (Visual Basic, Visual C++, Visual J++, Delphi, etc), y esos mismos componentes
pueden ser después utilizados desde una aplicación, o desde otro componente, escrito en
cualquier lenguaje de programación que soporte COM.

• Compatibilidad con sistemas operativos distintos: Los componentes COM deben desarrollarse
para una plataforma específica, como Windows NT o UNIX ya que no pueden ejecutarse en
una plataforma que no sea para la que fueron escritos; sin embargo, sí que pueden
comunicarse con otros objetos COM situados en otras plataformas.

• Localización transparente: Permite usar un componente desde un cliente sin importar si ese
componente pertenece al mismo proceso, a otro proceso en la misma máquina o a otro proceso
en una máquina distinta.

144
© Grupo EIDOS 7. El modelo COM

• Reutilización del código: Es la mayor ventaja de COM. Los componentes COM contienen
clases que exponen grupos de métodos, conocidos como interfaces, a través de los cuales los
clientes se comunican con objetos. Dado que estos interfaces están bien documentados, el
código que implementan puede reutilizarse con facilidad.

El Modelo de Objetos Componentes (COM) es una especificación de cómo estos objetos interactúan
con sus clientes. Como tal especificación, COM define una funcionalidad estándar, pero no define
cómo debe implementarse, sino qué es lo que debe tener. Entre lo que especifica COM se incluye lo
siguiente:

• Cómo se crean las instancias de los objetos COM.

• Cómo acceden los clientes a las características de estos objetos

• La responsabilidad de su auto-destrucción cuando ya no quedan instancias suyas.

Además de lo que es meramente la especificación, existen "bibliotecas COM", su runtime, que


contienen:

• Una reducida API para facilitar la creación de aplicaciones COM, tanto clientes como
servidoras. Esta API ofrece, por un lado, las funciones para trabajar con clientes y, por otro,
las que permiten a los servidores proporcionar objetos COM.

• Servicios de localización, a través de los cuales se averigua en qué servidor se encuentra el


objeto y la ubicación dentro de éste. Indirectamente esto incluye un soporte entre el
identificador de objeto y el paquete en el que se encuentra su código, que normalmente se
obtiene a través del Registro de Windows. De esta manera, los clientes quedan totalmente
independientes de los posibles cambios de ubicación de los objetos COM, ya sean cambios de
paquete o -incluso- cambios de máquina servidora.

• Servicios para la transparencia en las llamadas a procedimientos remotos, es decir, cuando un


objeto está ejecutándose en un proceso aparte o en una máquina distinta desde la que se usa.

Ventajas del uso de componentes


Veamos las ventajas del uso de componentes, en general, y en particular para las aplicaciones ASP:

• Se divide un problema en partes más pequeñas, más fáciles de entender y resolver, y luego se unen
todas las partes.

• Reduce la complejidad, que queda oculta dentro del componente, “disfrazada” bajo un interfaz
más amigable.

• Reparto del trabajo entre distintos equipos de programadores, que pueden trabajar simultánea-
mente en paralelo, disminuyendo el plazo de desarrollo de la aplicación.

• Más fácil mantenimiento y mejora de cada parte individualmente.

• Los componentes son reutilizables. Si queremos reutilizar el código de una página ASP, hay que
copiar las líneas de script, pero si hemos usado componentes, éstos pueden reutilizarse en la nueva
página.

145
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Pueden ser escritos es cualquier lenguaje, y luego utilizados en una aplicación en otro lenguaje.
Un componente escrito en C++ puedo ser usado en una aplicación VB o, en nuestro caso, desde el
script de una página ASP. Esto permite que el componente pueda ser desarrollado en el lenguaje
en el que se encuentre más cómodo el programador.

• Se pueden comprar a terceras partes, empresas especializadas en el desarrollo de componentes.


Existen muchas empresas de este tipo especializadas en el desarrollo de componentes para uso en
aplicaciones ASP.

• Más rapidez de ejecución: los componentes están compilados, mientras que el script de una página
ASP es interpretado. Además, hay cierta funcionalidad que no podemos conseguir con un lenguaje
de script, que no deja de tener sus limitaciones.

• Protección del código: el script se puede ver y modificar; un componente compilado no. Es cierto
que a partir del IIS 5.0 puede utilizarse la herramienta Windows Script Encoder para codificar el
script de las páginas ASP, convirtiéndolas en caracteres ASCII ilegibles, pero esto no supone una
solución segura ni definitiva. Sin embargo, el código de un componente compilado en una DLL es
inaccesible.

• Los depuradores de VB y sobre todo VC++ son mejores que el script debugger de páginas ASP.

Los interfaces
Un interfaz representa un contrato entre un servidor y un cliente. Un objeto siempre implementará la
función para un interfaz determinado y de una manera estándar. El contrato posibilita el desarrollo de
objetos en cualquier lenguaje, mientras éstos expongan sus interfaces acorde con el estándar COM. En
virtud del contrato puede afirmarse que:

• El interfaz tiene un Identificador Único Global (IID).

• Las funciones tendrán siempre los mismos parámetros y conservarán sus tipos.

• Las funciones devolverán siempre el mismo tipo de valor.

• Las funciones se implementarán cada una siguiendo la semántica del interfaz.

• El interfaz tendrá siempre el mismo conjunto de funciones. No se pueden añadir ni borrar


funciones.

Un interfaz no puede instanciarse en un objeto. Las clases que implementen el interfaz sí pueden ser
instanciadas.

Encapsular funcionalidades en los objetos a los que se accederá a través de interfaces hacen de COM
un sistema abierto y extensible; cualquiera puede proporcionar una implementación de un interfaz
definida y desarrollar aplicaciones que usen esos interfaces. Un desarrollador que use luego estos
componentes no necesitará entender las implementaciones internas de los interfaces; tan sólo
necesitará entender las especificaciones del interfaz.

El diseño y utilización de interfaces es mucho más importante en grandes aplicaciones. En


aplicaciones empresariales minimiza las interdependencias. Así, el cambiar un componente no
requiere rediseñar todos los demás. Un sistema bien diseñado y construido con interfaces COM

146
© Grupo EIDOS 7. El modelo COM

permite a los usuarios de los componentes trabajar independientemente de los desarrolladores de


componentes y, también, hacer pruebas rápidamente.

La mayoría de las herramientas de desarrollo que soportan COM permiten la creación de clases,
objetos y componentes. Sin embargo, hay diferencias en cómo permiten a los programadores acceder a
los interfaces en cada lenguaje.

Para el programador de Visual Basic, los interfaces permanecen ocultos. Cada vez que se crea una
nueva clase, se crea automáticamente un interfaz por defecto. Este interfaz está compuesto por todos
los métodos públicos de la clase. Visual Basic 6.0 permite que una clase implemente un interfaz COM
previamente definido, por ejemplo, usando MIDL en una biblioteca de tipos.

En C++ los interfaces se implementan como clases abstractas. El programador de C++ trabaja
directamente con clases e interfaces. La biblioteca ATL proporciona un mecanismo simple para
generar el código necesario para usar e implementar interfaces.

Por definición, un interfaz COM no puede cambiar una vez haya sido utilizado. Se dice que el interfaz
es inmutable.

Aunque ni los métodos ni la firma puedan cambiar, la implementación de estos componentes sí que
puede variar. La interfaz contractual, entendida por el cliente y el componente, proporciona la base de
unas actualizaciones de versiones seguras. Mientras las interfaces permanezcan inmutables, los
componentes podrán intercambiarse.

Los componentes con enlace temprano no pueden cambiar sus interfaces; sin embargo, los que usan
enlace tardío tienen la capacidad de añadir, borrar y cambiar métodos. Cuando se actualicen estos
componentes, hay que asegurarse de que no se rompe el contrato de la interfaz.

Dado que las funcionalidades están agrupadas en interfaces del modelo de programación COM, se
pueden añadir nuevas funcionalidades añadiendo nuevas interfaces y manteniendo las anteriores,
aunque como veremos, desde una página ASP sólo podemos acceder a un interfaz.

Identificadores Únicos Globales (GUIDs)


Cuando un cliente necesita de los servicios de un componente, realiza una petición de la clase e
interfaz deseada al sistema operativo. COM utiliza Identificadores Globales Únicos (GUIDs) para
identificar cada interfaz y clase. Un GUID es un número entero de 128 bits único en el tiempo y
espacio. A estos números se les asigna también un texto legible sólo por conveniencia y con un ámbito
más local (el PROGID). Este GUID asegura que no se solicitará ningún componente COM ni interfaz
ni método accidentalmente, incluso en redes de miles de objetos componentes.

Además de las clases y los interfaces, COM utiliza GUIDs para identificar entidades que requieren
valores únicos. Así, los GUIDs reciben nombres distintos según la entidad a la que se aplique:

• CLSID: para clases

• IID: para interfaces

• LIBID: para librerías de tipos

• CATID: para categorías (tipos enumerados)

147
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Los GUIDs pueden generarse utilizando las herramientas "guidgen" o "uuidgen", o realizando
llamadas a la función del API Win32 CoCreateGuid. Guidgen.exe y uuidgen.exe se instalan con
Visual Studio en el directorio de Visual Studio\Common\Tools. Estos generadores utilizan un
complejo algoritmo definido por la Open Software Foundation DCE estándar. El GUID generado
depende del instante en que se llamó a la función y del número de su tarjeta de red (o de otra
característica única en ordenadores sin tarjeta de red). Utilizar un ordenador con tarjeta de red para
generar el GUID, garantiza que el número obtenido será único.

Cuando se desarrollan componentes se trabaja con muchos CLSIDs. El teclear estos GUIDs
directamente en el código es engorroso y suelen cometerse errores. Para simplificar esta operación, se
utiliza un texto "legible" que sustituye a esos CLSIDs.

Los PROGID, o identificadores de programas, son cadenas de caracteres que identifican una clase y
que están asociadas a un CLSID en el registro de Windows. Una vez establecida la relación entre el
CLSID y el PROGID se puede utilizar éste para crear componentes. Algunos lenguajes como Visual
Basic, crean automáticamente PROGID y GUID para cada clase. Visual Basic usa el esquema
nombreproyecto.nombredeclase como el PROGID.

El gran inconveniente que tiene el uso de los PROGID es que puede generar conflictos con otros
nombres ya existentes en la red, ya que no está garantizada su unicidad, como sucede con los CLSIDs.

Enlace temprano y tardío


El enlace tardío (late binding) ocurre en cualquier lenguaje en el que se use un tipo de datos neutral
para llamar a un objeto. En Visual Basic, esto se hace declarando una variable del tipo Object. En
Visual C++ se hace utilizando el interfaz IDispatch para invocar los métodos del objeto. El compilador
o la herramienta de desarrollo no puede comprobar los métodos, propiedades, tipo y sintaxis de
ninguna llamada a los métodos del objeto en tiempo de compilación, ya que no se ha definido el tipo
de objeto concreto.

El Código fuente 118 es un ejemplo de enlace tardío a un objeto Connection de ADO en Visual Basic.

Dim objCnx As Object


Set objCnx = CreateObject(“ADODB.Connection”)
objCnx.metodo

Código fuente 118

Una ventaja del enlace tardío es que no es necesario identificar el objeto que se quiere crear hasta el
tiempo de ejecución. Por otro lado, una desventaja es que el compilador no puede comprobar la
sintaxis. Como resultado de ello, errores como el pasar un número incorrecto de parámetros o con
tipos incorrectos no se detectarán hasta el tiempo de ejecución. Aparte de esto, el enlace tardío es
menos eficiente que el enlace temprano, ya que requiere un mayor nivel de comunicación entre el
cliente y el componente para cada operación.

Algunos entornos, tales como los motores de scripts, sólo soportan el enlace tardío para llamar a los
componentes. Este es el caso de las páginas ASP.

Por el contrario, el enlace temprano (early binding) ocurre cuando se conoce el tipo del objeto en
tiempo de compilación. Se establece un enlace temprano cuando se declaran variables de un tipo
específico de datos. En ese caso el compilador puede contrastar todas las referencias del objeto con las

148
© Grupo EIDOS 7. El modelo COM

que define el entorno del objeto (normalmente incluidas en bibliotecas de tipos). Las bibliotecas de
tipos pueden ser ficheros *.TLB independientes o estar incorporadas en los componentes.

En Visual Basic, los enlaces tempranos requieren que se haga una referencia al componente y a la
clase específica usada cuando se declaran variables de ese tipo de objeto. Visual C++ utiliza la
cláusula #import o cabeceras de interfaces para obtener la información de tipos de los componentes.

El Código fuente 119 es un ejemplo de enlace temprano a un objeto Connection de ADO en Visual
Basic.

Dim objCnx As ADODB.Connection


Set objCnx = New ADODB.Connection
objCnx.metodo

Código fuente 119

Dado que el compilador tiene acceso a la biblioteca de tipos del objeto, puede comprobar la sintaxis de
todas las llamadas que usan esa variable de objeto e informarle de cualquier error sintáctico en tiempo
de diseño.

Los métodos de los componentes que se llamen utilizando enlace temprano son los más eficientes en
términos de rendimiento. Dado que se dispone de la información en tiempo de compilación, el
compilador puede generar un código más óptimo para las llamadas.

Los entornos de programación como Visual Basic y Visual C++ soportan la creación de componentes
que serán llamados con enlaces tempranos o tardíos. No así los motores de script que utilicemos desde
una página ASP, que sólo permiten el enlace tardío.

Espacio de proceso de un componente


Un componente que se ejecuta en el mismo proceso que la aplicación comparte el mismo espacio de
memoria que la aplicación contenedora; ésta puede ser una aplicación propiamente dicha u otro
componente que se ejecuta en el mismo proceso. Los componentes que se ejecutan en el mismo
proceso se denominan COM DLLs.

Los componentes que se ejecutan en otro proceso aparte tienen su propio espacio de memoria
separado del de la aplicación contenedora. Estos componentes se denominan COM EXEs. Este curso
se centra en la construcción de componentes COM DLLs, que son los que soportan los Servicios de
Componentes (antes denominados MTS).

Visual Basic 6.0 y Delphi (entre otros) utilizan el término "DLL ActiveX" para referirse a los COM
DLLs y "EXE ActiveX" para los COM EXEs. Esta denominación es equivalente a la de COM DLL y
COM EXE, respectivamente.

Los controles ActiveX son un tipo especial de COM DLL que proporciona un interfaz visual al
usuario. Obviamente, este tipo de componentes no son de utilidad en el script de servidor de una
aplicación ASP, aunque sí podrían serlo como controles ActiveX que se ejecutaran el la máquina
cliente.

Cada tipo de componente tiene sus ventajas e inconvenientes:

149
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Un componente DLL proporciona un acceso al objeto más rápido, pero es menos tolerante al
fallo: si el componente DLL falla, lo hará el proceso servidor completo.

• En un componente EXE, los fallos no afectan a todos los procesos del sistema, sólo al proceso
en el que se encuentra el componente EXE. Es más lento porque las llamadas del método han
de ser reconfiguradas entre los procesos.

El interfaz IUnknown
Por convención, el nombre de un interfaz siempre lleva el prefijo "I".

Para poder utilizar un objeto, un cliente necesita conocer qué interfaces soporta ese objeto. Según la
especificación COM, un objeto debe poder ser consultado por un cliente para averiguar esta
información. Esto se consigue gracias al interfaz denominado IUnknown, que deben soportar todos los
objetos. Es más, cualquier otro interfaz en el proyecto debe incluir la funcionalidad proporcionada por
el interfaz IUnknown. Aunque Visual Basic maneja muchas de estas cuestiones sin que nos demos
cuenta, de forma transparente para nosotros.

Tradicionalmente, los clientes destruyen los objetos cuando ya no los necesitan. Debido a que
múltiples clientes de diferentes procesos, e incluso de diferentes ordenadores, pueden conectarse y
utilizar un objeto, cuando un cliente destruye un objeto podría dejar a otros clientes "colgados". Por
eso es conveniente mantener un contador, de forma que si el contador es mayor que "0", significa que
el objeto sigue en activo; y si indica un número igual a "0", el objeto se autodestruye.

Para llevar este recuento, el interfaz IUnknown tiene dos funciones:

• AddRef: Incrementa en uno el contador del objeto cuando éste asigna un puntero de interfaz Se
llama a AddRef cuando utiliza una sentencia Set para inicializar una variable del objeto.

• Release: Disminuye en uno el contador cuando una variable que señala al objeto se sale del
ámbito. Se llama esta función cuando se asigna Nothing a una variable del objeto, o cuando la
variable sobrepase el ámbito de su definición.

Además, el interfaz IUnknown presenta la función QueryInterface, que permite a los clientes
preguntar por otros interfaces proporcionados por el objeto. Si el objeto soporta ese interfaz,
QueryInterface devolverá un puntero a ese interfaz. Luego podrá utilizar los métodos que contiene ese
interfaz para comunicarse con el objeto.

Windows DNA
Se agrupa bajo el nombre de Windows DNA (Windows Distributed InterNet Application Architecture)
a un conjunto de tecnologías que permiten desarrollar aplicaciones distribuidas para internet/intranet
usando el modelo de n capas.

Los servicios DNA ofrecen interfaces COM para que otras aplicaciones puedan utilizarlos. Puedo usar
cualquier herramienta de desarrollo que soporte COM para escribir componentes para el DNA.

Windows DNA se compone de los siguientes servicios (todos ellos basados en COM):

• Componentes COM

150
© Grupo EIDOS 7. El modelo COM

• HTML dinámico (DHTML)

• Servidor IIS

• Páginas ASP

• Componentes de acceso a datos (MDAC)

• Servicios de componentes (antes MTS)

• Colas de mensajes (MSMQ)

• Active Directory (ADSI)

• Servicios de seguridad de Windows

151
Componentes en Visual Basic

Introducción
En este tema vamos a empezar a construir nuestros propios componentes. El entorno de trabajo
utilizado en este tema y en los posteriores será un sistema operativo Windows 2000 Server, con el
servidor web IIS 5, con Visual Basic 6 y Visual InterDev 6, y el Internet Explorer 5.

Habrá ocasiones en que existan diferencias en la funcionalidad de los componentes si éstos se ejecutan
en Windows 2000 o en Windows NT. En esos casos se hará mención de estas diferencias.

Componentes en Visual Basic y Visual C++


Después de haber visto en temas anteriores que desde páginas ASP podemos utilizar un gran número
de componentes ya escritos (componentes ActiveX de servidor, componentes ADO, componentes
CDONTS, …), vamos a avanzar un paso más. Escribiremos estos componentes nosotros mismos, y los
utilizaremos después desde las páginas ASP de nuestra aplicación web.

Al ser COM una especificación binaria, podemos escribir los componentes en el lenguaje de
programación que nos resulte más cómodo, y que más se adapte a los resultados que queremos
obtener. Por supuesto que el lenguaje de programación elegido deberá ser capaz de generar
componentes que cumplan con la especificación COM. Entre estos lenguajes están Visual Basic,
Visual C++, Delphi, etc.

El lenguaje que vamos a emplear para los ejemplos va a ser el Visual Basic, y esto será así por varios
motivos.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

El primero, y más importante, es que el Visual Basic es un lenguaje conocido, al menos a un nivel
básico, por la mayoría de los programadores, y no habrá necesidad de dedicar tiempo y esfuerzo al
aprendizaje de un lenguaje de la complejidad de Visual C++.

Además el Visual Basic, a pesar de su facilidad de uso, nos permite hacer prácticamente lo mismo que
podríamos hacer con Visual C++.

Es cierto que no podremos alcanzar el nivel de control que puede lograrse programando en Visual
C++, pero esto es así porque el Visual Basic simplifica muchas de las operaciones que en Visual C++
tendríamos que codificar nosotros.

Por ejemplo, todos los componentes COM implementan el interfaz IUnknown, pero esto a mí como
programador me queda oculto por el Visual Basic. También el Visual Basic crea automáticamente el
interfaz por defecto, agrupando en él todos los métodos públicos del componente. Será a este interfaz
por defecto al único al que tengamos acceso desde el script de una página ASP.

Por supuesto que programar los componentes en Visual C++ tiene una serie de ventajas, aparte de la
desventaja principal de la mayor dificultad del lenguaje. Entre estas ventajas están:

• El lenguaje Visual C++ tiene mayor potencia y flexibilidad que el Visual Basic, y puedo
lograr un mayor control sobre la funcionalidad del componente.

• El rendimiento conseguido por un componente escrito en Visual C++ es mayor que el de uno
escrito en Visual Basic.

• El depurador de Visual C++ es más completo que el de Visual Basic.

• Los componentes escritos en Visual C++ pueden beneficiarse de algunos servicios COM+ a
los que no tienen acceso (por el momento) los componentes escritos en Visual Basic. Entre
estos servicios están el pooling de objetos. Este concepto es parecido al del pooling de
conexiones de una base de datos: cuando una aplicación cliente ha acabado de utilizar una
conexión con la base de datos, en vez de ser destruida, esta conexión se guarda en un pool
(como si dijéramos un almacén) de donde puede ser utilizada luego por otra aplicación cliente
que la necesite. De igual manera, en el pooling de objetos, cuando un cliente ha terminado de
utilizar un objeto, éste no se destruye sino que se guarda en el pool, de donde puede ser
recuperado y reutilizado por otro cliente

Un primer ejemplo de componente


Vamos a proceder a escribir nuestro primer componente. Este componente va a ser muy simple, pero
de todas formas nos tomaremos un tiempo en pensar detenidamente cómo vamos a diseñarlo.

La funcionalidad de este componente será la de calcular el resultado de incrementar una cantidad en


un 7 por ciento de su valor. Como se ve, no parece necesario crear un componente para esta operación
tan sencilla, pero nos vendrá muy bien como primer ejemplo.

Fases en el diseño del componente


Lo primero en el diseño de un componente sería definir claramente qué funcionalidad espero que
tenga. Recordemos que un componente puede compararse con una caja negra que tiene una
funcionalidad, a la que yo puedo acceder a través de un interfaz, sin preocuparme en absoluto de cómo

154
© Grupo EIDOS 8. Componentes en Visual Basic

se lleva a cabo internamente esa funcionalidad, ni siquiera en qué lenguaje de programación está
escrito el componente.

La funcionalidad del componente será la de calcular la nueva cantidad, resultado de incrementar en un


porcentaje del 7 por ciento.

El lenguaje de programación ya hemos dicho que va a ser el Visual Basic, que nos permite con relativa
facilidad obtener unos resultados satisfactorios.

Para acceder a la funcionalidad del componente, diseñaremos un interfaz formado por un único
método, que recibe como parámetro la cantidad sobre la que queremos efectuar el cálculo.

Más adelante construiremos componentes mucho más complicados que requieran un análisis más
completo, pero para éste no necesitamos más que ponernos ya manos a la obra.

Creación del proyecto ActiveX DLL


Arrancamos Visual Basic 6.0, y en la ventana de Nuevo Proyecto, elegimos la opción ActiveX DLL,
pulsando a continuación el botón Abrir. Visual Basic pone por defecto el nombre de Proyecto1 para el
proyecto y el nombre de Class1 para el módulo de clase. Vamos a cambiarlos por otros más
descriptivos. Los nombres que elijamos serán los que luego formen el ProgId del componente, una vez
compilado y registrado en el equipo.

Figura 34

Cada módulo de clase de Visual Basic define un tipo de objeto. Es la plantilla que define los métodos
y propiedades para un objeto. Los objetos se crean, en tiempo de ejecución, al crear una instancia de
una clase. Un componente puede contener varios módulos de clase, y servir por tanto de plantilla para
la creación de varios tipos de objeto. Es lo que se llama un COM Server. En este caso, nuestro
componente sólo contendrá un módulo de clase.

155
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Para cambiar el nombre del proyecto, seleccionarlo haciendo click sobre él en la ventana del
Explorador de proyectos, y luego modificando su nombre en la ventana de Propiedades (cambiarlo a
CompPorcentaje). Repetir la operación seleccionando el módulo de clase en la ventana del Explorador
de proyectos y luego modificando el nombre en la ventana de Propiedades (cambiarlo a Porcentaje).

Diseño del interfaz


Para diseñar el interfaz que presentará el componente, puedo hacerlo codificando directamente los
métodos (en este caso sólo uno) en la ventana de edición de código, pero es más cómodo hacerlo
usando el Generador de clases.

Esta utilidad aparece en el menú Complementos. De no ser así, pulsar dentro del menú Complementos
la opción Administrador de complementos. En la ventana que aparece, seleccionar la utilidad
Generador de clases de VB 6, y activar las casillas de verificación Cargado/Descargado y Cargar al
iniciar (si queremos que siempre que iniciemos el Visual Basic esté disponible esta utilidad en el menú
de Complementos).

Ahora la utilidad está disponible en el menú Complementos, y no hemos de hacer más que
seleccionarla para que se inicie. Se muestra un mensaje de advertencia, debido a que el módulo de
clase no fue creado desde el Generador, sino al crear el nuevo proyecto; responder que Sí.

Una vez abierta, seleccionar en el panel izquierdo el módulo de clase (en este caso lo hemos llamado
Porcentaje), y pulsando sobre él con el botón derecho seleccionar Nuevo método.

Figura 35

En la ventana del Generador de métodos que aparece, similar a la de la Figura 36, asignarle un nombre
al método. Le llamaremos “calcular”. En esta misma ventana, pulsar el botón que aparece etiquetado
con un signo “+, para definir el argumento que va a recibir este método.

156
© Grupo EIDOS 8. Componentes en Visual Basic

Figura 36

En la nueva ventana Agregar argumento que aparece, similar a la de la Figura 37, darle un nombre al
argumento (por ejemplo “cantidad”), activar la casilla de verificación de ByVal, y seleccionar Variant
de la lista desplegable Tipo de dato. Más adelante veremos los problemas que se nos pueden plantear
al utilizar tipos de datos distintos de Variant dentro de un componente que luego usemos desde una
página ASP; recordemos que el VBScript, como JavaScript, es un lenguaje de script débilmente
tipado, donde sólo existe el tipo de dato Variant.

Figura 37

Al activar la casilla de verificación de ByVal, estamos indicando que el parámetro se pasará por valor,
es decir, el método recibirá internamente una copia del valor que se le pase, y trabajará con esa copia,
y no con el valor original. La otra forma de pasar el valor sería ByRef, por referencia, que es la que se

157
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

usa si usamos explícitamente la palabra ByRef o si no indicamos absolutamente nada, ya que es el


valor por defecto.

Después de pulsar Aceptar, y ya de vuelta en la ventana del Generador de métodos, seleccionaremos


Variant de la lista desplegable de Tipo de datos de retorno (es decir, estamos definiendo un método
que será una función de Visual Basic, en vez de un procedimiento). El resultado final será el de la
Figura 38. Pulsando Aceptar volvemos a la ventana del Generador de clases, donde ya podemos ver el
método que acabamos de declarar.

Figura 38

Es recomendable, antes de salir de la utilidad Generador de clases, seleccionar la opción Actualizar


proyecto del menú Archivo, para asegurarnos de que los cambios que acabamos de introducir en el
interfaz tienen efecto, y no los perdemos al salir. Incluso antes de cerrar la utilidad podemos ver, en la
ventana de edición de código que queda debajo de la del Generador de clases, que se genera el Código
fuente 120, necesario para implementar el método recién creado.

Public Function calcular(ByVal cantidad As Variant) As Variant


End Function

Código fuente 120

Cerremos ya la utilidad sin miedo. Ya en la ventana de edición de código, escribamos las líneas
necesarias para que el método tenga la funcionalidad que queremos. Esto se reduce a la línea que
aparece en el Código fuente 121.

158
© Grupo EIDOS 8. Componentes en Visual Basic

Public Function calcular(ByVal cantidad As Variant) As Variant


calcular = cantidad * 1.07
End Function

Código fuente 121

Guardemos el módulo de clase (PORCENTAJE.CLS) y el proyecto (COMPPORCENTAJE.VBP) en


la carpeta que queramos, y ya estamos en disposición de generar la DLL.

Generación de la DLL y registro del componente


Para generar la DLL, no tenemos más que seleccionar la opción “Generar CompPorcentaje.dll” dentro
del menú Archivo. Esto creará el fichero DLL en la ubicación que hayamos elegido.

Pero al mismo tiempo que se ha generado la dll, Visual Basic se ha encargado de registrar el
componente en nuestro equipo. Comprobémoslo.

Si en el menú Inicio seleccionamos Ejecutar y tecleamos “regedit”, se inicia el editor del registro de
Windows. Recordar siempre que no debemos modificar nada del registro a no ser que estemos
completamente seguros de lo que estamos haciendo. En este caso nos limitaremos a comprobar que el
componente está registrado, pero no modificaremos absolutamente nada.

En el registro es posible buscar por el ProgId del componente y por el CLSID.

Si expandimos, dentro del editor del registro, la carpeta etiquetada como HKEY_CLASSES_ROOT,
podremos encontrar una carpeta denominada “CompPorcentaje.Porcentaje”, como se muestra en
laFigura 39, que contiene la información del registro del componente que acabamos de generar. Como
podemos ver, el ProgId que genera el Visual Basic para el componente está formado por el nombre del
proyecto y el nombre del módulo de clase, separados por un punto. Este ProgId será el que utilicemos
al instanciar el componente desde nuestra página ASP. Si expandimos esta carpeta, podemos
seleccionar con un click la subcarpeta Clsid que aparece, y en el panel de la derecha aparecerá el
GUID asignado al componente.

Figura 39

Si ahora seleccionamos la opción Buscar del menú Edición, e introducimos como valor de búsqueda el
valor del CLSID, encontraremos algo similar a laFigura 40.

159
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 40

Si quisiéramos utilizar nosotros este componente en otro equipo, deberíamos copiar el fichero
COMPPORCENTAJE.DLL a la ubicación del equipo destino que elijamos (normalmente se copian en
la carpeta C:\WINNT\SYSTEM32, pero puede ser cualquier carpeta). A continuación abriremos una
ventana MSDOS y nos moveremos hasta esa carpeta, para escribir lo que indica el Código fuente 122.

Regsvr32 CompPorcentaje.dll

Código fuente 122

Esto registrará el componente en el equipo.

Vamos a comprobar ahora que podemos usar este componente desde una página ASP. Iniciamos el
InterDev y creamos una página con el Código fuente 123.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objPorcentaje = Server.CreateObject("CompPorcentaje.Porcentaje")
Response.Write "El resultado es: " & objPorcentaje.calcular(1000)
Set objPorcentaje = Nothing
%>

</BODY>
</HTML>

Código fuente 123

Como el componente está registrado en la misma máquina en la que estamos usando el InterDev, éste
es capaz de acceder al registro de Windows (concretamente a la type library) para proporcionarnos el
“IntelliSense”: al escribir el nombre de la instancia del componente seguida del punto, nos aparece la
lista de los métodos disponibles (en este caso sólo el método “calcular”).

160
© Grupo EIDOS 8. Componentes en Visual Basic

Al solicitar esta página desde el navegador, obtendremos el resultado correcto de 1070.

Rediseño del componente


Supongamos que decidimos rediseñar el componente, de tal forma que el parámetro del método
calcular no se pase por valor, sino por referencia, es decir, que el método trabaje con el mismo valor
original que se le pasa por parámetro, y no con una copia de uso interno del método.

Este cambio supone rediseñar el interfaz, porque vamos a modificar el método calcular, que ahora no
recibirá el parámetro por valor, sino por referencia. No podemos rediseñar este método desde la
utilidad Generadora de clases. Ni siquiera eliminarlo y volverlo a crear. Tenemos que hacerlo
manualmente desde la ventana de edición de código, borrando el cuerpo del método y volviéndolo a
definir con la utilidad Generadora de clases. Ahora debemos dejar desactivada la casilla de
verificación de ByVal.

Aprovecharemos también para hacer que el método devuelva un booleano, en este caso True, para
indicar que ha finalizado con éxito.

Esto tendrá como resultado el Código fuente 124.

Public Function calcular(cantidad As Variant) As Boolean


End Function

Código fuente 124

Vemos que ahora, dentro de los paréntesis, el nombre del parámetro no está precedido de la palabra
ByVal. Con esto se sobreentiende ByRef, que es el valor por defecto para Visual Basic.

Podríamos haber conseguido lo mismo modificando nosotros manualmente todo el código, en vez de
usar la utilidad Generadora de clases. Añadamos ahora el código necesario a la función. Esta vez
hacemos el cálculo del porcentaje y se lo asignamos al mismo parámetro, cantidad. Devolvemos como
valor de retorno de la función el valor True. Todo esto tal cual aparece en el Código fuente 125.

Public Function calcular(cantidad As Variant) As Boolean


cantidad = cantidad * 1.07
calcular = True
End Function

Código fuente 125

El problema se nos presenta ahora en el momento de recompilar, puesto que la dll está actualmente en
uso por dos clientes:

• El Visual InterDev la está usando para proporcionarnos el IntelliSense. Esto tiene fácil
arreglo: cerremos la ventana de edición de la página ASP en que hemos utilizado el
componente

• La aplicación web está usando también el componente, puesto que al pedir la página desde el
navegador se ha ejecutado la página y se ha cargado la dll en memoria. Si hemos tenido la

161
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

precaución de configurar, desde la consola de administración de los servicios IIS, las


propiedades de nuestra aplicación web, en la pestaña Directorio, como se ve en la Figura 41,
de tal forma que la Protección de la aplicación esté en el nivel Medio (agrupado) o Alto
(aislado), podremos, desde esta misma ventana de configuración del IIS, pulsar el botón
Descargar, que descarga de memoria esta aplicación web (y todas las que estuvieran
agrupadas con ella).

Figura 41

• Si con lo anterior no consiguiéramos liberar la dll, deberemos ir a Inicio | Programas |


Herramientas administrativas | Servicios. Dentro de la lista de servicios que aparece,
buscaremos el denominado “Servicio de admin. IIS”, como se ve en la Figura 42. Pulsaremos
sobre él con el botón derecho del ratón y seleccionaremos la opción Detener. Esto detiene
otros servicios aparte del de publicación web, como la publicación FTP y el correo SMTP.
Después de detener el servicio, debemos pulsar otra vez con el botón derecho del ratón y
seleccionar la opción Iniciar, para reiniciarlo. Luego tenemos que ir a la consola
administrativa del IIS, en Inicio | Programas | Herramientas administrativas | Administrador de
servicios Internet, y reiniciar manualmente el Sitio web predeterminado, que estará detenido
como consecuencia de haber detenido el servicio de publicación web. Esta operación se ve en
la Figura 43. Este proceso resulta pesado pero debe bastar para descargar definitivamente la
dll.

162
© Grupo EIDOS 8. Componentes en Visual Basic

Figura 42

Figura 43

• La solución definitiva, si todo lo anterior no es suficiente, es obvia: reiniciar el equipo. Esto


no debe ser necesario en Windows 2000, mientras que en Windows NT es más frecuente tener
que recurrir a esta solución extrema.

Para poder utilizar el nuevo componente, tendremos que modificar la página ASP, que quedará como
en el Código fuente 126.

<%@ Language=VBScript %>

163
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%
Set objPorcentaje = CreateObject("CompPorcentaje.Porcentaje")
cantidad = 1000
retorno = objPorcentaje.calcular(cantidad)
Response.Write cantidad
Set objPorcentaje = Nothing
%>
</BODY>
</HTML>

Código fuente 126

Ahora creamos una variable de página llamada cantidad, a la que asignamos el valor 1000. Esta
variable la pasamos como parámetro, y el método del componente trabaja directamente con ella, con
lo que al escribir después su valor obtenemos el valor correcto de 1070.

En cualquier caso, suele ser más aconsejable pasar los parámetros a un método por valor (ByVal)
mejor que por referencia (ByRef). De esta forma conseguimos más encapsulación del código del
componente.

Reutilización del componente


El componente que acabamos de diseñar es un componente universal. Quiere esto decir que podemos
usarlo desde cualquier aplicación que permita el acceso a componentes COM. Esta es una de las
principales ventajas de la componentización: la posibilidad de reutilizar el código. Antes hemos visto
cómo podíamos utilizar el componente desde una página ASP. Vamos ahora a hacer un proyecto
Visual Basic que también haga uso de él. Para eso seleccionaremos la opción Agregar proyecto del
menú archivo, y seleccionaremos la opción Exe estándar, como en laFigura 44.

Figura 44

164
© Grupo EIDOS 8. Componentes en Visual Basic

Nombraremos al nuevo proyecto como PruebaPorcentaje. Añadimos al formulario un campo de texto


y un botón, y les asignamos, mediante la ventana de Propiedades, los nombres que nos parezcan
oportunos. El formulario quedará algo parecido a la Figura 45.

Figura 45

A este nuevo proyecto debemos añadirle la referencia al componente anterior CompPorcentaje, desde
el menú Proyecto | Referencias, y seleccionado la casilla de verificación correspondiente al
componente CompPorcentaje, como se ve en la Figura 46. De este modo Visual Basic nos facilitará el
IntelliSense, y además podremos utilizar el early binding.

Figura 46

Haciendo doble click sobre el botón, podemos escribir el código necesario para el evento Click del
mismo, que será el del Código fuente 127.

165
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Private Sub Command1_Click()


Dim objPorcentaje As CompPorcentaje.Porcentaje
Dim retorno As Boolean
Dim cantidad As Variant

cantidad = 1000
Set objPorcentaje = New CompPorcentaje.Porcentaje
retorno = objPorcentaje.calcular(cantidad)
Text1.Text = cantidad
End Sub

Código fuente 127

Las líneas Dim objPorcentaje As CompPorcentaje.Porcentaje y Set


objPorcentaje = New CompPorcentaje.Porcentaje sólo son posibles gracias a que
hemos incluido en el proyecto EXE la referencia al componente CompPorcentaje. Esto permite
declarar la variable objPorcentaje como una variable de referencia a un objeto que se creará como una
instancia a partir del módulo de clase Porcentaje incluido en el proyecto ActiveX CompPorcentaje.
Además permite usar el operador New para hacer un enlace temprano, en tiempo de compilación, con
el código de ese componente.

Si no hubiéramos incluido la referencia a CompPorcentaje, no podríamos hacer nada de lo anterior, y


tendríamos que dejar el código como se ve en el Código fuente 128. El uso del método CreateObject
significa que no estamos haciendo enlace temprano, sino enlace tardío.

Private Sub Command1_Click()


Dim objPorcentaje As Object
Dim retorno As Boolean
Dim cantidad As Variant

cantidad = 1000
Set objPorcentaje = CreateObject("CompPorcentaje.Porcentaje")
retorno = objPorcentaje.calcular(cantidad)
Text1.Text = cantidad
End Sub

Código fuente 128

En el Explorador de proyectos, pulsando con el botón derecho sobre el proyecto PruebaPorcentaje,


debemos seleccionar la opción “Establecer como inicial”, de tal forma que cuando pulsemos el icono
de ejecución de Visual Basic, sea éste el proyecto de arranque, se muestre el formulario, y podamos
pulsar el botón.

Esta pulsación desencadenará la instanciación del componente CompPorcentaje, y la invocación al


método calcular, pasándole como parámetro, por referencia, la variable cantidad. A continuación se le
asigna su valor, ya actualizado por el método, al campo de texto. Por supuesto que este proyecto EXE
estándar podremos depurarlo desde el entorno de Visual Basic, estableciendo puntos de interrupción
con la tecla de función F9 y ejecutando paso a paso con F8.

166
© Grupo EIDOS 8. Componentes en Visual Basic

Tipos de componentes
Hemos visto en los apartados anteriores que este primer componente que hemos desarrollado en
Visual Basic era utilizable tanto desde una página ASP como desde un proyecto estándar de Visual
Basic. Por supuesto que también podríamos utilizarlo desde otra aplicación escrita en cualquier
lenguaje que soporte COM, como podría ser Visual C++. Es un componente que podríamos llamar de
uso universal.

Podemos, sin embargo, diseñar componentes que sean más restrictivos en cuanto a su aplicabilidad.
Supongamos que desarrollamos un componente como el anterior, pero que permita la introducción del
número sobre el que vamos a calcular el porcentaje desde un campo de texto, es decir, que tenga un
interfaz visual.

Si creamos una página HTML desde cualquier editor de HTML, por ejemplo FrontPage, y
seleccionamos la opción Avanzadas del menú Insertar, y a continuación Control ActiveX, podemos
marcar de la lista que aparece el control “Control Calendar 9.0”.

El código html que generará el FrontPage por nosotros quedaría como el Código fuente 129.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Pagina nueva 1</title>
</head>
<body>
<p>
<object classid="clsid:8E27C92B-1264-101C-8A2F-040224009C02" id="Calendar1"
width="288" height="192">
<param name="_Version" value="524288">
<param name="_ExtentX" value="7620">
<param name="_ExtentY" value="5080">
<param name="_StockProps" value="1">
<param name="BackColor" value="-2147483633">
<param name="Year" value="2001">
<param name="Month" value="1">
<param name="Day" value="11">
<param name="DayLength" value="1">
<param name="MonthLength" value="2">
<param name="DayFontColor" value="0">
<param name="FirstDay" value="2">
<param name="GridCellEffect" value="1">
<param name="GridFontColor" value="10485760">
<param name="GridLinesColor" value="-2147483632">
<param name="ShowDateSelectors" value="-1">
<param name="ShowDays" value="-1">
<param name="ShowHorizontalGrid" value="-1">
<param name="ShowTitle" value="-1">
<param name="ShowVerticalGrid" value="-1">
<param name="TitleFontColor" value="10485760">
<param name="ValueIsNull" value="0">
</object>
</p>
</body>
</html>

Código fuente 129

167
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Y en el navegador veremos algo como la Figura 47.

Figura 47

¿Podríamos utilizar este componente desde una página ASP? La respuesta en no, lógicamente, puesto
que las páginas ASP se ejecutan de forma desatendida en el servidor, y no podemos generar ningún
interfaz visual (recordemos que dentro de una página ASP no podemos ni siquiera usar funciones
estándar de Visual Basic como MsgBox).

Con un código en un página ASP como el del Código fuente 130, no lograremos nada; no llegaré a ver
el control, porque en este contexto no tienen sentido los componentes con interfaz visual.

<%
Set objCom = Server.CreateObject("MSCAL.Calendar")
objCom.Day = 11
%>

Código fuente 130

Este sería un componente que sería utilizable en una aplicación Visual Basic, o Visual C++, que
tengan un interfaz visual, pero no dentro de una página ASP.

Vayamos al caso opuesto. Si yo diseño un componente que acceda al modelo de objetos de ASP para
escribir directamente código HTML a través del objeto Response, o para leer el valor de una variable
de aplicación (más adelante veremos que todo esto es posible), estaré haciendo uso de un modelo de
objetos que no tendría ningún sentido si utilizase este componente desde una aplicación Visual Basic o
Visual C++. Este componente no sería utilizable en estas aplicaciones, pero sí en páginas ASP.

168
© Grupo EIDOS 8. Componentes en Visual Basic

Un componente ActiveX Server, como el adRotator, genera una cadena HTML, que tendría sentido
sólo si se estuviera ejecutando dentro de un servidor web, para enviar este resultado HTML a un
navegador.

Estos tres ejemplos de componentes mencionados son dependientes del entorno en el que se ejecutan,
y no podrían considerarse de aplicabilidad universal.

Podríamos diseñar componentes que fueran capaces de reconocer por sí mismos el tipo de entorno en
el que se ejecutan, es decir, si están instanciados desde una página ASP o desde una aplicación Visual
Basic. Serían componentes que se adaptarían en cada caso al entorno, usando el modelo de objetos de
ASP sólo si reconocieran que lo tienen disponible. Esta capacidad de adaptación los convertiría en
reutilizables dentro de una aplicación Visual Basic.

Hay componentes que hacen uso de otros componentes. Un caso muy frecuente es el de los
componentes que acceden a una base de datos, haciendo uso de los componentes de ADO:
Connection, Command y Recordset.

Otro tipo de componente, que veremos con detalle más adelante, es aquél que está involucrado en una
transacción sobre una base de datos. Estos componentes transaccionales tienen unas características
especiales.

169
Modelos de aplicaciones cliente/servidor

Introducción
Antes de continuar con el diseño de componentes para ASP, vamos a ver de forma resumida cómo ha
ido evolucionando el concepto de aplicación cliente/servidor, desde la idea tradicional hasta el modelo
cliente/servidor que podemos conseguir en una aplicación web y, en el caso que a nosotros nos
interesa, en una aplicación ASP.

Arquitectura cliente/servidor en dos capas


En las aplicaciones cliente/servidor tradicionales, como podría ser una aplicación escrita en Visual
Basic, sólo hablamos de dos capas:

• Una sería la capa del servidor de base de datos, lo que se llama el “backend”. Es la capa
encargada de servir los datos, garantizando su consistencia por medio de procedimientos
almacenados y triggers. Esto es lo que se llama la lógica de datos.

• Otra sería la capa del cliente con el interfaz de usuario (el GUI), lo que se llama el “frontend”.
Es la capa encargada de gestionar el interfaz de la aplicación con el usuario, para mostrarle
información, y también para recibirla a través de formularios. Esto es lo que se llama la lógica
de presentación. También es la capa encargada de asegurar que se cumplen las reglas
conforme a la funcionalidad que queremos para nuestra aplicación. Esto es lo que se llama la
lógica de negocio.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Conviene aclarar que habría que distinguir entre capas lógicas y capas físicas. Cuando hablamos de
capas normalmente nos referimos a capas lógicas. Por capas lógicas entendemos software, y por capas
físicas hardware. Lo usual es que la capa de datos esté en una capa física separada, es decir, en un
equipo separado donde sólo se está ejecutando el software del gestor de base de datos, por ejemplo
SQL Server u Oracle, y que la capa con la lógica de presentación y la de datos esté en otra capa física
separada, es decir, el equipo del usuario, donde se ejecute la aplicación cliente escrita en un lenguaje
como Visual Basic.

Pero es también muy normal que durante el tiempo de desarrollo estas dos capas lógicas estén en la
misma capa física, es decir, que en nuestro equipo de desarrollo esté ejecutándose el software del
gestor de base de datos y el software de la aplicación cliente.

En estas aplicaciones cliente/servidor tradicionales hay una conexión permanente entre las dos capas,
lo que las hace distintas de las aplicaciones web, como veremos a continuación.

Los equipos cliente en esta arquitectura tienden a ser lo que se llama “fat clients”. Necesitan bastante
capacidad de proceso en su CPU para poder ejecutar la lógica de presentación y la de negocio.

Arquitectura cliente/servidor en tres capas


El paso siguiente sería la de separar la capa cliente en dos capas, una para la lógica de presentación y
otra para la lógica de negocio. Podríamos hablar entonces de tres capas: capa de presentación, capa de
negocio o intermedia, y capa de datos.

De esta forma descargamos al cliente, para el que ya no es necesaria tanta capacidad de proceso en su
CPU, y llevamos esta lógica de negocio a un servidor, que normalmente estará separado del servidor
de base de datos.

Podemos considerar que las aplicaciones web entran dentro de esta arquitectura, aunque con algunos
matices.

Una aplicación web (y a nosotros nos interesan las aplicaciones ASP, que serían un caso particular)
son aplicaciones especiales. Están formadas por un conjunto de páginas HTML, ASP, y otros recursos,
que se ejecutan dentro de un servidor.

La capa de presentación estaría en el PC del cliente, donde se ejecuta el software del navegador (como
puede ser el Internet Explorer o el Netscape Navigator), con la capacidad que éste tiene de interpretar
el código HTML para generar el interfaz de usuario, junto con la capacidad de ejecutar scripts escritos
en lenguajes como JavaScript o VBScript, para dotar de un cierto dinamismo e interactividad a este
interfaz. Otras posibilidades serían el uso de controles ActiveX, applets de Java, etc.

La capa de negocio o intermedia estaría en el servidor web, donde se ejecuta el software de servidor
web (como puede ser el IIS o un servidor Netscape), con la capacidad de atender peticiones vía HTTP
de múltiples clientes. Esta lógica de negocio se codifica por medio de páginas web y componentes de
servidor.

La capa de datos está en el servidor de datos, donde se ejecuta el software del gestor de base de datos
(como puede ser el SQL Server o el Oracle), con la capacidad de gestionar el acceso de clientes a los
datos almacenados, asegurando su consistencia, administrando bloqueos, transacciones, haciendo uso
de procedimientos almacenados, triggers, etc.

Si la aplicación va a tener que soportar accesos a la base de datos de muchos usuarios


simultáneamente, conviene que las capas de negocio y de datos estén en equipos físicos indepen-

172
© Grupo EIDOS 9. Modelos de apliciones cliente / servidor

dientes, para que el gestor de base de datos pueda ejecutarse de una forma mucho más eficiente, ya
que consume bastante memoria, CPU y accesos al disco.

En tiempo de desarrollo es muy frecuente que las tres capas estén en el mismo equipo físico, y que se
estén ejecutando en la misma máquina el gestor de base de datos, el servidor web y el navegador.

En las aplicaciones web no hay una conexión permanente entre cliente y servidor. Recordemos que el
protocolo HTTP es un protocolo sin estado. Cuando un navegador cliente hace una petición de un
recurso a un servidor, se crea una conexión temporal entre los dos, el servidor atiende la petición y
manda la respuesta e inmediatamente se cierra la conexión. El servidor pierde completamente el rastro
de este cliente, y destruye toda la información de estado que hubiera utilizado durante el tiempo en que
estaba procesando la petición, de forma que si este mismo cliente vuelve a hacer otra petición al
servidor, sería como empezar otra vez de cero.

Varias soluciones se han desarrollado para conseguir mantener el estado, y convertir así el HTTP en
un protocolo capaz de “recordar al cliente” entre distintas peticiones. Unas soluciones pasan por
almacenar esta información de estado en el propio cliente, por medio de cookies, y otras pasan por
almacenar esta información en el servidor, en bases de datos, en ficheros, o en el caso de páginas ASP
conservando variables dentro del objeto Session. También se usa con frecuencia el paso de
información de una página a otra, por medio de parámetros ocultos (del tipo “hidden”) dentro de
formularios. En cada caso será más adecuada una u otra solución.

El hecho de que la conexión entre cliente y servidor no sea permanente supone una desventaja de las
aplicaciones web respecto a las aplicaciones cliente/servidor tradicionales. Resulta más difícil
conseguir que la lógica de la aplicación funcione correctamente cuando se trata de un conjunto de
páginas distintas que son enviadas al cliente una detrás de otra, sin mantenimiento de estado entre
ellas.

Sin embargo las aplicaciones web también tienen una ventaja, y es el hecho de que la parte cliente se
actualiza automáticamente, sin necesidad de ir a reinstalar cada vez que hay una modificación. Esto es
así porque el código con la lógica de presentación se ejecuta en el cliente, es cierto, pero es en el
servidor donde se genera de forma dinámica en el momento en que se hace cada petición (es decir, el
script de la página ASP se encarga de generar dinámicamente el código HTML que será enviado al
navegador del cliente para que éste lo interprete y construya el interfaz visual: marcos, tablas, etc). De
esa manera si yo modifico las páginas HTML o ASP, cuando el cliente haga una nueva petición
recibirá la versión actualizada de forma inmediata.

Precisamente este hecho hace que las aplicaciones web no encajen perfectamente bien en el concepto
de aplicaciones en tres capas, porque la lógica de presentación realmente se genera en el servidor web,
es decir, en la capa de negocio, aunque luego se ejecute en la capa de presentación.

Los procedimientos almacenados también se salen un poco del modelo de tres capas, porque suponen
una lógica de negocio pero que está almacenada no en la capa intermedia sino en la capa de datos, es
decir, en el gestor de la base de datos.

En cualquier caso, esta división de una aplicación en capas lógicas no debe entenderse como una
clasificación estricta.

Normalmente el cliente en las aplicaciones web es lo que se llama un “thin client”, porque necesita
muy poca capacidad de CPU. Realmente sólo la necesaria para ejecutar un navegador, capaz de
generar el interfaz de usuario con HTML y JavaScript. Si quiero conseguir un interfaz más rico, tengo
que cargar más el cliente, con controles ActiveX y applets, pudiendo entonces descargar de trabajo al
servidor.

173
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Ejemplo de una aplicación ASP en tres capas


Para aclarar los conceptos expuestos, apliquemos en un ejemplo todo lo dicho sobre las capas de una
aplicación.

La aplicación estará formada por tres capas:

• La capa de datos estará formada por el gestor SQL Server. Usaremos la base de datos pubs
que viene incorporada como ejemplo al instalar el gestor. Servirá los datos a la aplicación
cliente, en este caso una página ASP.

• La capa intermedia estará formada por una página ASP, que genere dinámicamente una tabla
HTML con los registros de la tabla stores de la base de datos pubs, y que será servida por un
servidor web, en este caso el IIS.

• La capa de presentación será un navegador, en este caso el Internet Explorer, que se encargará
de pedir la página ASP al servidor y de recibir la respuesta en forma de código HTML que
mostrará por pantalla.

El código de la página ASP podría ser como el Código fuente 131.

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objCnx = Server.CreateObject("ADODB.Connection")
objCnx.Open "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data
Source=varrondo"
Set objRst = objCnx.Execute("stores",,adCmdTable)
%>

<div align="center">
<table border="1">
<tr>
<%for each elem in objRst.Fields%>
<th><%=elem.Name%></th>
<%next%>
</tr>
<%while not objRst.EOF%>
<tr>
<%for each elem in objRst.Fields%>
<td><%=elem.Value%></td>
<%next%>
</tr>
<%objRst.MoveNext%>
<%wend%>
</table>
</div>

<%
objRst.Close
objCnx.Close
Set objRst = Nothing
Set objCnx = Nothing
%>

174
© Grupo EIDOS 9. Modelos de apliciones cliente / servidor

</BODY>
</HTML>

Código fuente 131

y conseguiríamos el resultado que se muestra en la Tabla 6.

stor_id stor_name stor_address city state zip

6380 Eric the Read Books 788 Catamaugus Ave. Seattle WA 98056

7066 Barnum's 567 Pasadena Ave. Tustin CA 92789

7067 News & Brews 577 First St. Los Gatos CA 96745

7131 Doc-U-Mat: Quality Laundry and Books 24-A Avogadro Way Remulade WA 98014

7896 Fricative Bookshop 89 Madison St. Fremont CA 90019

8042 Bookbeat 679 Carson St. Portland OR 89076

Tabla 6

Componentización de la capa intermedia

Diseño de la capa intermedia con un componente


Vamos a rescribir la capa intermedia de nuestra aplicación, para poder usar las ventajas que ofrece la
componentización. En realidad, escribiremos un componente orientado a los datos, a medio camino
entre la capa intermedia y la capa de datos, puesto que su función será la de recuperar los datos del
gestor de base de datos y entregárselos a la capa intermedia.

El código de acceso a la base de datos vamos a incluirlo en un componente que devuelva un recordset
con los registros de la tabla stores. La página ASP simplemente se encarga de crear el objeto e invocar
al método correspondiente, recuperando el recordset y formateando sus datos en una tabla HTML. De
esta forma conseguiremos al menos tres ventajas:

• Este mismo componente que creemos podríamos reutilizarlo en otras páginas ASP de esta
misma aplicación que necesiten obtener un recordset con los registros de la tabla stores.
También podríamos utilizarlo en páginas ASP de otras aplicaciones web. Incluso en
aplicaciones tradicionales escritas en Visual Basic o Visual C++.

• Si por cualquier motivo cambia la forma de acceder a los datos, porque cambiamos el gestor
de base de datos de SQL Server a Access, o porque ahora decidimos acceder a través de un
procedimiento almacenado, sólo tendremos que cambiar este componente y no hará falta que
modifiquemos el código de la página ASP.

175
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• El acceso a los datos será más rápido, porque el código estará compilado dentro del
componente, y además podremos utilizar el operador New de Visual Basic, lo que supone un
enlace temprano al componente. Desde el script de una página ASP sólo podemos utilizar el
enlace tardío que proporciona el método CreateObject.

Para crear el componente, abrimos un nuevo proyecto ActiveX DLL de Visual Basic y cambiamos el
nombre del proyecto a “CompStores” y el del módulo de clase a “Stores”.

Como el componente va a acceder a la base de datos a través de ADO, necesitamos añadir al proyecto
la referencia a ADO. Esto lo hacemos seleccionando la opción Referencias del menú Proyecto, como
puede verse en la Figura 48, y en la lista que aparece seleccionando “Microsoft ActiveX Data Objects
2.5 Library”.

Figura 48

Al incluir esta referencia conseguimos dos cosas:

• Por una parte se carga la type library de ADO, y Visual Basic nos ayudará con el IntelliSense
a medida que vamos escribiendo el código.

• Podré utilizar el early binding, lo que supone un mayor rendimiento a la hora de ejecutar el
componente

El código del componente quedaría como en el Código fuente 132.

Option Explicit

Public Function listaStores() As ADODB.Recordset


Dim objCnx As ADODB.Connection
Dim objRst As ADODB.Recordset
Dim strCnx As String

176
© Grupo EIDOS 9. Modelos de apliciones cliente / servidor

strCnx = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data


Source=varrondo"
Set objCnx = New ADODB.Connection
objCnx.Open strCnx
Set objRst = New ADODB.Recordset
objRst.Open "Stores", objCnx, , , adCmdTable
Set listaStores = objRst
End Function

Código fuente 132

Como se ve, el interfaz está formado por un único método que no recibe parámetros, y que devuelve
un objeto recordset. Todo el acceso a la base de datos queda dentro del componente, y descargo de esa
tarea al script de la página ASP, que ahora tendría el Código fuente 133.

Los objetos objCnx y objRst son locales al método del componente, y se liberan automáticamente al
salir del mismo, sin necesidad de que yo lo haga explícitamente.

<%@ Language=VBScript %>


<!--#include file="ADOVBS.inc"-->
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objCom = Server.CreateObject("CompStores.Stores")
Set objRst = objCom.listaStores
%>

<div align="center">
<table border="1">
<tr>
<%for each elem in objRst.Fields%>
<th><%=elem.Name%></th>
<%next%>
</tr>
<%while not objRst.EOF%>
<tr>
<%for each elem in objRst.Fields%>
<td><%=elem.Value%></td>
<%next%>
</tr>
<%objRst.MoveNext%>
<%wend%>
</table>
</div>

<%
Set objCom = Nothing
%>

</BODY>
</HTML>

Código fuente 133

177
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Sólo tengo que crear una instancia del componente e invocar al método listaStores, recuperando el
recordset que me devuelve en el objeto objRst. Luego sólo hay que formatear los datos que contiene
en una tabla HTML, igual que en el caso anterior.

Rediseño del componente de la capa intermedia


Si ahora decidimos cambiar el componente, para que acceda a través de un procedimiento almacenado,
no tengo que tocar el código de la página ASP.

El procedimiento almacenado es el del Código fuente 134.

CREATE PROCEDURE RecuperaStores


AS
SELECT * FROM Stores

Código fuente 134

Y el Código fuente 135 es el que tendría ahora el componente (recordar los pasos que hay que seguir
para descargar la dll de la memoria y poder así recompilar).

Option Explicit

Public Function listaStores() As ADODB.Recordset


Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objRst As ADODB.Recordset
Dim strCnx As String

strCnx = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data


Source=varrondo"
Set objCnx = New ADODB.Connection
objCnx.Open strCnx
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "RecuperaStores"
Set objRst = New ADODB.Recordset
objRst.Open objCmd, , , , adCmdStoredProc
Set listaStores = objRst
End Function

Código fuente 135

Un segundo rediseño del componente de la capa intermedia


Incluso puedo rediseñar esta capa intermedia, llegando a una solución que seguramente resulta más
óptima: en vez de que el método del componente devuelva un objeto Recordset, que siempre consume
más recursos, haremos que devuelva una cadena, ya con el código HTML necesario para construir la
tabla.

Nos serviremos para ello del método GetString del Recordset de ADO, que devuelve los datos del
recordset en forma de cadena.

178
© Grupo EIDOS 9. Modelos de apliciones cliente / servidor

Este método recibe cinco parámetros:

• El primero puede tomar únicamente el valor por defecto adClipString.

• El segundo es el número de registros que van a ser convertidos a la cadena. Si no especifico


este parámetro, se convierten todos los registros.

• El tercero es el delimitador que se usará en la cadena para separar los distintos campos de un
registro. El valor por defecto es un tabulador. Para conseguir formatear esta cadena en forma
de tabla de HTML, nos interesa que este separador sea “</td><td>”, con lo cual cerramos la
etiqueta de la celda anterior y abrimos la de la celda siguiente.

• El cuarto es el delimitador que usará en la cadena para separar los distintos registros. El valor
por defecto es un retorno de carro. Nos interesa que este separador sea “</td></tr><tr><td>”,
para cerrar la celda y fila anteriores, y abrir la fila y celda siguientes.

• El quinto y último parámetro es el carácter que se usará para los campos que tengan valor
NULL. Por defecto es la cadena vacía.

El Código fuente 136 es el de la nueva versión del componente.

Option Explicit

Public Function listaStores() As String


Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objRst As ADODB.Recordset
Dim strCnx As String
Dim strRst As String

strCnx = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data


Source=varrondo"
Set objCnx = New ADODB.Connection
objCnx.Open strCnx
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "RecuperaStores"
Set objRst = New ADODB.Recordset
objRst.Open objCmd, , , , adCmdStoredProc
strRst = objRst.GetString(, , "</td><td>", "</td></tr><tr><td>")
strRst = Mid(strRst, 1, Len(strRst) - 8)
strRst = "<table border=1><tr><td>" & strRst & "</table>"
listaStores = strRst
End Function

Código fuente 136

La línea strRst = Mid(strRst, 1, Len(strRst) - 8) es necesaria para quitar el


“<tr><td>”del final de la cadena, que sobra puesto que ya no hay más filas.

El Código fuente 137,de la página ASP, queda ahora mucho más reducido.

<%@ Language=VBScript %>


<!--#include file="ADOVBS.inc"-->
<HTML>

179
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objCom = Server.CreateObject("CompStores.Stores")
Response.Write objCom.listaStores
Set objCom = Nothing
%>

</BODY>
</HTML>

Código fuente 137

Arquitectura cliente/servidor en n capas


Cuando se divide la capa de negocio o intermedia de la aplicación ASP en varias capas, empieza a
hablarse de arquitectura en n capas.

Generalmente se trata de cuatro capas, al dividir la capa intermedia en dos:

• Una capa está formada por componentes orientados al cliente, es decir, más cercanos a la capa
de presentación, que hacen uso del modelo de objetos de ASP para escribir directamente
código HTML a través del objeto Response.

• Otra capa está formada por componentes orientados a los datos, es decir, más cercanos a la
capa de datos, a los que accede a través de ADO.

Más adelante retomaremos este ejemplo, y veremos cómo se pueden crear estos componentes
orientados al cliente, accediendo al modelo de objetos de ASP.

Incluso la capa intermedia puede dividirse no sólo en estas dos capas lógicas, sino en dos capas físicas
en dos servidores distintos: uno tiene el “servidor web” junto con los componentes orientados al
cliente, y otro tiene el “servidor de aplicación” junto con los componentes de lógica de negocio y los
orientados a datos.

180
Diseño de componentes para ASP

Introducción
En este tema nos vamos a centrar en las particularidades que tienen los componentes que diseñemos
para ASP.

Ya hemos visto en temas anteriores que, si bien todos los componentes cumplen la especificación
COM, unos serán utilizables en un contexto y otros no. A nosotros nos interesan los componentes que
sean utilizables desde páginas ASP, así como los condicionantes que esto nos impone a la hora de
desarrollar el código de los mismos.

Tipos Variant
Los que programamos en ASP sabemos que los lenguajes de script, como el VBScript, son lenguajes
débilmente tipados, donde sólo existe el tipo Variant.

Este hecho tiene su importancia a la hora de diseñar un componente pensado para ser utilizado desde
una página ASP. Dentro del componente estoy programando en Visual Basic, que es un lenguaje que
sí me permite declarar las variables con distintos tipos.

Volvamos al componente que ya hemos empleado en temas anteriores, que presentaba un método que
calculaba el incremento en un porcentaje del 7 por ciento sobre una cantidad pasada como argumento.
En una de las versiones de este componente, diseñamos el método de tal forma que recibía este
parámetro por referencia, es decir, va a trabajar con la variable original y no con una copia de la
misma.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Ya hemos probado este componente y hemos visto que funcionaba correctamente al instanciarlo desde
una página ASP. Pero esto era así mientras en el método teníamos el parámetro definido como
Variant. De esta forma, desde la página ASP pasamos al componente una variable Variant, el
componente trabaja con ella, tomándola como Variant, y puede modificar su valor.

Si ahora abrimos el proyecto ActiveX DLL correspondiente a este componente, y modificamos el


código del módulo de clase hasta dejarlo como queda en el Código fuente 138, podemos volver a pedir
la página ASP que lo utilizaba para ver lo que ocurre.

Public Function calcular(cantidad As Integer) As Boolean


cantidad = cantidad * 1.07
calcular = True
End Function

Código fuente 138

Lo único que hemos cambiado es el tipo de datos del parámetro: ya no es Variant, sino Integer.
Recordemos que el código de la página ASP era como el Código fuente 139.

<%
Set objPorcentaje = CreateObject("CompPorcentaje.Porcentaje")
cantidad = 1000
retorno = objPorcentaje.calcular(cantidad)
Response.Write cantidad
Set objPorcentaje = Nothing
%>

Código fuente 139

El resultado que obtenemos es un error No coinciden los tipos: 'calcular'.

¿Por qué ahora no funciona y antes sí? Muy sencillo. Al pasar la variable por referencia, lo que le
estamos pasando al método es precisamente eso: una referencia a una variable, con lo cual el código
del método va a acceder a la variable original. Como esta variable original estaba definida como
Variant en el script de ASP, no es posible que el método intente considerarla como Integer, y se
produce el error.

Escrito de esta forma, el componente no sería utilizable desde una página ASP.

Veamos qué ocurre si el parámetro se pasa por valor, y no por referencia. Para ello volvemos a
modificar el componente, hasta conseguir el Código fuente 140.

Public Function calcular(ByVal cantidad As Integer) As Currency


calcular = cantidad * 1.07
End Function

Código fuente 140

182
© Grupo EIDOS 10. Diseño de componentes para ASP

Para probar esta nueva versión del componente, preparemos una página ASP con un formulario, que
nos permita probar con distintos valores del parámetro. El Código fuente 141 es el que escribimos para
la página.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%if Request.Form("boton")="" then%>


<form method="post">
<input name="cantidad")>
<input type="submit" name="boton">
</form>
<%else%>
<%
Set objPorcentaje = Server.CreateObject("CompPorcentaje.Porcentaje")
cantidad = Request.Form("cantidad")
Response.Write "El resultado es: " & objPorcentaje.calcular(cantidad)
Set objPorcentaje = Nothing
%>
<%end if%>

</BODY>
</HTML>

Código fuente 141

En la rama if generamos el formulario, con un campo de texto para introducir la cantidad y un botón
de submitir, que enviará el formulario a la misma página. En la rama else recibimos ese formulario y
recuperamos el valor del campo de texto, cuyo valor asignamos a la variable cantidad, que es la que
pasamos como parámetro al método del componente.

Si pedimos la página y metemos el valor 1000 en el campo de texto, el funcionamiento es correcto. Le


estamos suministrando al método un parámetro de tipo Variant, cuando el método espera recibir un
parámetro del tipo Integer. Pero entre medias está el motor del script, que es el que se encarga de hacer
automáticamente la transformación, tomando la variable cantidad, del tipo Variant, y suministrándole
al método del componente una copia, del tipo Integer.

Por supuesto que si el motor de script no puede hacer conversión automática, porque los tipos son
incompatibles, también falla. Probémoslo metiendo en el campo de texto una cadena, en vez de un
entero. Obtendremos el error No coinciden los tipos: 'objPorcentaje.calcular' .

Como último ejemplo, y para que esto quede claro, volvamos a rediseñar el componente para que
reciba el parámetro por referencia, es decir otra vez como en el Código fuente 142.

Public Function calcular(cantidad As Integer) As Boolean


cantidad = cantidad * 1.07
calcular = True
End Function

Código fuente 142

183
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Y rescribamos la rama else de la página ASP para que quede como en el Código fuente 143.

<%
Set objPorcentaje = CreateObject("CompPorcentaje.Porcentaje")
cantidad = Request.Form("cantidad")
retorno = objPorcentaje.calcular(cantidad)
Response.Write cantidad
Set objPorcentaje = Nothing
%>

Código fuente 143

De esta forma, pongamos lo que pongamos en el campo de texto, obtendremos siempre el error de No
coinciden los tipos: 'objPorcentaje.calcular'.

Sin embargo, si cambiamos la línea en la que se invoca el método calcular del componente por la que
aparece en el Código fuente 144, veremos que, al menos, no obtendremos error.

retorno = objPorcentaje.calcular(Request.Form("cantidad"))

Código fuente 144

Este es un comportamiento un tanto curioso, pero que en todo caso no nos sirve en absoluto. Parece
como si al pasarle al método directamente el valor de Request.Form(“cantidad”), fuera
considerada como una variable de tipo Integer, que es la que necesita el método. Pero esto no es así en
absoluto. En realidad, Request.Form(“cantidad”) no es ni siquiera una variable, sino un valor
constante: ninguna propiedad, ni ningún elemento de cualquier colección del objeto Request es
modificable directamente.

Estamos consiguiendo lo mismo que si pusiéramos la línea del Código fuente 145.

retorno = objPorcentaje.calcular(1000)

Código fuente 145

Como pasamos un valor constante, no una variable, no podemos acceder al valor modificado que
debería devolvernos el método. Luego no nos da error, pero tampoco nos es de ninguna utilidad.

En definitiva, para componentes diseñados para ser utilizados desde páginas ASP, me interesa pasar
por valor los argumentos de los métodos. Y esto por dos motivos:

• Consigo una mayor encapsulación del código del componente.

• Es posible que el componente trabaje internamente con tipos Integer, String, etc. El motor de
script se encargará de hacer la transformación automática, siempre que los tipos sean
compatibles, del Variant al tipo necesario en el método del componente.

184
© Grupo EIDOS 10. Diseño de componentes para ASP

Dentro del componente podemos usar los tipos de datos que permite el Visual Basic, como Integer,
Currency, etc, siempre tengamos la precaución de pasar los argumentos por valor.

El motor de script hará la transformación automática del tipo variant de la página ASP al tipo
determinado en el argumento del método del componente.

Si activamos el control de errores en la página ASP con On Error Resume Next antes de
invocar al método, podremos capturar un posible error en la conversión en el caso de haber pasado un
valor incorrecto (por ejemplo una cadena como argumento de un método que espera un Integer).

Acceso al modelo de objetos de ASP desde un componente


Vamos a tratar ahora de los componentes que son específicos de ASP, puesto que hacen uso de su
modelo de objetos, es decir, harán uso directo de los objetos Application, Response, etc.

A partir de la versión 4 del IIS, todos los objetos instanciados bajo MTS tienen acceso al
ObjectContext. En la versión 5 ya no se llama MTS, sino Servicio de Componentes, integrado en
COM+, pero el ObjectContext sigue existiendo.

Más adelante veremos con más detalle este objeto, pero ahora podemos adelantar que representa el
contexto en el que se está ejecutando el componente, y le permite acceder a él.

En este caso el contexto de ejecución del componente será la página ASP, ejecutándose dentro del
entorno del servidor IIS, luego podrá acceder al modelo de objetos de ASP.

Acceso al objeto Application


Para explicar esto, veremos el ejemplo de un componente que acceda al valor de una variable con
ámbito de aplicación. Tendrá un único método, que recibirá como parámetro el nombre de la variable
a la que queremos acceder, y devolverá el contenido de la misma.

El parámetro se pasará por valor, no por referencia. Llamaremos al componente CompApplication, y


al módulo de clase le llamaremos Application.

Llamaremos objContext a la variable que va a contener la referencia al objeto de contexto. La


instrucción mediante la que conseguimos que esta variable obtenga la referencia al contexto es
GetObjectContext.

Es imprescindible añadir al proyecto la referencia a “COM+ Services Type Library”, como se muestra
en la Figura 49, si estamos trabajando en Windows 2000.

De no hacerlo así, no nos fallará el componente en el momento de la compilación, pero si en


ejecución, en el momento de capturar la referencia al objeto de contexto, es decir, justo en la línea
Set objContext = GetObjectContext. Además de esta forma podemos declarar con Dim
la variable objContext.

A partir del objeto de contexto, puedo acceder al entorno en que se ejecuta el componente. En este
caso, dentro de este entorno están los objetos integrados de ASP, y entre ellos el objeto Application.
Para obtener una referencia a este objeto, lo hacemos con Set objApplication =
objContext("Application").

185
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 49

El Código fuente 146 muestra el contenido completo del método leeVariable.

Public Function leeVariable(ByVal nombre As Variant) As Variant


Dim objContext As ObjectContext
Set objContext = GetObjectContext
Set objApplication = objContext("Application")
leeVariable = objApplication.Contents(nombre)
End Function

Código fuente 146

Para poder probar este componente, lo haremos desde una página en la que demos valor a una variable
de aplicación, cuyo nombre pasaremos luego como parámetro al método. El Código fuente 147 podría
ser el de la página.

Set objCom = Server.CreateObject("CompApplication.Application")


Application("variable1")="aquí iría el valor de la variable"
Response.Write objCom.leeVariable("variable1")

Código fuente 147

Si ejecutamos esta página, veremos que se muestra correctamente el valor de esa variable de ámbito de
aplicación.

Sin embargo, probemos a añadir un proyecto EXE estándar que haga uso de este componente, con un
formulario que presente un botón y un campo de texto Text1, y escribamos como código asociado al
evento Click del botón el contenido en el Código fuente 148. Agregemos a este nuevo proyecto las

186
© Grupo EIDOS 10. Diseño de componentes para ASP

referencias al componente CompApplication y a “COM+ Services Type Library”, y luego con el botón
derecho sobre el nombre del proyecto en el explorador de proyectos seleccionemos la opción
“Establecer como inicial”.

Private Sub Command1_Click()


Dim objCom As CompApplication.Application
Dim valor As Variant

Set objCom = New CompApplication.Application


valor = objCom.leeVariable("variable1")
Text1.Text = valor
End Sub

Código fuente 148

Si ejecutamos este proyecto veremos que da error en la línea Set objApplication =


objContext("Application") del componente. Esto se debe a que ahora el componente no se
está ejecutando en el contexto de una aplicación ASP, sino en el contexto de una aplicación tradicional
de Visual Basic, luego no puede acceder a través de su contexto al objeto Application, que sólo tiene
sentido en una aplicación ASP.

Acceso a los objetos Request y Response


Veamos ahora que tenemos acceso a otros objetos integrados de ASP, como son el Request y el
Response. Haremos un componente que, en un único método, procese el formulario enviado mediante
POST en la petición del navegador del cliente y genere una tabla HTML con el contenido de sus
elementos.

Para mayor comodidad de programación (la que nos proporciona el IntelliSense), añadiremos al
proyecto la referencia al modelo de objetos de ASP, como aparece en la Figura 50. Por cierto, que la
dll correspondiente se encuentra en dos ubicaciones distintas: en Winnt\system32\inetsrv\asp.dll y en
Archivos de Programa\Microsoft Visual Studio\Common\IDE.

Figura 50

187
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Si llamamos CompFormulario al proyecto ActiveX DLL, y Formulario al módulo de clase, el Código


fuente 149 muestra las líneas que debería contener el método.

Public Sub procesaForm()


Dim objContext As ObjectContext
Dim objRequest As Request
Dim objResponse As Response
Dim strHTML As String

Set objContext = GetObjectContext


Set objRequest = objContext("Request")
Set objResponse = objContext("Response")

strHTML = "<table border=1><tr><th>Campo</th><th>Valor</th></tr>"


For Each campo In objRequest.Form
strHTML = strHTML & "<tr><td>" & campo & "</td><td>" & _
objRequest.Form(campo) & "</td></tr>"
Next
strHTML = strHTML & "</table>"

objResponse.Write strHTML
End Sub

Código fuente 149

El código accede al objeto Request de ASP para ir leyendo, mediante un bucle for…each, a todos los
elementos de la colección Form, y va construyendo una cadena strHTML con sus valores. En la última
línea, utiliza el método Write del objeto Response para escribir esta cadena en la respuesta que será
enviada de vuelta al navegador del cliente.

Una página ASP para probar este componente podría ser la del Código fuente 150.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%if Request.Form("boton")="" then%>


<form method="post">
<input name="campoTexto")><br>
<input type="checkbox" name="campoCheck")><br>
<input type="submit" name="boton">
</form>
<%else%>
<%
Set objCom = Server.CreateObject("CompFormulario.Formulario")
objCom.procesaForm
Set objCom = Nothing
%>
<%end if%>

</BODY>
</HTML>

Código fuente 150

188
© Grupo EIDOS 10. Diseño de componentes para ASP

Vemos que en la rama else, cuando el formulario es submitido, sólo es necesario instanciar el
componente e invocar a su único método. El resultado que obtendremos será algo parecido al de la
Tabla 7.

Campo Valor

campoTexto cualquier cosa

campoCheck on

boton Enviar consulta

Tabla 7

Retomemos ahora el componente que ya hemos utilizado en temas anteriores, que accedía a la tabla
Stores de la base de datos Pubs, creada durante la instalación del SQL Server. Este componente tenía
un único método listaStores, que devolvía un objeto recordset, para ser formateado en la página ASP.

Lo modificaremos ligeramente, de forma que ahora tenga un método llamado recuperaTabla, que
reciba como parámetro el nombre de la tabla que queremos mostrar, y devuelva un array
bidimensional formado por todos los campos de todos los registros de la tabla. Este array lo
conseguiremos utilizando del método GetRows del objeto Recordset de ADO.

Pues bien, ahora que ya sabemos acceder desde un componente al modelo de objetos de ASP,
hagamos un segundo componente, que reciba el array que devuelve el primero, que formatee en una
tabla HTML este array, y que la escriba directamente sobre el objeto Response. Llamemos
CompStores1 al proyecto ActiveX del primer componente y CompStores2 al del segundo.

CompStores1 podría tener algo parecido al Código fuente 151.

Option Explicit

Public Function recuperaTabla(ByVal nombreTabla As Variant) As Variant()


Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objRst As ADODB.Recordset
Dim strCnx As String

strCnx = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data


Source=varrondo"
Set objCnx = New ADODB.Connection
objCnx.Open strCnx
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "SELECT * FROM " & nombreTabla
Set objRst = New ADODB.Recordset
objRst.Open objCmd, , , , adCmdText
recuperaTabla = objRst.GetRows
End Function

Código fuente 151

189
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Como este componente accede a la base de datos a través de ADO, y queremos hacer uso del early
binding, tenemos que añadir al proyecto la referencia a Microsoft ActiveX Data Objects 2.5 Library.
Formamos la cadena SQL dinámicamente, con el parámetro del nombre de la tabla, y devolvemos
como resultado del método la ejecución de GetRows sobre el recordset.

Compilemos este componente y pasemos al siguiente.

El componente CompStores2 quedaría con el Código fuente 152.

Option Explicit

Public Sub generaTabla(ByVal nombreTabla As Variant)


Dim objContext As ObjectContext
Dim objResponse As Response
Dim objCom As CompStores1.Stores1
Dim rec()
Dim strHTML As String
Dim i, j As Integer

Set objContext = GetObjectContext


Set objResponse = objContext("Response")
Set objCom = New CompStores1.Stores1

rec = objCom.recuperaTabla(nombreTabla)

strHTML = "<table border=1>"


For i = 0 To UBound(rec, 2)
strHTML = strHTML & "<tr>"
For j = 0 To UBound(rec, 1)
strHTML = strHTML & "<td>" & rec(j, i) & "</td>"
Next
strHTML = strHTML & "</tr>"
Next
strHTML = strHTML & "</table>"

objResponse.Write strHTML
End Sub

Código fuente 152

Dentro del método obtenemos la referencia al objeto de contexto, y a partir de él la referencia al objeto
Response. Creamos una instancia del componente CompStores1 e invocamos a su método
recuperaTabla, pasando el parámetro del nombre.

Con el array bidimensional devuelto vamos construyendo una cadena strHTML con el código
necesario para formatear una tabla HTML. En la última línea, escribimos esta cadena a través del
método Write del objeto Response.

Este componente no está orientado a los datos, es decir, no accede a la base de datos, luego no necesito
añadir al proyecto la referencia a ADO. Si embargo, está orientado al cliente, porque tiene parte de la
lógica de presentación, puesto que accede al objeto Response del modelo de ASP. Para eso necesito
añadir las referencias a las librerías de COM+ y ASP.

Además, este componente va a hacer uso del otro componente que acabamos de crear, CompStores1.
Tendremos que añadir al proyecto esta referencia, como se muestra en la Figura 51.

190
© Grupo EIDOS 10. Diseño de componentes para ASP

Figura 51

El Código fuente 153 es todo lo que necesita la página ASP para usar los componentes. Se reduce a
instanciar el componente CompStores2, e invocar su método generaTabla, pasando como parámetro el
nombre de la tabla que queramos visualizar. Probar con varias de ellas, como “stores”, “authors”,
“publishers”, …

Set objCom = Server.CreateObject("CompStores2.Stores2")


objCom.generaTabla "Stores"
Set objCom = Nothing

Código fuente 153

De esta manera hemos creado lo que podríamos considerar una aplicación en cuatro capas:

• La capa de presentación está en la máquina del cliente, donde se ejecuta el software del
navegador web, capaz de hacer la petición de la página ASP y recibir la respuesta, todo a
través del protocolo HTTP. El código HTML que recibe puede interpretarlo, y mostrar en este
caso una tabla. Parte de la capa de presentación se encuentra en la página ASP, que es la que
realmente genera dinámicamente ese código HTML, que luego se encargará de visualizar en el
formato adecuado el navegador.

• La capa intermedia orientada al cliente estaría formada por el componente CompStores2, que
se encarga de hacer la petición al componente CompStores1 de la tabla, pero que además
accede directamente al objeto Response para escribir el código HTML de la tabla. Este
componente se ejecuta en la misma máquina que el servidor web, y es gestionado por la
página ASP, cuyo código se encarga de crearlo y destruirlo cuando es oportuno.

• La capa intermedia orientada a los datos la implementa el componente CompStores1, que


recibe la petición de tabla que le hace el componente CompStores2, y que accede al gestor de

191
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

base de datos a través de los objetos de ADO. Si este componente se ejecutara en una máquina
distinta de la del servidor web, podríamos denominarla “servidor de aplicación”.

• La capa de datos está formada por el gestor de base de datos, y podría ejecutarse en una
máquina diferente de la del servidor web y de la del servidor de aplicación.

El hecho de separar la capa intermedia en una capa orientada al cliente y otra orientada a datos, nos
permite una mayor posibilidad de reutilizar los componentes. Si por ejemplo decidimos cambiar el
componente que accede a la base de datos, porque vamos a usar otro servidor, o un procedimiento
almacenado en vez de una cadena SQL, sólo tendría que tocar ese componente y recompilarlo.
Mientras el interfaz que le presenta al componente orientado al cliente no cambie (es decir, que siga
recibiendo como parámetro un nombre de tabla y devolviendo un array bidimensional), todo
funcionará igual que antes.

Además, al no depender el componente orientado a datos del modelo de objetos de ASP, puedo
utilizarlo en otras aplicaciones, por ejemplo desde un EXE estándar de Visual Basic.

Recompilación de componentes con enlace temprano


Cambiemos por ejemplo el código del componente orientado a los datos por el Código fuente 154.

Option Explicit

Public Function recuperaTabla(ByVal nombreTabla As Variant) As Variant()


Dim objCnx As ADODB.Connection
Dim objRst As ADODB.Recordset
Dim strCnx As String

strCnx = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=pubs;Data


Source=varrondo"
Set objCnx = New ADODB.Connection
objCnx.Open strCnx
Set objRst = New ADODB.Recordset
objRst.Open nombreTabla, objCnx, , , adCmdTable
recuperaTabla = objRst.GetRows

End Function
Código fuente 154

Ahora no usamos el objeto Command, sino que pasamos como parámetro al método Open del
Recordset el nombre de la tabla. La ejecución de la página ASP será igual que antes. No tenemos que
modificar el código del componente orientado al cliente.

Lo que sí tenemos que hacer es recompilarlo. Como dentro de CompStores2 estamos usando enlace
temprano (early binding) al CompStores1 mediante la instrucción Set objCom = New
CompStores1.Stores1, al haber recompilado el CompStores1 tenemos que recompilar el
CompStores2.

De no hacerlo, al ejecutar la página ASP obtendríamos el error Esta clase no admite


Automatización o la interfaz esperada.

Esta recompilación no sería necesaria si utilizáramos enlace tardío (late binding), es decir, si
hubiéramos usado Set objCom = CreateObject(“CompStores1.Stores1”).

192
© Grupo EIDOS 10. Diseño de componentes para ASP

Implementación de interfaces en Visual Basic


Los interfaces se definen en Visual Basic a través de clases abstractas. Una clase abstracta no contiene
código de implementación, sólo las declaraciones de las funciones. La implementación real del
interfaz ocurre en otras clases, en aquéllas que implementan el interfaz.

Vamos a crear un nuevo proyecto ActiveX DLL de Visual Basic al que llamaremos CompInterfaz, con
un módulo de clase llamado IInterfaz1. Por convenio, a los nombres de interfaz se les suele anteponer
la “I”. Estableceremos la propiedad de instanciación de esta clase como PublicNotCreatable, como se
ve en la Figura 52. Esto significa que no se pueden instanciar objetos a partir de esta clase. Sólo será
una plantilla para otras clases que implementen el interfaz.

Figura 52

Con ayuda de la utilidad Generador de clases, añadimos un método al módulo de clase, como se
muestra en el Código fuente 155.

Public Function metodo1(ByVal par As Integer) As Integer


End Function

Código fuente 155

Vamos a utilizar esta clase abstracta en otro módulo de clase. Para ello añadimos un nuevo módulo de
clase al proyecto, llamado “Implementa”, y en su sección de Declaraciones añadimos la línea
Implements IInterfaz1. Esto significa que esta clase acepta el “contrato”, con lo cual está
obligada a implementar todos y cada uno de los métodos que formaran parte del interfaz. En este caso
el metodo1. Si no lo implementara, obtendríamos un error de compilación.

Nada más añadir esta línea, aparecerá en el combo de “Objetos”, en la esquina superior izquierda del
panel de edición, el nombre del interfaz. Si lo seleccionamos de la lista, automáticamente aparecerá la
estructura del método metodo1, al que se le antepone el nombre del interfaz y un guión bajo “_”, listo

193
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

para que lo implementemos con las líneas de código que creamos oportunas. Dejémoslo con el Código
fuente 156.

Implements IInterfaz1

Public Function metodo3(ByVal par As Integer) As Integer


metodo3 = par + 3
End Function

Private Function IInterfaz1_metodo1(ByVal par As Integer) As Integer


IInterfaz1_metodo1 = par + 1
End Function

Código fuente 156

Como puede verse, hemos añadido además un método llamado “metodo3”, pero que no es fruto de
implementar el interfaz anterior, sino un método como los que hemos escrito hasta ahora.

Vamos a repetir el proceso llevado a cabo con el módulo de clase IInterfaz1, para crear otro módulo de
clase IInterfaz2, con un método llamado metodo2. Haremos que la clase Implementa “firme el
contrato” también con este interfaz, lo que le obliga a implementar también el metodo2. El resultado
final se muestra en el Código fuente 157.

Implements IInterfaz1
Implements IInterfaz2

Public Function metodo3(ByVal par As Integer) As Integer


metodo3 = par + 3
End Function

Private Function IInterfaz1_metodo1(ByVal par As Integer) As Integer


IInterfaz1_metodo1 = par + 1
End Function

Private Function IInterfaz2_metodo2(ByVal par As Integer) As Integer


IInterfaz2_metodo2 = par + 2
End Function

Código fuente 157

Si ahora compilamos el proyecto, y desde una página ASP instanciamos este objeto, veremos que sólo
tenemos disponible el metodo3, pero no el metodo1 ni el metodo2. ¿Qué es lo que está ocurriendo?

Cuando se compila un proyecto ya sea con ActiveX DLL o con EXE, Visual Basic crea un interfaz por
defecto para cada clase. Dicho interfaz toma el nombre de la clase, precedido por un carácter de
subrayado bajo. Por ejemplo, el interfaz por defecto para la clase “Implementa” es “_Implementa”.
Los métodos que no forman parte de otro interfaz (precedido por la línea de subrayado y el nombre del
interfaz), como es el caso del metodo3, pasan a ser miembros del interfaz por defecto. Todo esto lo
realiza Visual Basic sin que tengamos que especificárselo.

Pues bien, al ser el VBScript de una página ASP un lenguaje en que no es posible asignar tipo a las
variables, sólo nos está permitido acceder a este interfaz por defecto, es decir, sólo podemos acceder al
metodo3. A continuación veremos que para acceder a los otros interfaces, distintos del interfaz por
defecto, es necesario declarar variables con el tipo específico de esos interfaces.

194
© Grupo EIDOS 10. Diseño de componentes para ASP

Vamos a probar este componente desde Visual Basic.

Para ello, desde el menú Archivo, seleccionemos la opción “Agregar proyecto”, que elegiremos del
tipo EXE estándar. El grupo de proyectos quedará al final como se muestra en la Figura 53.

Figura 53

Al formulario que se crea automáticamente, añadimos un botón y hacemos doble clic sobre él para
implementar el código asociado a su evento Click, como se ve en el Código fuente 158.

Private Sub Command1_Click()


Dim objCom As Implementa
Dim i1 As IInterfaz1
Dim i2 As IInterfaz2
Dim var As Integer

Set objCom = New Implementa


var = objCom.metodo3(1)

Set i1 = objCom
var = i1.metodo1(1)

Set i2 = objCom
var = i2.metodo2(1)
End Sub

Código fuente 158

En él definimos las variables i1 e i2 del tipo de los dos interfaces IInterfaz1 e IInterfaz2. Luego
instanciamos el componente e invocamos al metodo3. Luego asignamos sucesivamente a las variables
de referencia i1 e i2 la referencia al objeto e invocamos el método correspondiente a cada interfaz.

Cuando usa Set para asignar valores a una variable de tipo interfaz, Visual Basic pregunta al objeto si
éste tiene implementada dicha interfaz. El método que utiliza para ello se llama QueryInterface y
pertenece a la interfaz IUnknown.

Vamos a establecer el proyecto EXE estándar recién creado como proyecto de inicio. Para ello
pulsamos con el botón derecho sobre él en el Explorador de proyectos y seleccionamos la opción
“Establecer como inicial”.

195
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Si depuramos el componente, veremos que podemos invocar todos los métodos correctamente, y la
variable var toma los valores adecuados después de la ejecución de cada método. En definitiva, desde
el proyecto EXE estándar de Visual Basic conseguimos lo que no podíamos hacer desde el script de
ASP: acceder tanto a los métodos del interfaz por defecto como a los métodos de los interfaces
IInterfaz1 e IInterfaz2.

Como desarrollador de aplicaciones ASP, tal vez nunca utilicemos otras interfaces aparte de las que
Visual Basic proporciona por defecto. La elección a la hora de realizar la implementación depende de
la complejidad de la aplicación y de los requisitos del diseño. Desde luego, desde un componente
instanciado desde una página ASP sí podría acceder a interfaces, distintos del interfaz por defecto, que
implemente un segundo componente.

Las bibliotecas de tipos se utilizan para especificar los interfaces de un objeto, es decir, para definir los
métodos y parámetros de un interfaz, y no tiene nada que ver con la implementación de ésta. COM
utiliza IDL para describir interfaces, ya que permite ver lo que realmente ocurre cuando se crea un
componente. Las bibliotecas de tipos pueden formarse a partir de archivos IDL, los cuales, a su vez,
pueden utilizarse en tiempo de compilación o de ejecución. La definición de interfaces en visual Basic
se hace con clases abstractas (o simplemente utilizando la interfaz por defecto) en vez de con el IDL.
Visual Basic crea bibliotecas de tipos y las incluye en los componentes. Existen herramientas, como el
OLE/COM Object Viewer incluido en Microsoft Visual Studio, que le permiten convertir cualquier
biblioteca de tipos en IDL, incluso aquellas que se crearon con Visual Basic. Es importante observar
que actualmente no pueden aprovecharse todas las posibilidades del IDL en Visual Basic.

196
Servicios de Componentes

Introducción
En este tema se va a tratar de los Servicios de Componentes que proporciona el Windows 2000.

Sin embargo, se empezará hablando del MTS (Microsoft Transaction Server), para situar al lector en
el proceso de evolución de Windows NT a Windows 2000, y poder establecer comparaciones útiles
para aquellos que ya conocieran este producto. Además, los conceptos del MTS son aplicables en su
práctica totalidad a los Servicios de Componentes, exceptuando algunas diferencias que se comentarán
adecuadamente.

Características del MTS (Microsoft Transaction Server)


Aunque su nombre pueda producir confusión, el MTS no es sólo un monitor transaccional, sino que es
un servidor de componentes, con soporte de Transacciones, que simplifica el desarrollo y distribución
de las aplicaciones servidoras que utilizan la tecnología COM. Este servidor define un modelo de
programación y proporciona un entorno de ejecución, además de unas herramientas de administración
para las aplicaciones más complejas.

Pueden utilizarse herramientas como Visual Basic, Visual C++, Delphi, Visual J++, Visual FoxPro o
incluso COBOL para construir aplicaciones servidoras para MTS sin necesidad de tener que codificar
los complejos mecanismos de comunicación entre componentes. Por esta razón, MTS reduce
significativamente el tiempo necesario para la construcción de aplicaciones de n-capas, y aumenta la
extensibilidad, escalabilidad y reusabilidad de las aplicaciones.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Forma parte del Windows DNA (Windows Distributed interNet Application Architecture) para
aplicaciones internet/intranet de n-capas.

El MTS es un servicio creado para Windows NT. No es una parte integral del sistema operativo, sino
que se instala como un servicio adicional, con el Windows NT Option Pack. Supone una
funcionalidad añadida a la del COM, proporcionando un nuevo runtime para los objetos que se están
ejecutando en la capa intermedia de una aplicación distribuida según el modelo de n-capas.

Esta funcionalidad añadida consiste sobre todo en soporte para transacciones distribuidas, seguridad
integrada, pooling de hilos de ejecución, y facilidades de configuración y administración de los
componentes mediante el uso de propiedades declarativas.

Para Windows 95/98 existe una versión reducida del MTS, del mismo modo que existe una versión
reducida del servidor IIS denominada PWS (Personal Web Server).

Como ya se ha comentado, el MTS no sólo proporciona la funcionalidad necesaria para gestionar las
transacciones distribuidas, sino que incluso componentes no-transaccionales pueden obtener ventajas
en su ejecución si hacen uso de los servicios que aporta.

El MTS aúna dos funcionalidades principales:

• Object request broker (ORB). Gestiona la instanciación de objetos para los clientes. Sabe dónde
están los objetos y cómo crearlos. Gestiona la comunicación entre los componentes, ahorrando al
programador el trabajo de escribir el código en C++ que hiciera lo mismo). En una página ASP,
intercepta los Server.CreateObject y crea él mismo los objetos, usando sus propios objetos COM
para supervisar a estos objetos.

• Monitor transaccional: MS DTC (Distributed Transaction Coordinator). Tiene dos funciones:

• Comparte recursos (procesos, hilos, …) del sistema entre los clientes. Cada proceso necesita
una cantidad de memoria. Si se tienen muchos clientes simultáneos, es necesaria mucha
memoria, que no es un recurso especialmente caro en la actualidad, pero se requiere mucho
tiempo de CPU para gestionar todos los procesos. Es mejor compartir procesos: un mismo
proceso sirve a varios clientes. De esta forma hace falta menos CPU para gestionarlos, así que
el rendimiento es mayor, con menos memoria RAM.

• Usa los protocolos de transacciones para coordinar las acciones de los distintos componentes.
El programador no tiene que poner el código para gestionar el rollback; todo está centralizado
en el MTS.

Servicios que aporta el MTS


El MTS proporciona una serie de ventajas para el programador:

• Administrando los recursos del Sistema Operativo como procesos e hilos. Por esto varios
usuarios pueden acceder y ejecutar la aplicación a la vez.

• Sincronizando el acceso a los datos, para que no se reduzca el rendimiento cuando muchos
usuarios accedan a la misma información de la base de datos.

• Obteniendo información acerca de los usuarios: quiénes son, qué tipo de operaciones realizan,
así se evita que el trabajo de un usuario interfiera en el de otro.

198
© Grupo EIDOS 11. Servicios de componentes

• Implementando la seguridad, de tal manera que los usuarios sólo tengan acceso a los recursos
para los que el administrador les ha dado permisos.

• Implementando la administración y configuración, esto reduce el coste de distribución,


administración y/o modificación.

Estas ventajas las proporciona a través de un conjunto de servicios:

Transacciones para componentes


Son similares a las de las bases de datos, a diferencia de que éstas últimas engloban un conjunto de
sentencias SQL, mientras que las de componentes engloban el trabajo de uno o más componentes.
MTS monitoriza la interacción entre los componentes y los recursos transaccionales, como pueden ser
bases de datos, y coordina los cambios. Si algún componente corta la transacción, MTS facilita el
rollback de todos los cambios realizados por el componente y de todos los demás componentes que se
ejecutaban en el mismo contexto de transacción.

Comercio de objetos
MTS actúa como un comerciante de objetos, sirviendo las peticiones de instanciación que varios
clientes hacen. MTS reconoce la petición, coordina su creación y mantenimiento, y destruye las
instancias de componentes COM y de los hilos que éstas hayan creado. La independencia de la
ubicación de los componentes respecto de los clientes es otra de las ventajas que aporta este servicio
que, además, soporta la reconfiguración dinámica y el cambio de la ubicación de un objeto sin tener
que cambiar el código de la aplicación cliente, después de la fase de desarrollo.

Independencia entre procesos


MTS permite agrupar varios componentes en paquetes, cada uno de los cuales trabaja en su propia
área de direcciones protegida. Esto es crítico cuando, por ejemplo, se está integrando un componente
comprado a terceros en una solución existente. Los errores de un componente no deberían propagarse
a los componentes que están en otro paquete.

Fondo común de recursos


MTS administra de forma automática y reúne en un fondo común, llamado pooling, dos de los
recursos más críticos para el servidor: hilos y conexiones a bases de datos. El fondo común de
conexiones a bases de datos administrado por el controlador ODBC o OLE DB evita al desarrollador
el manejo complejas características de sincronización. MTS emplea un fondo común de hilos para
acelerar los tiempos de respuesta a los clientes, aportando también los mecanismos necesarios para
que varios componentes compartan información sin que el desarrollador tenga que preocuparse de
problemas de concurrencia ni de sincronización.

Activación en el momento (Just-In-Time)


Permite que el sistema desactive y reactive un objeto mientras sus clientes le siguen haciendo
referencia; esta posibilidad se traducen en un uso más eficiente de los recursos del servidor. Desde el

199
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

punto de vista del cliente, sólo existe una copia del objeto desde que lo mandó crear hasta que lo
destruyó. Realmente, el objeto puede desactivarse y reactivarse varias veces. La administración de
instancias de objetos y la activación en el momento benefician la escalabilidad de las aplicaciones.

Seguridad
MTS proporciona un servicio de seguridad plenamente integrado con la seguridad de Windows NT,
facilitando el control de uso por parte de personal no autorizado, incluso a los componentes comprados
a terceros. Existen dos modelos de seguridad soportados por MTS: el modelo declarativo y el modelo
programado". En el modelo declarativo la seguridad se configura con el Administrador de Usuario de
Windows NT y con el Explorador de MTS. El modelo "programado" proporciona la funcionalidad
necesaria para preguntar, en tiempo de ejecución, quién quiere utilizar el componente. Un componente
puede alterar su comportamiento dependiendo de la respuesta.

Conceptos principales del MTS

La intercepción
El modelo COM no fue modificado para albergar esta nueva funcionalidad que ofrece el MTS. La
capa de runtime del COM permanece igual que antes, pero convive ahora con una capa que se le
superpone, que es la capa del runtime del MTS. Debido a esto, la compenetración entre COM y MTS
no es siempre todo lo eficiente que podría ser, y supone un esfuerzo adicional para el programador,
que debe conocer los dos modelos. Veremos más adelante que algunas técnicas de programación que
son perfectamente válidas para COM no lo son para MTS.

Al ser dos capas de runtime distintas, para que el MTS pueda aportar funcionalidad añadida a los
objetos COM que instancie un cliente, debe interponerse entre el cliente y el objeto en el momento de
la creación del mismo, para poder controlar así su ciclo de vida y sus características de ejecución.

Propiedades declarativas. El catálogo del MTS


Para hacer uso de la funcionalidad del MTS, debemos darle la autorización para que se encargue de
gestionar y supervisar las instancias de nuestros componentes.

Esto se hace añadiendo los componentes a un “paquete” del MTS. Estos paquetes deben ser
entendidos como unidades de administración de componentes.

Una vez registrado el componente, es posible configurar muchas de sus características de ejecución
mediante lo que se llaman propiedades declarativas.

Ya se entiende la flexibilidad que todo esto supone. El programador construye sus componentes igual
que antes, sin tener que hacer apenas nada especial. Después de compilados, se registran en el MTS, y
se modifican sus atributos declarativos, que describen las propiedades de ejecución de ese
componente. No hay necesidad de programar nada nuevo, ni recompilar.

Las propiedades declarativas suponen un nuevo enfoque de programación. Tradicionalmente, una


aplicación hacía uso de los servicios proporcionados por el sistema operativo mediante un API de bajo
nivel. Si más adelante se quería variar el uso que la aplicación hacía de estos servicios, era necesario

200
© Grupo EIDOS 11. Servicios de componentes

modificar el código y recompilar. Ahora este esfuerzo no es necesario, y basta con modificar una serie
de propiedades declarativas, sin necesidad de tocar una sola línea de código.

La información de estas propiedades declarativas de cada componente registrado en algún paquete del
MTS se guarda en el catálogo. Este catálogo almacena información adicional a la que se guarda en el
registro de Windows.

Funcionamiento del MTS


Explicaremos aquí, de forma muy breve y simplificada, el funcionamiento interno del MTS. Esto
ayudará a comprender algunas cosas que se verán más adelante.

El MTS Executive, encargado de gestionar el funcionamiento del MTS, trabaja con dos objetos COM
principales (el objeto Context Wrapper y el objeto de contexto), y dos interfaces (interfaz
ObjectContext e interfaz ObjectControl):

El objeto Context Wrapper


Es el objeto que ve realmente el cliente que pide una instancia de un objeto MTS, resultado de la
intercepción que éste realiza en el momento de la creación.

El MTS inspecciona el interfaz del objeto, crea un objeto Context Wrapper con ese interfaz y le
devuelve esa referencia al cliente. No activa el objeto real hasta que el cliente pide el primer método
de ese objeto (JIT: Just-In-Time Activation). Así ahorra recursos. También lo destruye lo antes posible
(ASAP: As-Soon-As-Possible Deactivation), sin que el cliente lo sepa: él sigue “engañado” con su
referencia al Context Wrapper.

El objeto es desactivado después de cada llamada a un método. Esto es eficiente, aunque no lo


parezca, porque MTS guarda la DLL en su memoria caché, y no le lleva tiempo desactivar y reactivar.
Pero el estado se pierde entre llamadas a los métodos. Esto me asegura la propiedad de aislamiento de
las transacciones. Si quiero guardar el estado del componente, tengo recurrir a otros mecanismos,
como guardarlo en una base de datos o pasarlo como parámetro entre métodos.

En definitiva, el Context Wrapper sirve para “engañar” al cliente, que se crea que tiene la referencia al
objeto mientras que realmente es el MTS el que gestiona cuándo se activa y desactiva.

El objeto de contexto
Almacena información asociada con el objeto (creador del objeto, información de seguridad,
transacción en curso, etc). Es usada por el entorno para saber cómo el componente interactúa. Todos
los componentes registrados en MTS lo tienen. Suele decirse que el objeto de contexto es como la
“sombra” del objeto.

Almacena información de seguridad, que le permite al Context Wrapper saber si el cliente tiene
permiso para usar los métodos que ha pedido.

Almacena información del contexto de ejecución del componente. Esta información es la que le
permite al MTS meter un componente, programado de forma aislada y como si fuera monousuario,
dentro de una transacción distribuida con gestión de concurrencia, sin que yo tenga que programar

201
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

nada especial. Gracias a este objeto es posible monitorizar las transacciones y ver estadísticas desde la
consola administrativa del MTS.

IIS almacena información en el objeto de contexto, ya que IIS es parte del entorno y está construido
con tecnología MTS. Entre esta información almacenada están las referencias a los objetos integrados
de ASP. Luego no será necesario pasar estas referencias a un objeto COM para que pueda utilizarlas.

En resumen, el objeto de contexto puede utilizarse para:

• Informar que se ha terminado el trabajo del objeto.

• Evitar que se confirme una transacción, ya sea temporal o permanentemente.

• Instanciar otros objetos de MTS e incluir su trabajo en el ámbito de la transacción del objeto
creador.

• Averiguar la función del usuario que utiliza el objeto.

• Averiguar si dispone de los permisos necesarios.

• Averiguar si el objeto se está ejecutando en una transacción.

• Recuperar objetos incorporados en Microsoft Internet Information Server.

El interfaz ObjectContext
El objeto de contexto que acabamos de ver implementa este interfaz ObjectContext. Puedo acceder a
este interfaz desde cualquier objeto MTS con las líneas mostradas en el Código fuente 159.

Dim objContext As ObjectContext


Set objContext = GetObjectContext()

Código fuente 159

Los principales métodos de este interfaz son:

• SetComplete: indica al MTS que el trabajo realizado por el objeto ha finalizado con éxito, y
puede ser confirmado cuando todos los objetos implicados en la transacción terminen su
trabajo. El método SetComplete también indica al MTS que cualquier recurso retenido por el
objeto, incluido el propio objeto, puede ser reciclado.

• SetAbort: indica al MTS que el trabajo realizado por el objeto no ha finalizado con éxito, con
lo cual todos los cambios hechos por este objeto y los realizados por otros objetos en la misma
transacción quedarán anulados. El método SetComplete también indica al MTS que cualquier
recurso retenido por el objeto, incluido el propio objeto, puede ser reciclado.

• CreateInstance: crea un nuevo objeto, pero además MTS copia la información del objeto de
contexto del objeto creador al objeto de contexto del objeto creado. Así formará parte de la
misma transacción y tendrá las mismas características de seguridad. Para que forme parte de la
misma transacción, el nuevo objeto debe tener su atributo transaccional configurado como
“Requiere una transacción” o “Compatible con transacciones”.

202
© Grupo EIDOS 11. Servicios de componentes

• EnableCommit: Declara que el trabajo de un objeto no está necesariamente terminado, pero


que sus actualizaciones de la transacción son consistentes y que podrían confirmarse en su
forma actual. El objeto mantiene su estado interno durante las llamadas realizadas a otros
métodos hasta que le llame a SetComplete o SetAbort, o hasta que la transacción termina.
EnableCommit es la establecida por defecto para un objeto, si éste no llama a otros métodos
del objeto de contexto. Por ejemplo, una compañía tiene un objeto Pedido que crea pedidos
que hay que suministrar a los clientes. Un cliente puede crear un pedido a través de internet y
solicitar que le sea enviado a una dirección determinada. O el cliente puede ir a buscar el
pedido directamente a uno de los almacenes de la compañía. Un fichero ASP crea el pedido
llamando al método AñadirPedido del objeto Pedido. Éste llama a EnableCommit ya que no
sabe dónde irá el cliente a buscarlo. Una vez el cliente elija, la página ASP podrá llamar a los
métodos DejarEnAlmacén o EnviarADirección, para indicar qué debe hacerse con el pedido.
Cada uno de estos dos último métodos llamarán a SetComplete.

• DisableCommit: Declara que el trabajo de un objeto no está terminado, que sus


actualizaciones de la transacción son inconsistentes y que, por lo tanto, no pueden ser
autorizadas en su forma actual.

El interfaz ObjectControl
Un buen componente MTS implementa este interfaz. Tiene tres métodos:

• Activate: es invocado automáticamente por el MTS Excecutive cuando el cliente invoca un


método del componente. Aquí pondría el código de inicialización que sea global para todos
los métodos del componente. Por ejemplo, si todos los métodos acceden a una BD, aquí
crearía la conexión.

• Deactivate: es invocado automáticamente por el MTS Executive cuando finaliza la ejecución


del método, dando como resultado un SetComplete o un SetAbort. Aquí pondría el código de
finalización que sea global para todos los métodos del componente. En el ejemplo utilizado,
éste sería el lugar en el que liberar la conexión.

• CanBePooled: devuelve un booleano que indica si el MTS puede meter este componente en
un fondo compartido, o pooling, de objetos. En MTS 2.0 devuelve siempre False.

En estos métodos Activate y Deactivate es donde se debe poner el código de inicialización y de


finalización del objeto, no en los eventos Class_Initialize y Class_Terminate de Visual Basic. Por
varios motivos:

• El MTS tiene que crear un objeto de prueba la primera vez que se usa un objeto MTS, para ver
si es un objeto Java. Lo crea y lo destruye. En esta prueba llama a los métodos Class_Initialize
y Class_Terminate, con lo que haría la inicialización y destrucción cuando no es necesaria. Si
embargo en esta prueba no llama a Activate y Deactivate.

• El MTS no permite mantener el estado del componente entre llamadas, lo que significa que la
inicialización y finalización debe hacerse en Activate y Deactivate, que son los métodos
invocados precisamente entre estas llamadas.

• Desde los métodos Class_Initialize y Class_Terminate no está disponible el objeto de


contexto, con lo que no podremos acceder desde ellos, por ejemplo, a las características de
seguridad del componente.

203
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Instanciar un objeto a partir de un componente MTS

Instanciar un objeto desde otro objeto de Visual Basic


Usando el operador New:

• Si se instancia el objeto a partir de un componente incluido en el mismo servidor COM (es


decir, un mismo proyecto ActiveX DLL que contiene varios módulos de clase), el Visual
Basic sabe cómo tiene que crearlo, y no necesita llamar a las rutinas de COM para hacerlo.
Entonces se crea una instancia privada, de la cual el MTS no sabe que existe. Como
consecuencia, no se crea un objeto de contexto, aunque el componente esté registrado en
MTS, y el objeto creado no podrá intervenir en transacciones. Es un objeto COM tradicional.

• Si se instancia desde otro servidor COM, sí se llama a las rutinas de COM y el objeto creado
dispondrá de un objeto de contexto, en el caso de que estuviera registrado en COM.

Usando el método CreateObject:

• Equivale a New cuando se instancia desde servidores COM distintos; sin embargo, New es
más eficiente por utilizar el enlace temprano. Siempre usa las rutinas de COM para crear el
objeto, con lo que se crea un objeto de contexto para él. Pero no se transmite la información
sobre el contexto del componente que lo creó, sino que se crea un objeto de contexto
completamente nuevo, y no podrá formar parte de su transacción.

Usando el método CreateInstance:

• Al crear el nuevo objeto, el MTS copia toda la información del objeto de contexto del objeto
creador al objeto de contexto del objeto creado, con lo cual podrá compartir la transacción y
las características de seguridad.

Instanciar un objeto desde una página ASP con VBScript


Usando el método CreateObject:

• Esta función de VBScript hace una llamada directa a las rutinas COM, luego no pasa por IIS,
y por lo tanto tampoco por MTS, luego el objeto correrá en una transacción separada en el
caso de ser transaccional. No debe usarse en ASP.

Usando el método Server.CreateObject:

• Este método del objeto integrado Server transmite la petición de instanciación del objeto al
IIS, que se encargará de utilizar el MTS en caso necesario. Si la página es transaccional, este
componente correrá en la misma transacción. En definitiva, este método emplea internamente
el método CreateInstance del interfaz ObjectContext.

204
© Grupo EIDOS 11. Servicios de componentes

Introducción a las transacciones


Para que una transacción sea fiable ha de hacer lo que los usuarios y administradores esperan que
haga; esto incluye la capacidad de devolver el estado a una situación conocida, válida y consistente en
caso de que se produzca un fallo. Son las transacciones las que dan esta garantía al sistema.

Una transacción es una unidad indivisible de trabajo que puede ser deshecha en cualquier momento
antes de que finalice. Son muy importantes pues proporcionan a los desarrolladores el marco adecuado
para trabajar con componentes no muy fiables.

Toda transacción tiene un comienzo, uno o dos finales y algunos procesos que se ejecutan en medio.
Una transacción puede terminar sin más o terminar y realizar una "vuelta atrás" al estado anterior
como si no se hubiese realizado operación alguna. Las transacciones pueden ser abortadas, tanto por
los clientes como por el propio sistema de transacciones. Por ejemplo, un servidor de datos puede
abortar una transacción si ésta mantiene bloqueos durante demasiado tiempo o si se produce un ínter
bloqueo.

Para el comercio electrónico, el comportamiento transaccional es fundamental. Es inaceptable que un


sistema funcione parte del tiempo sí y parte no, aunque el tiempo que sí está funcionando sea del 99%.
Por ello, se ha hecho especial hincapié en los aspectos de recuperación de las transacciones, para
garantizar el 100% de fiabilidad.

En una situación en la que los elementos de la transacción incluyen transferencias de una cuenta
bancaria a otra, si el proceso de aumento de la cuenta se ejecuta correctamente, pero el de disminución
no, la transacción debería fallar y no dar dinero que no se tiene.

Cualquier actividad que pueda ser deshecha forma parte activa de la transacción. Los accesos a bases
de datos son los ejemplos clásicos que se incluyen en componentes transaccionales. Estos sistemas
gestores de datos soportan las transacciones, permitiendo confirmar (Commit) o realizar una vuelta
atrás (Roll Back) a sus actividades internas. El nombre genérico que se da a los sistemas que soportan
este comportamiento es el de "administrador de recursos transaccionales". Microsoft Message Queue
Server es un buen ejemplo de lo que es un administrador de recursos transaccionales.

Una transacción debe cumplir cuatro propiedades, que suelen denominarse propiedades ACID
(tomando la primera letra de cada propiedad):

• Atomicidad: Las transacciones deben ser indivisibles, es decir, o se ejecutan todas las acciones
o no se ejecuta ninguna.

• Consistencia: Toda transacción debe llevar el sistema de un estado consistente a otro.

• Independencia: Para ser consistentes debemos imaginar que somos los únicos que estamos
realizando operaciones en un momento dado; las demás transacciones deben ser invisibles
para nosotros. Sirva de ejemplo el siguiente caso: un viajero pide cambiar un billete de un
vuelo por otro distinto. Una transacción se ocuparía de cancelar el billete del primer vuelo, al
tiempo que otra transacción intentaría añadir un pasajero nuevo en el otro vuelo. Supongamos
ahora que la primera transacción falla, el viajero no podría volver a su "asiento" anterior
porque ahora estaría ocupado con el nuevo viajero. Este tipo de anomalías se evitan gracias a
la independencia de las transacciones.

• Durabilidad: Cuando una transacción se confirma, permanece así aunque se produzcan errores
del sistema, de las comunicaciones o del propio ordenador servidor.

205
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Duración y resultado de una transacción


Una transacción empieza cuando un cliente llama a un objeto de MTS con su atributo de transacción
establecido en “Requiere una transacción” o “Requiere una nueva transacción”. Este objeto es,
entonces, considerado la raíz de la transacción porque fue el primer objeto que la creó. Cuando ésta
termina, MTS determina el resultado de la transacción, ya sea autorizando o abortando la transacción.
En el caso de las páginas ASP, esta raíz de la transacción suele ser la propia página ASP, que es
ejecutada bajo la supervisión de los componentes COM del IIS, que en realidad son componentes
MTS.

Existen tres formas en las que una transacción puede terminar:

• El objeto raíz llama a SetComplete o SetAbort: Es, además, el único objeto que puede terminar
una transacción de esta forma, ya que los objetos que se han creado como parte de esa misma
transacción no influyen en su duración. Eso sí, pueden emitir su voto, que influirá en el
resultado final de la transacción: en cuanto que alguno de los elementos que forman la
transacción emitan un voto negativa, la transacción se da por no-válida.

• La transacción expira porque se agota su tiempo: El tiempo de expiración por defecto para una
transacción es de 60 segundos. Si desea cambiar este valor, haga clic con el botón derecho del
ratón en el icono del ordenador que aparece en el Explorador de MTS y, luego, clic en
Propiedades. Establezca entonces la propiedad “Transacción TimeOut” en la pestaña
Opciones.

• El cliente libera al objeto raíz.

Cuando una transacción termina, MTS debe determinar su resultado y decidir si debe autorizar o
abortar la transacción. Determinar el resultado de la transacción es un proceso análogo a la toma de
decisión de un grupo en el que se ha de llegar a una decisión unánime. Si un miembro del grupo
disiente, habrá que negociar una decisión unánime.

Del mismo modo, cada objeto en una transacción emite su voto llamando a un método, ya sea
SetComplete, SetAbort, EnableCommit o DisableCommit. MTS hace el recuento de los votos emitidos
por cada objeto y determina el resultado. Si todos los objetos llamaron a SetComplete o a
EnableCommit, se autorizará la transacción, pero si alguno de ellos llamó a SetAbort o a
DisableCommit, se abortará la transacción.

Activación en el momento
Una de las consideraciones del diseño más importantes a la hora de desarrollar componentes de MTS
es el establecer de que forma se va a controlar el estado. El control del estado influye directamente en
la escalabilidad de los componentes de MTS. Por otro lado, estos componentes administran el estado
de forma diferente a como lo hacen los componentes COM tradicionales.

El Estado es un conjunto de datos del objeto que se guarda después de haber realizado más de una
llamada al objeto. El estado puede almacenarse en una de las tres capas: en la capa del cliente, en la de
los objetos de MTS o en la de la base de datos.

El estado almacenado en objetos de MTS también recibe el nombre de estado local. Las propiedades
son un buen ejemplo de lo que es un estado. Un objeto puede tener propiedades que almacenen el
nombre de un cliente, su dirección y número de teléfono. También puede tener métodos que utilizan
los valores de dichas propiedades. Un método añade la información del cliente a la base de datos y,

206
© Grupo EIDOS 11. Servicios de componentes

más tarde, otro método hace un ingreso en su cuenta. El objeto existe y guarda la información del
cliente hasta que es liberado. Un objeto como éste, que mantiene el estado internamente después de
múltiples llamadas, recibe el nombre de stateful.

Sin embargo, si el objeto no expone propiedades y, el nombre del cliente, su dirección y su número de
teléfono se pasan en cada llamada al método, estaremos ante un objeto stateless. Este tipo de objetos
no recuerdan las llamadas realizadas con anterioridad, no mantienen el estado.

Es muy frecuente programar en un entorno de mono-usuario pensando que un objeto estará en activo
todo el tiempo que lo necesite. Las llamadas a métodos son sencillas porque el objeto recuerda la
información de una llamada a otra. Sin embargo, los objetos que mantienen el estado ("stateful")
influyen en la escalabilidad de una aplicación. El estado puede consumir los recursos del servidor,
como memoria, espacio en el disco y conexiones de la base de datos. Y, dado que el estado está
vinculado a un cliente específico, se mantendrán los recursos hasta que el cliente libere al objeto. Esta
decisión ha de tenerse en cuenta junto con otros requisitos de la aplicación.

Como norma general, se debe evitar mantener el estado que consume recursos escasos o caros;
almacenar las conexiones de la base de datos reduce la escalabilidad dado que existe un número
limitado de conexiones a la base de datos que pueden asignarse y las conexiones que se han utilizado
no pueden reunirse en el fondo común.

La Activación en el momento ayuda a reducir el consumo de los recursos del sistema, ya que recicla
los objetos cuando éstos terminan su trabajo. Este tipo de activación también asegura el aislamiento de
las transacciones, de forma que la información de una transacción no se llevará a la siguiente.

Cuando un cliente llama a un método de un objeto, MTS activa el objeto, creándolo y permitiendo a la
llamada del método llegar hasta éste. Una vez que el objeto termina y llama a SetComplete o a
SetAbort y regresa de la llamada, MTS desactiva el objeto y libera sus recursos para que éstos puedan
ser utilizados por otros objetos. El objeto volverá a activarse cuando el cliente llame a otro método.

Al desactivar el objeto, MTS libera todas sus referencias, destruyendo también todo su estado local, es
decir, las variables locales y las propiedades. Sin embargo, MTS controla el puntero del cliente para
que siga siendo válido. Cuando el cliente llama a un método del objeto desactivado, MTS lo activa
creándolo de nuevo y permitiendo a la llamada del método continuar. MTS controla el puntero del
cliente para que de esta forma no sea consciente de que el objeto ha sido destruido y vuelto a crear. Sin
embargo, el estado local del objeto se ha perdido y no recuerda nada de la activación anterior.

Un objeto no está desactivado cuando llama a EnableCommit o al DisableCommit o cuando rechaza


hacer una llamada a cualquier otro método del objeto contextual. Tampoco estará desactivado cuando
termine la transacción por haberse agotado su tiempo. El objeto sólo se desactiva cuando llama a
SetComplete o a SetAbort y regresa de la llamada del cliente.

La activación en el momento tiene un efecto sustancial sobre el estado del objeto. Dado que, cuando
un objeto llama a SetComplete o a SetAbort pierde su estado en cuanto el método regresa, los objetos
que participan en las transacciones han de ser stateless. Es decir, no pueden mantener ningún tipo de
dato de la instancia desde que quedan desactivados. No obstante, esto no quiere decir que todas las
aplicaciones deben diseñarse hacia un modelo de programa que no mantenga el estado, ya que éste
puede almacenarse y mantenerse fuera del objeto.

207
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Almacenar el estado del objeto


La activación en el momento obliga a los objetos a ser stateless, es decir, que si el objeto llama a
SetComplete o SetAbort, no guardará el estado local en variables dentro del objeto. Sin embargo,
existe un lado práctico para el desarrollo del componente que debe examinarse.

Habrá ocasiones en las que se necesite almacenar el estado para los objetos de MTS, por ejemplo, una
aplicación en la que se necesite determinar el nombre de una ciudad a partir de un código postal dado.
Encontrará esta información en una base de datos, pero utilizar repetidamente una base de datos para
hacer búsquedas de este tipo de dato estático puede ser ineficaz. Sería mejor almacenar esta
información en un objeto para búsqueda rápida.

Existen varias posibilidades para almacenar el estado.

En una transacción es posible almacenar las propiedades de una instancia. Un objeto no tiene que
llamar a SetComplete o a SetAbort cuando regresa de una llamada. Las transacciones más complicadas
podrían necesitar varias llamadas desde el cliente, cada una realizando una parte del trabajo, hasta que
la última llamada al método llama a SetComplete o a SetAbort. En estas circunstancias, puede
mantenerse el estado como dato de una instancia en variables locales. Cuando el último método llame
a SetComplete o a SetAbort, el objeto quedará, finalmente, desactivado, liberando el dato de la
instancia.

Otra solución obvia es la de almacenar el estado en variables dentro de los objetos Application o
Session, según el ámbito que queramos que tengan.

También se puede almacenar el estado en un archivo. Los archivos crean una protección frente a
accesos concurrentes y mantienen el estado a lo largo de múltiples transacciones. Sin embargo, no
proporcionan bloqueo a nivel de registro; sólo puede bloquearse el archivo completo. Por lo tanto, no
son útiles para almacenar el estado que es compartido por muchos objetos, ya que un objeto puede
dejar fuera al resto.

Otra forma es utilizar el Administrador de Propiedades Compartidas, SPM (Shared Property


Manager).

Administrador de Propiedades Compartidas (SPM)


El SPM permite almacenar propiedades y compartir los datos con todos los objetos del mismo paquete
MTS. El SPM es rápido porque accede a sus propiedades en el mismo proceso (y el mismo paquete) y
proporciona mecanismos de bloqueo para protegerse ante accesos concurrentes. La información
almacenada en el SPM no puede ser compartida entre paquetes distintos.

Un ejemplo de aplicación del SPM podría ser el de una aplicación bancaria en la que una serie de
componentes realizan las operaciones de ingreso y retirada de efectivo, transferencias, ingreso de
cheques, etc, y queremos asignar a cada una de estas operaciones un identificador único por medio de
un contador. Es decir, queremos una propiedad que sea compartida por todos los objetos instanciados
a partir de estos componentes

La solución sería meter todos estos componentes en un mismo paquete, y crear otro componente que
utilice el SPM para definir ese contador. Interesaría no perder el estado si el paquete se descarga de
memoria por estar en un período de inactividad. Esto puede ser configurado en los Servicios de
Componentes del Windows 2000, de forma que en las propiedades avanzadas de configuración del

208
© Grupo EIDOS 11. Servicios de componentes

paquete dejaríamos activado el botón de radio “Dejar ejecutando cuando esté inactivo”, como se
muestra en la Figura 54.

Figura 54

El estado se guarda en propiedades con nombre (objeto SharedProperty), agrupadas en grupos de


propiedades también con nombre (objeto SharedPropertyGroup). Todo es gestionado por el
administrador de SPM (objeto SharedPropertyGroupManager).

En resumen, el SPM trabaja con tres objetos ordenados jerárquicamente:

• SharedPropertyGroupManager: Creargrupos de propiedades comunes y permite el acceso a


los ya existentes.
• SharedPropertyGroup: Crea y permite el acceso a las propiedades comunes dentro de un
grupo.
• SharedProperty: Establece o devuelve el valor de una propiedad concreta.

Obtener una referencia al SPM


Vamos a hacer un componente que implemente el contador comentado anteriormente.

En un nuevo proyecto ActiveX DLL, al que podemos llamar CompContador, con un módulo de clase
llamado Contador, tendremos que añadir la referencia al Shared Property Manager Type Library si

209
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

trabajamos con el MTS de Windows NT, o la referencia a los servicios COM+ si trabajamos en
Windows 2000, como se muestra en la Figura 55.

Figura 55

El Código fuente 160 basta para obtener una referencia al SPM.

Set objSPM = New SharedPropertyGroupManager

Código fuente 160

El MTS, o los Servicios de Componentes en Windows 2000, se encargan de asegurar que sólo exista
una instancia por servidor de este objeto. Si ya existía esta instancia, se nos asignará una referencia a
la ya existente.

Crear un grupo de propiedades compartidas


El Código fuente 161 es el necesario para crear un grupo de propiedades.

Set objPG = objSPM.CreatePropertyGroup("contadores", LockSetGet, Process, bExiste)

Código fuente 161

Este método recibe cuatro argumentos:

210
© Grupo EIDOS 11. Servicios de componentes

• Nombre: Es el nombre que tendrá el grupo de propiedades

• Nivel de aislamiento: Controla de qué forma funciona el bloqueo para el grupo. Dado que la
propiedades en el grupo son comunes, habrá muchos objetos que accedan y actualicen
propiedades al mismo tiempo. El SPM dispone de mecanismos de bloqueo para evitar el
acceso simultáneo a las propiedades comunes. En la Tabla 8 se muestra los dos tipos de
valores que existen para hacer el bloqueo.

Constante Valor Descripción

0 Establecido por defecto. Bloquea una propiedad durante una


LockSetGet llamada; de esta forma se asegura que, cada operación de
lectura/escritura común sea atómica. Con esto se consigue que dos
clientes no puedan leer o escribir la misma propiedad
simultáneamente, pero no impide que otros clientes de accesos
concurrentes lo hagan con otras propiedades en el mismo grupo.

1 Bloquea todas las propiedades del grupo para uso exclusivo del
LockMethod llamador; este bloqueo se mantendrá mientras esté ejecutándose el
método en curso. Es apropiado utilizar este modo cuando existen
interdependencias entre las propiedades o, en aquellos casos en los
que un cliente tenga que actualizar una propiedad inmediatamente
después de haberla leído y antes de que se acceda a ella otra vez.

Tabla 8

• Tiempo de vida: controla la forma en que se borra el grupo de propiedad común. En la Tabla 9
se muestran los dos valores posibles para este caso.

Constante Valor Descripción

0 Cuando todos los objetos MTS han liberado sus referencias del
Standard grupo de propiedad, éste se destruye automáticamente.

1 El grupo de la propiedad no se destruirá hasta que el proceso en el


Process que ésta fue creada haya terminado. Entonces tendrá que liberar
todos los objetos SharedPropertyGroup enviándolos a Nothing.

Tabla 9

• El cuarto parámetro devuelve un booleano que indica si el grupo de propiedades existía o no


previamente. Si ya existía, se devolverá una referencia al grupo existente, y entonces el
segundo y tercer parámetro no tienen validez, pues el grupo tendrá los valores que le fueron
dados en el momento de su creación. Puede usarse el valor devuelto por este parámetro para
verificar si se está creando el grupo o recibiendo la referencia a uno existente, ejecutando el
código de inicialización de las variables del grupo sólo en el primer caso.

211
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Crear una propiedad compartida


El objeto SharedPropertyGroup presenta dos métodos y dos propiedades:

• CreateProperty: Crea una nueva propiedad común, identificada por una cadena de caracteres
que es única en su grupo de propiedad.

• CreatePropertyByPosition: Crea una nueva propiedad común, identificada por un índice


numérico en su grupo de propiedad.

• Property: Devuelve una referencia a la propiedad común, una vez dado el nombre de la
cadena por el que se identifica a tal propiedad.

• PropertyByPosition: Devuelve una referencia a la propiedad común, una vez dado su índice
numérico en el grupo de propiedad común.

El Código fuente 162 permite crear una propiedad común llamada “id”. El segundo argumento del
método devuelve un valor booleano que indica si la propiedad es creada en ese mismo momento o ya
existía. En el primer caso, haríamos la inicialización del bloque “if”.

Set objP = objPG.CreateProperty("id", bExiste)


If bExiste = False Then objP.Value = 100

Código fuente 162

Si todo este código lo incluimos en un método que devuelva la propiedad compartida “id” y la
incremente en 1, el resultado final sería el del Código fuente 163.

Option Explicit

Public Function generaId() As Integer


Dim objSPM As SharedPropertyGroupManager
Dim objPG As SharedPropertyGroup
Dim objP As SharedProperty
Dim bExiste As Boolean

Set objSPM = New SharedPropertyGroupManager


Set objPG = objSPM.CreatePropertyGroup("contadores", LockSetGet, Process,
bExiste)
Set objP = objPG.CreateProperty("id", bExiste)
If bExiste = False Then objP.Value = 100
objP.Value = objP.Value + 1
generaId = objP.Value
End Function

Código fuente 163

Podemos probar este componente con una página ASP tan simple como la que se muestra en el
Código fuente 164.

Si pedimos esta página, incluso desde varias instancias distintas de navegador, podremos ver cómo el
contador se va incrementando.

212
© Grupo EIDOS 11. Servicios de componentes

Set objContador = Server.CreateObject("CompContador.Contador")


Response.Write objContador.generaId

Código fuente 164

Si queremos probar más a fondo el comportamiento del SPM, hagamos un nuevo proyecto ActiveX
DLL llamado CompContador2, con un módulo de clase Contador2. Este módulo de clase contendrá un
método exactamente igual al del componente anterior.

Hagamos otra página ASP similar a la del Código fuente 164, pero que trabaje con este nuevo
componente.

Podemos probar ahora varios casos:

• Si no registramos los componentes en el MTS, los componentes se ejecutan en el mismo


proceso que el de la aplicación web. Luego como comparten proceso, también comparten el
mismo grupo de propiedades, y la propiedad “id” a la que acceden es la misma.

• Si registramos uno de los dos componentes en un paquete del MTS, configurado como
“Aplicación de servidor”, este componente se ejecutará en un espacio de proceso distinto al de
la aplicación web, y no comparte el grupo de propiedades con el otro componente. En este
caso los contadores “id” son distintos

• Si registramos los dos componentes en el mismo paquete, se ejecutan en el mismo proceso y


comparten la propiedad “id”.

• Si los registramos en paquetes distintos, se ejecutan en distintos procesos y no comparten la


propiedad “id”.

En definitiva, lo normal sería que estos dos componentes tuvieran funcionalidad diferente, por ejemplo
uno podría ejecutar operaciones de transferencias bancarias y otro de ingreso de cheques, pero que
quisiéramos numerar esas operaciones distintas con un contador único. En ese caso, registraríamos
estos componentes en un mismo paquete, y dispondría cada uno de ellos de un método “generaId” que
accediera a la propiedad común “id”, como hemos visto.

Esta propiedad común se conserva, incluso aunque el MTS esté haciendo activación justo a tiempo de
los objetos.

Servicios de Componentes y COM+


El paso siguiente a COM y MTS era lógicamente integrar estos dos modelos en uno sólo, de tal forma
que se produzca una mayor compenetración entre dos capas de runtime que antes eran distintas, con
todos los problemas que esto suponía.

El resultado de la conjunción de estas dos tecnologías ha resultado en el COM+. Ahora el MTS no se


sirve como un añadido opcional a la funcionalidad del COM, sino como una parte constituyente del
modelo conjunto: al instalar Windows 2000, estamos instalando lo que antes se llamaba MTS 2.0, y
que ahora sería el MTS 3.0, pero ya integrado en el modelo COM+.

COM+ está basado en COM: sigue siendo una especificación binaria que permite que los componentes
se comuniquen entre sí, y permite una programación basada en interfaces.

213
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Pero COM+ también está basado en MTS. Esto significa que extiende la funcionalidad básica del
COM, igual que hacía el MTS, pero añade aún más funcionalidad que éste. Ahora esta funcionalidad
añadida no se llama MTS, sino Servicios de Componentes.

Igual que el MTS, los Servicios de Componentes aportan las siguientes funcionalidades:

• Monitor transaccional: Los componentes pueden ser ejecutados en el seno de una transacción,
sin tocar una sola línea de código, y los Servicios de Componentes se encargan de gestionarla,
haciendo el commit o rollback correspondiente. El comportamiento que un objeto tendrá ante
las transacciones puede ser configurado administrativamente mediante propiedades
declarativas.

• Gestor de componentes (ORB, Object Request Broker): Gestiona la vida del componente y
guarda información de su contexto. Cada objeto está asociado con un único contexto durante
su vida, como si se tratara de su sombra. Múltiples objetos pueden estar asociados a un mismo
contexto.

• Gestor de seguridad: Algunas características de seguridad no necesitan que sea añadida ni una
sola línea de código y son configuradas administrativamente.

Pero además, los Servicios de Componentes aportan algunas novedades:

• Componentes encolados: Combina COM con MSMQ. Permite invocar y ejecutar


componentes de forma asíncrona, estando el cliente o el servidor desconectados El cliente pide
un método de un componente encolado al Queued Components Recorder, que mete la petición
en una cola. El Queued Components Listener coge el mensaje y se lo pasa al Queued
Components Player, para que sea ejecutado finalmente el método sobre el componente.

• Fondo común de objetos (Object Pooling): Mantiene instancias de componentes en un fondo


común o pool, que pueden ser usadas por cualquier cliente. Igual que el pool de conexiones a
una BD. Actualmente sólo puede usarse con componentes C++.

• Component Load Balancing (CLB): Puedo tener un mismo componente COM+ servido por
varios servidores de aplicación. Se utilizará uno u otro servidor en función de su carga de
trabajo. No disponible todavía en Windows 2000.

Muchos de los conceptos de MTS siguen teniendo aplicación al hablar de los Servicios de
Componentes. A veces sólo cambia el nombre. Por ejemplo, el MTS ahora se llama Servicios de
Componentes. Es accesible desde la opción “Herramientas administrativas” del menú “Programas”.
Lo que antes se llamaba “paquete” ahora recibe el nombre de “aplicación COM+”.

Al igual que los paquetes del MTS, las aplicaciones COM+ pueden ser configuradas como:

• Aplicación de biblioteca: Los objetos instanciados a partir de componentes registrados en esa


aplicación se ejecutarán en el mismo espacio de proceso que el cliente que instació dicho
objeto.

• Aplicación de servidor: Los objetos instanciados a partir de componentes registrados en esa


aplicación se ejecutarán en un proceso de servidor dedicado. Este proceso que contiene la
ejecución de esta aplicación era MTX.EXE en el MTS, y ahora pasa a ser DLLHOST.EXE,
como puede verse en la Figura 56.

214
© Grupo EIDOS 11. Servicios de componentes

Figura 56

La intercepción en COM+
Ya hemos visto que para que el MTS fuera capaz de añadir su funcionalidad a los componentes COM,
es necesario que se “interponga” entre el cliente y el objeto, de forma que pueda controlar el ciclo de
vida y la ejecución de éste.

Para conseguir esto, la capa runtime del MTS, el MTXEX.DLL, se valía de dos objetos. El objeto
Context Wrapper es el que se interpone en el momento del intento de instanciación de un componente
MTS por parte de un cliente: éste recibe una referencia a este objeto Context Wrapper, siendo así
“engañado”, mientras que el MTS se encarga de gestionar la creación del objeto real.

Un segundo objeto, el objeto de contexto, se encarga de guardar información relativa al contexto de


ejecución del objeto, actuando como su “sombra”.

El hecho de que el MTS no estuviera plenamente integrado en el modelo COM suponía un problema
importante, una de cuyas consecuencias principales era la necesidad del uso del método
CreateInstance del objeto de contexto.

Si un componente MTS instanciaba otro componente MTS, y se empleaba el método de Visual Basic
CreateObject en vez de CreateInstance, ocurría lo siguiente: el runtime de Visual Basic pasaba la
petición de creación del objeto al runtime del COM, que a su vez se la pasaba al MTS. Pero el runtime
del COM no era capaz de pasar la información del contexto del objeto creador al del objeto creado.
Como consecuencia, el MTS creaba un nuevo contexto para el nuevo objeto, pero que no era reflejo
del contexto del objeto creador, luego no podía correr en la misma transacción.

215
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

La capa runtime del COM+ se basa en la idea del contexto. Todos los objetos se ejecutan dentro de un
contexto. Varios objetos pueden estar en un mismo contexto. La intercepción de la capa runtime del
COM+ sólo se produce cuando la llamada se hace desde un contexto hasta otro distinto. Para esta
intercepción no utiliza objetos Context Wrapper, sino objetos proxy especializados en hacer este
cambio de contexto.

En el momento de crear un objeto, la capa runtime del COM+ debe decidir si el nuevo objeto necesita
un nuevo contexto o puede utilizar el contexto de su creador. Para tomar esta decisión, COM+ se basa
en la información almacenada en el objeto de contexto.

En la mayoría de las ocasiones, los objetos instanciados a partir de componentes no-configurados se


crean en el contexto de su creador, y los instanciados a partir de componentes configurados se crean en
un nuevo contexto. Se entiende por componente configurado aquél que está registrado dentro de una
aplicación COM+, lo que significa que se le han configurado una serie de propiedades declarativas.
Los objetos de ADO sin embargo son instancias de componente no-configurado.

Ya no es necesario el uso del método CreateInstance, aunque sigue pudiendo utilizarse para garantizar
la compatibilidad con código ya escrito. Las rutinas de COM+ saben perfectamente cómo deben pasar
información del contexto en el momento de la creación de objetos. Esto significa que el método
CreateObject, que utiliza dichas rutinas COM+, tendrá el mismo efecto, y el objeto recién creado
podrá, por ejemplo, ejecutarse en la misma transacción que el creador.

El operador New de Visual Basic todavía puede ser problemático si los componentes correspondientes
al objeto creado y al creador están incluidos en la misma DLL. En este caso las rutinas de Visual Basic
saben cómo crear el nuevo objeto y no utilizan las rutinas de COM+. Si están en DLLs distintas, el
operador New equivale al método CreateObject y funcionará sin problemas.

La referencia a un objeto es siempre relativa al contexto. Si un objeto 1 dentro un contexto 1 obtiene


mediante CreateObject una referencia a un objeto 2 dentro de otro contexto 2, en realidad lo que tiene
es la referencia a un proxy capaz de hacer el cambio del contexto 1 al contexto 2. Si el objeto 1 invoca
un método de un tercer objeto 3 dentro de un contexto 3, y le pasa como parámetro del método esta
referencia al objeto 2, se creará automáticamente un nuevo proxy capaz de hacer el cambio del
contexto 3 al contexto 2, y será la referencia a este proxy la que utilice el objeto 3.

Por lo tanto, pasar una referencia a un objeto como parámetro de un método no supone ningún
problema, porque los proxys necesarios se crean automáticamente. Sin embargo, no sería correcto
guardar la referencia que tenía el objeto 1 al objeto 2, que es un proxy especializado en cambiar del
contexto 1 al 2, como una variable dentro de un módulo BAS de Visual Basic, ni como una propiedad
compartida del SPM, porque esa referencia sólo sería utilizable por el objeto 1, pero no por el objeto 3,
que necesitaría un proxy capaz de cambiar del contexto 3 al 2.

Otro problema que se ha eliminado con COM+ es el del registro de Windows. La clave
InProcServer32 de un componente COM contiene la ruta de la DLL donde se encuentra el
componente. Sin embargo, en el momento de registrar el componente dentro de un paquete MTS, el
valor de esta clave cambiaba por C:\WINNT\SYSTEM32\MTXEX.DLL, es decir, la ruta del MTS
Executive. Esto era necesario para que el MTS pudiera hacer la intercepción en el momento de la
creación del componente, ya que las rutinas COM no eran capaces de hacerlo por sí mismas.

El problema venía en el momento de recompilar el componente desde el Visual Basic: después de la


compilación, éste llama automáticamente a REGSVR32.EXE, y el componente volvía a ser registrado
según la manera de COM, y el valor de la clave InProcServer32 volvía a ser el de la ruta de la DLL,
con lo que el componente ya no funcionaba correctamente porque el MTS no podía hacer la
intercepción.

216
© Grupo EIDOS 11. Servicios de componentes

Para mitigar este problema, el MTS contaba con la posibilidad de Actualizar los componentes de un
paquete, que consistía en que el MTS inspeccionaba en el registro de Windows las claves de los
componentes de dicho paquete, y corregía automáticamente sus valores.

Todas estas dificultades se han eliminado con COM+. La clave InProcServer32 siempre contiene la
ruta de la DLL, sea el componente congifurado o no-configurado. La diferencia estriba en que los
componentes configurados tienen información adicional en el registro. No es necesario refrescar los
componentes después de cada recompilación en Visual Basic.

217
Diseño de componentes COM+

En este tema vamos a ver de qué forma los Servicios de Componentes de COM+ (antes MTS) pueden
afectar al diseño de los componentes.

Comprobaremos cómo se produce la interceptación de la que hemos hablado anteriormente, siendo


estos Servicios de Componentes los encargados de gestionar la creación y destrucción de los objetos
que estén registrados en el catálogo de COM+.

Veremos cómo por el simple hecho de registrar un componente en una aplicación COM+ y configurar
una serie de propiedades declarativas, podemos extender su funcionalidad sin necesidad de tocar una
sola línea de código.

El interfaz ObjectControl
Hemos dicho que cuando un componente es registrado en el catálogo de COM+, los Servicios de
Componentes se encargan de gestionar su creación y destrucción. Si queremos conocer cuándo es
creado y destruido realmente el componente, podemos hacerlo implementado dentro del mismo el
interfaz ObjectControl.

Este interfaz tiene tres métodos:

• Activate: es invocado cuando el componente es activado o re-activado (en el caso de los


componentes C++, que pueden ser desactivados, almacenados en el pool, y re-activados). Si
hago que mi componente implemente el interfaz ObjectControl, este método será invocado
automáticamente por las rutinas de COM+ antes que cualquier otro método. Sería lógico usar
este método para incluir código de inicialización que sea global para todos los métodos del
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

objeto. Si se trata por ejemplo de un objeto que accede a una base de datos a través de ADO,
en este método podríamos crear la conexión. También es normal obtener aquí la referencia al
objeto del contexto, para poder usarla luego en los métodos del componente.

• Deactivate: es invocado cuando el componente es desactivado. Igual que con el método


anterior, aquí pondría código de finalización del objeto, como cierre de una conexión a través
de ADO.

• CanBePooled: este método devuelve un valor booleano que indica si las rutinas COM+
pueden meter este objeto en un pool, una vez desactivado. Actualmente esta opción sólo
estaría disponible para componentes escritos en C++. Para componentes escritos en Visual
Basic este método devolverá siempre False.

Vamos a construir ahora un componente en Visual Basic para comprobar lo que acabamos de ver.

Abrimos un nuevo proyecto ActiveX DLL, al que llamaremos CompActivar, con un módulo de clase
al que llamaremos Activar. El componente hará uso del objeto Response de ASP, para escribir
directamente con Write. Implementará el interfaz ObjectControl, y además tendrá un método
adicional.

Añadimos al proyecto las referencias a las librerías de COM+ y del modelo de objetos de ASP. El
Código fuente 165 muestra cómo quedarían estos métodos.

Option Explicit
Implements ObjectControl

Private Sub ObjectControl_Activate()


Dim objContext As ObjectContext
Dim objResponse As Response
Set objContext = GetObjectContext
Set objResponse = objContext("Response")
objResponse.Write "activado a las " & Time & "<br>"
End Sub

Private Sub ObjectControl_Deactivate()


Dim objContext As ObjectContext
Dim objResponse As Response
Set objContext = GetObjectContext
Set objResponse = objContext("Response")
objResponse.Write "desactivado a las " & Time & "<br>"
End Sub

Private Function ObjectControl_CanBePooled() As Boolean


ObjectControl_CanBePooled = False
End Function

Public Sub escribe()


Dim objContext As ObjectContext
Dim objResponse As Response
Set objContext = GetObjectContext
Set objResponse = objContext("Response")
objResponse.Write "dentro del método a las " & Time & "<br>"
End Sub

Código fuente 165

220
© Grupo EIDOS 12. Diseño de componentes COM+

El hecho de añadir la línea Implements ObjectControl nos obliga a implementar los tres
métodos del interfaz; si no lo hacemos así, no podremos compilar. Esta línea es necesaria para que los
Servicios de Componentes puedan invocar automáticamente a los métodos del interfaz.

En los métodos ObjectControl_Activate, ObjectControl_Deactivate, y en el método “escribe”,


obtenemos una referencia al objeto de contexto, y a través de él una referencia al objeto Response,
para poder escribir directamente un corto mensaje y la hora del sistema.

Ahora escribimos una página ASP con el Código fuente 166.

<%@ Language=VBScript %>


<%Response.Buffer = false%>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objCom = Server.CreateObject("CompActivar.Activar")
inicio = Time
Response.Write inicio & "<br>"
while Time<>DateAdd("s",5,inicio)
wend
Response.Write Time & "<br>"
objCom.escribe & "<br>"
%>

</BODY>
</HTML>

Código fuente 166

Deshabilitamos el buffer en la segunda línea, para poder ir recibiendo gradualmente en el navegador el


resultado de la ejecución de la página. Instanciamos el componente, obtenemos la hora del sistema y la
escribimos con Write. El bucle While…Wend consiste en un temporizador muy rudimentario, que
ralentizará la ejecución de la página durante cinco segundos. Obtenemos nuevamente la hora del
sistema, la escribimos, e invocamos al método “escribe” del componente.

Si ejecutamos la página, obtendremos:

9:38:04
9:38:09
dentro del método a las 9:38:09

Es decir, no han sido invocados los métodos Activate ni Deactivate. Esto es así porque todavía el
componente no está registrado en el catálogo de COM+, y los Servicios de Componentes no se han
encargado de interceptar la creación y destrucción del objeto. Vamos pues a registrarlo.

Dentro de Programas | Herramientas administrativas, seleccionamos la opción Servicios de


Componentes. Esto abrirá la consola de administración de estos servicios, como se muestra en la
Figura 57. Vayamos extendiendo el árbol de la consola del panel izquierdo hasta llegar a la carpeta de
Aplicaciones COM+.

221
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 57

En el panel derecho veremos las aplicaciones COM+ actualmente instaladas en el equipo. Para crear
una nueva, pulsamos con el botón derecho sobre la carpeta Aplicaciones COM+ del panel izquierdo, y
seleccionamos Nuevo | Aplicación. En ese momento se inicia el asistente (ver Figura 58) para
instalación de aplicaciones COM+.

Figura 58

En la siguiente ventana (ver Figura 59) pulsaremos sobre el botón de Crear una aplicación vacía. A
esta aplicación añadiremos luego nuestro componente recién creado.

222
© Grupo EIDOS 12. Diseño de componentes COM+

Figura 59

En el siguiente paso (ver Figura 60) se nos pide el nombre de la nueva aplicación, y además se nos
permite configurar en qué espacio de memoria queremos que se ejecuten los componentes de la
aplicación. Tenemos dos opciones, según seleccionemos un tipo u otro de aplicación:

• Aplicación de biblioteca: las instancias de los componentes de la aplicación se ejecutan en el


mismo espacio de memoria que el cliente que las crea. Esta opción puede ser recomendable en
tiempo de desarrollo de los componentes.

• Aplicación de servidor: las instancias de los componentes se ejecutan en un proceso separado


del de la aplicación cliente que los utiliza. Esta opción es la que debe utilizarse en tiempo de
producción.

Figura 60

En la siguiente ventana (Figura 61) podemos elegir bajo que cuenta se ejecutarán los componentes. Si
seleccionamos la opción marcada por defecto, entonces cuando instanciemos el componente desde una
página ASP, y si está activado el acceso anónimo para la aplicación web a la que pertenece dicha
página, la cuenta utilizada será la de invitado a internet (IUSR_nombreMáquina).

223
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 61

En la siguiente ventana basta con pulsar el botón de Finalizar, y ya está nuestra aplicación creada.
Ahora tenemos que añadir el componente. Para ello la expandimos en el árbol del panel izquierdo
correspondiente a la aplicación COM+ recién creada hasta llegar a la carpeta Componentes, como
muestra la Figura 62.

Figura 62

224
© Grupo EIDOS 12. Diseño de componentes COM+

La carpeta de Componentes no tiene ningún elemento, puesto que hemos creado una aplicación vacía.
Vamos a añadirlo pulsando con el botón derecho sobre la carpeta Componentes del panel izquierdo, y
seleccionando Nuevo | Componente. Con esto arranca el asistente para la instalación de componentes
COM.

En la pantalla que se muestra en la Figura 63, debemos pulsar la opción Importar componentes ya
registrados, puesto que al compilar el proyecto de Visual Basic, ya se ha encargado de registrarlo por
nosotros en la máquina.

Figura 63

Después de un momento, el que tarda en buscar en el registro de Windows los componentes


registrados en la máquina, nos aparece la lista de la Figura 64, de la que debemos seleccionar nuestro
componente.

Figura 64

Con esto el componente queda registrado, como se ve en la Figura 65. Más adelante veremos las
propiedades declarativas que podemos configurar, pero de momento nos basta con las que tiene por
defecto.

225
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 65

Antes de volver a ejecutar la página ASP, vamos a descargarla, para descargar con ello la dll del
componente. Recordemos que esto podíamos hacerlo desde la consola de administración del IIS,
pulsando con el botón derecho sobre el nombre de la aplicación web y seleccionando la opción
Propiedades, y luego la pestaña Directorio. Si ahora pulsamos el botón Descargar, conseguiremos
nuestro objetivo. Al ejecutar otra vez la página, el resultado que obtendremos será:

10:50:50

10:50:55

activado a las 10:50:55

dentro del método a las 10:50:55

desactivado a las 10:50:55

Como podemos ver, ahora los Servicios de Componentes han interceptado la creación del
componente. Con la instrucción Server.CreateObject("CompActivar.Activar"), no se
produce la instanciación inmediata del componente, sino que esta se produce al invocar el primer
método, es decir, en la instrucción Response.Write objCom.escribe.

Por cierto, que si abrimos de nuevo la consola de administración de los Servicios de Componente y
expandimos el árbol de la izquierda hasta la carpeta Aplicaciones COM+, veremos en el panel de la
derecha que el icono correspondiente a la aplicación ASPComp muestra la bolita girando dentro de la
caja. Esto significa que la DLL está cargada en memoria. Una de las ventajas de registrar el
componente en una aplicación COM+ es que podemos controlar la descarga de las DLLs. Basta con

226
© Grupo EIDOS 12. Diseño de componentes COM+

pulsar con el botón derecho sobre el icono de la aplicación y seleccionar la opción Cerrar sistema. La
DLL se descarga, y la bolita ahora deja de girar.

Si no cerramos nosotros la aplicación explícitamente, se cierra ella sola según la opción elegida en la
pestaña Activación de las propiedades de la aplicación COM+, como se muestra en la Figura 66. Por
defecto aparece marcada la opción “Minutos de inactividad antes de cerrar”, con un valor de tres. Esto
significa que si transcurren más de tres minutos sin que se reciba una petición de algún método del
componente incluido en dicha dll, la aplicación se descargará.

Figura 66

También podemos forzar nosotros mismos el comienzo de la aplicación si pulsamos con el botón
derecho sobre el icono de la aplicación y seleccionamos la opción Iniciar. De esta forma la primera
petición de la página ASP, que hace uso del componente de esta aplicación, no se ralentizará por el
proceso de carga de la dll en memoria, sino que se encontrará con que ésta ya estaba cargada.

Activación Just-In-Time (JIT)


Esta intercepción que realiza los Servicios de Componentes, podemos deshabilitarla. Para ello,
descarguemos previamente la aplicación COM+, como hemos visto, y pasemos a ver las propiedades
declarativas del componente, pulsando con el botón derecho sobre su icono dentro del administrador
de los Servicios de Componentes, y seleccionando la opción Propiedades.

En la pestaña Transacciones (ver Figura 67), comprobemos que la opción seleccionada por defecto es
la de “No se admite”. De esta forma el componente nunca tendrá voto en una transacción, pero
dispone de un objeto de contexto (luego puede acceder al objeto Response de ASP).

227
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 67

Vayamos ahora a la pestaña Activación para deseleccionar la casilla “Habilitar activación puntual”, es
decir, deshabilitamos la activación Just-In-Time del componente. Esto se muestra en la Figura 68.

Figura 68

228
© Grupo EIDOS 12. Diseño de componentes COM+

Y si ahora ejecutamos de nuevo la página ASP, el resultado obtenido será:

12:02:13

12:02:18

dentro del método a las 12:02:18

que es el mismo que se obtenía cuando el objeto no estaba registrado en una aplicación COM+. Hemos
impedido que los Servicios de Componentes intercepten la creación del objeto, luego no se invocan los
métodos Activate y Deactivate.

Permitir que los Servicios de Componentes gestionen la activación del componente supone una mejora
en la escalabilidad de la aplicación, pues los recursos del servidor son administrados de forma más
eficiente. El cliente no tiene que preocuparse de en qué momento se activa el componente, sino que lo
hacen por él los Servicios de Componentes, activándolo en el momento preciso (activación Just-In-
Time).

Inicialización del componente


Utilicemos lo visto en los apartados anteriores para hacer un componente que obtenga la referencia al
objeto de contexto en la inicialización, dentro del método Activate, para que esté disponible en el resto
de métodos a través de una variable miembro. Escribiremos un método listaApplication que genere
una tabla HTML con las variables de ámbito de aplicación, y otro listaSession que haga lo mismo con
las variables de ámbito de sesión.

Creamos un nuevo proyecto ActiveX DLL con el nombre de CompApliSes, con un módulo de clase
llamado ApliSes. Añadimos al proyecto las referencias a las librerías de COM+ y del modelo de
objetos de ASP.

El código de este módulo será el del Código fuente 167.

Option Explicit
Implements ObjectControl
Private mvarobjContext As ObjectContext
Private mvarobjResponse As Response

Private Sub ObjectControl_Activate()


Set mvarobjContext = GetObjectContext
Set mvarobjResponse = mvarobjContext("Response")
mvarobjResponse.Write "activado a las " & Time & "<br>"
End Sub

Private Sub ObjectControl_Deactivate()


mvarobjResponse.Write "desactivado a las " & Time & "<br>"
End Sub

Private Function ObjectControl_CanBePooled() As Boolean


ObjectControl_CanBePooled = False
End Function

Public Sub listaApplication()


Dim strHTML As String
Dim objApplication As Application

229
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Dim variable As Variant

Set objApplication = mvarobjContext("Application")


strHTML = "<table border=1><tr><th>Variable</th><th>Valor</th></tr>"
For Each variable In objApplication.Contents
strHTML = strHTML & "<tr><td>" & variable & "</td><td>" & _
objApplication.Contents(variable) & "</td></tr>"
Next
strHTML = strHTML & "</table>"

mvarobjResponse.Write strHTML
End Sub

Public Sub listaSession()


Dim strHTML As String
Dim objSession As Session
Dim variable As Variant

Set objSession = mvarobjContext("Session")


strHTML = "<table border=1><tr><th>Variable</th><th>Valor</th></tr>"
For Each variable In objSession.Contents
strHTML = strHTML & "<tr><td>" & variable & "</td><td>" & _
objSession.Contents(variable) & "</td></tr>"
Next
strHTML = strHTML & "</table>"

mvarobjResponse.Write strHTML
End Sub

Código fuente 167

Como puede verse, en el método Activate del interfaz ObjectControl obtenemos la referencia al objeto
de contexto, y a partir de él la referencia al objeto Response. Como los dos objetos van a ser utilizados
por los métodos listaApplication y listaSession, nos interesa guardarlos como variables miembro.

En estos dos métodos tomamos la referencia al objeto de ASP correspondiente, Application o Session,
generamos el código HTML necesario para la tabla, recorriendo la colección con un bucle For…each,
y escribimos directamente sobre el objeto Response.

Registremos el componente dentro de la aplicación COM+, ASPComp, y probémoslo desde una


página ASP con un código similar al del Código fuente 168.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Application("var1")="valor1"
Application("var2")="valor2"
Application("var3")="valor3"

Session("var1")="valor1"
Session("var2")="valor2"

Set objCom = Server.CreateObject("CompApliSes.ApliSes")

Response.Write "<h3>Variables de Application</h3>"


objCom.listaApplication

230
© Grupo EIDOS 12. Diseño de componentes COM+

Response.Write "<h3>Variables de Session</h3>"


objCom.listaSession
%>

</BODY>
</HTML>

Código fuente 168

Tendremos el resultado que se muestra a continuación:

VARIABLES DE APPLICATION

activado a las 13:14:47

Variable Valor

var1 valor1

var2 valor2

var3 valor3

VARIABLES DE SESSION

Variable Valor

var1 valor1

var2 valor2

desactivado a las 13:14:47

Podemos ver que el método Activate ha sido invocado automáticamente, y sólo en el momento de
llamar al primer método, no en el CreateObject. Por eso el mensaje que genera este método aparece
entre el título, escrito desde la página ASP, y la primera tabla, generada por el componente en el
método listaApplication. El método Deactivate es invocado al finalizar la ejecución de la página, y
salir de ámbito la variable de referencia objCom. Más adelante veremos que un objeto puede ser
desactivado y reactivado entre las llamadas a sus métodos, en vez de activado en la primera llamada y
desactivado después de la última.

231
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Estado de un componente
El paradigma de Programación Orientada a Objetos nos dice que los objetos se caracterizan por su
identidad, estado y comportamiento. Pues bien, veremos que si queremos diseñar componentes que
aprovechen al máximo los Servicios de Componentes, debemos programar componentes sin estado.

Vamos a diseñar un componente que permita hacer un alta en una tabla. Esta tabla la crearemos en
SQL Server, aunque podría valer otro gestor de base de datos, incluso Access. Añadiremos además un
procedimiento almacenado para realizar el alta.

La secuencia SQL necesaria para crear la tabla y el procedimiento se muestra en el Código fuente 169.

CREATE TABLE Clientes(


id smallint PRIMARY KEY,
nombre varchar(25),
saldo money
)

CREATE PROCEDURE alta


@id smallint,
@nombre varchar(25),
@saldo money
AS
INSERT INTO Clientes VALUES(@id,@nombre,@saldo)

Código fuente 169

Diseñaremos el interfaz del componente de forma que disponga de una propiedad “cadenaConex”, que
contendrá la cadena de conexión, y un método “alta”, que será el que acceda mediante ADO a la base
de datos, empleando para ello el procedimiento almacenado recién creado.

En el Código fuente 170 está el código necesario para este componente.

Private mvarcadenaConex As String


Private objCnx As ADODB.Connection
Private objCmd As ADODB.Command
Private objRst As ADODB.Recordset
Private objContext As ObjectContext

Public Sub alta(ByVal id As Integer, ByVal nombre As String, ByVal saldo As


Currency)
Dim objPar1, objPar2, objPar3 As ADODB.Parameter
Set objContext = GetObjectContext
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "alta"
Set objPar1=objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2=objCmd.CreateParameter("par2", adVarChar, adParamInput, 25, nombre)
Set objPar3=objCmd.CreateParameter("par3", adCurrency, adParamInput, 8, saldo)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Execute , , adCmdStoredProc

232
© Grupo EIDOS 12. Diseño de componentes COM+

objContext.SetComplete
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Sub

Public Property Let cadenaConex(ByVal vData As String)


mvarcadenaConex = vData
End Property

Public Property Get cadenaConex() As String


cadenaConex = mvarcadenaConex
End Property

Código fuente 170

Comentemos algunas cosas de este código.

Como puede verse, una propiedad está implementada en Visual Basic mediante una variable miembro,
en este caso mvarcadenaConex, a la que puede accederse para modificar su valor mediante un
método Property Let, o para obtener su valor mediante Property Get. En realidad una
propiedad equivale pues a dos métodos, y modificar el valor de la propiedad o leerla supone invocar
un método.

En el código obtenemos también la referencia al objeto de contexto, y cuando el método “alta” ha


finalizado su ejecución, invocamos el método SetComplete. Esto puede parecer extraño, puesto
que este método suele asociarse siempre con transacciones sobre bases de datos, al igual que el
método SetAbort. Pero realmente este método nos permite aumentar la escalabilidad, porque
equivale a informar a los Servicios de Componentes de que el objeto ha terminado su labor y puede ser
desactivado. No es necesario que el componente sea transaccional para poder emplear cualquiera de
estos dos métodos.

Vamos a crear ahora una página ASP con la que probar este componente. El Código fuente 171 podría
ser un ejemplo.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%
Set objCom = Server.CreateObject("CompAlta.Alta")
objCom.cadenaConex = "Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=ASPComp;Data
Source=varrondo"
Response.Write "La cadena de conexión es: " & objCom.cadenaConex & "<br>"
objCom.alta 1,"pepe",1000
Response.Write "La cadena de conexión es: " & objCom.cadenaConex & "<br>"
On Error Resume Next
objCom.alta 2,"pepa",2000
if Err<>0 then Response.Write Err.description
On Error Goto 0
Set objCom = Nothing
%>
</BODY>
</HTML>

Código fuente 171

233
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Si la ejecutamos, veremos que funciona correctamente como podríamos esperar. Instanciamos el


componente y le asignamos valor a la propiedad cadenaConex. Esto equivale a invocar un método, el
Property Let. Luego obtenemos su valor y lo escribimos. Esto es una llamada al método Property Get.

Luego invocamos al método “alta”, y seguidamente volvemos a escribir la propiedad cadenaConex.


Activamos el gestor de errores de VBScript e invocamos una segunda vez al método “alta”, con un
nuevo valor de clave primaria.

Este componente parece correcto, desde el punto de vista de la Programación Orientada a Objetos.
Tiene una propiedad de estado, la cadena de conexión, que es utilizada en uno de sus métodos, “alta”.

Sin embargo, vamos a ver lo que ocurre cuando registramos el componente en la aplicación COM+
que venimos utilizando. Previamente hemos de quitar la DLL de memoria, mediante la descarga de la
aplicación web a través de la consola de administración del IIS.

Eliminemos también los registros de la tabla, para evitar infringir la restricción de clave primaria. Si
ahora volvemos a ejecutar la página ASP, la primera alta se ejecuta correctamente, pero luego vemos
que la propiedad cadenaConex aparece vacía, y la segunda alta falla al no serle suministrado al método
Open del objeto Connection una cadena de conexión correcta.

¿Qué es lo que ha ocurrido? Los Servicios de Componentes han interceptado la creación del objeto, y
están ahorrando recursos. Después de la llamada al primer método, el SetComplete comunica que el
objeto ha terminado su labor y puede ser desactivado, y eso es exactamente lo que ocurre, con lo cual
se pierde el estado.

Si llamáramos diez veces al método “alta”, estaríamos activando y desactivando el objeto diez veces,
perdiendo entre las llamadas la información de estado del mismo. Esto puede parecer que tendrá poco
rendimiento, pero no es así, y se ahorran recursos que estarían sin utilizar entre las llamadas.

Aunque la página ASP crea el objeto y no lo destruye hasta el final, realmente lo que obtiene no es la
referencia al objeto real, sino al context wrapper, mientras el objeto real es creado y destruido bajo la
gestión de los Servicios de Componentes.

Una forma de conseguir que el componente funcionara correctamente sería volver a asignar valor a la
propiedad cadenaConex antes de llamar por segunda vez al método “alta”.

Otra manera sería modificando una de las propiedades declarativas del componente. Si de entre sus
propiedades elegimos la pestaña Activación y desactivamos la casilla “Habilitar activación puntual”,
conseguimos que los Servicios de Componentes no gestionen la activación Just-In-Time de este
componente, que ahora se comportará como un objeto COM tradicional, cuyo tiempo de vida
coincidirá con el tiempo durante el que la página ASP guarda una referencia a él. Para que estos
cambios en la propiedad declarativa del componente tengan efecto, debemos descargar previamente de
memoria la aplicación COM+.

Pero de este modo perdemos las ventajas de escalabilidad mediante el ahorro de recursos que nos
proporcionan los Servicios de Componentes.

Más eficaz sería hacer que la cadena de conexión fuera suministrada como parámetro al método, junto
con los otros tres. O permitir que el componente acceda al modelo de objetos de ASP mediante el
objeto de contexto, para leer la cadena de conexión directamente de una variable de aplicación.

También podríamos usar el método Activate del interfaz ObjectControl, para asignar en ese momento
el valor a la cadena de conexión, leída desde el objeto Application. Hagámoslo así, como en el Código
fuente 172.

234
© Grupo EIDOS 12. Diseño de componentes COM+

Option Explicit
Implements ObjectControl
Private mvarcadenaConex As String
Private objContext As ObjectContext

Public Sub alta(ByVal id As Integer, ByVal nombre As String, ByVal saldo As


Currency)
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objRst As ADODB.Recordset
Dim objPar1, objPar2, objPar3 As ADODB.Parameter
Set objContext = GetObjectContext
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "alta"
Set objPar1=objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2=objCmd.CreateParameter("par2", adVarChar, adParamInput, 25, nombre)
Set objPar3=objCmd.CreateParameter("par3", adCurrency, adParamInput, 8, saldo)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Execute , , adCmdStoredProc
objContext.SetComplete
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Sub

Private Sub ObjectControl_Activate()


Dim objResponse As Response
Dim objApplication As Application
Set objContext = GetObjectContext
Set objApplication = objContext("Application")
mvarcadenaConex = objApplication("CadenaConex")
Set objResponse = objContext("Response")
objResponse.Write "activado a las " & Time & "<br>"
End Sub

Private Sub ObjectControl_Deactivate()


Dim objResponse As Response
Set objContext = GetObjectContext
Set objResponse = objContext("Response")
objResponse.Write "desactivado a las " & Time & "<br>"
End Sub

Private Function ObjectControl_CanBePooled() As Boolean


ObjectControl_CanBePooled = False
End Function

Código fuente 172

Si ahora ejecutamos la página con el Código fuente 173, funcionará correctamente, y podremos
comprobar en la ventana del navegador que el componente ha sido activado y desactivado dos veces.

Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial


Catalog=ASPComp;Data Source=varrondo"
Set objCom = Server.CreateObject("CompAlta.Alta")
objCom.alta 1,"pepe",1000
objCom.alta 2,"pepa",2000

235
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Set objCom = Nothing

Código fuente 173

Hagamos ahora otro componente, que extienda la funcionalidad del anterior, permitiendo un
mantenimiento sencillo de la tabla recién creada, por medio de tres métodos: uno para hacer altas, otro
bajas y otro consulta. Para hacer que el componente sea lo más reutilizable posible, haremos que la
cadena de conexión se suministre por medio de una variable de aplicación, como acabamos de ver, y
que el acceso a la base de datos sea a través de procedimientos almacenados. Tendremos que añadir
los dos nuevos que aparecen en el Código fuente 174.

CREATE PROCEDURE baja


@id smallint
AS
DELETE FROM Clientes WHERE id=@id

CREATE PROCEDURE consulta


AS
SELECT * FROM Clientes

Código fuente 174

El componente quedará como en el Código fuente 175.

Private objContext As ObjectContext


Private objApplication As Application
Private mvarcadenaConex As String
Private objCnx As ADODB.Connection
Private objCmd As ADODB.Command
Private objRst As ADODB.Recordset

Public Function consulta() As Variant()


Set objContext = GetObjectContext
Set objApplication = objContext("Application")
mvarcadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "consulta"
Set objRst = objCmd.Execute(, , adCmdStoredProc)
consulta = objRst.GetRows
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Function

Public Sub baja(ByVal id As Integer)


Dim objPar1 As ADODB.Parameter
Set objContext = GetObjectContext
Set objApplication = objContext("Application")
mvarcadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx

236
© Grupo EIDOS 12. Diseño de componentes COM+

objCmd.CommandText = "baja"
Set objPar1 = objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
objCmd.Parameters.Append objPar1
objCmd.Execute , , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Sub

Public Sub alta(ByVal id As Integer, ByVal nombre As String, ByVal saldo As


Currency)
Dim objPar1, objPar2, objPar3 As ADODB.Parameter
Set objContext = GetObjectContext
Set objApplication = objContext("Application")
mvarcadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "alta"
Set objPar1 = objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2 = objCmd.CreateParameter("par2", adVarChar, adParamInput, 25,
nombre)
Set objPar3 = objCmd.CreateParameter("par3", adCurrency, adParamInput, 8,
saldo)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Execute , , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Sub

Código fuente 175

Y la página ASP necesaria para probar su funcionamiento está en el Código fuente 176.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objCom = Server.CreateObject("CompMantenimiento.Mantenimiento")
Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial
Catalog=ASPComp;Data Source=varrondo"
%>

<%Select Case Request.Form("boton")%>


<%Case "alta"%>
<form method="POST">
<input name="id"><br>
<input name="nombre"><br>
<input name="saldo"><br>
<input type="submit" name="boton" value="confirmar alta">
</form>

<%Case "confirmar alta"%>


<%
objCom.alta Request.Form("id"),Request.Form("nombre"),Request.Form("saldo")

237
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

menu
%>

<%Case "baja"%>
<form method="POST">
<input name="id"><br>
<input type="submit" name="boton" value="confirmar baja">
</form>

<%Case "confirmar baja"%>


<%
objCom.baja Request.Form("id")
menu
%>

<%Case "consulta"%>
<%
contenido = objCom.consulta
Response.Write "<table border=1>"
for i=0 to UBound(contenido,2)
Response.Write "<tr>"
for j=0 to UBound(contenido,1)
Response.Write "<td>" & contenido(j,i) & "</td>"
next
Response.Write "</tr>"
next
Response.Write "</table>"
menu
%>

<%Case Else%>
<%menu%>

<%End Select%>

<%Set objCom = Nothing%>

<%Sub menu%>
<form method="POST">
<input type="submit" name="boton" value="alta">
<input type="submit" name="boton" value="baja">
<input type="submit" name="boton" value="consulta">
</form>
<%End Sub%>

</BODY>
</HTML>

Código fuente 176

Cadena del constructor


Otra forma de aumentar la posibilidad de reutilizar nuestros componentes es pasarle una cadena en el
momento de la construcción de un objeto a partir del componente. El contenido de esta cadena es
asignado por medio de propiedades declarativas.

Esto tiene una aplicación evidente en el caso de que el componente acceda a una base de datos. Si la
cadena de conexión, necesaria para conectarse a la fuente de datos, se le suministra por medio de un
parámetro en el instante de la construcción, podemos administrativamente modificar el contenido de
esta cadena para que el mismo componente nos sirva para el acceso a distintas fuentes de datos.

238
© Grupo EIDOS 12. Diseño de componentes COM+

Además si le pasamos la cadena de conexión de esta forma, en vez de recuperarla del objeto
Application, podemos reutilizar el componente desde clientes que no sean páginas ASP, puesto que ya
no hacemos uso de un modelo de objetos específico.

Retomemos el ejemplo del componente de altas, que habíamos llamado CompAlta. Eliminemos del
proyecto la referencia a la librería con el modelo de objetos de ASP, que ya no vamos a utilizar.
Eliminemos también, por claridad, los métodos del interfaz ObjectControl.

Si sustituimos: Implements ObjectControl por Implements IobjectConstruct,


tendremos disponible el combo izquierdo el interfaz, y al seleccionarlo se generará automáticamente el
código inicial de su único método, Construct.

En este método sólo tenemos que tomar el parámetro pCtorObj, que es un objeto cuya propiedad
ConstructString contiene precisamente la cadena del constructor, cuyo valor definimos mediante una
propiedad declarativa. A continuación asignamos a la variable miembro mvarcadenaConex este
valor. Todo esto puede verse en el Código fuente 177.

Option Explicit
Implements IObjectConstruct
Private mvarcadenaConex As String

Public Sub alta(ByVal id As Integer, ByVal nombre As String, ByVal saldo As


Currency)
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objRst As ADODB.Recordset
Dim objPar1, objPar2, objPar3 As ADODB.Parameter
Set objCnx = New ADODB.Connection
objCnx.Open mvarcadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "alta"
Set objPar1=objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2=objCmd.CreateParameter("par2", adVarChar, adParamInput, 25, nombre)
Set objPar3=objCmd.CreateParameter("par3", adCurrency, adParamInput, 8, saldo)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Execute , , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
End Sub

Private Sub IObjectConstruct_Construct(ByVal pCtorObj As Object)


mvarcadenaConex = pCtorObj.ConstructString
End Sub

Código fuente 177

Si ahora vamos a la consola de administración de los Servicios de Componentes, y dentro de las


Propiedades del componente CompAlta seleccionamos la pestaña Activación, podemos marcar la
casilla “Habilitar construcción de objetos” y dar valor a esta propiedad declarativa, como se ve en la
Figura 69.

Si ejecutamos ahora una página ASP con un código tan sencillo como el del Código fuente 178, todo
funcionará correctamente. Ya se adivina lo fácil que es hacer que este componente trabaje contra otra

239
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

base de datos, incluso de otro fabricante, con tal de que disponga de un procedimiento almacenado
llamado “alta” que necesite los mismos tres parámetros, y configuremos administrativamente su nueva
cadena de conexión, sin necesidad de modificar en absoluto el código del componente, ni por supuesto
de recompilarlo.

Figura 69

Set objCom = Server.CreateObject("CompAlta.Alta")


objCom.alta 1,"pepe",1000
objCom.alta 2,"pepa",2000
Set objCom = Nothing

Código fuente 178

240
Componentes transaccionales

Introducción
En este tema vamos a ver componentes transaccionales, es decir, aquellos cuyas operaciones sobre una
base de datos deben ser englobadas dentro de una transacción. El caso más típico lo constituye la
transferencia de dinero de una cuenta a otra.

Utilizaremos la tabla de Clientes, ya conocida de temas anteriores, a la que añadiremos una segunda
tabla LogTransferencias y dos procedimientos almacenados. Para lograr una mayor claridad,
reduciremos el código lo más posible.

El Código fuente 179 muestra las secuencias SQL para generar la tabla y los procedimientos
almacenados nuevos.

CREATE TABLE LogTransferencias(


fecha datetime NOT NULL,
idEmi smallint NOT NULL,
cantidad money NOT NULL,
idRec smallint NOT NULL,
PRIMARY KEY NONCLUSTERED(fecha,idEmi,cantidad,idRec)
)

CREATE PROCEDURE movimiento


@id smallint,
@cantidad money
AS
UPDATE Clientes SET saldo = saldo + @cantidad WHERE id=@id
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

CREATE PROCEDURE registro


@fecha datetime,
@idEmi smallint,
@cantidad money,
@idRec smallint
AS
INSERT INTO LogTransferencias VALUES(@fecha,@idEmi,@cantidad,@idRec)

Código fuente 179

En vez de usar un único procedimiento almacenado para hacer la transferencia, invocaremos al


procedimiento almacenado “movimiento” una vez para cada cuenta, la de origen y la de destino. Esto
nos permitirá entender mejor los problemas transaccionales en ASP con componentes.

Antes de nada veremos unos conceptos previos, relativos a componentes transaccionales.

El contexto y las transacciones

Pautas para el desarrollo de componentes transaccionales


• Conseguir una referencia del objeto de contexto. La información de contexto para un objeto
está almacenada en este tipo de objeto, que es capaz de vigilar el trabajo realizado por aquél,
así como su información de seguridad. Los objetos obtienen una referencia de su objeto de
contexto, llamando a la función GetObjectContext, función que proporcionan los Servicios de
Componentes. Los objetos COM+ utilizan el objeto de contexto para informar de si han
terminado o no su trabajo con éxito, así como para obtener información de la transacción o de
seguridad.

• Llamar a SetComplete si la transacción tiene éxito. Cuando un objeto termina su trabajo en


una transacción con éxito, debe llamar al método SetComplete del objeto de contexto. Esto
indicará a los Servicios de Componentes que el trabajo realizado por el objeto puede ser
confirmado cuando todos los objetos implicados en la transacción terminen su trabajo. El
método SetComplete también indica a los Servicios de Componentes que cualquier recurso
retenido por el objeto, incluido el propio objeto, puede ser reciclado.

• Llamar a SetAbort si la transacción falla. Cuando un objeto no termina con éxito su trabajo en
una transacción, ha de llamar al método SetAbort del objeto de contexto. Esto indicará a los
Servicios de Componentes que todos los cambios hechos por este objeto y los realizados por
otros objetos en la misma transacción quedarán anulados. Este método también indicará a los
Servicios de Componentes que cualquier recurso, incluido el propio objeto, puede ser
reciclado.

• Administrar el estado cuidadosamente. El estado representa los datos del objeto que se
mantienen después de haber realizado más de una llamada al método del objeto. Las variables
locales o globales permiten mantener el estado del objeto, pero no debe almacenar los estados
de esta forma si se participa en una transacción. Los Servicios de Componentes reciclan el
objeto, cuando la transacción termina, para liberar, de esta manera, recursos o hará que se
pierda cualquier tipo de información en las variables locales y globales.

La Propiedad MTSTransactionMode de Visual Basic, como se ve en la Figura 70, permite establecer


el atributo de transacción para una clase. Cuando se compile el proyecto, Visual Basic guardará esta

242
© Grupo EIDOS 13. Componentes transaccionales

propiedad en la biblioteca de tipos del componente. Luego, si el componente se añade a una aplicación
COM+, los Servicios de Componentes leerán el valor de la propiedad MTSTransactionMode y
automáticamente establecerá el atributo de la transacción en ese valor, tarea que simplifica
enormemente la administración de componentes escritos en Visual Basic.

Figura 70

Casos prácticos

La página contiene todo el código transaccional


En el presente capítulo veremos una serie de casos, con distintas opciones de diseño de la aplicación
para conseguir la misma funcionalidad final: efectuar una transferencia entre dos cuentas y guardar un
registro de la misma en una tabla de log. Veremos cómo afecta cada diseño al comportamiento
transaccional de la aplicación. Veremos una página ASP con todo el código, parte del código en
componentes, todo el código en componentes, distintas configuraciones de dichos componentes, etc.

De este modo, viendo una serie de casos prácticos, se pretende que quede claro cómo se encargan los
Servicios de Componentes de gestionar las transacciones cuando están involucradas páginas ASP y
componentes registrados en aplicaciones COM+.

Veamos un primer ejemplo en el que todo el código de acceso a la base de datos se encuentra en el
script de la página ASP, como se muestra en el Código fuente 180. En la rama if de esta página
crearemos un formulario que permita efectuar una transferencia de dinero de una cuenta a otra. El
formulario se submitirá a la propia página, que será tratado en la rama else.

<%@ transaction=required %>


<!--#INCLUDE file="ADOVBS.INC"-->
<html>
<head>
<title>Transferencia</title>

243
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

</head>
<body>

<%Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial


Catalog=ASPComp;Data Source=varrondo"%>

<%if Request.Form("boton")="" then%>

<form method="POST">
<input name="idEmi"><br>
<input name="cantidad"><br>
<input name="idRec"><br>
<input type="submit" name="boton" value="transferir">
</form>

<%else

idEmi=Request.Form("idEmi")
cantidad=Request.Form("cantidad")
idRec=Request.Form("idRec")

Set objCnx=Server.CreateObject("ADODB.Connection")
objCnx.Open Application("CadenaConex")
Set objCmd=Server.CreateObject("ADODB.Command")
objCmd.ActiveConnection=objCnx
objCmd.CommandText="movimiento"
Set objPar1=objCmd.CreateParameter("par1",adSmallInt,adParamInput,2,idEmi)
Set objPar2=objCmd.CreateParameter("par2",adCurrency,adParamInput,8,-
cantidad)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Execute numReg,,adCmdStoredProc
if numReg=0 then
Response.Write "<b>No existe el idEmi</b>"
ObjectContext.SetAbort
objCnx.Close
Response.End
end if

objCmd.Parameters("par1")=idRec
objCmd.Parameters("par2")=cantidad
objCmd.Execute numReg,,adCmdStoredProc
if numReg=0 then
Response.Write "<b>No existe el idRec</b>"
ObjectContext.SetAbort
objCnx.Close
Response.End
end if

objCmd.CommandText="registro"
objCmd.Parameters.Delete "par1"
objCmd.Parameters.Delete "par2"
Set objPar1=objCmd.CreateParameter("par1",adDate,adParamInput,8,date)
Set objPar2=objCmd.CreateParameter("par2",adSmallInt,adParamInput,2,idEmi)
Set objPar3=objCmd.CreateParameter("par3",adCurrency,adParamInput,8,cantidad)
Set objPar4=objCmd.CreateParameter("par4",adSmallInt,adParamInput,2,idRec)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Parameters.Append objPar4
objCmd.Execute numReg,,adCmdStoredProc

objCnx.Close
Set objCmd=Nothing
Set objCnx=Nothing

end if%>

244
© Grupo EIDOS 13. Componentes transaccionales

<%
Sub OnTransactionCommit
if Request.Form("boton")<>"" then
Response.Write "<h2>Transacción completada</h2>"
end if
End Sub

Sub OnTransactionAbort
Response.Write "<h2>Transacción abortada</h2>"
End Sub
%>

</body>
</html>

Código fuente 180

En la rama else creamos una única conexión sobre la que instanciamos un objeto Command, que
reutilizaremos modificando o eliminando los parámetros de su colección Parameters, invocando
sucesivamente a los procedimientos almacenados “movimiento”, una vez para la cuenta emisora y otra
vez para la receptora, y “registro”, para escribir en la tabla de log de transferencias.

La variable numReg que nos devuelve el método Execute será la que nos permita comprobar si el
cliente existía o no existía. Si su valor es igual cero, no hay ningún registro afectado, luego el cliente
no existía. Si su valor es igual a uno, sí existía el registro.

Por simplificar el código, ésta será la única comprobación que hagamos. No verificaremos por ejemplo
que el saldo del cliente después de la operación permanezca mayor que cero.

La directiva <%@ transaction=required %> indica a COM+ que la página debe ejecutarse en
el contexto de una transacción. Si no existe una, como es el caso, es creada automáticamente. Podemos
comprobar que si se produce un error porque alguno de los clientes no existe, o generamos nosotros
alguno intencionadamente con la línea Err.Raise vbObjectError + numError, o hacemos
explícitamente ObjectContext.SetAbort en la página, la transacción hace rollback
automáticamente.

Recordemos que los eventos OnTransactionCommit y OnTransactionAbort se disparan


automáticamente cuando la transacción ha tenido éxito o ha fracasado, respectivamente.

Acabemos diciendo que si la ejecución de la página llega a su fin y no se ha producido ninguna


instrucción SetAbort, se considera implícitamente que el voto de la página en la transacción es de
SetComplete.

Parte del código transaccional en un componente


Hemos visto cómo con la sencilla directiva <%@ transaction=required %> podemos hacer
que todo el código contenido en una página ASP sea englobado dentro de una misma transacción. Pero
este curso trata de componentes, así que vamos a diseñar un componente con el código que accede a la
tabla Clientes.

Creamos un nuevo proyecto ActiveX DLL, al que llamaremos CompTransferencia, con un módulo de
clase llamado Transferencia. Dispondrá de un único método, llamado “mover”, que accederá a la base
de datos a través del procedimiento almacenado “movimiento”. Este método será invocado tanto para

245
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

retirar el dinero del cliente origen como para ingresarlo en el de destino. Caso de no existir el cliente
solicitado, generará un error, que se encargará de recoger la página ASP.

Accederá a la cadena de conexión a través de la variable CadenaConex de ámbito de aplicación.

El código definitivo puede verse en el Código fuente 181.

Debemos añadir al proyecto las referencias a las librerías de los servicios de COM+, del modelo de
objetos de ASP, y de ADO.

Option Explicit

Public Sub mover(ByVal id As Integer, ByVal cantidad As Currency)


Dim objContext As ObjectContext
Dim objApplication As Application
Dim cadenaConex As String
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objPar1, objPar2 As ADODB.Parameter
Dim numReg As Integer

Set objContext = GetObjectContext


Set objApplication = objContext("Application")
cadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open cadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "movimiento"
Set objPar1 = objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2 = objCmd.CreateParameter("par2", adCurrency, adParamInput, 8,
cantidad)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Execute numReg, , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
If numReg = 0 Then Err.Raise vbObjectError + 1000, "mover", "No existe el
cliente"
End Sub

Código fuente 181

Compilemos el componente. La página ASP necesaria para utilizar este componente sólo necesita
instanciarlo e invocar sus métodos, como se ve en el Código fuente 182. El código necesario para
escribir en la tabla de log lo mantenemos todavía en la página ASP.

<%@ transaction=required %>


<!--#INCLUDE file="ADOVBS.INC"-->
<html>
<head>
<title>Transferencia</title>
</head>
<body>
<%Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial
Catalog=ASPComp;Data Source=varrondo"%>
<%if Request.Form("boton")="" then%>

246
© Grupo EIDOS 13. Componentes transaccionales

<form method="POST" id=form1 name=form1>


<input name="idEmi"><br>
<input name="cantidad"><br>
<input name="idRec"><br>
<input type="submit" name="boton" value="transferir">
</form>

<%else

idEmi=Request.Form("idEmi")
cantidad=Request.Form("cantidad")
idRec=Request.Form("idRec")

Set objCom=Server.CreateObject("CompTransferencia.Transferencia")
On Error Resume Next
objCom.mover idEmi,-cantidad
if Err<>0 then
Response.Write "<b>Error en el idEmi: </b>" & Err.description
ObjectContext.SetAbort
Response.End
end if
objCom.mover idRec,cantidad
if Err<>0 then
Response.Write "<b>Error en el idRec: </b>" & Err.description
ObjectContext.SetAbort
Response.End
end if
On Error Goto 0

Set objCnx=Server.CreateObject("ADODB.Connection")
objCnx.Open Application("CadenaConex")
Set objCmd=Server.CreateObject("ADODB.Command")
objCmd.ActiveConnection=objCnx
objCmd.CommandText="registro"
Set objPar1=objCmd.CreateParameter("par1",adDate,adParamInput,8,date)
Set objPar2=objCmd.CreateParameter("par2",adSmallInt,adParamInput,2,idEmi)
Set objPar3=objCmd.CreateParameter("par3",adCurrency,adParamInput,8,cantidad)
Set objPar4=objCmd.CreateParameter("par4",adSmallInt,adParamInput,2,idRec)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Parameters.Append objPar4
objCmd.Execute numReg,,adCmdStoredProc

objCnx.Close
Set objCmd=Nothing
Set objCnx=Nothing

end if%>

<%
Sub OnTransactionCommit
if Request.Form("boton")<>"" then
Response.Write "<h2>Transacción completada</h2>"
end if
End Sub

Sub OnTransactionAbort
Response.Write "<h2>Transacción abortada</h2>"
End Sub
%>
</body>
</html>

Código fuente 182

247
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Si probamos ahora la página, veremos que funciona correctamente. Si ponemos por ejemplo un cliente
de destino que no exista, aparece el mensaje de error correspondiente, y la transacción hace rollback
automático, no habiéndose restado la cantidad del saldo del cliente de origen. Esto es así porque el
código de la propia página es el encargado de decidir el rollback; el componente se limita a efectuar la
operación con la base de datos y generar el error en caso necesario.

Pero si ahora registramos el componente en la aplicación COM+, veamos qué sucede. Dejemos el
componente con la propiedad declarativa por defecto para las transacciones, es decir, “No se admite”.
Esto es configurable en la pestaña Transacciones de las Propiedades del componente, como aparece en
la Figura 71.

Figura 71

Si ejecutamos de nuevo la página y ponemos un cliente de destino que no exista, o se produce


cualquier otro error en la página, no se hace rollback de las operaciones efectuadas por el componente.
Esto es así porque la configuración “No se admite” para las transacciones significa que el componente
no participa en la transacción de la página, y no tiene voto en la misma.

Sin embargo, las operaciones efectuadas por la página sí hacen rollback en caso de error, y no llega a
escribirse la línea en la tabla LogTransferencias. Lo podemos comprobar haciendo
ObjectContext.SetAbort explícitamente, a continuación de la línea del Execute que escribe en
esa tabla. En este caso se hace rollback de la escritura en la tabla LogTransferencia, pero las
actualizaciones en la tabla Clientes se mantienen.

La opción “Deshabilitada” para las transacciones del componente tiene el mismo efecto.
Sin embargo, si configuramos el componente para que su comportamiento transaccional sea de
“Compatible”, esto significa que el componente participa de la transacción del cliente, en este caso la
página ASP, con lo cual todo el código ASP y el del componente forman una única transacción, que

248
© Grupo EIDOS 13. Componentes transaccionales

fallará o tendrá éxito en su conjunto. El componente también tiene voto en esa transacción, aunque no
estamos utilizando ahora mismo esta posibilidad (lo haremos más adelante).

Si la página no hubiera arrancado la transacción, es decir, no incluye la directiva <%@


transaction=required %>, entonces no habría ninguna transacción en curso y no se crearía
una nueva para el componente. Podemos comprobar que en ese caso no se hace rollback ni de las
operaciones efectuadas por el componente, ni de las efectuadas por el código de la página, en caso de
hacerse SetAbort.

Al no estar la página definida como transaccional, no tiene acceso al ObjectContext y la instrucción


ObjectContext.SetAbort genera un error. Dentro del bloque if de recepción del error del
componente está activado el gestor de errores de VBScript con On Error Resume Next, y lo pasa por
alto, pero no podría hacer este SetAbort por ejemplo al final de la página.

Si configuramos ahora la propiedad transaccional del componente como “Necesario”, y al no haber


arrancado una transacción la página, se creará una transacción particular para el componente. Sin
embargo, al no hacer el SetAbort desde dentro del componente, tampoco se hace rollback de las
actualizaciones del componente ni de la página.

La opción “Necesita nuevo” produce en este caso el mismo efecto.

Pero si volvemos a declarar la página como transaccional, y el componente como “Necesita nuevo”,
entonces se iniciará una transacción nueva para el componente, independiente de la transacción en la
cual se está ejecutando la página. En este caso, el SetAbort efectuado por la página sólo tiene efecto en
la transacción propia, y no hace rollback de los cambios en la base de datos que hubiera provocado el
componente.

Voto transaccional de un componente


En el caso de declarar la página como transaccional, y el componente como “Compatible” o
“Necesario”, hemos visto que todo funciona correctamente.

Pero lo normal es aprovechar más las ventajas que nos proporcionan los Servicios de Componentes, y
hacer que nuestro componente emita su voto respecto al SetCommit o SetAbort de la transacción. Esto
puede hacerlo gracias al objeto de contexto, que guarda información sobre la transacción dentro de la
cual se está ejecutando el componente. Ésta puede ser la suya propia, o la que ya había iniciado la
página. Veremos esto con detalle.

Rediseñemos el componente de forma que ahora pueda emitir su voto transaccional. Dejaremos que
siga generando un error en el caso de no existir el cliente, pero ya no será la página la que haga
SetAbort al captar ese error, sino que lo hará el propio componente. Por supuesto, primero hará el
SetAbort y luego generará el error, en ese orden. El Código fuente 183 será ahora el del componente.

Option Explicit

Public Sub mover(ByVal id As Integer, ByVal cantidad As Currency)


Dim objContext As ObjectContext
Dim objApplication As Application
Dim cadenaConex As String
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objPar1, objPar2 As ADODB.Parameter
Dim numReg As Integer

249
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Set objContext = GetObjectContext


Set objApplication = objContext("Application")
cadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open cadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "movimiento"
Set objPar1 = objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, id)
Set objPar2 = objCmd.CreateParameter("par2", adCurrency, adParamInput, 8,
cantidad)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Execute numReg, , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing
If numReg = 0 Then
objContext.SetAbort
Err.Raise vbObjectError + 1000, "mover", "No existe el cliente"
Else
objContext.SetComplete
End If
End Sub

Código fuente 183

En el Código fuente 184 puede verse el fragmento del código de la página ASP en que se invocan los
métodos del componente.

Set objCom=Server.CreateObject("CompTransferencia.Transferencia")
On Error Resume Next
objCom.mover idEmi,-cantidad
if Err<>0 then
Response.Write "<b>Error en el idEmi: </b>" & Err.description
Response.End
end if
objCom.mover idRec,cantidad
if Err<>0 then
Response.Write "<b>Error en el idRec: </b>" & Err.description
Response.End
end if
On Error Goto 0

Código fuente 184

Marquemos la página como transaccional, y el componente con la opción “Deshabilitada” o “No se


admite”. En este caso si introducimos en el formulario un cliente de destino inexistente, el componente
emite un voto de SetAbort, pero no tiene ninguna validez puesto que no participa de la transacción de
la página. La transacción se da por buena y los cambios en la base de datos son definitivos. Además se
disparará el evento OnTransactionCommit.

Si hago SetAbort desde la página, el registro insertado en la tabla de LogTransferencias hace rollback,
pero no las modificaciones en la tabla Clientes, que permanecen.

Si ahora declaro el comportamiento transaccional del componente como “Compatible” o “Necesario”,


entrará a formar parte de la transacción iniciada por la página, con lo que el código de la página y del

250
© Grupo EIDOS 13. Componentes transaccionales

componente forman una única transacción. El voto del componente afecta a toda la transacción
conjunta, igual que el de la página, y si alguno de ellos hace SetAbort, se hace rollback de todas las
modificaciones, tanto en la tabla LogTransferencias como en la tabla Clientes. Este es el
comportamiento que normalmente nos va a interesar.

Si hacemos el cambio en el código de la página, de forma que desactivemos el gestor de errores de


VBScript antes de la segunda llamada al método mover, y eliminamos las líneas del
Response.End, como se ve en el Código fuente 185, veremos que si introducimos un id de cliente
de origen inexistente, en la primera llamada a “mover” se hace SetAbort de la transacción, y en la
segunda obtenemos el error “Realizó una llamada a un método en un componente COM+ que tiene
una transacción que se ha anulado o que está en curso de anulación”. Esto se debe a que el SetAbort de
la primera llamada hace que en el objeto de contexto asociado al componente se marca la transacción
como anulada, y los Servicios de Componentes pueden así liberar los recursos asociados a este objeto,
que es inservible mientras dura la transacción, que ya está anulada.

Set objCom=Server.CreateObject("CompTransferencia.Transferencia")
On Error Resume Next
objCom.mover idEmi,-cantidad
if Err<>0 then
Response.Write "<b>Error en el idEmi: </b>" & Err.description
end if
On Error Goto 0
objCom.mover idRec,cantidad
if Err<>0 then
Response.Write "<b>Error en el idRec: </b>" & Err.description
end if

Código fuente 185

Sólo nos queda por probar declarar el componente como “Necesita nuevo”. Con esta configuración, se
inicia una transacción para el componente, pero es independiente de la transacción iniciada para la
página.

Si introducimos en el formulario un id de cliente inexistente, se hace rollback de la transacción del


componente, no de la de la página. Hay un detalle más: la transacción iniciada para la página abarca
todo el tiempo de ejecución de la página, pero para el componente se inicia una cada vez que se invoca
al método “mover”. Por lo tanto, la operación de retirar el dinero de un cliente y la operación de
ingresarlo en otro cliente no se consideran una única transacción, sino transacciones distintas, y no se
hará rollback de las dos sino de aquélla que haya fallado.

Dos componentes transaccionales invocados desde ASP


Hagamos ahora que el código que inserta el registro en la tabla LogTransferencias esté también dentro
de un componente. Llamaremos al proyecto ActiveX DLL CompLog, y al módulo de clase Log, con
un código como el del Código fuente 186.

Option Explicit

Public Sub registrar(ByVal fecha As Date, ByVal idEmi As Integer, ByVal cantidad As
Currency, ByVal idRec As Integer)
On Error GoTo Errores

251
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Dim objContext As ObjectContext


Dim objApplication As Application
Dim cadenaConex As String
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objPar1, objPar2, objPar3, objPar4 As ADODB.Parameter

Set objContext = GetObjectContext


Set objApplication = objContext("Application")
cadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open cadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "registro"
Set objPar1=objCmd.CreateParameter("par1", adDate, adParamInput, 8, fecha)
Set objPar2=objCmd.CreateParameter("par2", adSmallInt, adParamInput, 2, idEmi)
Set objPar3=objCmd.CreateParameter("par3",adCurrency,adParamInput,8,cantidad)
Set objPar4=objCmd.CreateParameter("par4", adSmallInt, adParamInput, 2, idRec)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Parameters.Append objPar3
objCmd.Parameters.Append objPar4
objCmd.Execute , , adCmdStoredProc
objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing

Exit Sub

Errores:
objContext.SetAbort
Err.Raise vbObjectError + 1000, "registrar", "Falló el registro en el log"
End Sub

Código fuente 186

El código necesario en la página ASP, como se ve en el Código fuente 187, queda mucho más
reducido. Es el entramado que aglutina la funcionalidad de los componentes, invocando
ordenadamente sus métodos.

<%@ transaction=required %>


<!--#INCLUDE file="ADOVBS.INC"-->
<html>
<head>
<title>Transferencia</title>
</head>
<body>
<%Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial
Catalog=ASPComp;Data Source=varrondo"%>

<%if Request.Form("boton")="" then%>

<form method="POST" id=form1 name=form1>


<input name="idEmi"><br>
<input name="cantidad"><br>
<input name="idRec"><br>
<input type="submit" name="boton" value="transferir">
</form>

<%else

252
© Grupo EIDOS 13. Componentes transaccionales

idEmi=Request.Form("idEmi")
cantidad=Request.Form("cantidad")
idRec=Request.Form("idRec")

Set objCom1=Server.CreateObject("CompTransferencia.Transferencia")
On Error Resume Next
objCom1.mover idEmi,-cantidad
if Err<>0 then
Response.Write "<b>Error en el idEmi: </b>" & Err.description
Response.End
end if
objCom1.mover idRec,cantidad
if Err<>0 then
Response.Write "<b>Error en el idRec: </b>" & Err.description
Response.End
end if
On Error Goto 0

Set objCom2=Server.CreateObject("CompLog.Log")
On Error Resume Next
objCom2.registrar date,idEmi,cantidad,idRec
if Err<>0 then
Response.Write "<b>Error en el log: </b>" & Err.description
Response.End
end if
On Error Goto 0

end if%>

<%
Sub OnTransactionCommit
if Request.Form("boton")<>"" then
Response.Write "<h2>Transacción completada</h2>"
end if
End Sub

Sub OnTransactionAbort
Response.Write "<h2>Transacción abortada</h2>"
End Sub
%>
</body>
</html>

Código fuente 187

Lo normal en este caso sería declarar la página como transaccional y declarar los componentes como
“Compatible” o “Necesario” respecto a su comportamiento transaccional. Hagamos las pruebas
pertinentes, y veremos que todo funciona correctamente.

Un componente transaccional invoca a otro


Cambiemos ahora el interfaz del componente CompTransferencia, de forma que el método “mover”
reciba los id del cliente de origen y de destino de la transferencia. De esta forma sólo hay que invocar
al método una vez.

Además, este mismo componente será el encargado de instanciar al componente CompLog e invocar
su método “registrar”.

En definitiva, el método del componente CompTransferencia queda como en el Código fuente 188.

253
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Option Explicit

Public Sub mover(ByVal idEmi As Integer, ByVal cantidad As Currency, ByVal idRec As
Integer)
Dim objContext As ObjectContext
Dim objApplication As Application
Dim cadenaConex As String
Dim objCnx As ADODB.Connection
Dim objCmd As ADODB.Command
Dim objPar1, objPar2 As ADODB.Parameter
Dim numReg As Integer
Dim objCom As CompLog.Log

Set objContext = GetObjectContext


Set objApplication = objContext("Application")
cadenaConex = objApplication.Contents("CadenaConex")
Set objCnx = New ADODB.Connection
objCnx.Open cadenaConex
Set objCmd = New ADODB.Command
objCmd.ActiveConnection = objCnx
objCmd.CommandText = "movimiento"
Set objPar1=objCmd.CreateParameter("par1", adSmallInt, adParamInput, 2, idEmi)
Set objPar2=objCmd.CreateParameter("par2",adCurrency,adParamInput,8,-cantidad)
objCmd.Parameters.Append objPar1
objCmd.Parameters.Append objPar2
objCmd.Execute numReg, , adCmdStoredProc
If numReg = 0 Then
objContext.SetAbort
Err.Raise vbObjectError + 1000, "mover", "No existe el cliente de origen"
End If

objCmd.Parameters("par1") = idRec
objCmd.Parameters("par2") = cantidad
objCmd.Execute numReg, , adCmdStoredProc
If numReg = 0 Then
objContext.SetAbort
Err.Raise vbObjectError + 1000, "mover", "No existe el cliente de destino"
End If

objCnx.Close
Set objCmd = Nothing
Set objCnx = Nothing

Set objCom = New CompLog.Log


objCom.registrar Date, idEmi, cantidad, idRec

objContext.SetComplete

End Sub

Código fuente 188

Y el código de la página ASP queda ahora todavía más reducido, como se muestra en el Código fuente
189.

<%@ transaction=required %>


<!--#INCLUDE file="ADOVBS.INC"-->
<html>
<head>
<title>Transferencia</title>
</head>

254
© Grupo EIDOS 13. Componentes transaccionales

<body>
<%Application("CadenaConex") = "Provider=SQLOLEDB.1;User ID=sa;Initial
Catalog=ASPComp;Data Source=varrondo"%>

<%if Request.Form("boton")="" then%>

<form method="POST" id=form1 name=form1>


<input name="idEmi"><br>
<input name="cantidad"><br>
<input name="idRec"><br>
<input type="submit" name="boton" value="transferir">
</form>

<%else

idEmi=Request.Form("idEmi")
cantidad=Request.Form("cantidad")
idRec=Request.Form("idRec")

Set objCom1=Server.CreateObject("CompTransferencia.Transferencia")
On Error Resume Next
objCom1.mover idEmi,cantidad,idRec
if Err<>0 then
Response.Write "<b>Se produjo un error: </b>" & Err.description
'Response.End
end if
On Error Goto 0

end if%>

<%
Sub OnTransactionCommit
if Request.Form("boton")<>"" then
Response.Write "<h2>Transacción completada</h2>"
end if
End Sub

Sub OnTransactionAbort
Response.Write "<h2>Transacción abortada</h2>"
End Sub
%>
</body>
</html>

Código fuente 189

255
Acceso a MSMQ desde ASP

Servicios de mensajería entre aplicaciones


Los sistemas distribuidos han ido evolucionando. Al principio sólo había una máquina potente, el host,
que era compartido por muchos ordenadores clientes. Al aumentar la potencia de cálculo y abaratarse
el coste, los clientes empezaron a compartir el peso de la aplicación, y empezó a poderse hablar de
aplicación distribuida, desapareciendo la figura del host. Pero empezó la preocupación por la
comunicación entre esos ordenadores que comparten la lógica de la aplicación.

Los modelos de componentes como DCOM o CORBA permiten la comunicación entre distintos
componentes de una aplicación, o entre distintas aplicaciones, ejecutándose en máquinas diferentes,
pero esto lo hacen a través de mensajes síncronos, es decir, que una aplicación envía un mensaje a
otra, y queda en espera hasta que no recibe una respuesta.

Esta suposición de que la aplicación de destino va a estar disponible para devolver una respuesta
inmediata no siempre es cierta, sobre todo en un entorno como el de una aplicación web.

En situaciones así tienen su aplicación los sistemas de mensajería, como el MSMQ de Microsoft.
Estos sistemas de mensajería entre aplicaciones usan mensajes asíncronos. Un cliente envía un
mensaje a un servidor, pero en vez de esperar una respuesta inmediata, prosigue con su trabajo. El
mensaje queda almacenado en una cola, de tal forma que cuando el servidor esté disponible para
procesarlo, lo hará, y le enviará una respuesta al cliente.

Las aplicaciones que se comunican pueden estar en plataformas distintas, sistemas operativos
distintos, incluso pueden no estar conectados permanentemente. Internet es un ejemplo de conexión no
permanente entre plataformas y sistemas operativos distintos.
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Suele compararse un servicio de mensajería con un servicio de email. La diferencia principal es que un
servicio de email intercambia información entre personas y un servicio de mensajería lo hace entre
aplicaciones.

Los servicios de mensajería entre aplicaciones trabajan con mensajes y colas.

Un mensaje es un trozo de información intercambiado entre dos aplicaciones. El formato y contenido


de este mensaje es específico de la aplicación (puede ser texto o binario), pero para el sistema de
mensajería esto es indiferente. No exige un formato específico para estos mensajes. De hecho, ni
siquiera inspecciona su contenido, limitándose a transmitirlo.

Sin embargo otras tecnologías, como HTTP, FTP, ODBC, etc, también permiten la comunicación
entre plataformas distintas, pero son para tipos particulares de aplicaciones, y se exige un formato
específico para los mensajes.

Las colas son los lugares donde se depositan estos mensajes.

MSMQ (Message Queuing Server)


MSMQ no es algo nuevo. Existen otras tecnologías de mensajería para PCs. Este servicio está
integrado en Windows NT Server en su versión Enterprise, y algo más reducido en la versión normal,
bajo el nombre de MSMQ 1.0. También está incluido en Windows 2000, donde es un servicio más que
recibe el nombre de Message Queuing Server. La parte cliente se encuentra en todas las versiones de
Windows.

El MSMQ proporciona la tecnología necesaria para el envío de mensajes entre aplicaciones, y la


gestión de colas.

Para acceder a estos servicios, podemos hacerlo a través de un API. Además ofrece un modelo COM
para poder ser usado desde cualquier lenguaje que soporte esta tecnología. Podremos utilizarlo por
tanto desde una página ASP.

Ya hemos dicho que los servicios de mensajería entre aplicaciones, como el MSMQ, son muy
parecidos a los servicios de correo, pero presentan diferencias:

• Los servidores de correo transmiten mensajes entre personas, y el MSMQ los transmite entre
aplicaciones.

• Garantizan mayor seguridad de que el mensaje ha llegado a su destino. En un servicio de


correo no podemos estar seguros de que el mensaje enviado haya llegado correctamente. Con
el MSMQ es posible saber exactamente cuándo ha llegado.

El MSMQ es más lento que otros métodos de comunicación de más bajo nivel, como sockets o
canalizaciones con nombre, named pipes. De hecho, MSMQ usa internamente estos métodos, aunque
de forma completamente transparente para mí. Sin embargo esta lentitud está compensada por las
ventajas que ofrece.

Si quiero obtener más rendimiento, los mensajes se pueden almacenar en RAM. Esto supone mayor
velocidad en la gestión de los mensajes, pero ante un fallo del servidor podría perderlos. Si quiero más
fiabilidad ante posibles fallos, los mensajes se pueden almacenar en el disco duro y ser recuperados
cuando el servidor se recupere del fallo.

258
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Habrá una serie de situaciones en las que interese utilizar el MSMQ para transmitir mensajes entre
máquinas que comparten la lógica de una aplicación distribuida:

• Si prevemos que las comunicaciones entre los ordenadores pueden fallar o no ser
permanentes, o incluso puede estar apagado temporalmente el ordenador de destino. Es decir,
no esta garantizada la disponibilidad de la máquina. El MSMQ simplemente depositará el
mensaje en la cola de destino correspondiente hasta que el ordenador vuelva estar disponible.

• Si queremos lograr más escalabilidad del sistema. Parece que esto no tiene sentido, puesto que
la transmisión de mensajes con MSMQ es más lenta, pero en realidad conseguimos más
escalabilidad en el sentido de que se puede manejar más tráfico con picos variables de
mensajes sin fallo, aunque cada uno de ellos los maneje más lentamente. Con comunicaciones
síncronas el servidor tiene que estar sobredimensionado para poder soportar los picos más
fuertes en el tráfico de mensajes, y aún así puede llegar a sobrecargarse. Con comunicaciones
asíncronas, sólo tiene que estar dimensionado para el tráfico medio esperado. En los
momentos de picos, los mensajes simplemente se almacenarán hasta que puedan ser
procesados.

• Cuando nos interese garantizar que el mensaje ha sido recibido por la aplicación de destino,
pero no es necesario obtener una respuesta inmediata al mensaje.

Funcionamiento del MSMQ


En un sistema MSMQ hay tres tipos de servidores:

• Controlador principal PEC (Primary Enterprise Controller): Gestiona la configuración de todo el


sistema. Contiene los certificados para validar las claves.

• Controlador del sitio (Site Controller): Contiene información de todos los ordenadores y colas de
un sitio (ordenadores conectados por una red rápida). Varios sitios forman el sistema. En el sitio
donde esté el PEC, éste sirve también de controlador de sitio.

• Enrutador de mensajes (Message Router): Gestiona el envío de mensajes entre sitios. El


controlador de sitio sirve también de router. Si un sitio tiene mucho tráfico se le pueden añadir
routers adicionales.

Y hay dos tipos de clientes:

• Cliente independiente: Tiene su propia cola de mensajes. Se usa normalmente en portátiles.

• Cliente dependiente: No tiene cola; depende de un servidor MSMQ.

El MSMQ permite tres modos de envío de mensajes:

• Basado en memoria: Es el más rápido. El mensaje se almacena en la memoria hasta que el gestor
puede contactar con el gestor de destino. Si hay un problema en la comunicación, esperará hasta
poder hacerlo. El inconveniente es que si hay un fallo en la máquina, se pierde el mensaje.

• Basado en disco: Es más lento. El mensaje se almacena en el disco. Cuando llega a la máquina de
destino, se elimina del disco. Si hay un fallo en la máquina, se puede recuperar luego del disco.
Hace falta un sistema de archivos como el NTFS que permite esta recuperación.

259
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

• Basado en transacciones: Utiliza los Servicios de Componentes, antes MTS.

Creación de una cola


Para acceder a la consola de administración del servicio MSMQ en Windows 2000, debemos
seleccionar “Administración de equipos”, dentro de las Herramientas administrativas del menú de
Inicio. Si expandimos el árbol correspondiente a “Servicios y Aplicaciones”, obtendremos un
resultado como el que aparece en la Figura 72.

Figura 72

Si pulsamos con el botón derecho sobre la carpeta de Colas privadas, y del menú emergente
seleccionamos la opción Nuevo | Cola privada, podemos crear una nueva cola. Sólo hay que
suministrar la información que se pide en una ventana como en la Figura 73, es decir, el nombre de la
cola y su comportamiento transaccional.

Figura 73

260
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Si extendemos ahora el árbol de la cola1 recién creada obtendremos lo que aparece en la Figura 74.

Figura 74

Vemos que en la subcarpeta de Mensajes de la cola no hay ahora ninguno.

Modelo de objetos de MSMQ


Trabajaremos con tres objetos principales de este modelo:

• MSMQQueueInfo: Permite almacenar las colas de un servidor MSMQ. Lo usaremos como


punto de partida para cualquier operación con colas: crear, modificar, borrar, etc.

• MSMQQueue: Representa una cola abierta por medio del método Open del objeto
MSMQQueueInfo.

• MSMQMessage: Representa un mensaje.

Vamos a ver que podemos utilizar este modelo COM para acceder al servicio MSMQ desde una
página ASP. Y lo primero que haremos será repetir lo mismo que conseguimos desde la consola de
administración del servicio. Para ello creemos una página ASP con el Código fuente 190.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>

261
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<BODY>

<%
Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")
objQueueInfo.PathName = ".\PRIVATE$\cola2"
objQueueInfo.Create
%>

</BODY>

</HTML>

Código fuente 190

En esta página simplemente recogemos una referencia al objeto MSMQQueueInfo. A continuación le


asignamos a la propiedad PathName el nombre de la cola.

En caso de tratarse de colas privadas, este nombre es de la forma “nombreDelServidor\PRIVATE$


\nombreDeLaCola”, aunque el nombre del servidor puede sustituirse por un punto, quedando de la
forma “.\PRIVATE$\nombreDeLaCola”.

Por último, basta llamar al método Create. Siempre debe asignarse la propiedad PathName antes de
llamar al método Create.

Si ejecutamos la página podremos ver en la consola de administración del MSMQ que se ha creado
una nueva cola privada, de nombre “cola2”. Ejecutando el Código fuente 191 conseguiríamos
eliminarla.

Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

objQueueInfo.PathName = ".\PRIVATE$\cola2"

objQueueInfo.Delete

Código fuente 191

Envío de mensajes
Ahora vamos a enviar un mensaje a la cola1 recién creada desde una página ASP. Lo primero es
comprobar que se disponen de los permisos necesarios para enviar un mensaje a la cola.

Si desde el administrador del servicio MSMQ, pulsamos con el botón derecho sobre el nombre de la
cola y seleccionamos la opción Propiedades, y luego la pestaña Seguridad, obtendremos lo que se
muestra en la Figura 75.

Vemos que por defecto, el grupo Todos, en el que desde luego estará incluida la cuenta de invitado a
internet IUSR_nombreServidor que normalmente usaremos al ejecutar una página ASP con acceso
anónimo, tiene activado el permiso de “Escribir mensaje”, pero no así los de recibir ni inspeccionar
mensaje. Pero como ahora sólo queremos escribir, con esto es suficiente.

En el Código fuente 192 se pueden ver las instrucciones que son necesarias para escribir un mensaje
en la cola.

262
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Figura 75

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<!--METADATA type="typelib" file="C:\winnt\system32\mqoa.dll"-->

<%
Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")
objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS,MQ_DENY_NONE)

Set objMsg = Server.CreateObject("MSMQ.MSMQMessage")


objMsg.Label = "Mensaje 1"
objMsg.Body = "Cuerpo del mensaje"
objMsg.Send objQueue

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing
%>
</BODY>
</HTML>

Código fuente 192

263
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Igual que antes, primero obtenemos la referencia al objeto MSMQQueueInfo, y le asignamos la


propiedad PathName que identifica a la cola. A continuación aplicamos el método Open sobre este
objeto para obtener una referencia a la cola.

Este método Open recibe dos parámetros, que son especificados por medio de constantes. Para poder
utilizar los nombres de estas constantes en vez de tener que recordar su valor numérico, utilizamos la
directiva METADATA con el nombre de la DLL en que se encuentran, que en este caso es
MQOA.DLL. El parámetro Access especifica el modo de apertura de la cola por la aplicación, es decir,
que va a hacer con ella. Puede tomar los valores:

• MQ_RECEIVE_ACCESS (1): Permite inspeccionar o recibir mensajes de la cola. En el


segundo caso, los mensajes se eliminan a medida que son leídos.

• MQ_SEND_ACCESS (2): Permite enviar mensajes a la cola.

• MQ_PEEK_ACCESS (32): Sólo permite inspeccionar los mensajes, pero no recibirlos.

El parámetro ShareMode especifica qué acceso tienen los demás a la cola mientras está siendo
utilizada por la aplicación. Puede tomar los valores:

• MQ_DENY_NONE (0): Si he abierto la cola en uno de los modos MQ_SEND_ACCESS o


MQ_PEEK_ACCESS, ésta es la única opción. Permito a los demás el acceso total a la cola.

• MQ_DENY_RECEIVE_SHARE (1): Si he abierto en modo MQ_RECEIVE_ACCESS, tengo


la opción anterior o ésta. Con ella impido que otros reciban mensajes de la cola, aunque
pueden seguir enviando.

Luego creamos un objeto MSMQMessage y le asignamos valor a sus propiedades Label, que contiene
la etiqueta del mensaje, y Body, que almacena su contenido. El método Send invocado sobre este
objeto necesita como parámetro el nombre de la cola a la que va a ser enviado. Para finalizar, cerramos
la cola y destruimos los objetos. En la consola de administración del servicio MSMQ podremos ver
que el mensaje se ha enviado y almacenado en la cola, como se ve en la Figura 76.

Figura 76

264
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Por cierto, que podemos configurar las propiedades del mensaje que queremos que aparezcan en el
panel izquierdo. Basta con pulsar con el botón derecho sobre la subcarpeta “Mensajes de la cola”, y
seleccionar la opción Ver | Elegir columnas. Aparecerá una ventana como la de la Figura 77 en la que
podremos seleccionar la información que queremos ver de los mensajes.

Figura 77

Prioridad de los mensajes


El mensaje que acabamos de enviar tiene una prioridad por defecto de 3, que es el nivel de prioridad
intermedio. Pero yo puedo modificar este valor por defecto, y asignar valores que van desde 0 para la
prioridad mínima hasta 7 para la máxima.

Si cambiamos ahora la página ASP por el contenido del Código fuente 193, estamos asignando al
nuevo mensaje una prioridad de 7, que es superior a la del mensaje anterior.

Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")


objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS,MQ_DENY_NONE)

Set objMsg = Server.CreateObject("MSMQ.MSMQMessage")


objMsg.Label = "Mensaje 2"
objMsg.Body = "Cuerpo del mensaje"
objMsg.Priority = 7
objMsg.Send objQueue

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

Código fuente 193

265
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Podemos comprobar en la consola de administración del MSMQ, como se ve en la Figura 78, que este
segundo mensaje se coloca delante del anterior en la cola, puesto que su prioridad es mayor.

Figura 78

Recepción de mensajes
Una vez enviado el mensaje, vamos a intentar recuperarlo desde una página ASP. Normalmente las
páginas ASP se limitarán a enviar mensajes a una cola, y será otra aplicación la encargada de
recuperarlos y procesarlos, pero vamos a ver que también desde el VBScript de una página ASP
podemos hacer esto mismo, aunque sólo sea en este caso para listarlos en una tabla.

De momento sólo vamos a inspeccionar el mensaje, es decir, veremos su contenido pero no lo


eliminaremos de la cola. Creamos para ello una nueva página ASP con el código que puede verse en el
Código fuente 194.

Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")


objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_PEEK_ACCESS,MQ_DENY_NONE)

Set objMsg = objQueue.PeekCurrent


Response.Write objMsg.Label & " - " & objMsg.Body

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

Código fuente 194

266
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Esta vez abrimos la cola en modo MQ_PEEK_ACCESS, es decir, para inspeccionar. A continuación
obtenemos una referencia a un objeto MSMQMessage a partir del método PeekCurrent invocado
sobre la cola. Este método recupera el mensaje sobre el que esté posicionado el cursor, que ahora
mismo es el primer mensaje de la cola, que será el segundo enviado puesto que tenía mayor prioridad.
Escribimos la etiqueta y el cuerpo del mensaje, cerramos la cola, y eliminamos los objetos.

Si ejecutamos esta página, obtendremos el error de Acceso denegado. Esto es debido a que la
cuenta de invitado a internet no tiene los permisos necesarios para inspeccionar los mensajes
almacenados en esta cola. Vamos a concedérselos a través del grupo Todos, como se ve en la Figura
79, activando la casilla de verificación correspondiente a “Inspeccionar mensaje”.

Figura 79

Ahora sí podremos ejecutar la página y obtener el contenido del mensaje sin problemas.

Si lo que queremos es inspeccionar el contenido de todos los mensajes de la cola, debemos hacerlo a
través de un bucle, como se puede ver en el Código fuente 195.

Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")


objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_PEEK_ACCESS,MQ_DENY_NONE)

Set objMsg = objQueue.PeekCurrent(,,100)


Do While True

267
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

If objMsg Is Nothing Then Exit Do


Response.Write "<b>" & objMsg.Label & "</b>: " & objMsg.Body & "<br>"
Set objMsg = objQueue.PeekNext(,,100)
Loop

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

Código fuente 195

Utilizamos el método de lectura anticipada, y luego un bucle infinito Do While True, del que sólo
se sale con Exit Do en el caso de que el mensaje leído en un paso del bucle sea igual a Nothing,
es decir, que ya no queden mensajes en la cola.

Tanto en el método PeekCurrent, que lee el mensaje actual, como el PeekNext, que lee el siguiente,
son sólo métodos de inspección de mensajes: leen el mensaje pero no lo eliminan de la cola.

Cuando se invoca a cualquiera de estos dos métodos, la ejecución se detiene hasta que se lee el
mensaje de la cola o hasta que se consume el tiempo de espera. Este tiempo de espera es el tercero de
los tres parámetros opcionales que pueden recibir estos métodos, el ReceiveTimeout, que se especifica
en milisegundos.

Antes no hemos utilizado este parámetro porque sabíamos que había un mensaje en la cola. Pero si en
este caso no lo utilizamos, la ejecución de la página entraría en un bucle infinito, porque después de
leer el último mensaje de la cola, volvería a ejecutar el bucle y se quedaría intentando leer uno nuevo
de forma indefinida. En el código de ejemplo se han asignado 100 milisegundos de timeout de
recepción.

Si en vez de sólo inspeccionar un mensaje queremos recibirlo, es decir, leerlo y eliminarlo de la cola,
debemos escribir algo parecido al Código fuente 196.

Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")


objQueueInfo.PathName = ".\PRIVATE$\cola2"

Set objQueue = objQueueInfo.Open(MQ_RECEIVE_ACCESS,MQ_DENY_NONE)

Set objMsg = objQueue.ReceiveCurrent(,,,100)


Response.Write objMsg.Label & " - " & objMsg.Body

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

Código fuente 196

Ahora abrimos la cola en modo MQ_RECEIVE_ACCESS en vez de MQ_PEEK_ACCESS, e


invocamos al método ReceiveCurrent en vez de PeekCurrent.

Para que funcione la página, debemos conceder permisos de recepción de mensajes en la cola
activando la casilla de verificación correspondiente a “Recibir mensaje”, como se aprecia en la Figura
80.

268
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Figura 80

Se puede observar en la consola de administración del servicio MSMQ que el mensaje ha


desaparecido de la cola después de haber sido leído.

Colas de diario
Se pueden almacenar copias de los mensajes para llevar un control de los mensajes enviados y
recibidos. Estas copias se llaman mensajes de diario, y se almacenan en las copias de diario.

Para que la cola permita almacenamiento de diario, desde el administrador del servicio MSMQ
pulsamos con el botón derecho sobre el nombre de la cola y seleccionamos la casilla de verificación
“Habilitado” dentro del apartado “Diario”, como se ve en la Figura 81.

De esta forma, al eliminarse el mensaje de la cola, se almacena una copia del mismo en la subcarpeta
denominada “Mensajes del diario”.

Lo podemos comprobar si enviamos un nuevo mensaje y luego lo recibimos. La copia del mensaje
queda almacenada como se ve en la Figura 82.

269
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Figura 81

Figura 82

270
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Envío de un componente COM+


Veamos ahora que a una cola podemos enviar datos binarios como cuerpo de un mensaje, y en
concreto vamos a enviar la instancia de un componente. Continuamos trabajando con nuestra tabla de
Clientes de capítulos anteriores.

Hacemos para ello un nuevo proyecto ActiveX DLL de Visual Basic al que llamaremos CompCliente,
con un módulo de clase llamado Cliente. Con el complemento Generador de clases, añadimos tres
propiedades al módulo, una por cada campo de la tabla Clientes.

Los objetos COM+ que envíe en un mensaje tienen que soportar la persistencia, o dicho de otra
forma, ser serializables. Con C++ se pueden construir componentes que implementen el interfaz
IPersist, de tal forma que cuando el MSMQ tiene que meter un objeto en el cuerpo de un mensaje, lo
serializa a través de este interfaz. Esos datos binarios serializados son los que se almacenan en el
cuerpo del mensaje.

Con Visual Basic no puedo hacer esto, pero las rutinas de su runtime se encargan de simular este
interfaz: lee las variables y valores del objeto y las mete en un PropertyBag, y luego las serializa a
través del interfaz IPersist. Realmente las clases de Visual Basic no implementan ese interfaz, sino que
cuando el MSMQ busca ese interfaz, las rutinas del runtime se encargan de invocar los eventos
Class_ReadProperties y Class_WriteProperties.

Por lo tanto, tenemos que declarar primero de todo el módulo de clase como serializable, configurando
su propiedad “Persistable” al valor “1-Persistable”, como se ve en la Figura 83.

Figura 83

Además debemos implementar los procedimientos Class_ReadProperties y Class_WriteProperties,


que serán invocados automáticamente en la serialización y en el proceso inverso, leyendo o
escribiendo en el PropertyBag. El Código fuente 197 muestra el resultado definitivo.

Private mvarid As Integer

271
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Private mvarnombre As String


Private mvarsaldo As Currency

Public Property Let id(ByVal vData As Integer)


mvarid = vData
End Property

Public Property Get id() As Integer


id = mvarid
End Property

Public Property Let nombre(ByVal vData As String)


mvarnombre = vData
End Property

Public Property Get nombre() As String


nombre = mvarnombre
End Property

Public Property Let saldo(ByVal vData As Currency)


mvarsaldo = vData
End Property

Public Property Get saldo() As Currency


saldo = mvarsaldo
End Property

Private Sub Class_ReadProperties(PropBag As PropertyBag)


mvarid = PropBag.ReadProperty("id")
mvarnombre = PropBag.ReadProperty("nombre")
mvarsaldo = PropBag.ReadProperty("saldo")
End Sub

Private Sub Class_WriteProperties(PropBag As PropertyBag)


PropBag.WriteProperty "id", mvarid
PropBag.WriteProperty "nombre", mvarnombre
PropBag.WriteProperty "saldo", mvarsaldo
End Sub

Código fuente 197

Hay un inconveniente más, y es que las limitaciones inherentes a un lenguaje de script como es el
VBScript nos impiden ejecutar desde una página ASP el código necesario para enviar, y
posteriormente recibir, este componente serializado a una cola de MSMQ. Tenemos que hacerlo
también a través de otro componente.

Vamos a hacerlo creando un nuevo módulo de clase en el mismo proyecto, al que llamaremos
GestionMSMQ, y que tendrá dos métodos: uno para enviar el objeto a la cola y otro para recibir el
objeto desde la cola. Debemos añadir al proyecto la referencia a la librería de “Microsoft Message
Queue”, como se ve en la Figura 84.

El Código fuente 198 muestra el estado final de este nuevo módulo de clase.

En el método “EnviaComponente” asignamos a la propiedad Body del mensaje el objeto instanciado.


En el otro método, “RecibeComponente”, recorremos en un bucle, sólo de inspección, la cola de
mensajes hasta encontrar el primero cuya etiqueta coincide con la que recibe el método como
parámetro. En ese momento, se recibe el mensaje, con lo que se borra de la cola, se sale del bucle y
posteriormente de la función.

272
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Figura 84

Option Explicit
Private objCliente As CompCliente.Cliente
Private objQueueInfo As MSMQ.MSMQQueueInfo
Private objQueue As MSMQ.MSMQQueue
Private objMsg As MSMQ.MSMQMessage

Public Function EnviaComponente(ByVal id As Integer, ByVal nombre As String, ByVal


saldo As Currency) As Variant
On Error GoTo Errores

Set objCliente = New CompCliente.Cliente


objCliente.id = id
objCliente.nombre = nombre
objCliente.saldo = saldo

Set objQueueInfo = New MSMQ.MSMQQueueInfo


objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)

Set objMsg = New MSMQ.MSMQMessage


objMsg.Label = "Mensaje Cliente"
objMsg.Body = objCliente
objMsg.Send objQueue

objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

EnviaComponente = "Mensaje enviado"


Exit Function

Errores:
EnviaComponente = Err.Description
End Function

273
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Public Function RecibeComponente(Etiqueta As String) As Variant


On Error GoTo Errores

Set objQueueInfo = New MSMQ.MSMQQueueInfo


objQueueInfo.PathName = ".\PRIVATE$\cola1"

Set objQueue = objQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)

RecibeComponente = "Mensaje no encontrado"

Set objMsg = objQueue.PeekCurrent(, , 100)


Do While True
If objMsg Is Nothing Then Exit Do
If objMsg.Label = Etiqueta Then
Set objMsg = objQueue.ReceiveCurrent(, , , 100)
Set RecibeComponente = objMsg.Body
Exit Do
Else
Set objMsg = objQueue.PeekNext(, , 100)
End If
Loop

Salida:
objQueue.Close
Set objMsg = Nothing
Set objQueue = Nothing
Set objQueueInfo = Nothing

Exit Function

Errores:
RecibeComponente = Err.Description
Resume Salida
End Function

Código fuente 198

Si enviamos ahora varios mensajes a la cola, algunos como ya hemos visto, y otros utilizando el
módulo de clase recién creado, con una página ASP tan simple como la del Código fuente 199,
tendremos en la cola un aspecto parecido al que muestra la Figura 85.

Set objCom = Server.CreateObject("CompCliente.GestionMSMQ")


objCom.EnviaComponente 7,"pepe",7000
Set objCom = Nothing

Código fuente 199

Probemos ahora que funciona el otro método. Cada vez que ejecutemos la página mostrada en el
Código fuente 200, deben ir apareciendo uno a uno todos los objetos que hayamos almacenado en la
cola, y deben irse eliminando de la misma conforme son recuperados. En esta página ASP sólo
estamos recuperando el objeto, pero es fácil imaginar que podríamos hacer un alta en la tabla con los
datos recuperados. De esta forma podríamos, por ejemplo, ir almacenando las peticiones de altas de
clientes en la cola, para luego ser procesadas todas de golpe en otro momento.

274
© Grupo EIDOS 14. Acceso a MSMQ desde ASP

Figura 85

Set objCom = Server.CreateObject("CompCliente.GestionMSMQ")


'objCom.EnviaComponente 7,"pepe",7000
'Set objCom = Nothing

'Response.End

On Error Resume Next


Set objCliente = objCom.RecibeComponente("Mensaje Cliente")
if Err=0 then
Response.Write objCliente.id & "<br>"
Response.Write objCliente.nombre & "<br>"
Response.Write objCliente.saldo & "<br>"
else
Response.Write "No hay más mensajes de clientes en la cola"
end if

Código fuente 200

275
Acceso a ADSI desde ASP

Directorios y Servicios de Directorio


Lo primero que podríamos decir es que un directorio. Un directorio es una forma de almacenar
información. Pero realmente una base de datos relacional es también una forma de almacenar
información, aunque distinta. ¿Dónde está entonces la diferencia?

Las diferencias que hay entre un directorio y una base de datos relacional, aunque tampoco se puede
establecer una separación muy precisa entre una y otra cosa, serían:

• Un directorio guarda información jerárquica, en forma de árbol, del mismo modo que el
sistema de archivos está organizado en carpetas, subcarpetas y ficheros.

• La información que contiene el directorio está almacenada en objetos. Un directorio contiene


objetos.

• Suelen estar replicados, es decir, su información almacenada de forma redundante en varios


lugares distintos, sin que sea vital el hecho de que estén o no sincronizados.

• Están más optimizados para lectura que para escritura. Disponen, por ejemplo, de sofisticadas
funciones de búsqueda, utilizando indexaciones para optimizar la velocidad.

El ejemplo más utilizado para explicar lo que podría ser un directorio sería el de un listín telefónico.
Almacena información, que está replicada, sin importar demasiado que la información almacenada en
distintos sitios coincida exactamente. Está más optimizado para lectura (búsqueda de un número de
teléfono) que para escritura (en este caso la escritura significa hacer una nueva impresión de listines).
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

No importa como esté implementado internamente el directorio (podría utilizarse una base de datos
relacional). Lo que importa es cómo veo y como accedo yo a esa información desde fuera.

Si un Directorio es un almacenamiento de información, un Servicio de Directorio es el software que se


encarga de obtener esa información. También se encarga de proporcionar la gestión de seguridad, de
forma que sólo accedan a la información almacenada las personas que estén autorizadas.

Lo normal es que el Servicio de Directorio contenga internamente al Directorio. Ejemplos de esto son
el Active Directory de Microsoft y los Servicios de Directorio de Novell.

Hay muchos más directorios, como los de Microsoft Exchange, Lotus Notes, etc. El problema está en
que cada uno de ellos tiene su propio servicio de directorio específico, y la forma de acceder es
distinta

Active Directory de Microsoft


Es un directorio que almacena la información correspondiente a todos los recursos de un dominio:
ordenadores, impresoras, aplicaciones, archivos, cuentas de usuario, permisos de seguridad, etc.
Además, los administradores pueden añadir nuevos contenidos a este directorio, de forma que
almacene otra información adicional que consideren útil.

El concepto que incorpora Active Directory no es algo nuevo. Lleva existiendo mucho tiempo en otros
sistemas operativos. En Windows ha tardado en aparecer por la sencilla razón de que este sistema
operativo empezó siendo un simple entorno gráfico. Es sólo últimamente, con Windows NT y
Windows 2000 cuando ha comenzado a ser un sistema operativo realmente potente, y ha necesitado de
la incorporación de este directorio.

En Windows NT 4 ya se almacenaba la información en un directorio, pero sólo era accesible con una
API específica para ese directorio. Active Directory cumple el protocolo estándar LDAP. Además el
modelo de seguridad es mejor que el que había en Windows NT.

Por defecto, el Active Directory guarda información global del dominio, pero apenas guarda
información local de cada miembro del dominio, como cuentas de usuario. Esta información se guarda
en las máquinas individuales.

Pero hemos dicho que el Active Directory puede ser configurado por los administradores para que
almacene nueva información. Algunas posibilidades serían:

• Replicar en el directorio la información de las máquinas individuales, de forma que podría ser
recuperada en el caso de que una de estas máquinas falle.

• Guardar mis preferencias de escritorio en el Active Directory. Así puedo recuperarlas cuando me
conecte desde cualquier PC del dominio.

• Si una aplicación que corre en una máquina necesita un componente que no tiene instalado, podría
buscar en el Active Directory para encontrar en qué máquina se encuentra y solicitarlo.

La información almacenada en el Active Directory es realmente de dos tipos. Por una parte está la
información que se necesita para la gestión del dominio, como equipos, cuentas, impresoras, etc. Por
otra parte está la información que se quiera guardar, como un directorio de propósito general

Disponer de un servicio de directorio como el Active Directory de Microsoft supone varias ventajas:

278
© Grupo EIDOS 15. Acceso a ADSI desde ASP

• Simplifica la gestión del administrador, que a través de este servicio de directorio puede
configurar de igual forma cuentas, servidores, equipos, aplicaciones, etc.

• Permite la instalación automática de aplicaciones según el usuario, independientemente de la


maquina desde la cual éste haga login en el sistema, utilizando tecnologías IntelliMirror. Por
ejemplo, se podría configurar de tal forma que todos los empleados del departamento de
Nóminas de la compañía tengan acceso a una cierta aplicación de gestión de nóminas.

• Permite una búsqueda más eficientes de los recursos de los que dispone el dominio. Por
ejemplo, se puede hacer una búsqueda de una impresora con unas determinadas características
de impresión en color o calidad.

• Permite que el administrador delegue funciones y asigne privilegios a usuarios y grupos.

• Proporciona una seguridad más robusta, al ser este servicio de directorio el único punto de
entrada a los recursos del dominio. Funciona como la autoridad central que gestiona el control
de acceso a los recursos del dominio, mediante varios mecanismos de autenticación. Además
este control de acceso es independiente de si el usuario hizo login a través de la red local o
desde internet.

• Permite controlar mejor los equipos, concediendo sólo a ciertos usuarios o grupos la
posibilidad de instalar aplicaciones en los equipos y modificar el registro de la máquina

• Permite el desarrollo de aplicaciones que se adapten al usuario, según la información que hay
almacenada sobre él en el directorio. En el caso del departamento de Nóminas, por ejemplo, se
podría desarrollar una aplicación que tuviera una opción de menú disponible sólo para el jefe
del departamento, pero no para los empleados.

ADSI (Active Directory Services Interface)


Hemos dicho que hay muchos servicios de directorio, cada uno con su forma específica de acceder a la
información almacenada. Esto complica mucho la labor del administrador.

Es aquí donde interviene el ADSI. Es un conjunto de interfaces COM que permiten acceder a los
servicios de directorio para los que tenga un proveedor ADSI. Estos proveedores son objetos COM
que implementan estos interfaces.

La idea es similar a la de OLE DB, que es un conjunto de objetos COM de propósito general que
permiten acceder a distintas BDs para las que tenga un proveedor OLE DB. No importa que estas
bases de datos sean de fabricantes distintos y estén implementadas de formas completamente
diferentes. De igual forma, con ADSI puedo acceder a los servicios de directorio para los que tenga
proveedor.

Los proveedores ADSI hacen de intermediarios. La aplicación cliente se comunica con ellos a través
de los interfaces estándar de ADSI, y ellos se comunican con el servicio de directorio con su API
específica. Igual sucede con OLE DB: la aplicación cliente (a través de ADO) se comunica con los
proveedores OLE DB a través de interfaces comunes, y estos proveedores se comunican con el gestor
de base de datos correspondiente.

El Active Directory de Microsoft es sólo uno de los muchos servicios de directorio a los que se puede
acceder utilizando ADSI. No hace falta que los servicios de directorio sean compatibles con LDAP.
Mediante ADSI también puedo acceder a servidores web, FTP, de email, etc.

279
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

Gracias a que ADSI proporciona unos interfaces comunes para acceder a varios servicios de directorio
distintos, los administradores pueden gestionar de forma única varios de estos servicios de directorio,
y los usuarios pueden tener un único login para acceder a todos los servicios (no uno para la red, otro
para email, etc).

Hay varios proveedores ADSI:

• Proveedor ADSI para LDAP (Lightweight Directory Access Protocol). Para acceder a Active
Directory de Windows 2000, al directorio de servidores Exchange, o al directorio de Site Server,
se usa este proveedor, puesto que estos tres directorios mencionados cumplen con el protocolo
LDAP. ADSI usa COM y es de alto nivel, luego puedo usarlo desde cualquier lenguaje que acepte
COM. LDAP sin embargo está escrito en C, y es de bajo nivel. Es algo parecido a ODBC y OLE
DB: ODBC es de bajo nivel, y es más difícil trabajar con él que con OLE DB. De igual manera, se
puede acceder a Active Directory directamente con LDAP, pero resulta más cómodo como
programador hacerlo con ADSI. Desde nuestras páginas ASP usaremos el proveedor ADSI para
LDAP para acceder a Active Directory.

• Proveedor ADSI para Winnt. Sirve para dos cosas. Por una parte, en Windows NT 4.0 no había
Active Directory. Este proveedor recoge información de las máquinas de la red, y la presenta
como si estuviera almacenada en un directorio similar al Active Directory de Windows 2000,
aunque con una funcionalidad más limitada. Por otra parte, en Windows 2000 sirve para recuperar
información de la máquina local, que por defecto no se almacena en el Active Directory.

• Proveedor ADSI para IIS. Accede a la metabase que guarda la información de configuración del
servicio web, ftp, etc.

• Proveedor ADSI para los servicios de directorio de Novell.

Estructura de Active Directory


La información de un directorio se guarda en unas entidades llamadas objetos, como impresoras,
equipos, etc. Estos objetos se organizan en una estructura jerárquica, tipo árbol, y disponen de una
serie de propiedades o atributos. Pero no hay que confundir estos objetos con los objetos COM. Es
decir, estas propiedades no son las propiedades de un objeto COM, sino una pequeña información
almacenada en el objeto bajo un nombre, por ejemplo la propiedad Description.

Las propiedades de un objeto del directorio son parejas nombre – valor, aunque algunas propiedades
pueden tener múltiples valores.

Hay dos tipos de objetos ADSI: containers y leafs. Parecido a las carpetas y archivos del sistema de
ficheros de Windows, con la diferencia de que los contenedores también son objetos con su propia
información. Es decir, que siguiendo con el símil, un contenedor es como una carpeta y un archivo
juntos.

ADO ofrece un conjunto de interfaces que permiten acceder de forma similar a los datos contenidos en
distintas bases de datos, usando sentencias SQL para localizar estos datos. De la misma forma, ADSI
ofrece un conjunto de interfaces que permiten acceder de forma similar a los objetos contenidos en
distintos directorios, usando cadenas que podríamos comparar con la ruta física (Path) de un archivo
en el sistema de ficheros. Estas cadenas se llaman ADsPath, y sirven para localizar esos objetos dentro
del directorio.

Cada proveedor usa una sintaxis distinta para estas cadenas que identifican los objetos del directorio,
pero la cadena siempre empieza con el nombre del proveedor. De esta forma, no hay que hacer igual

280
© Grupo EIDOS 15. Acceso a ADSI desde ASP

que cuando abrimos una conexión de ADO, a la cual estoy obligado a suministrar el proveedor
mediante una cadena de conexión. Con ADSI la propia cadena ADsPath incluye las dos cosas: el
proveedor y la identificación del objeto del directorio.

Proveedor ADSI para WinNT


Recordemos que en Windows 2000 este proveedor sirve para recuperar información de la máquina
local, que por defecto no se almacena en el Active Directory.

Para acceder a los objetos del directorio se usa el binding. Los objetos ya existen en el directorio, es
decir, no tengo que hacer CreateObject. Lo que quiero es recoger una referencia a una instancia
concreta, mediante el método GetObject, pasando como parámetro el nombre del objeto, en vez del
nombre de la clase del componente, como haría con el método CreateObject. Por ejemplo, para coger
una referencia a la raíz del directorio WinNT hago Set objRaiz = GetObject(“WinNT:”) .

De esta forma obtengo un objeto COM que referencia al objeto real almacenado en el directorio. Este
objeto COM tendrá unas propiedades por ser objeto COM, y otras propiedades por ser objeto de
directorio, en las cuales almacena información.

Si escribimos en una página ASP el Código fuente 201, obtendremos un resultado parecido al que se
muestra en la Tabla 10.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>
<%Set objRaiz = GetObject("ADs")%>
<div align="center">
<table border="1">
<tr><th>Nombre</th><td><%=objRaiz.Name%></td></tr>
<tr><th>Ruta</th><td><%=objRaiz.ADsPath%></td></tr>
<tr><th>Clase</th><td><%=objRaiz.Class%></td></tr>
<tr><th>Padre</th><td><%=objRaiz.Parent%></td></tr>
</table>
</div>
</BODY>
</HTML>

Código fuente 201

Nombre ADs:

Ruta ADs:

Clase Namespaces

Padre

Tabla 10

281
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

En la página ASP nos limitamos a recoger una referencia al objeto raíz del directorio, y luego
mostramos en una tabla algunas de sus propiedades. Comentemos algunas cosas.

La clase del objeto ha resultado ser Namespaces, y no tiene padre. Conviene recordar que la estructura
de los objetos dentro del directorio sigue una estructura jerárquica en forma de árbol. Si este objeto no
tiene padre, significa que es el raíz. Este objeto también se conoce como ADSI Router, y de él derivan
en jerarquía todos los proveedores ADSI intalados en el equipo. Estos proveedores son objetos de la
clase Namespace, como podemos ver si ejecutamos la página anterior, sustituyendo el parámetro
ADsPath por Set objProveedor = GetObject("WinNT:"). En la Tabla 11 está el
resultado obtenido.

Nombre WinNT:

Ruta WinNT:

Clase Namespace

Padre ADs:

Tabla 11

Como se puede ver, hemos bajado un nivel en el árbol del directorio. Este objeto, de la clase
Namespace, tiene como padre al anterior. Este objeto representa al proveedor ADSI para WinNT. Hay
que hacer notar que el nombre del proveedor es sensible a las mayúsculas (devolvería error si ponemos
“Winnt” en vez de “WinNT”), pero no así el resto de la cadena ADsPath. Esto es así en la mayoría de
los proveedores ADSI.

Si ponemos Set objDominio = GetObject("WinNT://nombreDominio"), donde


nombreDominio es el nombre del dominio al que está conectado el equipo, avanzamos un nivel más, y
obtenemos el objeto que representa al dominio. Pero además para este objeto podemos mostrar una
propiedad más, llamada Schema. La Tabla 12 muestra un posible resultado.

Nombre GRUPO

Ruta WinNT://GRUPO

Clase Domain

Padre WinNT:

Schema WinNT://GRUPO/Schema/Domain

Tabla 12

282
© Grupo EIDOS 15. Acceso a ADSI desde ASP

La propiedad Schema devuelve el ADsPath del objeto Schema, que especifica qué información
(propiedades obligatorias y opcionales) debe contener un objeto de la clase a la que pertenece el objeto
con el que estamos trabajando.

Estas propiedades que estamos mostrando pertenecen al interfaz IADs, que deben implementar todos
los objetos de un directorio ADSI. Son propiedades de objeto COM, no de objeto de directorio.

Las propiedades, todas de sólo lectura, del interfaz IADs son las siguientes:

• ADsPath: contiene la ruta que identifica al objeto en el directorio.

• Class: clase a la que pertenece el objeto.

• Guid: GUID único que identifica al objeto o a su clase, según el proveedor.

• Name: nombre común del objeto.

• Parent: contiene la ruta que identifica al objeto superior en la jerarquía del árbol.

• Schema: contiene la ruta del objeto Schema que describe a este objeto, es decir, qué tipo de
información debe almacenar (propiedades obligatorias y opcionales).

Y los métodos del interfaz IADs son:

• Get: obtiene el valor de una propiedad identificada por su nombre.

• GetEx: igual que Get, pero también para propiedades con valores múltiples. En vez de
devolver un valor único, devuelve un array de valores Variant.

• GetInfo: carga los valores de las propiedades de este objeto desde el servicio de directorio. En
vez de acceder a las propiedades del objeto del directorio allí donde estén almacenadas,
normalmente en otra máquina de la red, lo que sería muy lento, accedo a una copia local in-
process llamada caché de propiedades. Esta copia local puede actualizarse con este método.

• Put: asigna valor a una propiedad.

• PutEx: igual que Put, pero también para propiedades con valores múltiples.

• PutInfo: hace el proceso inverso a GetInfo, es decir, envía los valores de las propiedades desde
la caché hasta el servicio de directorio.

• GetInfoEx: igual que GetInfo, pero no carga los valores de todas la propiedades, sino sólo lo
de aquellas en las que esté interesado.

Contenedores
El último objeto del cual hemos obtenido una referencia, el correspondiente al dominio, es en realidad
un objeto container dentro de la jerarquía del directorio suministrado por el proveedor para WinNT.

Un contenedor tiene objetos hijo, que pueden recorrerse con la misma sintaxis empleada para recorrer
las colecciones de ASP, como puede verse en el Código fuente 202.

283
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%Set objDominio = GetObject("WinNT://nombreDeDominio")%>


<div align="center">
<table border="1">
<tr>
<th>Nombre</th>
<th>Ruta</th>
<th>Clase</th>
<th>Padre</th>
</tr>
<%for each objHijo in objDominio%>
<tr>
<td><%=objHijo.Name%></td>
<td><%=objHijo.ADsPath%></td>
<td><%=objHijo.Class%></td>
<td><%=objHijo.Parent%></td>
</tr>
<%next%>
</table>
</div>

</BODY>
</HTML>

Código fuente 202

Donde nombreDeDominio es el nombre de un dominio. El resultado devuelto sería similar al de la


Figura 86.

Figura 86

284
© Grupo EIDOS 15. Acceso a ADSI desde ASP

Los contenedores, aparte de ofrecer el interfaz IADs, presentan también el interfaz IADsContainer.
Este interfaz tiene las propiedades:

• Count: Contiene el número de objetos hijo del contenedor. Es sólo de lectura. No está
implementada actualmente por el proveedor para WinNT.

• Filter: contiene un array de cadenas que representan las clases de objetos que queremos listar
del contenedor. Es de lectura/escritura.

• Hints: Permite especificar qué propiedades queremos cargar de los objetos incluidos en el
contenedor. Es de lectura/escritura.

Los métodos del interfaz IADsContainer son:

• CopyHere: copia aquí, como hijo de este contenedor, un objeto del servicio de directorio
desde su ubicación original.

• Create: crea un objeto de directorio como hijo de este contenedor.

• Delete: elimina un objeto de directorio que es hijo de este contenedor.

• GetObject: obtiene una referencia a un objeto de directorio que es hijo de este contenedor.

• MoveHere: igual que CopyHere, con la diferencia de que elimina el objeto de su ubicación
original.

Propiedades de los objetos del directorio


Las propiedades que hemos visto hasta ahora, de los interfaces IADs e IADsContainer, son las
propiedades que tienen los objetos de directorio igual que otros objetos COM.

Pero además de estas propiedades, los objetos de un directorio tienen otras propiedades, en unos
objetos distintas que en otros, según esté especificado en el Schema correspondiente. A estas
propiedades no puedo acceder con la sintaxis del punto, como hago con las propiedades de cualquier
objeto COM, sino con unos métodos especiales, ya nombrados antes, como Get y Put.

Si por ejemplo queremos obtener una tabla con las propiedades obligatorias y opcionales del objeto
que representa al servicio del IIS, escribiremos el Código fuente 203.

<%@ Language=VBScript %>


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<BODY>

<%
Set objIIS = GetObject("WinNT://GRUPO/varrondo/IISADMIN")
Set objSchema = GetObject(objIIS.Schema)
%>
<h3>Propiedades obligatorias</h3>
<table border="1">
<%for each prop in objSchema.MandatoryProperties%>
<tr>

285
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<th><%=prop%></th>
<td><%= objIIS.Get(prop)%></td>
</tr>
<%next%>
</table>

<h3>Propiedades opcionales</h3>
<table border="1">
<%On Error Resume Next%>
<%for each prop in objSchema.OptionalProperties%>
<tr>
<th><%=prop%></th>
<td><%= objIIS.Get(prop)%></td>
</tr>
<%next%>
</table>

</BODY>
</HTML>

Código fuente 203

Es necesario activar el manejador de errores de VBScript con On Error Resume Next, por un
motivo que veremos a continuación, pero que podemos adelantar diciendo que algunas propiedades no
almacenan un valor, sino un array de valores, que no es directamente representable con Response.
Write.

Después de obtener la referencia al objeto que representa al servicio IIS, obtenemos la referencia al
objeto que representa al Schema, es decir, al objeto que especifica qué propiedades obligatorias y
opcionales tiene que guardar el primer objeto. Los dos son objetos del directorio: el segundo especifica
las propiedades que almacena el primero, es decir, su “esquema”. Podemos ver que el Path del objeto
Schema es WinNT://GRUPO/Schema/Service.

Si queremos ver todos los objetos Schema almacenados en el directorio, no tenemos más que ejecutar
el Código fuente 202, pero obteniendo ahora la referencia al objeto contenedor de esquemas, con Set
objSchema = GetObject("WinNT://GRUPO/Schema").

Después recorremos cada una de las colecciones de propiedades, obteniendo el valor almacenado para
cada propiedad mediante el método Get con objIIS.Get(prop). Utilizamos las propiedades
MandatoryProperties y OptionalProperties, que pertenecen al interfaz IADsClass implementado por
todos los objetos Schema, y que contienen la colección de propiedades obligatorias y opcionales que
debe tener un objeto de directorio representado por ese objeto Schema.

El resultado que se obtiene será parecido al de la Tabla 13.

PROPIEDADES OBLIGATORIAS

StartType 2

ServiceType 32

DisplayName Servicio de admin. IIS

286
© Grupo EIDOS 15. Acceso a ADSI desde ASP

Path G:\WINNT\System32\inetsrv\inetinfo.exe

ErrorControl 1

PROPIEDADES OPCIONALES

HostComputer WinNT://GRUPO/varrondo

LoadOrderGroup

ServiceAccountName LocalSystem

Dependencies

Tabla 13

Las propiedades de un objeto del directorio pueden estar almacenadas en cualquier lugar del dominio.
Acceder a esa propiedad allí donde esté sería muy lento. Por eso lo que se hace es acceder a una copia
local llamada caché de propiedades. Realmente los métodos Get, GetEx, Put y PutEx acceden a esta
copia local.

Para actualizar el contenido de este caché se usa el método GetInfo del interfaz IADs. Si sólo quiero
actualizar algunas propiedades, se usa GetInfoEx. Para hacer la actualización en sentido contrario, es
decir, para enviar las propiedades de nuestra caché, que quizá hemos modificado, de vuelta al
directorio, se usa el método SetInfo.

La primera vez que pido una propiedad, se invoca automáticamente al método GetInfo para traer las
propiedades a la caché. No hace falta que lo haga yo explícitamente, a no ser que piense que la copia
local está obsoleta.

Propiedades con valores múltiples


Los valores de las propiedades obtenidos con el método Get son del tipo Variant. Eso significa que
puede ser cualquier cosa: vacío, una cadena, un objeto, un array de valores, etc. De hecho, en el
ejemplo anterior había algunas propiedades que guardaban un array de valores. Para recorrer estos
valores puede usarse el método GetEx.

Si en el código anterior cambiamos por el Código fuente 204 las líneas encargadas de recorrer las
propiedades opcionales, obtendremos un resultado como en la Tabla 14.

<h3>Propiedades opcionales</h3>
<table border="1">
<%for each prop in objSchema.OptionalProperties%>
<tr>
<th><%=prop%></th>
<td>
<%for each valor in objIIS.GetEx(prop)%>

287
Desarrollo de aplicaciones COM+ para Internet / Intranet con ASP 3.0 © Grupo EIDOS

<%=valor%>
<%next%>
</td>
</tr>
<%next%>
</table>

Código fuente 204

PROPIEDADES OPCIONALES

HostComputer WinNT://GRUPO/varrondo

LoadOrderGroup

ServiceAccountName LocalSystem

RPCSS
Dependencies
ProtectedStorage

Tabla 14

La propiedad Dependencies, que antes no podía mostrarse, ahora sí. Vemos que es un array formado
por dos elementos.

Modificación de propiedades
Para poder modificar una propiedad de un objeto del directorio, primero debemos contar con los
permisos adecuados. La cuenta bajo la que se ejecutan normalmente las páginas ASP es la cuenta de
invitado a internet IUSR_NombreMáquina, y esta cuenta normalmente tiene permisos muy
restringidos.

Luego para poder ejecutar con éxito una página ASP que modifique propiedades de objetos de
directorio debemos, o bien conceder permisos a esta cuenta de invitado a internet, o bien ejecutar la
página ASP bajo otra cuenta con los permisos adecuados.

Una vez hecho esto, el Código fuente 205 muestra cómo podríamos cambiar el valor de la propiedad
DisplayName del objeto que representa al servicio IIS. Esta propiedad es el texto descriptivo del
servicio que aparece al seleccionar la opción Servicios de las Herramientas administrativas de
Windows 2000.

Set objIIS = GetObject("WinNT://GRUPO/varrondo/IISADMIN")


objIIS.Put "DisplayName","Nuevo nombre para el Servicio"
objIIS.SetInfo

Código fuente 205

288
Si quiere ver más textos en este formato, visítenos en: http://www.lalibreriadigital.com.

Este libro tiene soporte de formación virtual a través de Internet, con un profesor a su disposición,
tutorías, exámenes y un completo plan formativo con otros textos. Si desea inscribirse en alguno de
nuestros cursos o más información visite nuestro campus virtual en: http://www.almagesto.com.

Si quiere información más precisa de las nuevas técnicas de programación puede suscribirse
gratuitamente a nuestra revista Algoritmo en: http://www.algoritmodigital.com. No deje de visitar
nuestra reviata Alquimia en http://www.eidos.es/alquimia donde podrá encontrar artículos sobre
tecnologías de la sociedad del conocimiento.

Si quiere hacer algún comentario, sugerencia, o tiene cualquier tipo de problema, envíelo a la
dirección de correo electrónico lalibreriadigital@eidos.es.

© Grupo EIDOS

http://www.eidos.es
Si quiere ver más textos en este formato, visítenos en: http://www.lalibreriadigital.com.
Este libro tiene soporte de formación virtual a través de Internet, con un profesor a su
disposición, tutorías, exámenes y un completo plan formativo con otros textos. Si desea
inscribirse en alguno de nuestros cursos o más información visite nuestro campus virtual en:
http://www.almagesto.com.

Si quiere información más precisa de las nuevas técnicas de programación puede suscribirse
gratuitamente a nuestra revista Algoritmo en: http://www.algoritmodigital.com. No deje de
visitar nuestra reviata Alquimia en http://www.eidos.es/alquimia donde podrá encontrar
artículos sobre tecnologías de la sociedad del conocimiento.

Si quiere hacer algún comentario, sugerencia, o tiene cualquier tipo de problema, envíelo a la
dirección de correo electrónico lalibreriadigital@eidos.es.

© Grupo EIDOS
http://www.eidos.es

Das könnte Ihnen auch gefallen