June 2008 - Posts
Permítaseme esta vez escribir una visita ficticia del personaje de Tato Bores (para quienes no lo conocen, alguna información al final), a un supuesto Ministerio de Software recién creado en Argentina:
Andaba por la Plaza de Mayo, cuando decidí dirigirme al Ministerio de Software. Ahí me atreví a preguntar por qué tanto problema para crear software.
¡Eso pasa por que abandonaron el camino y no se escribe en C!, gritó José LenguajeC, mientras escribía una nueva versión del Hello World de Kernighan y Ritchie, y trataba de depurar un programa que manejaba punteros y malloc, sin free.
¡Mentira!, exclamó José C++. Gracias a que aparecí yo pudieron comenzar a aplicar clases y objetos.
¡Uds dos no saben nada! confunden clase con tipo, y cuándo un int fue un objeto?, desgañitó Jose Smalltalk, mientras trataba de hacer un formulario de ingresos de datos con primitivas de Squeak. Nosotros fuimos los que inventamos todo.
¡No, nosotros fuimos los primeros! ¡Vuelvan a las fuentes! se escuchó fuerte decir a José COBOL, mientras escribía páginas y páginas de Data Division.
¡Las fuentes las pelotas! Usen lo nuevo que somos nosotros, decía José Ruby, mientras trataba de explicar porqué usar @ y @@ para acceder a variables es bonito.
¡No saben lo que dicen! Denme tiempo y creo cualquier sistema, decía José RedNeuronal, mientras se entrenaba a sí mismo reconociendo videos de Wanda Nara.
¡Qué tiempo ni que tiempo! ¡Persistencia ahora para todo el mundo!, armaba lío José NHibernate, que se tiró a dormir porque tenía el atributo de lazy en true.
¡Copiones! Persistencia eran las de antes, vociferaba José Hibernate, mientras escribía 20 capítulos de mejores prácticas de manejar la sesión, y trataba de explicarle a un DBA por qué las Stored Procedures eran obra del diablo.
¡Vuelvan a las bases! proclamaba José Oracle, mientras escribía toda la lógica en paquetes de procedimientos almacenados.
¡Sacrilegio! le recriminaba José DDD, mientras se entablaba en una dicusión interminable consigo mismo sobre si poner acceso a los repositorios desde las entidades, se lo vió hablar solo por horas.
¡Dejá de decir pavadas! le espetó José DataSet, mientras trataba de dominar una grilla con edición y paginado, y miraba con ganas a Josefina Linq, que como era nuevita en el grupo, todavía no se atrevía a decir nada.
¡No la compliquen tanto! ¡Yo la hago fácil! decía José PHP, mientras creaba sitios con cuarenta includes uno dentro del otro, pero que soportaban 40 idiomas, lunfardo incluido.
¡Pero callate! A ver si te armás una aplicación empresarial como la gente, decía José J2EE, mientras trataba de ocultar a todos que tenía algo de EJB en la aplicación que estaba armando.
¡Aplicaciones empresariales son las nuestras! bramaba José CSharp, mientras trataba de mandar un mensaje WCF con WsHttpBinding que ocupara menos de un mega.
¡Callénse, cipayos! ¡Ututo o muerte! pataléo José Ututo, mientras estrangulaba a un fan de Tuquito (ambas distros de Linux argentinas)
¡Dejen de programar! Usen nuestra API, proclamó José Facebook mientras publicaba una nueva encuesta aplicación del estilo "Qué tipo de árbol bonsai eres".
¡No! ¡Vengan a programar para nosotros! A ver si arreglan el lío que tenemos, suplicaba a tres voces José Twitter, que desde hacía media hora estaba dándole al F5 del browser, a ver si reaparecía su página.
¡No saben ni de qué estan hablando! La programación no es nada, exclamaba José PatrónUML, mientras dibujaba en su Enterprise Architect diagramas y más diagramas de secuencia.
¡Callate! Menos dibujos y más entregables ¡A ver sí hacemos algo hoy mismo! se enojaba José Agil, mientras trataba de hacer pair programming con la secretaria del ministro.
¡Inútiles! Siempre se me van de presupuesto, exclamabla José ProgramManager, mientras abría en su notebook la última versión de Microsoft Project, que le calculaba sin ninguna sombra de duda, qué debería haber pasado en el desarrollo de su proyecto.
¡Guardá eso, y ponete a trabajar! gritaba José Customer, que no entendía por qué si él había pedido un sistema de CRM, le entregaban una lista de contactos en el Outlook.
¡Muerte al new! ¡Ese tiene toda la culpa! comenzaron a corear José SpringFramework (los dos, el de Java y el de .NET), mientras se inyectaban dependencias uno al otro.
¡Lo que pasan es que programan cualquier cosa, sin hacer el test! despotricaba José TDD, que estaba escribiendo una serie assert.AreEqual(1,0+1), assert.AreEqual(2,1+1) y así, para comprobar si Peano tenía razón.
¡Antes tienen que simular los sistemas! exclamaba José Mock, mientras trataba de explicarle a su esposa que su secretaria no era su amante, solo una mock de una noche.
¡Los sistemas no salen porque usan esas IDEs que complican todo! decía José Notepad, mientras miraba a la gente que usaba Dreamweaver con desprecio manifiesto.
¡Pero dejá de flagelarte! Es todo cuestión de encontrar el plugin adecuado, decía José Eclipse, que estaba buscando cuatro plugins de modelado (porque cada uno dependía de los otros tres), que corrieran en menos de 4 gigas, sin quemar el disco por paginado de memoria.
¡A ver si alguna vez madurás! Fijate, yo ya vengo con todo completo, exclamaba José VisualStudio, mientras trataba de calcular cuantos litros tenía que hipotecar de su sangre para conseguir las licencias del Team Foundation Server para su equipo de trabajo.
¡Lo importante es la experiencia de usuario! partía de la garganta de José Ajax, mientras ponía autocomplete hasta a los botones de aceptar un formulario.
¡Experiencia de usuario son las mías! exclamaba José Silverlight20, que se estaba peleando de antes con José Silverlight11, por razones de incompatibilidad manifiesta.
...
Como veía que el bolonki venía in crescendo, me retiré por una puerta lateral. Al salir al aire libre, recordé la frase del maestro ajlopez, que me había dicho "la mejor herramienta está entre nuestras orejas".
Así que, mis queridos chichipíos, la neurona atenta, vermouth con papas fritas, y ¡¡¡GOOOD SHOOOWWW!!!
Bueno, espero que les guste, no sé si salió muy bien, pero recordé un monólogo real en mi otro blog en:
La culpa, según Tato Bores
Un regalo offtopic, un verdadero monólogo, el monólogo 2000 de Tato, imperdible:´
La segunda parte en
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Ayer fue el último día de Gates como "full time" en Microsoft. Encontré una lista de videos de una entrevista que le hizo en estos días la BBC en:
How a geek changed the world
Veran parte de la historia de MS, la vieja historia de la Altair, cómo Allen viaja a Alburquerque con un intérprete Basic, hecho en Harvard con Gates, sin tener idea de si iba a funcionar en la máquina real. Se mudan ahí (siempre se cuentan historias de Gates visitando el barrio rojo de la ciudad). Veo a Mitch Kapor, a Myriam Lubow, contando cómo confundió a Gates con un intruso en la oficina de Microsoft, no podía creer que fuera el presidente, a Scott Wood, (recién descubro que Marla es Marla Wood ahora), la vuelta a Seattle (Gates viajó en su auto coleccionando multas), el crecimiento del gigante... Encontramos a Steve Ballmer, viejo compañero de noches de poker en los dormitorios de Harvard.
Aparece gente de BurguerMaster, donde varios de Microsoft, incluido Gates, iban a comer hamburguesas (una rutina de manejo de memoria de los primeros Windows se llama así internamente, en honor a este local). Escucharan a Sir Alan Sugar hablando de Gates. Está la aparición de Novell. Está John Batelle describiendo la aparición de la web. Y cómo Microsoft llega tarde a la red, pero luego del memo interno de Gates (1995?) todo comienza a cambiar. Más adelante, Janet Reno comienza el juicio contra el gigante de Microsoft.
Mucho de lo que se muestra, lo viví directamente. Recuerdo haber trabajado con el GWBasic, el Lattice C que compraron, el venerable edlin, el Xenix (antes de MS, trabajé con el CP/M de Kildall, y antes... bueno, eso dará pie a otro post). Recuerdo cuando aparecieron las ventanas, no habia Overlapped (aunque ya estaba la constante definida en los archivos .h). Pero antes, el Multiplan, y el editor de texto que tenían para desplazar a Wordstar.... ooops... ya ni recuerdo el nombre. Pero el Multiplan estaba hecho sobre las interrupciones de DOS, mientras que Lotus trabajaba directamente sobre el BIOS, y la dirección de video 0xb000:0 creo para monocromático, o 0xb800:0 para color (para evitar flicker había que testear una puerta, antes de mandar algo a esa memoria... ;-). No fue lo único que influyó, pero realmente Lotus corría más rápido que el Multiplan.
Excelente documental, acá estan las partes de la entrevista, gracias a Youtube:
Veremos que le depara el futuro a Gates: es una persona inteligente, que sabe cómo tomar decisiones con la información disponible, y llevar adelante lo que decide. Esperemos que despliegue esas habilidades en su nuevo desafío.
Para los nostálgicos, pueden encontrar otras piezas de la historia en
Los piratas de Silicon Valley
Gracias al bueno de Ezequiel Jadib por el enlace original.
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Ayer comenté un libro de conceptos de programación, de actualidad (ver Libro Foundation of Programming, listo para bajar). Quisiera hoy comentar un texto, que me parece un "debe ser leído" por todos los que nos dedicamos al desarrollo de software. Es un escrito del bueno de Alan Kay, sobre la historia de Smalltalk:
Smallhistory.pdf
Es un excelente texto para leer, y que influye más allá de simplemente estar interesado en Smalltalk. Es una historia de cómo algunas ideas fueron surgiendo, dentro de la comunidad de desarrollo americana. Cómo la programación a objetos fue surgiendo, y cómo la forma de interactuar con las computadoras actuales fue tomando forma. Hace un tiempo vimos por acá la demo de Douglas Engelbart. De nuevo aparece en esta historia.
Lean, por ejemplo, cómo Kay detecta algunas ideas germinales (datos junto con conducta) en desarrollos de la Fuerza Aerea, que hoy veríamos muy alejados de la OOP. Lean sobre su contacto con Lisp, y con Seymour Papert. Lean sobre los problemas internos de Xerox, la compentencia con DEC, cómo algunas ideas fueron casi fruto de la casualidad, de que un proyecto siguió adelante aunque otros se oponían. Lean sobre Simula, Euler, el IPL predecesor de Lisp. Lean sobre el Sketchpad de Evans (que yo encontré por primera vez en alguna revisión histórica de la Scientific American). Es una lectura, por lo menos para mí, deliciosa.
Kay tiene un idea desde hace años, que comparto: la idea de que la máquina, y la computación en general, nos debe servir para expandir las capacidades humanas. Excelente idea. Es lo que de alguna forma también hoy estamos logrando con Internet. Hoy, ramas del conocimiento y del hacer humano, han sido potenciadas por el software, el hardware y todo lo que ha pasado en nuestra profesión en las últimas décadas.
En mi infancia, se pensaba que los viajes espaciales iban a revolucionar la historia humana. Bueno, eso no ha pasado todavía. Pero de alguna forma producto de la guerra fría, y de la carrera espacial, el desarrollo de la computación (recordemos sus inicios modernos en la segunda guerra, y la aparición de la cibernética impulsada por temas militares) y de Internet misma, es lo que ha provocado un cambio, que poco a poco va alcanzando a gran parte de la humanidad.
Leer la historia de Kay es indispensable para ir entendiendo que ha pasado. Alguien que ha dicho "la mejor forma de dominar el futuro, es inventándolo" (frase aproximada, lean el texto, para ver dónde surgió exactamente).
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Gracias a Cladio M.E. Bastos Iorio de http://www.blumer.com.ar me entero de la aparición de este libro. Claudio posteó la información en una lista pública del MUG de Argentina (http://www.mug.org.ar/ ):
Foundations of Programming Ebook
I'm excitted to finally release the official, and completely free, Foundations of Programming EBook. This essentially contains all 9 Foundation parts including a conclusion and some typical book fluff (table of content, acknowledgement and so on). A number of spelling errors were corrected, along with some small technical changes and clarifications - largely based on feedback, so thanks for everyone who provided it! Otherwise it's exactly the same as what's been posted here over the past several months.
Download it from http://codebetter.com/files/folders/codebetter_downloads/entry179694.aspx
If the above link fails, you can also get it from http://www.openmymind.net/FoundationsOfProgramming.pdf
Acabo de leer el índice, y de leer algunos capítulos rápidamente. Es muy interesante los temas que toca, y parece bien encarados. El bueno de Karl Seguin ha hecho un magnífico trabajo, colocando en un solo libro temas que nos tocan en el desarrollo de software de todos los días. Si bien está algo orientado a .NET, es totalmente aprovechable en otras tecnologías, como Java. El texto había ido apareciendo en los últimos meses, en forma de posts, y revisado por colegas, en general dentro del movimiento ALT.NET, así que el trabajo de Seguin se ha enriquecido con los comentarios de la comunidad de desarrollo.
Sobre el autor
Karl Seguin is a developer at Fuel Industries, a former Microsoft MVP, a member of the influential CodeBetter.com community and an editor for DotNetSlackers. He has written numerous articles and is an active member of various Microsoft public newsgroups. He lives in Ottawa, Ontario Canada.
His personal webpage is: http://www.openmymind.net/
His blog, along with that of a number of distinguished professionals, is located at: http://www.codebetter.com/
Enumero algunos temas tratados:
- El movimiento ALT.NET, enumerando y desarrollando sus pilares, algo confrontados con la visión MSDN
- Domain-Driven Design, explicando los términos, y justamente, explicando cuándo usarlo y cuándo no, cuál es el contraste con una aproximación Data-Centric.
- Persistencia, algo con lo que lidiamos en prácticamente cada proyecto.
- Dependency Injection, una de las mejores técnicas inventadas, desde que alguien dijo que el pan tenía que venir en rebanadas.
- Unit Testing, indispensable en el bolso de la desarrolladora y en el bolsillo del caballero desarrollador. Hay días en los que pienso que hasta el Hello World de Kernighan y Ritchie debería tener su Unit Test.
- Object Relational Mappers, todo un capítulo explicando la idea y algunas implementaciones, usando NHibernate.
- Manejo de memoria, un back to basics que veo necesario, como comento en mis cursos.
- Excepciones, otro back to basics que se debe explicar.
- Proxy, tengo que ver en detalle este capítulo, para ver hasta dónde llega la idea del autor.
El libro también viene acompañado de código, con ejemplos de los temas tratados, por ejemplo, las formas de usar NHibernate, documentando sus características. Lo pueden bajar desde:
http://openmymind.net/CodeBetter.Foundations.zip
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Desde hace unos días, va tomando forma mi curso en línea de JavaServer Pages:
http://ajlopez.net/cursos/Course.php?Id=5
Está basado en el servidor Tomcat, pero creo que lo pueden aprovechar para cualquier contenedor web que soporte JSP.
Como otros cursos que están ahí, nace de los cursos presenciales que doy cada semana. Digo en la introducción:
En los noventa, aparece de la mano de Sun el lenguaje Java, que es más que un lenguaje, es todo un conjunto de tecnologías. Como lenguaje de uso general, tiene el soporte de una robusta y amplia librería de clases. Al estar implementado con una máquina virtual, sus programas son multiplataforma: pueden ejecutarse desde distintos entornos operativos, donde haya una implementación de la máquina virtual de Java.
En este sitio ya estoy publicando las primeras lecciones del curso Introducción a Java. Quisiera aprovechar este curso de JavaServer Pages para mostrar lo que se puede hacer con Java, más allá de los programas de consola o las ventanas gráficas. Internet, y la web en particular, hoy están en todos lados. JSP nos permite hacer aplicaciones web, con interfaces de páginas dinámicas, y aprovechar al mismo tiempo toda la potencia de Java.
Estas lecciones son un pasaje a escrito de las que doy en mis cursos presenciales. Espero que les resulten útiles. Un curso a distancia no es lo mismo que un curso presencial, donde uno puede interactuar con el instructor y sus compañeros. Pero también tiene sus ventajas, como la falta de horario, y el avance a su propio ritmo.
Estas son las primeras páginas del curso. Todos los días publico una lección adicional (no necesariamente de este curso). Mantengo una lista de lecciones publicadas en mi post:
A lesson a day keeps the doctor away
Espero que les sirva. Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
La semana pasada escribí una aplicación de base para agentes distribuidos usando DSS/VPL, agentes que intercambian mensajes arbitrarios e implementan un balanceo de carga rudimentario. Pueden leer los detalles en:
Agentes Distribuidos usando DSS/VPL
Distributed Agents using DSS/VPL
Ayer, extendí el ejemplo con un nuevo proyecto, Fractal:
Pueden bajarlo desde mi Skydrive.
Tiene dos DSS Service Components, uno es el Calculator: calcula un sector del fractal de Mandelbrot. El otro componente es el Renderer, que tiene un formulario para controlar y mostrar los resultados del cálculo. El mensaje que transporta la información del sector a calcular es:
public class SectorInfo : MessagePayload
{
public double RealMinimum { get; set; }
public double ImgMinimum { get; set; }
public double Delta { get; set; }
public int FromX { get; set; }
public int FromY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int MaxIterations { get; set; }
public int MaxValue { get; set; }
}
Otra clase es el mensaje que retorna el cálculo:
public class Sector : MessagePayload
{
public int FromX { get; set; }
public int FromY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int[] Values { get; set; }
}
El Calculator divide el sector a calcular, si lo considera demasiado grade. Podría calcularlo en un solo paso, pero me parece interesante esta solución, para repartir el trabajo entre varios nodos:
private void Calculate(AgentMessage msg)
{
LogInfo("Entering Calculator with Action: " + msg.Action);
SectorInfo sectorInfo = (SectorInfo) msg.Payload;
LogInfo(String.Format("X {0} Y {1} Width {2} Height {3}", sectorInfo.FromX, sectorInfo.FromY, sectorInfo.Width, sectorInfo.Height));
if (sectorInfo.Width > 100 && sectorInfo.Height > 100)
SplitSector(sectorInfo);
else
CalculateSector(sectorInfo);
}
Como en el ejemplo de la semana pasada, podemos ejecutar este ejemplo desde un diagrama VPL, que se llama FractalVpl1:
Acá hay un solo Renderer, y dos agentes Calculator. Si lanzamos este programa VPL, aparece una ventana. Esta es su apariencia (luego de presionar el botón Calculate):
Podemos arrastrar el mouse para seleccionar una nueva región, o usar los botones de Zoom In y Zoom Out. El botón de Reset vuelve a la posición y colores iniciales. El botón Reset Colors vuelve a los colores iniciales, que pueden ser cambiados al azar con el botón New Colors.
Hay otro programa VPL, llamado FractalVpl2, que puede ser usado para ejecutar el mismo ejemplo de forma distribuida. Tiene un diagrama con dos AgentHosts:
y dos nodos:
Debemos compilar el ejemplo VPL y pedir de ejecutar sus nodos en forma distribuida, usando el rundeployer.cmd (para más detalles, ver mi anterior post, mencionado al principio).
Estos son algunos de los gráficos que produce el sistema:
(Este post es una traducción del original en "Anglish":
Distributed Agents and Fractals using DSS/VPL
)
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com
En este post, exploraremos algunas ideas para implementar agentes distribuidos, aprovechando las capacidades que nos brindan Decentrilized Software Services (DSS) y el Visual Programming Language (VPL), includos en el Microsoft Robotics Developer Studio (estoy trabajando con la versión CTP 2.0, con VS 2008). Pueden bajarse el código desde mi Skydrive:
AjDssAgents-0.1.zip
En un anterior post:
Web Crawler example using DSS (Decentralized Software Services)
Ejemplo de Web Crawler usando DSS (Decentralized Software Services)
escribí algunos componentes DSS orquestados desde VPL, para implementar un web crawler. En ese ejemplo, hay un Dispatcher, un Resolver, un Downloader, y un Harvester. Pueden leer ahí el detalle de sus funciones.
Pero supongamos que ahora tenemos varias máquinas para poder ejecutar el proceso de web crawling. Queremos instalar y ejecutar varios Downloaders y Harvesters, en una grilla de máquinas, usando load balancing automático. El problema con la orquestación desde VPL es que no soporta conceptos como load balancing, por lo menos no directamente. Entonces, escribí este ejemplo donde los componentes se comunican entre sí, como agentes, usando mensajes especiales.
Un agente, en este ejemplo, es un DSS service component, capaz de recibir y procesar mensajes que les envían los otros agentes. Puede enviar mensajes a otros componentes. En vez de indicar a cuál agente va dirigido un mensaje, se especifica el tipo lógico de agente al que va destinado, por ejemplo "WebCrawler/Harvester".
Otro componente especializado, el AgentHost, se encargar de recibir esos mensajes a enviar, y los destina a un agente local o remoto, que corresponda al tipo lógico especificado.
La solución
La solución .NET tiene tres proyectos:
AjDssAgents contiene el contrato genérico de un agente, y sus tipos, y la implementación concreta del AgentHost.
DecrementAgent and WebCrawler son simples agentes a usar en el ejemplo. El web crawler implementado es similar al de mi anterior post, mencionado arriba.
El mensaje
Los agentes intercambian mensajes, objetos del tipo AgentMessage:
[DataContract]
public class AgentMessage
{
[DataMember]
public string From { get; set; }
[DataMember]
public string To { get; set; }
[DataMember]
public string Action { get; set; }
[DataMember]
public object Payload { get; set; }
}
El campo From indica el origen del mensaje (no estoy usando ese campo todavía) El campo To es la dirección física (dirección DSSP) del agente destino, o su tipo lógico. En el ejemplo de este post, solamente estoy usando los tipos lógicos. ¿Por qué usar un tipo lógico? Así, si un mensaje tiene como To el valor "WebCrawler/Dispatcher", será enviado a un agente que corresponda a ese tipo lógico.
¿Cómo un agente conoce cuáles otros agentes están siendo ejecutados, y cuáles son sus tipos lógicos? Pues bien, no lo sabe. El componente que mantiene esa información es el AgenHost local, único en cada DssHost activo. Cada agente envía sus mensajes salientes a su AgentHost local, y éste los reenvía a los agentes locales o remotos apropiados.
Los agentes
Cada agente es un DSS service component, con una dirección asignada cuando es creado. Durante el comienzo de su ejecucuón, el agente envía a su AgentHost local un mensaje DSS, indicando su dirección y su tipo lógico (p.ej. WebCrawler/Dispatcher). Esta es la forma por la que el AgentHost conoce los agentes que estan ejecutándose localmente, en su DssHost. Veamos el código de inicio de un agente Dispatcher del ejemplo WebCrawler:
protected override void Start()
{
base.Start();
// Add service specific initialization here.
_state.AgentType = "WebCrawler/Dispatcher";
host.NewNode newNode = new host.NewNode(new host.AgentInfo() { Address = this.ServiceInfo.Service, AgentType = _state.AgentType });
_hostPort.Post(newNode);
}
El tipo del agente es mantenido en su estado.
Este es un código típico, de un agente, en este caso un Dispatcher, mostrando el tratamiento de un mensaje entrante y la producción de mensajes salientes:
[ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public IEnumerator<ITask> PostMessageHandler(generic.PostMessage postMessage)
{
if (postMessage.Body.Action.Equals("Dispatch"))
Dispatch(postMessage.Body);
else if (postMessage.Body.Action.Equals("Resolve"))
Resolve(postMessage.Body);
postMessage.ResponsePort.Post(DefaultSubmitResponseType.Instance);
yield break;
}
private void Dispatch(AgentMessage msg)
{
LogInfo("Entering Dispatcher with Action: " + msg.Action);
LogInfo("URL: " + msg.Payload);
DownloadTarget target = new DownloadTarget();
target.Uri = (string) msg.Payload;
target.Depth = 1;
AgentMessage postmsg = new AgentMessage() { Action = "Resolve", To = _state.AgentType, Payload = target };
host.PostMessage post = new host.PostMessage(postmsg);
_hostPort.Post(post);
}
private void Resolve(AgentMessage msg)
{
LogInfo("Entering Dispatcher with Action: " + msg.Action);
DownloadTarget downloadtarget = (DownloadTarget)msg.Payload;
LogInfo("URL: " + downloadtarget.Uri + ", Depth: " + downloadtarget.Depth);
DownloadTarget target = ProcessUrl(downloadtarget);
if (target != null) {
AgentMessage agentmsg = new AgentMessage() { To = "WebCrawler/Downloader", Action="Download", Payload = downloadtarget };
host.PostMessage postmsg = new host.PostMessage(agentmsg);
_hostPort.Post(postmsg);
}
}
El AgentHost
Hay uno y sólo uno por DssHost en ejecución. El AgentHost recibe la información de los nuevos agentes que se crean (su dirección y tipo lógico), y mantiene esa información en su propio estado.
Recibe mensajes de sus agentes locales, y los reenvía a otros agentes locales o a un AgentHost remoto. En este último caso, serializa el contenido del mensaje en un string, usando serialización XML (no podemos enviar un objeto genérico, debido a limitaciones en el Proxy que usa DSS). Esta es la estructura de un mensaje remoto:
[DataContract]
public class RemoteAgentMessage
{
[DataMember]
public string From { get; set; }
[DataMember]
public string To { get; set; }
[DataMember]
public string Action { get; set; }
[DataMember]
public string PayloadTypeName { get; set; }
[DataMember]
public string Payload { get; set; }
}
Notemos que el mensaje remote tiene un string Payload, que es la serialización XML del contenido original, y también tiene su tipo calificado, así el host destino podrá deserializarlo a su tipo correspondiente al objeto original.
Un AgentHost soporta subscripciones. Otros AgentHosts pueden subscribirse a recibir información de sus nuevos agentes. En general, si tenemos tres máquinas, debemos susbcribir a los tres AgentHosts entre ellos, así tendrán toda la información de los agentes que se encuentre corriendo, en las distintas máquinas.
Un ejemplo de Web Crawler con VPL
El ejemplo WebCrawlerVpl2 en VPL contiene dos diagramas, el primero:
Hay un Dispatcher, dos Downloaders y dos agentes Harvesters. El Dispatcher lanza la URL inicial a procesar, y mantiene una lista de URLs ya procesadas. El Downloader obtiene el contenido de cada página en proceso. El Harvester examina el contenido y obtiene los nuevos links a procesar.
Notemos que hay dos AgentHosts, y ellos se relacionan por notificaciones para informar sus nuevos agentes al otro.
Todos estos agentes y componentes se distribuyen en dos nodos:
El nodo Windows ejecutará en localhost:50000/50001, y el nodo Windows0 usar localhost:50002/50003 como dirección. Podemos modificar estos parámetros, agregar más agentes y nodos, sin cambiar el código de la aplicación.
Para ejecutar la distribución en forma distribuida, debemos compilarlar usando Build -> Compile as a Service en el menú de VPL. Deberá cambiar las propiedades de VPL, ahora en el ejemplo están apuntando a directorios locales en mi máquina de desarrollo:
Al compilar, VPL mostrará su avance:
Luego de la compilación, ir a la consola de DOS de MRDS, cambiar al directorio bin y lanzar el comando rundeployer.cmd:
Yo ejecuto el deployer en mi máquina local. Uds. pueden ejecutar el ejemplo en otras máquinas remotas, iniciando el deployer en cada una de ellas.
Ahora, estamos listos para ejecutar el web crawler. Seleccionamos la opción Run -> Run on distributed nodes , y la aplicación comenzará a ejecutar. Un ventana de diálogo nos pedirár la URL inicial. La ingresamos, y el proceso comienza a recuperar las páginas de ese sitio. Pueden ver el estado del primer AgentHost (ejemplo) en:
http://localhost:50000/agenthost
Hay tres agentes locales y dos agentes remotos.
En el otro DssHost, hay otro AgentHost:
http://localhost:50002/agenthost0
Vemos la diferencia: aquí hay dos nodos locales y tres remotos.
Para ver el avance del proceso, pedir en el navegador
http://localhost:50000/console/output
Conclusiones
Con estas ideas, podemos implementar aplicaciones tipo grilla, ejecutándose en varios nodos físicos. Perdemos la orquestación de VPL, no podemos dibujar el camino de los mensajes. Pero ganamos en balanceo de carga y deploying dinámico. Con algo de esfuerzo adicional, podemos escribir un servicio que inicia e instale el sistema en una nueva máquina remota, en el medio de un proceso en ejecución. La serialización de objetos arbitrarios es posible, ahora está usando serialización XML, podemos cambiarla por serialización "custom" o binaria.
Podría agregar subscripción a mensajes, en una futura versión. Un agente podría recibir mensajes que no estaban destinados a él, especificando algún criterio de subscripción. Esos criterios se mantendrían en los AgentHosts. Cuando un AgentHost rutea un mensaje saliente, podría reenviar una copia del mismo a cualquier agente interesando, ya sea local o remoto.
Tienen la versión original de este post en "Anglish":
http://ajlopez.wordpress.com/2008/06/15/distributed-agents-using-dssvpl
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/en
El mes que viene, Julio, voy a dar una charla de introducción a Scrum. Hace ya un año y medio que tomé un curso de ScrumMaster, y desde entonces, he estado trabajando en distintos proyectos, usando Scrum. Me pareció interesante compartir lo que he aprendido, y gracias a la organización del MUG de Argentina, voy a tener el gusto de dar esta charla, el 2 de Julio, aquí en Buenos Aires. La charla no es gratuita, tiene un costo, pero creo que el tema es lo bastante interesante como para postearlo por aquí.
Parte de lo que vamos a ver:
Equipo de trabajo. Roles: ScrumMaster. Product Owner. Daily Scrum. Chickens y Pigs. ScrumMaster vs Project Manager.
Objetivos. Control empírico del proceso de desarrollo. Dar visibilidad de lo que se está haciendo. Generar una relación de confianza y comunicación constante entre el cliente y el equipo.
Conceptos: Iteraciones. Autoorganización. Descripción del método.
Documentación: Qué es, cómo se arman y cómo se utilizan el Product Backlog, el Sprint Backlog y Burndown Chart
Para los que no puedan asistir, ya vendrán posts sobre algunos temas de Scrum.
Las metodologías ágiles, en general, llegaron para quedarse. Hay varios casos de éxito (así como fracasos), y me parece que es el camino a seguir cuando se tiene el equipo adecuado de gente.
Más información e inscripción en:
http://www.mug.org.ar/Eventos/3041.aspx
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Este fin de semana estuve programando algunas primitivas de un intérprete Lisp usando DSS Service Compoenntes. El núcleo del intérprete fue derivado de mi trabajo previo con AjLisp, un intérprete que todavía no publiqué. Escribí el ejemplo usando Visual Studio 2008 con el CTP v2.0 de Microsoft Robotics Developer Studio. El ejemplo está publicado en mi Skydrive como DssLisp.zip.
La solución
Consiste de dos proyectos: una librería de clases llamada AjLisp (una versión revisada de mi intérprete original en VB.NET):
y un proyecto C# de Dss Service, que tiene varios service components implementados:
No tiene manifiesto inicial. El proyecto está pensado para ser usado desde diagramas de Visual Programming Language (VPL).
Varios de estos componentes reciben y retornan una SymbolicExpression, el tipo base de mi intérprete. Veamos por ejemplo, los mensajes del componente Rest, que dada una lista devuelve la misma lista sin el primer elemento:
[DataContract()]
public class ExecuteRequest
{
[DataMember]
public SymbolicExpression Expression;
}
[DataContract]
public class ExecuteResponse
{
[DataMember]
public SymbolicExpression Result;
}
Para compilar estos componentes, deben ajustar las referencias y directorios del proyecto DSS (ahora apuntan a mis directorios locales y a mi instalación local de Microsoft Robotics Developer Studio).
Usando VPL
Hay tres programas VPL incluidso en los ejemplos. Usan los componentes compilados de la solución. Tuve un problema con estos programas VPL. No pude ejecutarlos desde la opción de Run. Hay que usar Build -> Compile as a Service, y ahora, lanzarlos con Run -> Run Compiled Services. Creo que el origen del problema es que al armar el diagrama estoy mapeando mensajes propios, no solamente tipos primitivos o Strings. Para recompilar los diagramas, tienen que ajustar las propiedades en cada proyecto VPL:
El primero, DssLispVpl1, calcula (first '(a b c)):
Hay dos componentes LispParser y LispToString, que ayudan a convertir de string a SymbolicExpression y viceversa.
El segundo diagrama es DssLispVpl2:
Usa el componente LispAppend para concatenar dos listas predefinidas como Data.
El tercero es un min intérprete DsspLispInterpreter:
Tengo que mejorar el tratamiento de errores. Ahora, si ocurre una excepción, no devuelve un Fault, y encontes se genera una causality, que hace que el programa se interrumpa.
Podría implementar contratos genéricos, y reusarlos en los componentes, por ejemplo, un contrato GenericBinaryOperation.
Problemas
Fallé completamente cuando quise escribir una Activity, para implementar una función Reverse de una lista. La actividad se llamaba a sí misma recursivamente, pero en el código generador retornaba desde la última invocación recursiva, sin pasar por las intermedias. El problema es causado porque en todo ese proceso se utiliza la misma instancia de actividad, y al retornar por una puerta el resultado, TODOS los procesos pendientes de esa actividad están escuchando por esa puerta (eso es lo que veo que pasa). Otra piedra en el camino es que las actividades solo aceptan valores primitivos o string como datos en el mensaje de entrada. Puedo solucionar el problema, generando el código de la actividad, levantando la solución, y cambiando los parámetros que trata de, digamos, string a SymbolicExpression.
Un componente interesante, es el componente Uncons, que retorna DOS expresiones simbólicas en su mensaje de respuesta: dada una lista, retorna su cabeza y su cola. Esos dos miembros de la respuesta pueden ser procesados luego en paralelo en dos ramas del diagrama.
Conclusiones
Como en otros posts y ejemplos, encontré la orquestación de componentes DSS via VPL como algo poderoso e interesante, pero le faltan algunas cosas, como el soporte de un objeto genérico o tipos nuestros. Creo que se puede implementar varias primitivas de lenguajes funcionales y usar a VPL para programar sobre ellas. VPL tendría problemas con la recursividad (o no encontré una forma aceptable de hacerla funcionar). Este ejercicio, podría expandirse a otras ideas:
- Manejar una pipeline/tubería para procesar un mensaje genérico (puedo usar un object como DataMember en un mensaje, siempre y cuando no lo envía a otro Host de DSS)
- Tener balanceo de carga
- Serializar/deserializar objetos entre hosts
- Implementar algún workflow o patrones de integración de aplicaciones usando VPL
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com
Ayer dí una charla gratuita sobre generación de código, usando AjGenesis. Espero haber transmitido la idea de que es más que generación de código, y que va más allá de la tecnología actual.
Quisiera enumerar algunos posts relacionados con el tema, y comentarlos, así como enumerar enlaces y recursos.
Posts sobre generación de código
Sobre la generación de código
Este es el primer post a leer. Vemos ahí que el cambio de tecnología es una constante en la historia de desarrollo de software, y que no se ve en el horizonte una unificación de nada, ni de lenguajes ni de plataformas. Cada año que pasa, sabemos más de hacer sistemas, pero curiosamente, cada sistema nuevo necesita más codigo que el anterior, en general. Aún usando tecnologías que nos alivian el trabajo, por ejemplo, algún ORM (Object Relational Mapper), si la tecnología triunfa, el propio mercado nos lleva a hacer sistemas más complejos y grandes, y lo que ahorramos en código a escribir, luego lo perdemos en la mayor complejidad de lo sistemas que tenemos a nuestro alcance construir.
También se enumeran otras alternativas a la generación de código, y sus problemas. Las herramientas CASE son muy rígidas, o generales o abarcativas, así como UML y otras iniciativas. Lo que necesitamos son modelos específicos de nuestro dominio, y poder generar lo que querramos, de la forma que nos convenga. De ahí la necesidad de contar con un generador de código que no esté orientado a una tecnología a generar en particular, que sea de código abierto, y que genere lo que nosotros le indicamos, no lo que los autores de la herramienta quieren o pensaron inicialmente.
El usar frameworks propietarios, quedarse en una sola tecnología, tratar de generar todo en runtime apelando a metadata, o generar código desde XML aplicando transformaciones XSLT, son alternativas que creo que son inferiores o más complejas, como se explica en ese post.
Generando Código- Hello World con AjGenesis
Esta es la "prueba ácida" de todo generador de código. Si una herramienta de ese tipo no nos ayuda a crear programas "Hola mundo", con variantes, en distintas tecnologías, entonces no es lo bastante flexible. AjGenesis hace hincapié en la flexibilidad, la adaptibilidad a lo que Uds quieran producir, sin atarlos a una forma o metadata en particular.
Generando aplicaciones con AjGenesis
Un post algo viejo, pero que muestra cómo se pueden generar aplicaciones no triviales con esta herramienta.
AjGenesis- Modelo generado desde los assemblies
AjGenesis- Modelo desde la Base de Datos
Posts sobre cómo el modelo puede partir de otras fuentes, cualquiera que Uds. vean que sea conveniente. Luego, la propia herramienta puede mejorar, completar el modelo, hacer inferencias, o Uds. mismos completarlos, por ejemplo, si el modelo de base de datos no es lo bastante rico para darnos algo de semántica sobre el sistema que tenemos entre manos.
Generando código para NHibernate (Parte 3)
Generando código para NHibernate (Parte 2)
Generando código para NHibernate (Parte 1)
Para ver que pueden usar AjGenesis para facilitar el desarrollo usando alguna librería reconocida por la comunidad, pueden leer esta serie de enlaces, donde gracias a una idea de Omar del valle Rodríguez, se desarrollaron templates para usar NHibernate, y generar hasta una capa de presentación web para probar lo generado.
Hacia el AjGenesis Recipes
Es un trabajo en progreso, para incorporar interacción con el usuario durante el proceso de generación de código.
Usando AjGenesis y NAnt
Explicación y enlaces para usar AjGenesis desde NAnt. Si alguien necesita una interfaz gráfica NAnt-GUI, pueden bajarla desde NAnt-GUI (preparado por Carlos Marcelo Santos, del grupo de generación de código)
Genesis Studio- una IDE para AjGenesis
No todo lo tienen que hacer desde la línea de comando, o desde el NAnt/NAnt-GUI. El bueno de Jonathan Cisneros desarrolló esta herramienta WinForm, con sintaxis de color y muchas mejoras en la experiencia de usuario, que pueden bajar desde:
http://www.codeplex.com/ajgenesisstudio
Preservando código en AjGenesis
Si necesitan generar código, pero luego modificarlo, pueden usar algunas capacidades de AjGenesis para delimitar qué parte regenerar y cuales conservan.
Code Generation as a Service
La última adición al AjGenesis: ahora pueden generar código usando una interfaz web. Esto permite que ya sea en Internet, o desde la intranet de su empresa, puedan generar código controlado, que cumpla con los estándares que se hayan adoptado.
Más posts sobre AjGenesis en
AjGenesis
Si necesitan pasarle información a alguien que no maneja el español, pueden probar con mis artículos en "Anglish" (Angel's English):
http://ajlopez.wordpress.com/category/ajgenesis
Enlaces
EL sitio sobre generación de código es
http://www.codegeneration.net
Ahí encontraran comentarios, herramientas, preguntas frecuentes sobre generación de código en general. Tienen una base de datos de herramientas publicadas. Hay enlaces sobre generación de código para distintas tecnologías, como Java, .NET, PHP y también temas de MDA (Model-Driven Architecture).
Otros enlaces que tratamos en las charlas son:
CodeSmith Tools
MyGeneration Software
AndroMDA.org - Home
Mantengo todo tipo de enlaces relativos a generación de código en
http://del.icio.us/ajlopez/codegeneration
Lista de correo sobre generación de código
Existe un Google Group que se puede consultar por email, en español, dedicado a la generación de código. Ahí, además de otros temas, se trata de generación de código con AjGenesis, la generación de templates. En los archivos del grupo se publican ejemplos armados por los miembros.
Pueden ver los mensajes y suscribirse, en:
http://groups.google.com/group/codegeneration
Conclusión
Espero que quede claro que lo importante es usar generación de código como solución pragmática, pero basado en la abstracción de un modelo. Hacer el ejercicio de abstracción de generar un modelo adecuado para lo que necesitemos, es un "must" que me parece el primer paso para separarnos de los problemas de cambios en las tecnologías, herramientas y plataformas.
Luego, el modelo debe ser de libre definición. Y el texto o código generado, también: debe ser el tipo de texto o código que hubiéramos generado nosotros mismos. Finalmente, la herramienta de generación de código tiene que estar disponible en código totalmente abierto, para que se pueda extender, entender o usar de la forma que querramos.
Tengo que escribir más sobre la importancia del modelo, y que pueden hacer el suyo propio: no hay modelo fijo, pueden partir desde la base de datos, desde los archivos XML de los ejemplos, o desde donde quieran. También tengo que escribir sobre la posibilidad de hacer modelos que se deriven de otros modelos. Y escribir haciendo énfasis en que lo que construimos, en realidad, es un sistema experto. De ahí, que desde el principio, incorporé un lenguaje de programación que puede tomar decisiones, completar y modificar el modelo.
Como ven, con AjGenesis, ¡sky is the limit! :-)
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Desde hace unas semanas, que estoy publicando lecciones de mis cursos presenciales de PHP y MySQL en mi mini sistema de
AjLearning
Pueden ver el curso en:
Desarrollo de Sitios con Php y MySql
Como explico en la introducción del curso:
Como el curso de Introducción a Java, estas lecciones nacen de los ejemplos que dicto en mis cursos presenciales. PHP, junto con MySQL, se ha convertido en la tecnología más usada en desarrollo web con programación en el servidor. Con PHP, se puede comenzar sencillo, y luego, armar aplicaciones web más complejas.
Como no siempre es posible asistir a un curso, me pareció interesante poner estas páginas en línea. No es lo mismo que un curso presencial, donde uno comparte experiencias con otros, o está en contacto con algún instructor. Pero también tiene sus ventajas: uno puede ir avanzando a su propio ritmo, y repasar todo cuanto quiera.
Espero que les sirva. El curso va ir creciendo. Todos los días publico una lección adicional (no necesariamente de este curso). Mantengo una lista de lecciones publicadas en mi post:
A lesson a day keeps the doctor away
Siempre quería publicar las lecciones, pero estaba el cuestión tiempo para armar un curso completo. Por eso, tomé la decisión de publicar una lección por día, para tener siempre un mini avance, que no será mucho, pero ya se han acumulado como 100 lecciones, entre este curso y el de introducción a Java. Estoy comenzando a publicar algo del curso de JSP, y tengo pendiente comenzar con el de .NET.
El propio sistema de base va a ir avanzando: quiero incorporar preguntas y respuestas, pequeños problemas y ejercicios interactivos.
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Casi todos los cursos y charlas que doy, termino mencionando generación de código, desde un modelo, como una solución pragmática a varios problemas que tenemos al desarrollar sistemas. El lunes que viene, doy una charla gratuita sobre el tema, en el Club de Programadores, acá en Buenos Aires.
Más información e inscripción:
Curso Generacion de Código
Objetivos
Durante la charla, examineremos el concepto de modelo independiente de la plataforma de un sistema, y mediante tecnologias de templates, generaremos codigo para gran parte de una aplicacion, en distintas tecnologias, como PHP, Java con JSP, .NET con ASP.NET, contra MySql o SQL Server, usando distintas estrategias de persistencia, como procedimientos almacenados o NHibernate. Todo basado en un mismo modelo, con distintas plantillas de generacion de codigo, manejados desde el proyecto de codigo abierto AjGenesis.
Plan de Estudio
- Modelo independiente de la plataforma
- Generacion de artefactos de texto con logica y plantillas definibles por cada usuario (no produce algo fijo, sino codigo como el que hubiéramos producido en nuestro equipo de programacion)
- Capacidad de ejecutar varias tareas adicionales (compilacion, instalacion en la base, ....)
- Organizacion de esa tareas de generacion y adicionales en forma automatica y repetible
- Adaptacion a varias tecnologias destino
Lo mío es un apostolado, espero que puedan "ver la luz" .... :-) ;-)
Hay bastante sobre el tema publicado en este blog en:
AjGenesis
Generación de Código
Artículo inicial a leer
Sobre la generación de código
Algunos enlaces a recursos en la web:
http://del.icio.us/ajlopez/codegeneration
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Me encuentro con esta interesante aplicación, escrita por Stephan Grothkopp:
http://goosh.org
Con una interfaz de línea de comando, podemos pedir help:
Con search buscamos
La página se basa en JavaScript, ver el programa que usa:
http://goosh.org/goosh.js
Por lo que ví, usa la facilidad de incorporar una búsqueda al propio navegador:
AddSearchProvider Method
Integrating Site Search Into Firefox and IE7
No sé si funciona Goosh en IE6 y anteriores.
Ese método necesita un XML que usa para la búsqueda. Acá está en:
http://goosh.org/goosh.xml
Eliminando el dato de la imagen, se ve:
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:ms="http://www.mozilla.org/2006/browser/search/">
<ShortName>Goosh</ShortName>
<Description>Goosh.org - The unofficial Google Shell</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">...</Image>
<ms:SearchForm>http://goosh.org/</ms:SearchForm>
<Url type="text/html" method="GET" template="http://goosh.org/?q={searchTerms}&sourceid=mozilla-search"/>
</OpenSearchDescription>
Pueden probar de buscar directamente con:
http://goosh.org/?q=search+mario+bunge&sourceid=mozilla-search
Otra "review" en:
Goosh: A retro Web app with cutting-edge interface
Enlaces que encuentro relacionados con Google:
http://del.icio.us/ajlopez/google
Gracias por el enlace a mi compañero de equipo Juan Manuel Moyano.
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com/
Ayer tuve el gusto de presentar algunas características de la nueva versión, Visual Studio 2008, en Rosario, provincia de Santa Fé, gracias a la organización del Microsoft User Group de Argentina. Mi charla fue al final de una serie de charlas que ocuparon toda la tarde, desde las 14hs hasta entrada la noche, cerca de las 22hs.
Pueden ver el detalle en
Rosario - Jornada de Actualización Profesional Microsoft 2008
Quisiera exponer en este post, los enlaces y recursos que utilicé. En principio, la presentación fue "inspirada" (un eufemismo por "robada"... ;-) del
Download details- Visual Studio 2008 Training Kit
que contiene información, "hand-ons labs" (algunos los escribimos en equipo de Southworks el año pasado), y presentaciones con notas adicionales.
Para una introducción general a lo nuevo de VS 2008 y .NET 3.5, pueden ver
Pro VB 2008 and the .NET 3.5 Platform
Be the first to put .NET 3.5 and VB 2008 to work, with expert tuition and guidance from esteemed author, Andrew Troelsen. In Pro VB 2008 and the .NET 3.5 Platform, you’ll find complete coverage of .NET 3.5, including WPF, WCF, and WF, guided by a deep understanding of the .NET platform and appreciation of the subtleties of VB that could only come from this award-winning author’s invaluable experience.
Source Code
Para el tema de Windows Presentation Foundation, me basé en concepto de:
Pro WPF with VB 2008- Windows Presentation Foundation with .NET 3.5
Find out how WPF really works with .NET expert and author Matthew MacDonald. No–nonsense, practical advice will get you building high–quality WPF applications quickly and easily, while a thorough investigation of the more advanced aspects of WPF, and its relation to other elements of the WinFX stack and the .NET Framework 3.5, will complete your understanding of WPF and VB 2008.
Source Code
Aunque usé como ejemplos los de C# del mismo autor:
Pro WPF in C# 2008: Windows Presentation Foundation with .NET 3.5, Second Edition
- WPF basics: XAML, layout, control essentials, and data flow
- WPF applications: Navigation, commands, localization, and deployment
- Advanced controls: Custom controls, menus, toolbars, and trees
- WPF documents: Text layout, printing, and document packaging
- Graphics and multimedia: Drawing shapes, sound and video, animation, geometric transformations, and imaging.
Source Code
No llegué a usarlo, pero me pareció interesante:
Pro ASP.NET 3.5 Server Controls and AJAX Components
- Covers the new features in ASP.NET 3.5 and ASP.NET AJAX technology
- Describes the ASP.NET server control architecture in Visual Studio 2008/ASP.NET 3.5 covering state management, events, rendering, cross-platform support, control life cycle, localization, and deployment.
- Demonstrates how to integrate client–side devel