cesnavarra 2008-boletín 12
DESCRIPTION
Respuesta DigitalTRANSCRIPT
Título Del oficio.
Texto "El sector de la informática, tan vivaz e innovador en el
trabajo pero tan dormido en la defensa de sus intereses..." (Little Bighorn y las ingenierías informáticas). Hace unas semanas recibí un correo diciendo que la carrera de informática, carrera docente se entiende, iba a
desaparecer. Por ello se animaba a secundar las movilizaciones convocadas. El germen creo que estaba en la postura del PSOE a la hora de rechazar una iniciativa del PP respecto a una proposición
no de ley en comisión parlamentaria. En ellas el PP insta a "crear las correspondientes fichas de grado y máster donde se
reflejen las competencias de los titulados en ingeniería informática, dando así igual trato que al resto de las ingenierías". Más allá de lo que esto implique, tras ver los
videos de la comisión lo único que me quedó claro es que nuestros representantes actúan y hablan de forma cuando
menos curiosa. El diputado del BNG apoya al PP quizás porque, como indica él mismo, es paisano del diputado popular. Lo felicita y suscribe
su iniciativa con el único argumento de que de seguir las cosas así la titulación desaparecería. No razona si esto es
bueno o malo, parece dar por sentado que el que desaparezca una titulación es malo. En este caso coincido con él pero no
parece razonar más su postura. Tras la renuncia de algunos grupos a intervenir, la diputada de CiU toma la palabra. Tras realizar un panegírico de la
profesión informática empieza a hablar "En este marco del espacio europeo superior..." y a partir de ahí no entiendo ya
casi nada salvo que es una especie de sí pero no y que el no es para lo que propone el PP: perdón, la abstención, porque como he dicho es un sí pero no. Finalmente habla el diputado del PSOE, flanqueado por una, supongo, compañera de partido que asiente su intervención
en silencio y que, además, tiene problemas con su chaqueta. Dicho parlamentario también alaba al sector informático y luego empieza a citar ofertas de titulaciones o masters como
el de "biología computacional" y otros. Tras varios minutos de conversación null, en mi opinión ni cierta ni falsa, da por
cerrada su argumentación. Tras esto se vota y se rechaza la proposición no de ley. Esta claro que si hay un problema con el sector, en las Cortes no me lo han aclarado. Me llegó entonces un enlace de un
blog en el que se trata la cuestión: ¿qué pasa con la ingeniería informática? Y leyéndolo es cuando empecé a tener una visión más clara del bosque formado por los árboles que
arriba he descrito.
En todo esto el tema que más me interesa es el de si la profesión informática debe estar regulada o no. En mi opinión
no es ésta una pregunta a responder de forma aislada sino teniendo en cuenta el contexto laboral. Esto es: ¿por qué hay que regular unas profesiones y otras
no? ¿Por qué, de hecho, hay profesiones reguladas y otras no? Un argumento suele ser el que las profesiones que están reguladas se dedican a actividades que por su dificultad o implicación deben tener algún tipo de control o de garantía de
que quienes la realizan están capacitados para ello. Los ejemplos habituales son los siguientes: los planos de un
edificio deben estar firmados por un arquitecto para garantizar su seguridad, habitabilidad, etc. Una operación debe ser realizada por un médico para garantizar su éxito,
etc. ¿Y un proyecto informático no? Muchas personas en estos
casos realizan el siguiente razonamiento: programar es fácil, luego no hace falta regular dicha actividad. Aparte de que la realización de un proyecto informático no es sólo codificar,
podría decirse que sí que programar ciertas cosas, por ejemplo una macro de Word (y depende cuales, os lo
aseguro) es fácil pero también lo es aplicar mercromina a una herida y colocar encima de ella un apósito pero nadie llama a eso "intervención quirúrgica". Por otro lado es curioso como para realizar ciertas actividades que no considero triviales no se necesita más que pasar unas
pruebas o incluso sólo tener la intención de realizarlas: y si no piénsese en que para opositar a plazas de policía o alistarse en las fuerzas armadas se necesita acreditar pocas aptitudes.
Y no creo que estas profesiones sean menos críticas o más fáciles que desempeñar que todas las aquí descritas. Otro tema que ha salido a relucir con esta polémica es el del
intrusismo, que es la otra cara de la moneda de lo antes expuesto: puesto que no es necesario acreditarse de alguna forma para ejercitar la profesión de informático, cualquiera
puede hacerlo. A este respecto voy a contar mi experiencia: cuando
coordinaba un equipo de desarrolladores, solicitamos de una empresa de servicios un perfil de programador. Dicha empresa, que ya nos había proporcionado anteriormente
personas que cubrieron satisfactoriamente nuestras necesidades no envió el currículum de su candidato. El mismo
era licenciado en Farmacia y había realizado un curso de varios meses de programación en Java. Teníamos dos opciones: rechazarlo a priori o confiar en nuestro proveedor y
ver qué tal trabajaba esa persona. Resultó que era lo que necesitábamos: un programador que realizaba a conciencia su
trabajo. Acertamos: no nos importó el pedigrí del candidato sino sus resultados.
Meses más tarde mi rol cambió y era analista en un proyecto
internacional con tres equipos de desarrollo situados en España, Argentina y los Estados Unidos respectivamente.
El project leader del mismo era un estadounidense cuya formación era en... ¡música! El recuerdo que tengo es que sabía orquestar muy bien al equipo, combinando
idiosincrasias y manejando ritmos. En la actualidad me encuentro trabajando con colegas que en
su mayor parte son titulados en telecomunicaciones y sigo aplicando el mismo criterio: no me digas de dónde vienes sino vamos a ver cómo podemos trabajar juntos. Creo que uno de los problemas de la informática es la forma
en que se percibe: algo de lo que ya hemos tratado en estos artículos. Y otro el de la ignorancia de lo que un titulado en informática
tiene que estudiar para titularse, ya sea como ingeniero técnico o superior. Podríamos hacer un pequeño test: ¿cuáles de estas asignaturas aparecen en los planes de estudios de dichas carreras?
Álgebra Algoritmia Contabilidad Analítica Economía de la Empresa Estadística Estrategia y Política de Empresa Financiación e Inversión Inteligencia Artificial e Ingeniería del Conocimiento Introducción a la Mercadotecnia Organización de Empresas Planificación y Gestión de Proyectos Informáticos Robótica Industrial Simulación Informática Sistemas de Información Contable
La respuesta es: todas. Mi conclusión es la siguiente: la profesión informática está presente en todos los ámbitos de la vida actual. Aún así la
ignorancia acerca de los conocimientos que deben adquirirse para ser un titulado en la misma es más que notable. Y si uno ignora qué conocimientos tiene su empleado, difícilmente
podrá saber utilizar todo su potencial. Por otro lado no creo que haya que blindar el terreno profesional para que sólo los
que tengan carnet de socio puedan acceder al club de la informática. Pero esto debería ser una regla general a todo el
mercado laboral y no sólo a la informática: o todos o ninguno. Finalmente una pequeña autocrítica: los informáticos estamos
habituados a trabajar con máquinas que únicamente atienden a programas, que son la formalización unívoca de unos
requerimientos. E intentamos trasladar esa lógica del
verdadero/falso a las relaciones humanas, y por ende laborales, comerciales... En esto debemos aprender que las
cosas no son blanco o negro o que, si lo son, puede que no logremos convencer de ello a aquellos con quienes interactuamos. En esto seguimos siendo un poco utópicos: pensamos que "tenemos razón" y que con esto basta. Es similar a lo que aparecía en una escena del biopic "Pirates of Silicon Valley" en el que hay una escena con un supuesto
diálogo entre Steve Jobs y Bill Gates. En el mismo, Jobs intenta decir la última palabra exclamando algo así como:
"Somos mejores que tú... Tenemos productos mejores.", a lo que responde Gates: "¿No lo entiendes, Steve? Eso no importa."
Si quieres enviar algún comentario o sugerir temas a tratar en otros artículos, escribe a:
curtasun[simboloArroba]cein.es
Categorías General
Tema Varios
Autor Carlos Urtasun
Mes Diciembre
Año 2008
Boletín 12
Títu
lo
Proyecto piloto TDT: parte 4: fase 2
Tex
to
Esta segunda fase arrancó con el segundo comité. Comité en el cual
se presentaban los trabajos realizados hasta ese momento, es decir,
la primera fase completada, ya comentada en artículos anteriores, y
el planteamiento del proyecto desde ese momento hasta el final del
proyecto.
Este planteamiento consistía en establecer los trabajos a realizar por
parte del equipo local, número y planificación, junto con la
transferencia de conocimiento y papel que SiTVi debía ejercer durante
esta segunda fase. Para ello se partía del resultado del análisis de
servicios a adaptar a TDT realizados durante la primera fase, que
trajeron no pocos quebraderos de cabeza y de la finalización de la
preparación formal del equipo local.
Del análisis teníamos como resultado una lista de posibles servicios a
implementar en TDT, no obstante, no había identificados unas fuentes
fiables de información en ninguno de ellos y tampoco había asegurada
la incorporación de ningún servicio interactivo (con capacidad de
realizar peticiones mediante el uso del canal de retorno) dada la
imposibilidad de contar con una interacción con los sistemas de
Gobierno de Navarra (a través de servicios web) en la fecha presente.
Dado que había muchos flecos en prácticamente todos los servicios
que se irían aclarando con el estudio concreto de cada uno y dada la
existencia de un plan de modernización paralelo que iba afectar a los
sistemas de la información y por tanto, cambiar la situación actual, la
lista, prioridad y posición de cada servicio dentro de la misma podía
variar. De hecho, debía irse adaptando conforme se obtuviese más
información de los mismos a lo largo de los desarrollos de la segunda
fase. En resumen, había una gran indeterminación.
Servicio Prioridad Fuente
Meteorología Alta Extracción automatizada de la
información directamente de la fuente
Información
estática
Alta Inserción manual
Agenda de ocio y
cultura
Alta Extracción directa de BBDD
Farmacias de
guardia
Alta Extracción directa de BBDD
Centros de salud Alta Extracción directa de BBDD
Carreteras Alta Tempos21: analizar alternativas
Empleo público
(convocatorias)
Alta Tempos21: previo filtrado de la
información
Vivienda Alta-
Media
A determinar
BON Media Dentro del plan de modernización: a
determinar en octubre
Empleo privado Media Dentro del plan de modernización: a
determinar en otoño
Esto es, se habían identificado 10 posibles servicios, de los cuales, los
7 primeros eran informativos mientras que los 3 últimos podían
presentar algo de interactividad.
No obstante, existía una gran indeterminación en el posible uso de
estos 3 últimos puesto que en el momento del análisis, tal y como he
comentado, no existía ningún servicio web disponible para
implementar la interactividad en el servicio, circunstancia que podía
cambiar por el plan de modernización que afectaba directamente a
dos de estos servicios.
Entre los informativos, tras muchas averiguaciones se vio la dificultad
y riesgo de emplear el desarrollo de Tempos21 como fuente de
información ya procesada con lo que se planteó buscar la fuente de
datos original. Para los dos primeros ya se tenía detectada una
alternativa útil y por tanto podían implementarse en TDT, mientras
que en los restantes se sabía de la existencia de bases de datos que
almacenaban cierta información necesaria para cada uno de los
servicios pero se desconocía con certeza su naturaleza y por tanto, no
se podía asegurar en todos los casos su uso. De echo, para los
servicios de Carreteras y Empelo público, ni siquiera se tenía
constancia de la existencia de una base de datos, lo cual implicaría el
uso de Tempos21 como fuente de información ante la inexistencia de
una alternativa.
Por otro lado, el equipo local se encontraba “preparado”, había
recibido la formación adecuada relacionada con conceptos generales
de la TDT, el estándar MHP y el uso de la herramienta de autor a
emplear iDesginer. No obstante, como hemos adelantado, la
formación no terminaba ahí, esta tan solo había sido la parte teórica.
A continuación comenzaría la formación práctica basada en la
experimentación real con los trabajos a realizar, para lo cual se
definió junto con SiTVi un planteamiento de transferencia de
conocimiento: se identificaron una lista de habilidades a adquirir y
unos niveles para cada una de ellas, A, B y C, siendo A el nivel más
básico y C el más avanzado. Estas habilidades o conocimiento se
agruparon según su aplicación a las tareas de desarrollo, quedando el
siguiente planteamiento:
Niveles
A B C
Toma de requisitos
Ingeniería de software - Metodología de
desarrollo: toma de requisitos, análisis, diseño
aplicación.
X X X
Análisis funcional
Ingeniería de software - Metodología de
desarrollo: toma de requisitos, análisis, diseño
aplicación.
X X X
Diseño
Desarrollo MHP - Diseño y usabilidad. X X X
Integración y formateo de datos de entrada
Backend – Procesado de datos. X X X
Backend – Tratamiento de XML, XSLT. X X X
Conocimiento del entorno - Desarrollo con el
entorno de IDesigner.
X X X
Java – Desarrollo basado en Java (bajo nivel, sin uso de IDesigner).
X
Implementación del interfaz
Desarrollo MHP – Interfaz
gráfico (disposición/layout de componentes)
X X X
Java – Desarrollo de plugins (personalización
y creación de componentes gráficos para
X
IDesigner).
Java – Desarrollo basado en Java (bajo nivel, sin uso de IDesigner).
X
Conocimiento del entorno - Desarrollo con el entorno de IDesigner
X X X
Implementación de funcionalidades
Desarrollo MHP – Uso de formularios de entrada
(procesado de datos). X X X
Java – Desarrollo de plugins (personalización
y creación de componentes gráficos para IDesigner).
X
Java – Desarrollo basado en Java (bajo nivel, sin uso de IDesigner).
X
Conocimiento del entorno - Desarrollo con el entorno de IDesigner
X X X
Integración con servicios web
Desarrollo MHP - Canal de retorno. X
Backend - Interacción con servicios web:
post/xml, get/xml, soap.
X
Java – Desarrollo basado en Java (bajo nivel, sin uso de IDesigner).
X
Conocimiento del entorno - Desarrollo con el entorno de IDesigner
X X
Ejecución de pruebas
Conocimiento del entorno - Conocimiento de
puesta en producción de aplicaciones en el
laboratorio: encapsulador (iMux).
X
Conocimiento del entorno - Realización de pruebas
de aplicaciones. X X X
Backend - Parametrización de aplicaciones. X
Ingeniería de software - Metodología de
pruebas.
X X X
Puesta en emisión:
Backend - Parametrización de aplicaciones. X
Puesta en producción de aplicaciones: X X
comunicación con Abertis.
Dada la nula experiencia en TDT del equipo local se decidió comenzar
con un servicio sencillo de implementar en el que participasen todos
los miembros del equipo. Además, esto permitía arrancar la segunda
fase del proyecto con un servicio concreto y definido y así tener un
colchón suficiente para ir concretando el resto. Posteriormente, tras la
experiencia adquirida, se podían ir acometiendo más servicios en
paralelo mediante la distribución del trabajo entre los miembros del
equipos local. Se trabajaría con la lista identificada anterior como si
fuera un “pool” de servicios, decidiendo su incorporación al desarrollo
según la información recopilada en cada caso, la viabilidad y
evolución de las circunstancias:
Por tanto, este plan nos daba 5 servicios a incorporar de aquí al final
del proyecto, 4 informativos y un último interactivo aún por
determinar dependiendo del plan de modernización. Se decidió
comenzar por un servicio de modo que todo el equipo pudiera
participar e ir adaptándose al desarrollo de aplicaciones interactivas
con MHP para TDT de forma paulatina. De este modo, en este primer
servicio se podía hacer más hincapié en la labor de transferencia de
conocimiento que posteriormente se iría diluyendo a lo largo del
proyecto conforme el equipo adquiriese soltura y conocimientos y
convirtiéndose más bien en una labor de consultoría. Así pues, en los
servicios posteriores, tras esta toma de contacto y adaptación, podía
configurarse un equipo más eficiente de modo que se dividiesen los
trabajos y se pudiesen implementar y afrontar dos servicios en
paralelo cada vez. Dada la disponibilidad de 4 personas (se contaba
con 3 personas en el equipo local y un becario cedido por SiTVi como
refuerzo y apoyo), se podía dividir el equipo en 2 grupos de 2
personas cada uno. De modo que en los servicios informativos, una
persona del grupo pudiese dedicarse a las labores de extracción y
formateo de datos y la otra persona del grupo concentrarse en el
desarrollo MHP. En el caso del servicio interactivo, no existía
extracción y formateo de datos, pero la interacción complicaba el
desarrollo MHP que podía equilibrarse con la dedicación de dos
personas en este caso. Los roles y responsabilidades de cada
participante así como los propios grupos irían cambiando y rotando
con cada desarrollo de modo que todos los miembros del equipo local
experimentasen con todas las partes del desarrollo. La lista de
conocimientos y niveles antes citada encajaba dentro de este
planteamiento de la siguiente manera:
Esto es, los conocimientos de nivel A, se adquirirán con el desarrollo
del primer servicio. Durante el desarrollo del siguiente servicio, se
asimilarán los conocimientos de nivel B, consolidando, a su vez, los
de nivel A adquiridos en el desarrollo anterior. De igual modo,
durante el último servicio, se aprenderán los conocimientos de nivel
C, consolidando los de nivel B adquiridos en anteriores desarrollos y
asumiendo la completa responsabilidad de las tareas de nivel A.
Así pues, como se ha adelantado, se determinó comenzar con un
servicio, buscando el más sencillo y a la vez el que más garantías de
éxito pudiese ofrecer de la lista de servicios posibles. De entre todos
ellos, serios candidatos eran: Meteorología, Farmacias de Guardia,
Centros de Salud, Información Estática y Agenda de cultura y ocio.
Dado que de todos ellos, tan solo Meteorología teníamos realmente
identificada la fuente de información se decidió comenzar con el.
METEOROLOGÍA
Para profundizar en el análisis de la fuente de información y el
servicio, se organizó una reunión con el área de Meteorología
del Departamento de Agricultura, Ganadería y Alimentación del
Gobierno de Navarra, responsables del desarrollo de meteorología
para el portal web www.navarra.es.
Aunque se propuso un servicio muy interesante por el cual se
proporcionaba información en tiempo real de estaciones
meteorológicas, este hubiese requerido un análisis más profundo y
tratándose de un desarrollo más complejo hubiese retrasado el
desarrollo, por lo que fue descartado y se continuó con los planes
iniciales. Así pues, se determinó que el servicio a implantar en TDT
sería el de la predicción meteorológica. En concreto, las siguientes
predicciones:
Predicción meteorológica en Navarra para hoy.
Predicción meteorológica en Navarra para mañana.
Predicción meteorológica en Navarra para los próximos días.
Predicción meteorológica en el pirineo.
Predicción nivológica, sólo disponible en temporada de nieve.
Predicción avisos y alertas cuando los hubiere.
Imagen de radar.
Esta información, ofrecida en el portal web, se extraía del Instituto
Nacional de Meteorología (en adelante INM), ahora la actual Agencia
Estatal de Meteorología (AEMET). El formato de datos proporcionado
por el INM venía heredado de mucho tiempo atrás, cuando no se
empleaban los caracteres particulares del español como los acentos,
las eñes, etc. Esto unido al uso exclusivo de mayúsculas y a la
inexistencia de traducciones a diferentes idiomas (entre ellos el
Euskera) dificultaba las cosas. La aplicación a diseñar sería
multilingüe, como en todos los casos, aunque la disponibilidad de la
información estaría sujeta a su existencia. En este caso, la
información tan solo podía ser servida en español.
NOTA: posteriormente, a finales del 2007, los responsables del portal
realizaron un filtrado de la información de modo que esta se
visualizaba en minúsculas y realizaban correcciones ortográficas para
incluir los acentos y las eñes. No obstante, esta información contenía
tags html, con lo que requería un procesado previo antes de poder
ser incluida en TDT. Esta novedad no pudo ser incorporada finalmente
al proyecto por falta de tiempo y recursos.
Toda esta información era facilitada en forma de ficheros individuales
por cada una de las predicciones anteriores. Estos ficheros estaban
localizados en un servidor de FTP al que nos dieron acceso para la
conexión y descarga de la información.
La visualización de esta información, interfaz, presentó algunos
problemas en TDT. En el portal www.navarra.es, la información de
predicción correspondiente al día actual, el día siguiente y los
próximos días, eran visualizadas de forma conjunta. Esta información
consistía en un texto y en la imagen de un mapa de navarra
actualizados diariamente pero de forma independiente. La imagen del
mapa se correspondía con la predicción para el día siguiente, no
obstante, esta información se actualizaba en torno al medio día, con
lo que en las primeras horas del día el mapa se correspondía con la
predicción del día anterior, o visto de otra forma, la previsión del día
actual realizada el día anterior.
El interfaz en TDT impedía mostrar las 3 predicciones anteriores de
forma conjunta por problemas de espacio. Por esa razón se
implementaron de forma separada, lo cual, planteaba el problema de
donde ubicar el mapa de predicción común. Finalmente se decidió que
la predicción del día siguiente contendría el texto y la imagen del
mapa correspondiente, actualizado a medio día, momento en el cual,
la imagen del mapa anterior pasaría a la predicción del día actual.
Probablemente esta circunstancia podrí haberse resuelto de forma
más óptima de poder haber tenido más libertad sobre la disposición
de componentes en el interfaz, no obstante, la herencia de los
servicios en TDT anteriores marcaba una línea gráfica de la cual no
era posible salir.
Otro escollo asociado al interfaz en TDT resultaron ser las propias
imágenes por el origen de las mismas. Se trataba de imágenes con
tamaño y formato adaptado al entorno web, además de tratarse de
imágenes de calidad reducida, óptima para entorno web. Estas
debieron ser escaladas para adaptare al interfaz de TDT, acentuando
la mala calidad. Esto unido a la diferente visualización de los colores
en TDT dieron como resultado unas imágenes bastante pobres, no
obstante, no se contaba con otra fuente de datos alternativa.
A continuación se muestran unos bocetos del interfaz que indican la
ubicación de cada elemento gráfico:
Es decir, y como se ha mencionado, por coherencia se mantuvo el
diseño gráfico establecido en las aplicaciones anteriores:
Menú superior de acceso a otros servicios en TDT,
Menú inferior con los controles directos del mando de
televisión.
Menú propio del servicio actual situado a la izquierda.
La emisión del vídeo en la esquina inferior izquierda.
El espacio útil de visualización situado en el centro.
En este caso, el menú izquierdo se correspondía con cada una de las
predicciones a visualizar de forma independiente a modo de
secciones.
El área útil mostraba la información de predicción correspondiente a
la sección elegida. Para el caso de la predicción de hoy y mañana, se
desarrolló un componente que conmutaba entre la predicción de texto
y la predicción con la imagen, dado que las limitaciones del interfaz
en TDT impedían su visualización simultánea.
De igual modo, se mantuvo (como se verán en las imágenes de
resultado final) el estilo heredado de las aplicaciones anteriores en
cuanto a uso de colores, diseño de botones, etc.
En cuanto al proceso que recogía, procesaba y publicaba la
información en TDT, el sistema presentaba la siguiente arquitectura:
Esto es:
1. Proceso de adquisición de datos: se trataba de un sistema independiente que monitorizaba los ficheros determinados
ubicados en el servidor FTP. En caso de que hubiese cambios, los descargaba y procesaba para construir los ficheros XML que servirían como fuente de información a la aplicación MHP a
ejecutar en el televisor. Este proceso dejaba los ficheros XML generados en unas ubicaciones de carpetas preestablecidas en
un servidor local. 2. Proceso de publicación: se trataba de un sistema que
conjugaba los ficheros obtenidos y formateados en el proceso
anterior y los mezclaba con los fuentes de la aplicación para
generar el empaquetado compilado final. No obstante, no se enviaba a publicación el empaquetado completo, puesto que
sería requisito indispensable que la aplicación ya se encontrase disponible en el aire, por lo cual, tan solo se actualizaría la parte correspondiente a la información, manteniendo la
aplicación intacta. Tal y como se explicó en el artículo anterior, esto se hace así, puesto que cada actualización de la lógica
conlleva un proceso de validación previo por parte de Abertis que sería inmanejable en el día a día de una aplicación informativa de contenidos actualizables diariamente como es el
caso. La actualización se realiza de nuevo mediante un acceso por FTP a la información publicada en TDT proporcionada por
Abertis.
Esto es:
1. Proceso de adquisición de datos: se trataba de un sistema
independiente que monitorizaba los ficheros determinados ubicados en el servidor FTP. En caso de que hubiese cambios, los descargaba y procesaba para construir los ficheros XML que
servirían como fuente de información a la aplicación MHP a ejecutar en el televisor. Este proceso dejaba los ficheros XML
generados en unas ubicaciones de carpetas preestablecidas en un servidor local.
2. Proceso de publicación: se trataba de un sistema que conjugaba los ficheros obtenidos y formateados en el proceso anterior y los mezclaba con los fuentes de la aplicación para
generar el empaquetado compilado final. No obstante, no se enviaba a publicación el empaquetado completo, puesto que
sería requisito indispensable que la aplicación ya se encontrase disponible en el aire, por lo cual, tan solo se actualizaría la parte correspondiente a la información, manteniendo la
aplicación intacta. Tal y como se explicó en el artículo anterior, esto se hace así, puesto que cada actualización de la lógica
conlleva un proceso de validación previo por parte de Abertis que sería inmanejable en el día a día de una aplicación
informativa de contenidos actualizables diariamente como es el
caso. La actualización se realiza de nuevo mediante un acceso por FTP a la información publicada en TDT proporcionada por
Abertis. Aquí surgió por primera vez, el concepto de la plataforma de TDT.
Como era de esperar, esta consistía en una extracción de información
del back office y su procesado para emisión en TDT. Debía
establecerse un mecanismo que fuese común y reutilizable en todos
los servicios que independizasen información y presentación. Lógico,
pero muy difícil. Cada servicio requería procesos de extracción
diferentes, como posteriormente se vería, según la fuente de datos y
la propia información a extraer en cada caso. No obstante la filosofía
en todos los casos fue la misma, definiendo un formato de fichero
XML particular para cada aplicación que independizaba al menos la
información de la aplicación MHP, la lógica. El proceso, no obstante,
que si podía ser común o al menos reutilizado era el de publicación.
Una vez procesada la fuente de información y generado el XML base,
los pasos a partir de ahí eran los mismos en todos los casos,
compilar, empaquetar y enviar la información a Abertis para su
actualización en emisión.
En torno a esta cuestión, empezaron a tenerse las primeras reuniones
con sistemas de Gobierno de Navarra para definir esta plataforma de
extracción de datos y publicación en TDT, tanto a nivel hardware
como software. Para esto se redactaron una serie de documentos
solicitados por sistemas en los cuales se trataba de definir la
plataforma, pero de forma bastante abstracta puesto que no se
conocían las implicaciones ni requerimientos del resto de servicios por
desarrollar. De nuevo, todo se debía ir definiendo sobre la marcha. En
cualquier caso, sistemas estableció una serie de condicionantes que
aplicaban tanto a nivel hardware como software que daban una idea
de por donde debían ir las cosas. Se comenzó definiendo que estos
procesos podían ser aplicaciones Java que corriesen como demonios,
sin necesidad de ningún framework o requisito adicional que el propio
proceso, lo cual limitaba mucho las posibilidades. No obstante, como
se verá más adelante, estos requerimientos y condicionantes irían
cambiando a lo largo del proyecto.
La dificultad para ir definiendo todo esto, hizo que la plataforma
estuviese alojada de forma temporal en la infraestructura
proporcionada por los CES, hasta el momento en que fuera viable su
traspaso a sistemas de Gobierno, una vez definida por completo la
plataforma y disponible una infraestructura donde alojarla. No fue,
como se verá posteriormente, hasta el final del proyecto que pudo
hacerse esta migración.
Todos estos trabajos se planificaron de la siguiente manera:
Puede verse que para este servicio había un gran esfuerzo en lo que
era la parte de adquisición y formateo de los datos para construir los
ficheros XML base que alimentarían la aplicación MHP. También
existía una gran dependencia entre la aplicación cliente MHP y el
proceso de adquisición de datos debido al fichero XML que relacionaba
ambas aplicaciones. El diseño del interfaz marcaba la organización de
la información en el XML, el cual, a su vez, incidía directamente en el
proceso de adquisición de datos y obtención del XML. El publicador,
por el contrario, era un proceso que podía ser desarrollado de forma
independiente.
Finalmente, el equipo local estaba constituido por 3 personas (ante la
baja del miembro proporcionado por SiTVi, lo cual complicaría la
planificación acordad inicialmente) para tres desarrollos: proceso de
adquisición de datos, proceso de publicación y aplicación MHP. Las
responsabilidades de cada miembro, tal y como se ha dicho, iría
rotando en cada servicio para que todos los participasen en los
diferentes desarrollos y comprendiesen de esta forma el sistema
global.
Las pruebas del sistema completo, adquisición y publicación, eran
cruciales. Se realizaron una serie de pruebas del sistema completo en
un entorno de pre-producción habilitado en el laboratorio del CES
para asegurar el correcto flujo y procesado de la información, así
como una prueba del sistema completo en producción, pactado con
Abertis.
Previo a la emisión, se realizó una demostración de la aplicación al
área de Meteorología del Departamento de Agricultura, Ganadería y
Alimentación del Gobierno de Navarra para que solicitasen cambios y
diesen su aprobación final.
En dicha demostración, se detectó que se precisaba de la autorización
del INM para el empleo de sus datos en TDT, algo que, no obstante,
no supuso ningún impedimento ni inconveniente pudiéndose resolver
de forma satisfactoria a tiempo.
En cualquier caso, un retraso aproximadamente de una semana en
las pruebas y la coincidencia de las fiestas de San Fermin con la
emisión, pospusieron los planes, ante posibles eventualidades, hasta
finales de julio. Un retraso pequeño pero que unido a la baja de un
miembro del equipo, iría pesando a lo largo de los siguientes
desarrollos.
A continuación se muestran algunas imágenes de lo que fue e aspecto
final de la aplicación:
Catego
rías
CES OpenSouce/Java
Te
ma
Varios
Autor
Raúl Sanz de Acedo
Mes Diciembre
Año 2008
Bol
etín
12
Título Integración de jQuery con SharePoint (II)
Texto En el artículo anterior vimos como se crea un feature para
integrar la biblioteca jQuery en nuestro sitio SharePoint, sin
embargo no llegamos a ver sus funcionalidades. jQuery nos
permite, entre otras cosas, hacer consultas personalizadas
mediante código JavaScript.
Para ello es necesario descargarnos los archivos JavaScript que
nos permiten personalizar la consulta y que son modificables por
el desarrollador. También son necesarias las Web Parts que
alojan los contenidos y los resultados de la consulta, de una
manera similar a las búsquedas de SharePoint. Todo este
paquete está disponible para los suscriptores a las noticias
semanales de EndUserSharePoint un portal de usuarios de
SharePoint donde se recoge información sobre novedades,
actualizaciones y recursos interesantes. Un “precio” racionable si
se tiene en cuenta lo interesante de la información recibida.
Una vez que tenemos la biblioteca jQuery instalada y todos los
archivos JavaScript necesarios, iremos al Sitio donde queremos
emplearla y crearemos una biblioteca de documentos
llamada QueryResources, que será donde alojemos todos los
recursos necesarios para la personalización.
Para crear la biblioteca de documentos vamos a View All Site
Content> Create >Document Library. Una vez creada la biblioteca
cargamos los archivos medianteUpload>Upload Multiple Documents.
A continuación creamos una página de Web Parts (Web Part
Page) llamada SearchQuery.aspx, que guardaremos en la misma
biblioteca en la que tenemos nuestros recursos.
Una vez que hemos creado la página de Web Parts, el siguiente
paso es añadir las que nos permiten el empleo de jQuery. En
modo edición, utilizamos la opción Add Web Part y dentro de la
galería que nos aparece, seleccionamos Advanced Web Part
Gallery and Options que se encuentra en la parte inferior.
Al seleccionar dicha opción, nos aparece un cuadro de acciones que nos permite importar las Web Parts directamente del disco
duro. Para ello seleccionamos Browse> Import. A continuación
examinamos y cargamos, una a una, cada Web Part. Cuando nos aparece en la zona Uploaded Web Part, la arrastramos a la zona
de nuestra página donde deseamos implementarla.
En mi caso, he añadido jQuery_SearchBox-Top.dwp a la cabecera y jQuery_ContentOfPageToBeSearched-Center.dwp y jQuery_CallToScripts-Bottom.dwp al cuerpo. Y he
personalizado el texto de prueba.
De esta manera la página queda de la siguiente manera:
Finalmente, para comprobar que todo funciona correctamente, salimos del modo edición de nuestra página. Y comenzamos a escribir en el cuadro de texto…¡¡¡ Et Voilà!! A medida que vamos
escribiendo, las palabras que coinciden con la consulta se van pintando de amarillo.
Como hemos dicho al principio, está es sólo una de las múltiples funciones que tiene esta biblioteca. Ya que tenemos un amplio
abanico de posibilidades en el que poder trabajar. A continuación podemos ver algunos de los ejemplos:
Página oficial de
JQuery: http://dev.jquery.com/wiki/Demos
http://codylindley.com/blogstuff/js/jquery/
http://www.kelvinluck.com/assets/jquery/styleswitch/
http://iamzed.com/jquery/flashonetime.html
http://icant.co.uk/sandbox/jquerycodeview/
Categor
ías
CES Microsoft
Tema Desarrollo
Autor Goretti Ortigosa Rada
Mes Diciembre
Año 2008
Boletín 12
Título Introducción a Adobe Flex (y II).
Texto En este segundo artículo, vamos a ver un tutorial para la creación de un primer
proyecto con Adobe Flex. Crearemos una pequeña aplicación Java en el servidor y
un cliente realizado en Flex que accederá a ella a través de BlazeDS.
Descargas necesarias
A continuación se muestran las descargas necesarias para la realización del tutorial.
El sistema operativo utilizado es Windows XP.
Eclipse 3.3:
http://www.eclipse.org/downloads/download.php?file=/technology/epp/download
s/release/europa/winter/eclipse-jee-europa-winter-win32.zip
Apache Tomcat:
http://tomcat.apache.org/download-60.cgi
Flex Builder:
http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3email
BlazeDS (Binary distribution):
http://opensource.adobe.com/wiki/display/blazeds/Release+Builds
Configuración del entorno de desarrollo
Se han instalado en la carpeta C:\FlexEclipse las herramientas que se utilizarán en
este tutorial. Se instala antes eclipse, ya que la instalación de FlexBuilder pide la ruta
donde está instalado eclipse. Por comodidad, colocamos el servidor Apache Tomcat
en la misma carpeta. Además, creamos una carpeta llamada FlexWS que servirá
como workspace de eclipse para el proyecto que crearemos.
Figura 1. Entorno de trabajo
Una vez instaladas las herramientas, arrancamos eclipse. Lo primero que debemos
hacer es registrar el servidor Apache Tomcat en eclipse. Para ello, vamos a Window -
> Preferences -> Server -> Installed Runtimes, y añadimos nuestro servidor Tomcat:
Figura 2. Servidor Apache Tomcat
Creación del proyecto de servidor
La descarga de BlazeDS consiste en una aplicación web (blazers.war), que
utilizaremos como base para nuestra aplicación de servidor. Para ello, desde eclipse,
vamos a File -> Import -> Web -> WAR File
Elegimos el fichero blazers.war, y ponemos el nombre de proyecto que queramos,
en este caso creamos un proyecto web llamado Coches.
Figura 3. Importación del archivo WAR de BlazeDS
En la siguiente pantalla, eclipse nos pregunta si queremos crear proyectos para las
librerías utilizadas en blazers.war. No seleccionamos ninguna, y finalizamos el
proceso de importación.
En este punto creamos las clases de la aplicación Java, que constará de dos clases:
Coche.java (Representa un coche con algunos atributos)
CocheService.java (Proporciona un método que devuelve una lista de coches)
Figura 4. Clases de la aplicación Coches
Para poder acceder a este servicio, debemos declarar la clase en el fichero remoting-
config.xml, de la siguiente forma
Figura 5. Configuración del acceso remoto a la aplicación
De esta forma, ya tenemos una pequeña aplicación Java en el servidor a la que
podremos acceder desde una aplicación Flex en el cliente, a través de BlazeDS. Para
publicar la aplicación en el servidor Tomcat, vamos a la pestaña de servidores, y
añadimos el proyecto Coches a las aplicaciones del servidor.
Creación del proyecto cliente
En este tutorial vamos a crear dos proyectos separados, uno para el servidor y otro
para el cliente. FlexBuilder nos permite crear la aplicación Java y el cliente Flex en un
mismo proyecto, pero en este caso se ha optado por separarlos para ganar en
claridad.
Antes de comenzar, debemos arrancar el servidor Tomcat, ya que en la creación del
cliente indicaremos donde encontrar la aplicación de servidor. Vamos a File -> New
Project -> FlexBuilder -> Flex Project y creamos el proyecto CochesClient:
Figura 6. Creación del proyecto cliente (I)
En la primera pantalla, introducimos el nombre del proyecto, CochesClient, y
dejamos el resto como se indica en la Figura 6. En la segunda pantalla, debemos
indicar la ruta y la URL del proyecto de servidor. La instalación por defecto de
Tomcat en eclipse no crea las aplicaciones en la carpeta del servidor
(C:\FlexEclipse\apache-tomcat-6.0.18), sino que las crea bajo la carpeta .metadata
del workspace. En la Figura 7 se muestra esta ruta para el entorno de trabajo
creado. Para comprobar que todo está bien, tenemos un botón llamado ‘Validate
Configuration'. Una vez que todo está bien, finalizamos el asistente.
Figura 7. Creación del proyecto cliente (II)
Al crear el proyecto se genera un fichero CochesClient.mxml, donde escribiremos la
parte de Flex que accederá a la aplicación. La aplicación cliente constará de un
botón y una tabla para mostrar los datos. Al pinchar en el botón ‘Obtener coches’,
se establecerá la conexión con el servidor a través de BlazeDS y se mostrará en la
tabla la información. A continuación se muestra el código:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Script>
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;
import mx.utils.ObjectUtil;
public function resultListaCoches(event:ResultEvent
):void {
tablaCoches.dataProvider = event.result;
tablaCoches.visible = true;
}
private function faultListaCoches(event:FaultEvent)
:void{
Alert.show( ObjectUtil.toString(event.fault)
);
}
</mx:Script>
<mx:RemoteObject id="remoteCochesService"
destination="CocheServiceDestination" showBusyCursor="true">
<mx:method name="listaCoches"
result="resultListaCoches(event)"
fault="faultListaCoches(event)"/>
</mx:RemoteObject>
<mx:Button label="Obtener Lista"
click="remoteCochesService.listaCoches()"/>
<mx:DataGrid id="tablaCoches" x="10" y="63" width="574"
height="166" visible="false">
</mx:DataGrid>
</mx:Application>
En este código podemos ver:
- Un objeto RemoteObject que permite la conexión con el servidor.
- Un botón que realiza la llamada a través del RemoteObject.
- Un objeto DataGrid para mostrar los datos.
- Las funciones que gestionan el resultado de la petición. En caso de que vaya
bien, se pone el resultado de la petición como dataProvider del DataGrid, y
la información se muestra.
Figura 8. Aspecto de la aplicación Flex.
Código fuente
- Descargar
Referencias
- Adobe Flex
- BlazeDS
- Mihai Corlan Blog
Catego
rías
General
Tema Desarrollo
Autor Miguel Bacaicoa
Mes Diciembre
Año 2008
Boletín 12
Título Azure es un color… y más
Texto Siguiendo con la temática del artículo del mes anterior, vamos a volver
a hablar de Windows Azure, la plataforma de Microsoft para el
desarrollo de servicios disponibles en Internet e intercomunicables.
En este artículo vamos a llevar a cabo otro de los ejemplos disponiblres
en el Azure Services Training Kit y conseguir hacer disponible en
Internet un programa desarrollado en Silverlight. Para eso, nos
serviremos de los Live Services, que mediante Live Mesh nos permitirán
acceder a esta aplicación una vez la hayamos hecho disponible en la
plataforma Windows Azure.
Si empleamos como base el diagrama de la Plataforma de Servicios
Azure que mostrábamos el mes pasado, la representación de lo que
pretendemos hacer sería esta:
Aquí por simplificar hemos mostrado que nuestra aplicación sólo haría
uso de Live Services, pero nada nos impediría hacer uso de SQL
Services (para almacenar información en la “nube” y compartirla
mediante cualquiera de nuestros medios de acceso) , .NET Services
(para interactuar con otras infraestructuras de trabajo o ejecutar
Workflows por ejemplo) u otra aplicación alojada en Windows Azure a
la que tengamos acceso. ¡El poder de Azure!
Una vez conocido lo que vamos a hacer, al tajo. El ejemplo que vamos
a desarrollar consistirá en la publicación mediante Live Mesh de una
aplicación desarrollada en SilverLight (aunque podría tratarse de
cualquier aplicación soportada por Windows Azure). Para empezar, los
requisitos que necesitamos cumplir son:
- Microsoft Visual Studio 2008 (versión Estándar o superiores)
- Microsoft Visual Studio 2008 SP1 (descargable aquí)
- Microsoft Silverlight 2 Tools for Visual Studio 2008 (mejor no
usar las Betas anteriores por las diferencias entre Silverlight 2 y
sus antecesores): descargable aquí
- Microsoft Live Framework SDK (Descargable desde Microsoft
Connect)
- Microsoft Live Framework Tools for Visual Studio 1.0 CTP (como
el anterior también descargable desde Microsoft Connect)
Si tenemos la “sana” costumbre de instalar en nuestro PC todo aquello
con lo que podamos trastear (de alguna manera hay que probarlo,
¿no?), la instalación de los elementos listados arriba no va a ser todo lo
sencilla que podríamos desear. En concreto, la instalación de las
Silverlight Tools puede producir un error inesperado e “inexplicado”. En
este caso tendremos que hacer la instalación de manera manual de los
disitintos elementos del paquete. Para ello hay que lanzar la ejecución
y cuando nos muestre la pantalla de error no cerrarla. Entonces
buscaremos el directorio temporal de instalación que se habrá creado
automáticamente en nuestro disco duro C:\ (normalmente tendrá un
nombre compejo, enrevesado… informático vamos) y dentro de este
estarán entre otros los archivos a instalar:
- El Software Development Kit para Silverlight “silverlight_sdk.msi”
- Las Silverlight Tools para Visual Studio (aunque no usaremos estas
sino las indicadas anteriormente)
- Y dos parches de Visual Studio: VS90-KB951460.msp y VS90SP1-
KB955215.msp
Una vez realizada esta instalación ya estaremos en condiciones de
realizar desarrollos con Visual Studio 2008 y Silverlight para Windows
Azure.
En este ejemplo vamos a llevar a cabo el laboratorio para la creación
de una aplicación Live Mesh. Si seguimos el artículo del mes
pasado habremos instalado en nuestro PC el Microsoft Azure Services
Kit. Dentro de este se encuentra el Laboratorio denominado
“BuildingMeshEnabledWebApplications” que es el que nos interesa en
este caso. Si abrimos Visual Studio 2008 como Administradores
podremos abrir sin problemas la solución de ejemplo, “Begin.sln”,
ubicada en la carpeta “Ex01-UnderstandingTheApplication”. Se trata de
una aplicación completa basada en SilverLight para la gestión de
campeonatos de un deporte, donde podemos crear ligas, campos,
subir fotos de los campos, usar un sencillo tablón de mensajes, ver la
previsión del tiempo, etc.
Si miramos un poco el detalle de esta aplicación veremos que está
compuesta de 2 proyectos, uno que es la aplicación SilverLight en sí
para controlar el interfaz de usuario
(WLQuickApps.FieldManager.Silverlight) y otro con las clases
necesarias para que el anterior funcione (FieldManager.ObjectModel):
El proyecto FieldManager.ObjectModel representa un muy buen
ejemplo de organización de la estructura de un proyecto, con una
carpeta para la definición de los Objetos y otra para los Gestores
(Managers) de estos, de manera que su organización queda muy clara.
Antes de proceder con las modificaciones necesarias para publicar esta
aplicación en Live Mesh, habremos de conseguir el acceso a una
cuenta de Azure Services para desarrollo. Para ello, accedemos a Azure
Services Developer Portal mediante la
web http://lx.azure.microsoft.com y nuestro Windows Live ID. En el
caso de que se trate de la primera vez, habremos de dar de alta la
información precisa como cuenta de desarrollo:
Hecho esto, pasamos a la que será la pantalla principal en nuestras
próximas visitas, desde donde gestionaremos nuestros proyectos,
cuenta, etc.
Aquí tendremos que seleccionar el enlace “New Project” (a izquierda o
derecha, da igual) y pasamos a la pantalla desde la que definiremos el
nuevo proyecto a crear. Como veremos existen varios tipos
disponibles, agrupados en dos bloques:
- Windows Azure: este bloque nos permite definir aplicaciones
que se ejecutarán sobre Windows Azure, para lo que podemos
optar por crear almacenamiento mediante “Storage Account” o
nuestro propio servicio mediante “Hosted Services”.
- Azure Services Platform: en este caso haremos uso de la
plataforma de servicios de Azure ya existente, para lo que
tendremos a nuestra disposición el “Live Services: Live
Framework CTP” para el desarrollo de servicios que hagan uso
de cualquier Live Services existente, o “Live Services: Existing
APIs” para usar APIs de los Live Services desde cualquier
aplicación Web.
En nuestro caso, veremos más adelante que crearemos un proyecto
con el Live Framework CTP del Azure Services Platform, ya que lo que
queremos es publicar una aplicación que haga uso del Live Service
“Live Mesh” como plataforma de publicación.
Por ahora eso es todo en este portal, cancelamos las modificaciones y
regresaremos a Visual Studio y preparamos una solución para su
publicación. Para eso, cerramos la que tenemos abierta y abrimos la
solución “Begin.sln” que está localizada en la carpeta “Ex02-
DeployingIntoLiveMesh” que prepararemos para publicar mediante los
siguentes pasos:
1. Añadimos al
proyecto WLQuickApps.FieldManager.Silverlight tres
referencias (botón derecho sobre el nodo “References”, opción
“Add Reference…”) a los ensamblados de Live Framework que
encontraremos en “[LiveFxSDK]\Libraries\Silverlight”:
- Microsoft.LiveFX.Client.dll
- Microsoft.LiveFX.ResourceModel.dll
- Microsoft.Web.dll
2. Editamos el fichero App.xaml.cs
de WLQuickApps.FieldManager.Silverlight
E introduciremos los cambios siguientes mostrados en azul:
using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Browser; using Microsoft.LiveFX.Client; namespace WLQuickApps.FieldManager.Silverlight { public partial class App : Application { public App() { this.Startup += this.Application_Startup; this.Exit += this.Application_Exit; this.UnhandledException += this.Application_UnhandledException; InitializeComponent(); } private void Application_Startup(object sender, StartupEventArgs e) {
MeshApplicationService meshApp =
Application.Current.GetMeshApplicationService(); meshApp.LoadCompleted += new EventHandler(this.App_Load); meshApp.Load(); } private void Application_Exit(object sender, EventArgs e) { } private void Application_UnhandledException(object sender,
ApplicationUnhandledExceptionEventArgs e) { // Commented out per security review. HtmlPage.Window.Invoke("alert", "DEBUG:\n" + e.ExceptionObject.Message); } // Evento de carga para Live Mesh public void App_Load(object sender, EventArgs e) { MeshApplicationService meshApp =
Application.Current.GetMeshApplicationService(); meshApp.Resource.Title = "App"; Mesh mesh = meshApp.LiveOperatingEnvironment.Mesh; this.RootVisual = new Page(); } } }
3. Añadiremos un nuevo proyecto a nuestra solución, que será el
encargado de prepararla como una aplicación Live Mesh. Al
haber instalado el Live Framework Tools tenemos disponible
entre las posibles plantillas de proyecto un nuevo nodo de
nombre “ Live Framework” que incluye el tipo que queremos
“Silverlight Mesh-enabled Web Application”.
Seleccionamos esta plantilla y le damos el nombre
“FieldManagerMeshApp”. Esto nos crear dos proyectos más en
nuestra solución, uno de los cuales hemos de borrar ya que no
nos hace falta. Se trata de “FieldManagerMeshAppSiverlight”
que es el esqueleto de proyecto que podríamos usar en caso de
que tuviéramos que crear una aplicación SilverLight desde cero,
que en nuestro caso está ya creada. Pincharemos con el botón
derecho del ratón sobre su nombre y seleccionaremos la opción
“Remove”
Para correguir este cambio en nuestra aplicación
“FieldManagerMeshApp” hemos de quitar la referencia a
“FieldManagerMeshAppSilverlight” y agregar la correcta a
nuestro proyecto “WLQuickApps.FieldManager.Silverlight”
Quitar Añadir
4. Cambiamos en el fichero Index.html del
proyecto FieldManagerMeshApp la referencia al paquete xap
del control de Silverlight modificando en la definición del
“silverlightControlHost” el valor del parámetro source como se
muestra a continuación:
<div id="silverlightControlHost"> <object data="data:application/x-silverlight," type="application/x-
silverlight-2" width="100%" height="100%"> <param name="source"
value="WLQuickApps.FieldManager.Silverlight.xap"/> <param name="onerror" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="2.0.30923.0" />
<param name="autoUpgrade" value="true" /> <param name="windowless" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=124807"
style="text-decoration: none;"> <img
src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft
Silverlight" style="border-style: none"/> </a> </object> <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe> </div>
5. Finalmente debemos modificar los valores del fichero
Manifest.xml del proyecto FieldManagerMeshApp para que la
publicación de nuestra aplicación tenga las propiedades que
nos interesen:
<?xml version="1.0" encoding="utf-8" ?> <Manifest xmlns="http://schemas.mesh.com/application/manifest/2008"> <Name>CES Field Manager</Name> <Description>Apliación de prueba de Silverlight en Live Mesh</Description> <PublisherName>User</PublisherName> <DisplayVersion>1.0</DisplayVersion> <MultiInstance>false</MultiInstance> <PrivacyPolicyLink>http://www.cesnavarra.net</PrivacyPolicyLink> <SupportLink>http://www.cesnavarra.net</SupportLink> <VendorLink>http://www.cesnavarra.net</VendorLink> <RequireDelegation>false</RequireDelegation> </Manifest>
6. Por último, establecemos el
proyecto FieldManagerMeshApp como proyecto de inicio,
pulsando con el botón derecho del ratón sobre su nombre y
seleccionando la opción “Establecer como proyecto de inicio”.
7. Ejecturaremos la aplicación sin debugging (Ctrl + F5) y nos
aparecerá la ventana de solicitud de nuestro Windows Live ID
para acceder a la plataforma Windows Azure.
8. Una vez hemos hecho esto, tendremos una nueva pantalla para
la definición del enlace la aplicación:
Lo primero a hacer ahora es pulsar el enlace “Navigate to the
Developer Portal” para definir la nueva aplicación Mesh en el
Azure Services Developer Portal. Como hemos dicho antes,
pulsaremos en “New Project” una vez en ese portal, y de las
opciones elegiremos “Live Services: Live Framework CTP” como
hemos comentado antes.
Esto nos pasa a otra pantalla donde introduciremos los datos de
la aplicación: su etiqueta y descripción. Aceptado esto,
tendremos la opción de elegir el tipo de aplicación, que en este
caso se trata de una Mesh-enabled Web application.
Seleecionaremos esta y pulsamos Create:
Tras esto habremos por fin creado nuestra aplicación Mesh (sin
contenido todavía, aún hemos de subirla) y se nos mostrará la
pantalla de propiedades de la misma:
Volvemos a Visual Studio y pulsamos el enlace “Copy full path of
FieldManagerMeshApp.zip to clipboard” en la ventana de
diálogo que teníamos abierta. Cambiamos de nuevo al
navegador, a Azure Services Developer Portal, y pulsamos el
botón “Upload Package”.
Pulsamos entonces el botón “Browse” para abrir la ventanda de
selección del paquete, pulsamos Ctrl+V para pegar el camino
del mismo y a continuación pulsamos Deploy, con lo que se
inicia la copia del paquete a Windows Azure.
Una vez completado, copiamos el “Application Self Link” que se
nos devuelve
Y de vuelta en Visual Studio lo introducimos en el 3er paso de la
ventana que tenemos activa:
Con esta información completa, una vez que pulsemos Ok se
produce la actualización final de la aplicación en Windows Live y
podremos ejecutarla (estos últimos pasos se simplifican en el
caso de que estemos actualizando una aplicación que ya exista
como Live Mesh application; en este ejemplo es más complejo
ya que hemos realizado a la vez la creación y definición de una
aplicación Live Mesh y la compilación y subida de una nueva
versión de la misma).
Para su ejecución, el mismo Visual Studio abrirá el Live Desktop
por nosotros. En caso de no ser así podemos acceder mediante
la webhttps://www.mesh.com/Welcome/Default.aspx
En definitiva, este ejemplo nos ha permitido ver cómo se pueden
desarrollar aplicaciones para Live Mesh, accesibles mediante cualquier
sistema que cuente con acceso a la web, desarrolladas desde nuestra
plataforma local y con todas las ventanas de los múltiples servicios que
Windows Azure pone a nuestra disposición:
- .NET Services para comunicación entre sistemas y
aplicaciones, workflows, etc.
- Storage Services para el almacenamiento de información
- Live Services para el uso del Live Framework y toda la
plataforma de servicios Live
Categorías
CES Microsoft
Tema Desarrollo
Autor Rafael Flores
Mes Diciembre
Año 2008
Boletín
12
Título Creación de un Gadget basado en una animación XAML
Texto Cada día los usuarios demandan el acceso a más información y que esta sea presentada de forma llamativa y eficiente. Una
de las novedades de Windows Vista ha sido Windows Sidebar.
Esta aplicación se nos presenta como una barra de herramientas formada por mini aplicaciones (Gadgets).Hay
diversos Gadgets, desde resultados deportivos, pasando por cotizaciones en bolsa y acabando por Radios o acceso al
Messenger.
En este artículo se pretende presentar nuestra información de la forma más llamativa posible, mediante un efecto 3D
realizado con nuestras fotos creado a partir de una animación XAML y la eficiente presentación de nuestras fotos en nuestra
Sidebar.
El primer paso que debemos seguir es crear la animación de XAML, en nuestro caso va ser un cubo en 3d que nos va
presentar nuestras seis fotos, una por cada cara del cubo, lo haremos con el siguiente código.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:SampleControls="SampleControls" WindowTitle="3D Rotation Example" Background="Black"> <Page.Resources> <!—Modelo 3D de cada una de las caras del cubo --> <MeshGeometry3D x:Key="CubeSide01" TriangleIndices="0,1,2 3,4,5 " Normals="-1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 " TextureCoordinates="0,1 0,0 1,0 1,0 1,1 0,1 " Positions="-0.5,0.5,-0.5 -0.5,-0.5,-0.5 -0.5,-0.5,0.5 -0.5,-
0.5,0.5 -0.5,0.5,0.5 -0.5,0.5,-0.5 " /> <MeshGeometry3D x:Key="CubeSide02" TriangleIndices="0,1,2 3,4,5 "
<!—en la animación se reconocen las imágenes como triangulos
para recrear cada cara del cubo --> Normals="0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 "<!—vector
perpendicular a la superficie del plano --> TextureCoordinates="0,0 1,0 1,1 1,1 0,1 0,0 ""<!— aplicación de
la textura a cada triangulo--> Positions="-0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 0.5,0.5,0.5 -
0.5,0.5,0.5 -0.5,-0.5,0.5 " />""<!— posicionamiento en las tres
dimensiones de los triangulos--> <MeshGeometry3D x:Key="CubeSide03" TriangleIndices="0,1,2 3,4,5 " Normals="0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 " TextureCoordinates="1,0 1,1 0,1 0,1 0,0 1,0 " Positions="0.5,-0.5,-0.5 0.5,0.5,-0.5 0.5,0.5,0.5 0.5,0.5,0.5
0.5,-0.5,0.5 0.5,-0.5,-0.5 " />
<MeshGeometry3D x:Key="CubeSide04" TriangleIndices="0,1,2 3,4,5 " Normals="1,0,0 1,0,0 1,0,0 1,0,0 1,0,0 1,0,0 " TextureCoordinates="1,0 1,1 0,1 0,1 0,0 1,0 " Positions="-0.5,-0.5,-0.5 -0.5,0.5,-0.5 0.5,0.5,-0.5 0.5,0.5,-
0.5 0.5,-0.5,-0.5 -0.5,-0.5,-0.5 " /> <MeshGeometry3D x:Key="CubeSide05" TriangleIndices="0,1,2 3,4,5 6,7,8 9,10,11 " Normals="0,-1,0 0,-1,0 0,-1,0 0,-1,0 0,-1,0 0,-1,0 0,1,0 0,1,0
0,1,0 0,1,0 0,1,0 0,1,0 " TextureCoordinates="0,0 1,0 1,1 1,1 0,1 0,0 1,1 0,1 0,0 0,0 1,0
1,1 " Positions="-0.5,-0.5,-0.5 -0.5,0.5,0.5 -0.5,-0.5,0.5 0.5,-
0.5,0.5 -0.5,-0.5,-0.5 -0.5,0.5,-0.5 0.5,0.5,-0.5 -0.5,0.5,-0.5 -0.5,0.5,0.5 -
0.5,0.5,0.5 0.5,0.5,0.5 0.5,0.5,-0.5 " /> <MeshGeometry3D x:Key="CubeSide06" TriangleIndices="0,1,2 3,4,5 6,7,8 9,10,11 " Normals="-1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 " TextureCoordinates="1,0 1,1 0,1 0,1 0,0 1,0 " Positions="-0.5,-0.5,0.5 -0.5,-0.5,-0.5 0.5,-0.5,-0.5 0.5,-
0.5,-0.5 0.5,-0.5,0.5 -0.5,-0.5,0.5" /> <!—Material 3D utilizado para cada cara del cubo --> <MaterialGroup x:Key="LeavesMaterial1"> <DiffuseMaterial> <DiffuseMaterial.Brush> <ImageBrush Stretch="UniformToFill" ImageSource="images\1.jp
g" TileMode="None" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top"Opacity="1.000000" /><!—Dibuja
una imagen a traves de un pincel definido --> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"><!—cantidad de luz que
incide sobre nuestro material --> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/>"><!—
define el color y la opacidad del pincel --> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="RocksMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush> <ImageBrush Stretch="UniformToFill" ImageSource="images\2.jp
g" TileMode="None" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top"Opacity="1.000000" /> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="BranchesMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush>
<ImageBrush Stretch="UniformToFill" ImageSource="images\3.jp
g" TileMode="None" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top"Opacity="1.000000" /> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="BerriesMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush> <ImageBrush Stretch="UniformToFill" ImageSource="images\4.jp
g" TileMode="None" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top"Opacity="1.000000" /> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="FlowersMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush> <ImageBrush Stretch="UniformToFill" ImageSource="images\5.jp
g" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top" Opacity="1.000000" /> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="SunsetMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush> <ImageBrush Stretch="UniformToFill" ImageSource="images\6.jp
g" ViewportUnits="Absolute" Viewport="0 0 1
1" AlignmentX="Left" AlignmentY="Top" Opacity="1.000000" /> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <MaterialGroup x:Key="SolidColorMaterial"> <DiffuseMaterial> <DiffuseMaterial.Brush> <SolidColorBrush Color="Orange" Opacity="1.000000"/> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="85.3333"> <SpecularMaterial.Brush> <SolidColorBrush Color="#FFFFFF" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup>
<!—Modelos visuales 3D --> <ModelVisual3D x:Key="PictureCubeModelVisual3DResource" > <ModelVisual3D.Children> <ModelVisual3D> <ModelVisual3D.Content> <AmbientLight Color="#333333" />
<!—el color que toma la textura según la incidencia de
la luz --> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content>
<DirectionalLight Color="#FFFFFF" Direction=
"-0.612372,-0.5,-0.612372" /><!—Dirección de la incidencia de
la luz sobre la textura --> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <DirectionalLight Color="#FFFFFF" Direction="0.612372,-
0.5,-0.612372" /> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide01}" Material="{StaticResource BranchesMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide02}" Material="{StaticResource FlowersMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide03}" Material="{StaticResource BerriesMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide04}" Material="{StaticResource LeavesMaterial1}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide05}" Material="{StaticResource RocksMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide06}" Material="{StaticResource SunsetMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> </ModelVisual3D.Children> </ModelVisual3D> </Page.Resources>
<DockPanel> <Viewbox> <!—La animación del cubo. --> <Viewport3D ClipToBounds="True" Width="400" Height="300"> <Viewport3D.Camera> <PerspectiveCamera x:Name="myPerspectiveCamera" LookDirection="0,0,-1" UpDirection="0,1,0" Position="0,0,2.1" FieldOfView="50" /> <!—dirección,prespectiva,pocición con la que la camara muestra el
cubo --> </Viewport3D.Camera> <ModelVisual3D> <ModelVisual3D.Children> <ModelVisual3D> <ModelVisual3D.Content> <AmbientLight Color="#333333" /> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <DirectionalLight Color="#FFFFFF" Direction="-
0.612372,-0.5,-0.612372" /> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <DirectionalLight Color="#FFFFFF" Direction="0.612372,
-0.5,-0.612372" /> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide01}" Material="{StaticResource BranchesMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <!—material 3D que carga para cada cara del cubo --> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide02}" Material="{StaticResource FlowersMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide03}" Material="{StaticResource BerriesMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide04}" Material="{StaticResource LeavesMaterial1}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide05}" Material="{StaticResource RocksMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D Geometry="{StaticResource
CubeSide06}" Material="{StaticResource SunsetMaterial}"/> </ModelVisual3D.Content> </ModelVisual3D> </ModelVisual3D.Children> <ModelVisual3D.Transform> <Transform3DGroup > <Transform3DGroup.Children> <RotateTransform3D> <RotateTransform3D.Rotation> <AxisAngleRotation3D x:Name="myHorizontalRotation"
Angle="0" Axis="0 1 0" /> </RotateTransform3D.Rotation> </RotateTransform3D> <RotateTransform3D> <RotateTransform3D.Rotation> <AxisAngleRotation3D x:Name="myVerticalRotation" A
ngle="0" Axis="1 0 0" /><!—Angulo de rotación del Cubo --> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D x:Name="myTranslateTransform" OffsetX="0" OffsetY="0" OffsetZ="0" />
<!—definicion de la translación del cubo --> </Transform3DGroup.Children> </Transform3DGroup> </ModelVisual3D.Transform> </ModelVisual3D> <Viewport3D.Triggers> <!—Animaciones del cubo despues de ser cargadas. --> <EventTrigger RoutedEvent="Viewport3D.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="myVerticalRotation" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:10" RepeatBehavior="Forever" /> <!—Rotacion vertical de duración de 10 segundos --
> <DoubleAnimation Storyboard.TargetName="myHorizontalRotation" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:9" RepeatBehavior="Forever" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Viewport3D.Triggers> </Viewport3D> </Viewbox> </DockPanel> </Page>
Una vez creada la animación deberemos de ir a la carpeta que
contiene todos los Gadgets de la Sidebar. La ruta habitual donde la encontramos es la
siguiente C:\Users\usuario\AppData\Local\Microsoft\Win
dows Sidebar\Gadgets Dentro de dicha carpeta deberemos
de crear una con el siguiente formato: “Nombre
Gadget.gadget” en nuestro caso CUBO3D.gadget. En el
interior de esta carpeta colocaremos nuestro archivo XAML, y
también hemos de crear los archivos XML y HTML que definen las propiedades y el objeto del Gadget (en nuestro caso la
animación XAML), pero vamos por partes.
El siguiente paso es crear el archivo XML, que debe tener la siguiente estructura:
<?xml version="1.0" encoding="utf-8" ?> <gadget> <name>CUBO3D</name> <namespace>Cesnavarra</namespace> <version>1.0</version> <author name="Cesnavarra"> <info url="http://www.cesnavarra.net" /> </author> <copyright>2008</copyright> <description>Gadget animacion 3Da través de un XAML</description> <hosts> <host name="sidebar"> <base type="HTML" apiVersion="1.0.0" src="default.html" /> <permissions>full</permissions> <platform minPlatformVersion="0.3" /> </host> </hosts> </gadget>
El significado de cada etiqueta es el siguiente
<name></name>: En esta etiqueta colocaremos el nombre
de nuestro Gadget, en nuestro caso “CUBO3D”
<namespace></namespace>: Esta etiqueta define el grupo al que asociaremos nuestros Gadgets con lo que
podremos gestionarlos de manera agrupada. Es una etiqueta informativa y en nuestro caso lo haremos por ejemplo
mediante la agrupación “Cesnavarra”
<version></version>: La siguiente etiqueta nos indicara la versión de nuestro Gadget para posteriores actualizaciones.
<author name="">: Indica el nombre del autor de Gadget
<info url="" />: Nos ayudará a definir una página de
referencia de nuestro Gadget.
<copyright></copyright>: Esta etiqueta indica los
derechos reservados de autor de nuestro Gadget.
<description></description>: La siguiente Etiqueta nos ayudara a presentar información descriptiva sobre nuestro
Gadget.
La parte contenida en las etiquetas <hosts></hosts> no la vamos a modificar ya que está predeterminada para el
Windows SideBar. Lo único que puede interesarnos modificar
va a ser el origen donde obtendremos el contenido de nuestro
Gadget, que se obtiene a través de una página HTML. El nombre de este origen puede ser diferente al nombre del
Gadget. Ej. scr="default.html".
El archivo XML lo deberemos de nombrar Gadget.xml, ya que esta es la manera que la Sidebar reconoce nuestro Gadget.
El penúltimo paso será crear nuestra página web de
presentación de nuestro Gadget:
<html> <head> <title>CUBO 3D</title> <style> body { width:130; height:130; padding:5; margin:0; background:black; } </style> </head> <body> <iframe height="130" width="130" src="3d_animation.xaml" />
</body> </html>
Como se puede ver es una simple pagina en HTML donde lo más importante la definición de la carga de nuestra animación
mediante un iframe.
Por último una vez creados todos los elementos
procederemos a incluir nuestro Gadget en la Sidebar de Windows Vista de la siguiente manera:
En la Sidebar en la parte superior presionaremos la tecla más lo que nos presentara todos los Gadget que tenemos
instalados en nuestro sistema, entre ellos nuestro CUBO
3D.Realizaremos doble click sobre este Gadget y aparecerá en nuestra Sidebar.
Categ
orías
CES Microsoft
Tema Desarrollo
Autor Raúl Mayo González
Mes Diciembre
Año 2008
Boletín
12
Título Copias de seguridad de un repositorio subversion.
Texto Si repasáis los artículos de este año encontrareis 2 artículos interesantísimos
que no deberíais perderos sobre la instalación y despliegue del controlador
de versiones de código Subversión.
En este artículo vamos a tratar con un poco más de profundidad el hecho de
realizar copias de seguridad de un repositorio Subversión ( SVN ).
Copias de un repositorio de SVN. se pueden hacer de varias formas,
podríamos partir incluso de copias basadas en comandos del propio sistema
operativo, tar, cp, xcopy, zip etc., podríamos utilizar gestores de copias
completos tipo los comerciales Backup Exec de symantec (antiguamente de
Veritas.) o como los open source “Bacula backup” o el freeware Cobian
Backup (aunque este gestor tiene alguna versión opensource). Pero nosotros
nos vamos a centrar en las herramientas que distribuye el propio SVN.
El SVN tiene un funcionamiento transaccional. Deberíamos tener en cuenta
que mientras se hacen las copias se pueden estar haciendo modificaciones
en los ficheros bajo el control de SVN y que por lo tanto tendremos nuevas
revisiones. El árbol de directorios y ficheros está fuertemente relacionado a
través revisiones y Vds, etc. para dar consistencia a SVN. En una palabra: es
un sistema complejo.
Digamos que debido a esto y a toda esas psicosis que rodean las gestiones de
copias de seguridad nos vamos a centrar en los propios comandos del SVN.
Éstos nos dan un conocimiento más fino de las modificaciones existentes en
el repositorio y además están pensados para garantizar las interrelaciones y
consistencia entre ficheros revisiones directorios etc. del entramado SVN.
Subversión permite hacer copias de seguridad con las siguientes
herramientas:
svnadmin hotcopy: permite hacer copias completas del repositorio en
caliente. En realidad es tan trivial como hacer una llamada al cp/copy del
sistema dependiendo de si el sistema es Unix o Windows.
svnadmin dump: permite generar ficheros de exportación e importación
“dump” de un repositorio concreto; svnadmin dump permite exportaciones
parciales hasta una revisión concreta o un rango de revisiones. Con el
modifica “–incremental” se pueden hacer ficheros de parciales incrementales
sin incluir las revisiones anteriores a la especificadas.
tools/backup/ hot-backup.py: esta herramienta se distribuye y se localiza en
el directorio “tools/backup”. Si no podríamos localizarlo en la siguiente
dirección, http://svn.collab.net/repos/svn/trunk/tools/backup/hot-
backup.py.in
Hot-backup es un script que nos va a permitir hacer copias calientes de
repositorios de SVN. Automáticamente gestiona los nombres de los ficheros
de backup por cada repositorio cuando tienes múltiples repositorios y se
preocupará de evitar las colisiones con los backups previos. Los backups
antiguos los calificará de “rótate off” borrándolos y conservando solo las
versiones de copia más recientes. Podría ser interesante estudiar un modo
de realizar copias incrementales con la herramienta.
Bien, ya hemos visto que SVN se preocupa por la coherencia de sus
repositorios y por eso dispone de 3 herramientas de volcado de datos. Tanto
hotcopy como hot-backup.py son muy fáciles de usar para realizar copias de
seguridad completas, sobre todo hot-backup.py que además ofrece un nivel
más fino de generación y automatización de copias.
Sin embargo nosotros nos vamos a basar en el svnadmin dump. El método
puede ser más elaborado pero nos va a permitir hacer backups más
personalizados y afinados, como por ejemplo backups incrementales. Sobre
esto último ni que decir tiene que un backup completo es el backup más
seguro y coherente que se puede realizar. Sin embargo cuando nuestros
repositorios empiezan a crecer en cuestión de tamaño, podemos tener otros
problemas como la cantidad de tiempo en la generación de las copias o el
tamaño de los ficheros a distribuir a través de nuestros sistemas hasta llegar
al sistema de archivados de copias, trafico de red, etc.
Las copias incrementales nos van a permitir aligerar las cargas de tráfico y
volumen de datos pero nos van a exigir más control en la gestión de copias.
Vale, para empezar debemos conocer los siguientes 2 comandos y unos
modificadores en particular:
Svnlook: interesante herramienta que distribuye el subversion. Esta
herramienta con sus distintos comandos y modificadores tiene como
objetivo mostrarnos información del repositorio. Tiene muchas opciones
pero para nuestros intereses hemos seleccionado estas tres:
svnlook –info [path/repositorio]
svnlook –info [path/repositorio] –r [ nº revision]
svnlook youngest [path/repositorio]
Por ejemplo:
#svnlook info /opt/Repositorios/PruebaRepo01
Kikorro
2002-11-04 09:29:13 -0600 (Mon, 04 Nov 2002)
57
¡¡¡ Esto martxaaaaaaaa : P !!! …… ¡¡¡ FIESTAAAAAAA !!!
Podemos observar por filas:
Nombre del usuario SVN: Kikorro
Fecha de la modificación : 2002-11-04 09:29:13 -0600 (Mon, 04 Nov 2002)
Número de caracteres del mensaje de modificación :27
Mensaje sobre la modificación : ¡¡¡ Esto martxaaaaaaaa : P !!!!
#svnlook info –r 19 /opt/Repositorios/PruebaRepo01
Kikorro
2002-11-05 09:29:13 -0600 (Mon, 04 Nov 2002)
14
1..2..3...14!
Vemos que la salida es igual que en el ejemplo anterior, pero esta vez nos
esta mostrando un resumen de la información de la revisión 19 (también
podemos observar que al usuario Kikorro se le va mucho la “olla” y que
cuenta tan mal como “Bono”)
Dejando a un lado las bromas, estas instrucciones las vamos a usar para
comprobar que tanto el repositorio como la revisión existen. En caso de que
el repositorio o la revisión fuesen incorrectos se nos mostrarían sendos
mensajes de error, además de devolver el valor estándar de error $? = 1.
La opción “youngest” del comando svnadmin nos va a mostrar la ultima
revisión del repositorio indicado. Por ejemplo :
#svnlook youngest /opt1/Repositorios/PruebaRepo01
19
Svnadmin dump/load: hay que decir que estas opciones están orientadas a
la exportación y migración de repositorios pero perfectamente nos sirven
para hacer copias de seguridad.
La opción “dump” a secas nos generará un volcado binario del repositorio
especificado. Se pueden especificar volcados hasta una revisión, hasta un
rango de revisiones y volcados incrementales de rangos de revisiones.
#svnadmin dump /opt/Repositorios/PruebaRepo01 >
PruebaRepo01_full.dump
#svnadmin dump /opt/Repositorios/PruebaRepo01 –r 15 >
PruebaRepo01_r15.dump
#svnadmin dump /opt/Repositorios/PruebaRepo01 –r 10:15 >
PruebaRepo01_r10_15.dump
#svnadmin dump /opt/Repositorios/PruebaRepo01 –r 16:19 --
incremental > PruebaRepo01_inc_r16_19.dump
Vamos a tratar de explicar la diferencia entre la fila 3 y la 4: SVN tiene el
siguiente comportamiento por defecto para la opción “dump”. Cada revisión
volcada debe poder ser restaurada por sí misma. Si es una revisión nueva no
hay ningún problema pero si la revisión esta basada en otras anteriores,
entonces en el fichero “dump” deben de existir las revisiones necesarias
anteriores para poder restaurarse las revisiones especificadas en el rango.
Como mínimo va a existir un volcado completo de la primera revisión del
repositorio.
Especificando el modificador “–-incremental” se cambia el comportamiento
por defecto de SVN. Evitaremos un volcado completo de la primera revisión
del repositorio pero debemos ser muy cuidadosos con los “dump” parciales
ya sean completos o incrementales si luego queremos restauraciones
coherentes.
Muy bien: hasta aquí sabemos que podemos hacer copias completas de
nuestros repositorios preferidos y que incluso podríamos diseñar una política
de copias incrementales por si nuestros repositorios son muy grandes o
queremos aligerar la carga de red. Ahora lo que debemos saber es cómo
restaurar estas copias que nos hemos feriado.
La opción “load” del comando svnadmin es la que andábamos buscando. Un
ejemplo básico:
#svnadmin load /opt1/Repositorios/PruebaRepo01 <
pruebaRepo01_full.dump.
Claro, éste es el caso más sencillo: la restauración desde una copia completa.
Bien en el caso de tener ficheros “dump” parciales incrementales,
deberemos tener en cuenta la advertencia que hemos hecho anteriormente
sobre la restauración coherente de la revisiones: si una revisión se basa en
una anterior, ésta ha de existir, así que siempre ha de haber una copia
completa desde de la que deben ir partiendo las copias incrementales y por
lo tanto la restauración de dichas copias deberá seguir un orden lógico con
respecto a las copias generadas.
Por ejemplo: secuencia de dumps.
#svnadmin dump /opt1/Repositorios/PruebaRepo01 -r 0:10 >
dumpfile1
#svnadmin dump /opt1/Repositorios/PruebaRepo01 -r 11:14 -
-incremental > dumpfile2
#svnadmin dump /opt1/Repositorios/PruebaRepo01 -r 15:19 -
-incremental > dumpfile3
Secuencia de loads.
#svnadmin load /opt1/Repositorios/PruebaRepo01 <
dumpfile1
#svnadmin load /opt1/Repositorios/PruebaRepo01 <
dumpfile2
#svnadmin load /opt1/Repositorios/PruebaRepo01 <
dumpfile3
En el ejemplo anterior la primera instrucción equivale a una copia completa
hasta la revisión 10. También podríamos haberlo indicado de la siguiente
manera:
#svnadmin dump /opt1/Repositorios/PruebaRepo01 -r 10 >
dumpfile1.
Restaurar un repositorio; Para restaurar………en una noche de verano ….
¡¡CÓMO!! ¡¡QUEEEEEEE!! ¡¡Quée dice este ahora!! … ¿¿No bastaba sólo con el
load?? ….. ¡¡Por favor que acabe yaaaaaa!! (Los lectores digitales)
Queridos lectores: un poco de paciencia, que queda poco.
Para restaurar el repositorio, éste debe existir. “svnadmin load” no crea un
repositorio: carga un una copia generada con el comando svnadmin dump;
En caso de no existir el repositorio debemos recrearlo con la instrucción,
svnadmin create [repositorio].
Por ejemplo:
svnadmin create /opt1/Repositorios/PruebaRepo01
Si el repositorio está corrupto yo recomendaría eliminar el repositorio entero
y recrearlo desde nuestras propias copias. Como os dije en el primer párrafo
la creación y despliegue de un controlador de versiones SVN está detallado
en artículos anteriores así que ante la duda os emplazo a que les volváis a
echar un vistazo.
¡¡¡CONCLUSIONESSSS!!!.
Svnlook nos da mucha información del repositorio SVN, pero nos va permitir
verificar que existe un repositorio o determinada revisión. Además podemos
saber a que revisión corresponde la última modificación registrada en SVN.
Svnadmin dump/load nos permite hacer y recargar volcados a fichero de
nuestros repositorios SVN, además con control por revisiones, de forma
incremental, etc.
Vemos que tenemos un control muy fino de lo que queremos hacer. Sin
embargo se me antoja un poco complicado automatizar todo esto.
Vale, no os preocupéis le doy unas vueltas a todo esto: repasamos un poco
de shell script y nos montamos un “pseudo script” que haga uso de estos
comandos y pueda ser llamado desde un “cron” para automatizar el sistema.
Enlaces de interés:
http://benr75.com/2007/04/24/backing-up-subversion-using-hot-backup-py
http://svnbook.red-bean.com/en/1.1/ch05s03.html
http://svnbook.red-bean.com/en/1.1/ch05s02.html
Categorías CES OpenSouce/Java
Tema Varios
Autor Kike Muro Arbizu
Mes Diciembre
Año 2008
Boletín 12
Título Ontologías
Texto En el artículo XML, RDFS, Ontologías: Camino a la Web semántica, se comenzó a
dar una primera visión de las ontologías, sin embargo quedaron muchos cabos
sueltos y no se llegó a profundizar.
Actualmente, no es algo muy extendido, pero ya comienza a haber iniciativas
como la extensión de MediaWiki (la Wikipedia utiliza este
software)Extensions:Semantic MediaWiki, que pretende incluirlas en las páginas
de esta Wiki. Por otra parte, Swoogle, es un buscador de la Web semántica que
permite buscar entre otras cosas ontologías y recursos de la misma.
A continuación se intentará explicar los pasos necesarios para la creación de una
de ellas. Y se irán ilustrando mediante un ejemplo de creación de ontologías
muy sencillo empleando el lenguaje OWL y en formato XML. Una ontología en
OWL está formada por individuos (son instancias de clases), propiedades (al
darles valores se crean las individuos) y clases.
En términos prácticos, el desarrollo de una otología incluye:
Definir las clases de la ontología.
Ordenar las clases en una jerarquía taxonómica (subclase-
superclase).
Definir propiedades y describir los valores permitidos para
ellas.
Introducir valores en las propiedades para crear instancias.
Se puede crear entonces una base de conocimiento definiendo instancias
individuales de esas clases introduciendo en propiedades específicas información
de valor y restricciones adicionales de esas propiedades.
Como ayuda a la hora de crear ontologías uno debe recordar las siguientes
reglas, aunque parezcan un poco dogmáticas:
No hay una forma correcta de modelar un dominio – siempre hay alternativas viables. La mejor solución casi
siempre depende de la aplicación que se le vaya a dar y de las extensiones que se anticipan.
El desarrollo de ontologías es necesariamente un proceso iterativo.
Los conceptos de la ontología deberían ser cercanos a objetos (físicos o lógicos) y a relaciones en el dominio de
interés. Probablemente serán nombres (objetos) o verbos (relaciones) en frases que describan el dominio.
Al final, lo que guiará las decisiones de diseño serán las decisiones tomadas sobre
cómo de general o detallada va a ser la ontología y para qué se va a usar. Lo que
siempre se debe recordar durante este proceso es que una ontología es un
modelo de la realidad y que los conceptos en la ontología, deben reflejar esa
realidad. Así nunca se podría tener un concepto que fuera ent, elfo o hobbit a no
ser que se estuviera reflejando la realidad del mundo del Señor de los
Anillos. Una vez definida la ontología debe evaluarse y probablemente será
necesario revisarla y hacer cambios.
Se seguirán una serie de pasos para definir la ontología que vamos a hacer:
Paso 1. Determinar el dominio y el ámbito de la ontología.
Es conveniente comenzar el desarrollo de una ontología definiendo su dominio y
ámbito. Esto es, responder a varias cuestiones básicas:
¿Cuál es el dominio que cubrirá la ontología?
¿Para qué se va a usar?
¿A qué tipos de preguntas de información en la ontología se debería responder?
¿Quién usará y mantendrá la ontología?
…
En este caso se creará una ontología sobre personajes del Señor de los Anillos
que se usará como ejemplo y ofrecerá relaciones entre ellos.
Paso 2. Considere reutilizar ontologías existentes
Siempre suele ser conveniente pensar en lo que han hecho otras personas y
comprobar si se puede reutilizar extendiendo las fuentes y refinándolas para que
se adecuen a la tarea. Por ejemplo, utilizar ontologías como la Dublin core que
puede servir para clasificar información de películas, libros, etc., o lafoaf (Friend
of a friend) que puede servir para identificar personas a lo largo de internet.
Así en la cabecera de la ontología si se van a emplear los conceptos definidos
dentro de la ontología foaf, se tendría que declarar así:
<rdf:RDF
xmlns="http://www.servidor.de.ejemplo.es/ontologia_de_prueba.owl#"
xml:base="http://www.servidor.de.ejemplo.es/ontologia_de_prueba.owl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:foaf=“http://xmlns.com/foaf/0.1/”>
Luego para llamar a los conceptos, simplemente se utilizaría foaf:Person o
cualquier otro de ellos y se le daría el valor, al igual que se utiliza owl:Class para
definir una clase o rdfs:subClassOf para decir que una clase depende de otra.
Estas líneas lo que están definiendo es un namespace, es decir, un conjunto de
nombres en el cuál todos son únicos. Las dos primeras líneas de esa declaración
indican el espacio de nombres asociado a esa ontología. La primera lo declara
como el espacio de nombres por defecto, diciendo que los nombres cualificados
que no tengan ningún prefijo se refieren a la ontología actual. La segunda
identifica la URI base del documento. El resto de declaraciones indica que
aquellos nombres cualificados que empiecen por rdfs:, owl:, rdf: o fofa: se
referirán a conceptos definidos en esas ontologías.
Paso 3. Enumere los términos importantes en la ontología
Es útil escribir una lista de términos que se quieren explicar al usuario o de los
que se quieren hacer afirmaciones.
En la ontología que se está creando se definirán términos como Elfo, Ent, Enano,
Hobbit, Orco, Humano, Anillo, …
Paso4. Defina las clases y la jerarquía de clases
Hay varias opciones a la hora de desarrollar una jerarquía de clases:
Comenzar por los conceptos más generales e ir especializándolos.
Comenzar por los conceptos específicos e ir agrupándolos
en clases más generales.
una mezcla de ambas, definir los conceptos generales más
importantes y luego ir generalizándolos y especializándolos.
Por ejemplo, de la clase ser de esta ontología (clase general), tendremos las
especializaciones de orco, elfo, hobbit, ent, etc.
Paso 5. Defina las propiedades de las clases
Una vez que se tienen definidas algunas de las clases, se debe describir la
estructura interna de los conceptos.
En general, hay varios tipos de propiedades de objeto que pueden convertirse en
propiedades en una ontología:
“intrínsecas” como el sabor de un vino,
“extrínsecas” como el nombre de un vino,
relaciones con otros individuos, estas son las relaciones
entre instancias de clases y otros ítems.
En la ontología que se está definiendo se pueden considerar como propiedades
lleva, es_amigo_de, …
Paso 6. Defina los valores que pueden tomar las propiedades de las clases
Las propiedades pueden tener diferentes facetas describiendo el tipo de valor,
valores permitidos, el número de valores (cardinalidad), y otras características de
los valores que pueden tomar. Por ejemplo, el valor de una propiedad de
nombre (como por ejemplo “el nombre de un vino”) es una cadena de
caracteres.
Tipos de valores
Entre otros pueden ser (lo más comunes):
String: Es el tipo de valor más sencillo que se utiliza en
propiedades como un nombre: El valor es una cadena de
texto.
Number (a veces tipos más específicos como Float e Integer): describe propiedades con valores numéricos.
Boolean propiedades que simplemente son si o no,
verdadero/falso.
Enumeración: Lista de valores específicos permitidos para
la propiedad.
Tipo instancia: Permiten la definición de relaciones entre instancias de las clases. También deben definir una lista de clases permitidas de las que las instancias pueden
provenir.
Dominio y Rango de la propiedad
Las clases permitidas de tipo instancia son llamadas Rango de una
propiedad. Las clases a las que la propiedad está unida o las clases que la
propiedad describe se llaman dominio de la propiedad.
Paso 7. Cree instancias de las clases
El último paso es crear instancias individuales de las clases en la jerarquía. La
definición de una instancia individual de una clase requiere:
1. Elegir una clase.
2. Crear una instancia individual de la clase
3. Darle los valores apropiados a las propiedades.
A continuación, se mostrará el ejemplo de ontología que se ha propuesto
tomando como base el mundo del Señor de los Anillos. No hay relaciones
complicadas ni tampoco se expresa toda la potencia que las ontologías pueden
tener, pero a modo de ejemplo sirve. Esto viene escrito en notación XML, se
debería guardar en un archivo .owl para que fuera reconocible. Llamaremos a
esta ontología LordOfTheRings.owl y se podría acceder a ella en
http://www.servidor.de.ejemplo.es/.
<?xml version="1.0"?> <!DOCTYPE rdf:RDF [ <!ENTITY owl "http://www.w3.org/2002/07/owl#" > <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" > <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#"
> <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-
ns#" > ]>
<!-- Declaración de espacios de nombres cualificados --> <rdf:RDF xmlns="http://www.servidor.de.ejemplo.es/LordOfTheRing
s.owl#" xml:base="http://www.servidor.de.ejemplo.es/LordOfTheRi
ngs.owl" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:owl="http://www.w3.org/2002/07/owl#" >
<!-- A continuación se define la ontología --> <!-- Se hacen una serie de afirmaciones explicándola --> <owl:Ontology rdf:about=""> <!-- Se le hace un comentario sobre ella explicándola
--> <rdfs:comment rdf:datatype="&xsd;string"> Ejemplo sencillo de ontología sobre personajes
del Señor de los Anillos. </rdfs:comment> </owl:Ontology>
<-- //////////// // Clases // //////////// --> <!-- A continuación se pasa a definir las clases de la
ontología --> <!-- La declaración de una clase se hace mediante la
sentencia --> <!-- owl:Class y su nombre se da con rdf:about =
#NombreClase --> <owl:Class rdf:about="#Cosa"> <rdfs:label>Cosa</rdfs:label> <rdfs:comment rdf:datatype="&xsd;string"> Clase superior de la ontología. Es buena cosa
que las clases de la ontología no dependan de la propia clase owl:Thing sino de
una clase que depende de ella. </rdfs:comment> </owl:Class> <owl:Class rdf:about="#Objeto"> <rdfs:label>Objeto</rdfs:label> <rdfs:comment rdf:datatype="&xsd;string"> Clase superior de las que describen las cosas
inanimadas. </rdfs:comment> <rdfs:subClassOf> <owl:Class rdf:about="#Cosa"/> </rdfs:subClassOf> </owl:Class> <!-- Con rdfs:subClassOf, lo que se hace es definir una
jerarquía --> <!-- de clases, después en el siguiente tag se dirá de que
clase --> <!-- hereda, en este caso es de la clase Objeto --> <owl:Class rdf:about="#Anillo"> <rdfs:label>Anillo</rdfs:label> <rdfs:subClassOf>
<owl:Class rdf:about="#Objeto"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Ser"> <rdfs:label>Ser</rdfs:label> <rdfs:comment rdf:datatype="&xsd;string"> Clase superior de las que describen las cosas
animadas, con vida. </rdfs:comment> <rdfs:subClassOf> <owl:Class rdf:about="#Cosa"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Elfo"> <rdfs:label>Elfo</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Enano"> <rdfs:label>Enano</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Humano"> <rdfs:label>Humano</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class>
<owl:Class rdf:about="#Hobbit"> <rdfs:label>Hobbit</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Orco"> <rdfs:label>Orco</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class>
<owl:Class rdf:about="#Mago"> <rdfs:label>Mago</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Ent"> <rdfs:label>Ent</rdfs:label>
<rdfs:subClassOf> <owl:Class rdf:about="#Ser"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Dunedain"> <rdfs:label>Dunedain</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Humano"/> </rdfs:subClassOf> </owl:Class> <owl:Class rdf:about="#Rohirrim"> <rdfs:label>Rohirrim</rdfs:label> <rdfs:subClassOf> <owl:Class rdf:about="#Humano"/> </rdfs:subClassOf> </owl:Class>
<!-- A continuación se creará una clase más compleja --> <!-- Será Portador del Anillo, y tendrá que heredar de ser
y --> <!-- además deberá tener la propiedad lleva --> <owl:Class rdf:about="#PortadorAnillo"> <rdfs:label>Portador del anillo</rdfs:label> <owl:intersectionOf rdf:parseType="Collection"> <owl:Class rdf:about="#Ser" /> <owl:Restriction> <owl:onProperty rdf:resource="#lleva" /> <owl:hasValue rdf:resource="#ElUnico" /> </owl:Restriction> </owl:intersectionOf> </owl:Class>
<!-- /////////////////////////// // Propiedades de objeto // /////////////////////////// -->
<!-- A continuación se definen las propiedades que pueden
tener --> <!-- las clases -->
<owl:ObjectProperty rdf:about="#es_amigo_de"> <!-- Con <rdf:type rdf:resource="&owl;SymmetricProperty"
/> se --> <!-- dice que la propiedad es simétrica, es decir, que si
A es --> <!-- amigo de B, B es amigo de A --> <rdf:type rdf:resource="&owl;SymmetricProperty" /> <!-- A continuación se define el rango y el dominio de la
--> <!-- propiedad --> <rdfs:domain> <owl:Class rdf:about="#Ser"/> </rdfs:domain> <rdfs:range> <owl:Class rdf:about="#Ser"/>
</rdfs:range> </owl:ObjectProperty> <owl:ObjectProperty rdf:about="#es_enemigo_de"> <rdf:type rdf:resource="&owl;SymmetricProperty" /> <rdfs:domain> <owl:Class rdf:about="#Ser"/> </rdfs:domain> <rdfs:range> <owl:Class rdf:about="#Ser"/> </rdfs:range> </owl:ObjectProperty> <owl:ObjectProperty rdf:about="#lleva"> <!-- Con la siguiente definición se especifica que son
inversas --> <!-- es decir, si A lleva a B, es porque B es llevado por
A --> <owl:inverseOf> <owl:ObjectProperty
rdf:about="#es_llevado_por"/> </owl:inverseOf> <rdfs:domain> <owl:Class rdf:about="#Ser"/> </rdfs:domain> <rdfs:range> <owl:Class rdf:about="#Objeto"/> </rdfs:range> </owl:ObjectProperty> <owl:ObjectProperty rdf:about="#es_llevado_por"> <owl:inverseOf> <owl:ObjectProperty rdf:about="#lleva"/> </owl:inverseOf> <rdfs:domain> <owl:Class rdf:about="#Objeto"/> </rdfs:domain> <rdfs:range> <owl:Class rdf:about="#Ser"/> </rdfs:range> </owl:ObjectProperty>
<!-- //////////////// // Instancias // //////////////// --> <!-- A continuación se definen las instancias de las
clases --> <Anillo rdf:about="#ElUnico"> <es_llevado_por rdfs:resource=#Frodo/> </Anillo> <Hobbit rdf:about="#Frodo"> <lleva rdfs:resource=#ElUnico/> <es_amigo_de rdfs:resource="#Trancos"/> <owl:sameAs rdf:resource="#FrodoPortador"/> <!-- Con esto se dice que las dos instancias son la misma
--> </Hobbit> <PortadorAnillo rdf:about="#FrodoPortador"> <es_amigo_de rdfs:resource="#Trancos"/> <owl:sameAs rdf:resource="#Frodo"/> <!-- Con esto se dice que las dos instancias son la misma
--> </PortadorAnillo>
<Dunedain rdf:about="#Aragorn"> <es_amigo_de rdfs:resource=#Frodo/> <es_enemigo_de rdfs:resource=#Saruman/> <owl:sameAs rdf:resource="#Trancos"/> <!-- Con esto se dice que las dos instancias son
la misma --> </Dunedain> <Dunedain rdf:about="#Trancos"> <es_amigo_de rdfs:resource=#Frodo/> <es_enemigo_de rdfs:resource=#Saruman/> <owl:sameAs rdf:resource="#Aragorn"/> </Dunedain> <Mago rdf:about="#Saruman"> <es_enemigo_de rdfs:resource=#Trancos/> </Mago>
<!-- ///////////// // Axiomas // ///////////// -->
<!-- Esta es la declaración de axiomas, define relaciones
entre --> <!-- clases. En owl, todas las clases están solapadas
salvo que --> <!-- se diga lo contrario con <owl:disjointWith> -->
<owl:Class rdf:about="#Objeto"> <owl:disjointWith> <owl:Class rdf:about="#Ser"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Ser"> <owl:disjointWith> <owl:Class rdf:about="#Objeto"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Elfo"> <owl:disjointWith> <owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith>
<owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class> <owl:Class rdf:about="#Enano"> <owl:disjointWith> <owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class> <owl:Class rdf:about="#Humano"> <owl:disjointWith> <owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Hobbit"> <owl:disjointWith> <owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith>
<owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Orco"> <owl:disjointWith> <owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Mago"> <owl:disjointWith> <owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Ent"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Ent"> <owl:disjointWith>
<owl:Class rdf:about="#Elfo"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Enano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Humano"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Hobbit"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Orco"/> </owl:disjointWith> <owl:disjointWith> <owl:Class rdf:about="#Mago"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Dunedain"> <owl:disjointWith> <owl:Class rdf:about="#Rohirrim"/> </owl:disjointWith> </owl:Class>
<owl:Class rdf:about="#Rohirrim"> <owl:disjointWith> <owl:Class rdf:about="#Dunedain"/> </owl:disjointWith> </owl:Class> </rdf:RDF>
ENLACES DE INTERÉS:
ONTOLOGÍAS
http://www.ksl.stanford.edu/people/dlm/papers/ontology101/ontology101-noy-mcguinness.html
http://es.wikipedia.org/wiki/Ontolog%C3%ADa_(Inform%C3%A1tica)
http://www.wshoy.sidar.org/index.php?2005/12/09/30-ontologias-que-son-y-para-que-sirven
http://www.hipertexto.info/documentos/ontologias.htm
http://elies.rediris.es/elies18/index.html
http://www.ccs.neu.edu/course/isu300/readings/ontology.pdf
http://ksl.stanford.edu/people/dlm/papers/ontology-tutorial-noy-mcguinness.pdf
http://www.co-
ode.org/resources/tutorials/ProtegeOWLTutorial.pdf
http://www.cs.man.ac.uk/~horrocks/ISWC2003/Tutorial/
http://semanticweb.org/
http://www.w3.org/TR/owl-guide/
WEB SEMÁNTICA
http://es.wikipedia.org/wiki/Web_sem%C3%A1ntica
http://www.w3c.es/Divulgacion/Guiasbreves/WebSemanti
ca
http://www.hipertexto.info/documentos/web_semantica.htm
http://www.informandote.com/jornadasIngWEB/articulos/jiw02.pdf
http://www.w3.org/2001/sw/
http://www.w3.org/2008/Talks/1009-bratt-W3C-SemTech/Overview.html
http://swoogle.umbc.edu/
Categorías
CES OpenSouce/Java
Tema Varios
Autor Blanca Cubas Cruz
Mes Diciembre
Año 2008
Boletín 12
Título Automatización copias subversión
Texto Automatización de copias incrementales de repositorios
Subversión.
En mi anterior articulo Copias de seguridad de un repositorio Subversión,
explicamos como a través de los comandos que distribuye el
propio Subversión(SVN), podíamos realizar copias de seguridad con cierto grado
de control y nivel de personalización; Podíamos hacer copias completas, copias
de una revisión en concreto, copias de rangos de revisiones, copias incrementales
etc.
Concluimos que ese nivel de control y personalización añadía también cierta
complejidad para agruparlos y automatizar las copias con una entrada en el
“crontab” por ejemplo.
Sin embargo, lejos de amedrentarnos aceptamos el desafío de plantear un seudo
script que aunase la combinación de comandos de SVN y de esta manera, dicho
script pudiese programarse en el cron.
Vale, pues ha llegado el día de ser consecuente con las promesas hechas, (“ Alea
iacta est”).
Para ir empezando, vamos a ir planteando una posible solución.
Humm .. ¿Que queremos? ¡¡Ya esta!!, queremos una copia completa de los
repositorios el Lunes, y copias incrementales asta el viernes inclusive,¡!Ahh¡¡
entonces;
El lunes copia completa, “svnadmin dump [ repositorio ] > [
filename ]”
El martes, obtenemos la ultima revisión,
svnlook youngest [ repositorio ] > [ filename ] svnadmin dump [ repositorio ] –r x:y –incremental > [
filename ] así sucesivamente asta el viernes inclusive.
¡¡Ja!! ¿dudas?, yo si, tengo alguna, me tengo que guardar de alguna manera la ultima revisión incluida en cada copia ya sean
completas o parciales; me tengo que plantear algún criterio para nombrar los ficheros de las copias y que reflejen cierta
secuencialidad,
por ejemplo; file01, file02, file03, file14 … ¿otra vez?, en fin.
Después debería de borrar todos los ficheros para poder volver a
comenzar el ciclo…
Vale, creo que ya tenemos un plan y suficientes dudas para
comenzar a decidir cosas.
Como nombre de ficheros, os propongo lo siguiente.
#echo
$FECHA‖_‖$HOSTNAME‖_‖$REPOSITORIO‖_‖$TYPOBACKUP‖.dump$R1:$R2 20090108_KIRNIS_PruebaRepo01_full.dump0:10 #
Donde
FECHA=` date +%Y%m%d` HOSTNAME, es una variable de entorno shell. REPOSITORIO=”PruebaRepo01” TYPOBACKUP=”full” R1=0 R2=10
En el nombre, le estamos indicando la fecha en que se realizo, el host desde donde se realiza, el tipo de backup, y las revisiones; Tenemos mucho.
Por ejemplo
#pwd /opt1/backups #ls -t *PruebaRepo01* 20090108_KIRNIS_PruebaRepo01_inc.dump17:19 20090107_KIRNIS_PruebaRepo01_inc.dump11:16 20090105_KIRNIS_PruebaRepo01_full.dump0:10 # # FILE_BACKUPS=(`ls -tc *PruebaRepo01*`) # echo ${FILE_BACKUPS[*]} 20090108_KIRNIS_PruebaRepo01_inc.dump17:19 20090107_KIRNIS_PruebaRepo01_inc.dump11:16 20090105_KIRNIS_PruebaRepo01_full.dump0:10 # # echo ${FILE_BACKUPS[0]} 20090109_KIRNIS_PruebaRepo01_inc.dump17:19 # A ver, me explico, estamos colocados en el directorio donde se generan los
ficheros de backups, con el primer listado os quería mostrar que podemos
obtener la lista de ficheros del repositorio que nos interesa ordenados por la
fecha de creación mas actual; En la siguiente instrucción, “FILE_BACKUPS=(`ls -tc
*PruebaRepo01*`)”, estoy guardando la lista por columnas y ordenada por fecha
de creación mas actual en una array de shell.
“echo $,FILE_BACKUPS**+-”, nos muestra todo el contenido del array.
“echo $,FILE_BAKUPS*0+-, nos muestra el primer elemento del array, que en
nuestro caso es el ultimo fichero de backup generado o mas actual;
Asta ahí quería llegar yo, tenemos el fichero de backup más actual y gracias a su
nombre tenemos las revisiones que incluye. En este ejemplo, el fichero de
backup, corresponde al repositorio “PruebaRepo01”, es una copia incremental y
abarca desde la revisión 17 a la 19.
Si tuviéramos que hacer una nueva copia incremental, obtendríamos la última
revisión incluida en el fichero de backup más actual y luego preguntaríamos al
repositorio cual es la última revisión.
#REPOSITORIES_DIR=‖/opt1/Repositories‖ #REPOSITORY=‖PruebaRepo01‖ #FILE_BACKUPS=(`ls -tc *$REPOSITORY*`) #FILE_BACKUP=‖${FILE_BACKUPS[0]}‖
#echo ${FILE_BACKUP#*‖:‖} 19 #R1=`${FILE_BACKUP#*‖:‖} #R1=` exp. $R1 + 1 ` #echo $R1 20 #svnlook youngest $REPOSITORIES_DIR‖/‖$REPOSITORY 23 #R2=` svnlook youngest $REPOSITORIES_DIR‖/‖$REPOSITORY` #echo $R2 23 #FECHA=`date +%Y%m%d` #TYPEBACKUP=‖inc‖ #NEW_BACKUP_FILE=$FECHA‖_‖$HOSTNAME‖_‖$REPOSITORY‖_‖$TYPEBAC
KUP‖.dump‖$R1‖:‖$R2 # #snvdump $REPOSITORIES_DIR‖/‖$REPOSITORY –r $R1:$R2 >
$NEW_BACKUP_FILE * dumped revision 20 * dumped revision 21 * dumped revision 22 * dumped revision 23
#ls –t *$REPOSITORY* 20090109_KIRNIS_PruebaRepo01_inc.dump20:23 20090108_KIRNIS_PruebaRepo01_inc.dump17:19 20090107_KIRNIS_PruebaRepo01_inc.dump11:16 20090105_KIRNIS_PruebaRepo01_full.dump0:10 # La expresión $,FILE_BACKUP#*”:”-, es una expresión para obtener substrings de
“shell”, esta en concreto nos devuelve todos los caracteres a la derecha de el
símbolo “:”, que en este caso corresponde al valor numérico que representa a la
ultima revisión incluida en el fichero de backup correspondiente a ese nombre de
fichero, en este caso, si el nombre del fichero es
20090108_KIRNIS_PruebaRepo01_inc.dump17:19, la expresión devuelve 19,
ósea la revisión 19.
En la siguiente expresión “R1=` exp. $R1 + 1 `” incrementamos el valor de la
ultima revisión en 1 para que el la nueva copia incremental incluya solo
exclusivamente las nuevas revisiones.
Vale, esta es la secuencia de comandos para generar el nuevo fichero de backup
incremental en una Shell; esto es lo que deberíamos basar nuestro script a partir
de aquí podemos añadir lo que queramos.
El script, el script esta organizado así :
Zona de definición de variables, donde tenemos apartados dedicados a las
variables que intervienen en el script:
La generales como el, PATH, FECHA, HOME etc.
Variables que definen los directorios y tipos de backup;
Variables relacionadas con los repositorios de SVN,
directorios, repositorios etc.
La siguiente zona corresponde a la zona donde definimos las funciones del que
usamos en el script:
getParametros; recoge los parámetros que se pasan al script.
checkRepositorio; comprueba si el repositorio existe o es correcto
checkRevision; Comprueba si la revisión es correcta.
getUltimaRevisionBackup; Obtiene la última revisión indicada en el nombre del fichero de backup.
doDump(); Método que gestiona las llamadas al comando “svnadmin dump” según sean copias completas o
incrementales.
Y finalmente la zona “Cuerpo”, esta zona recorre la lista de repositorios y va
haciendo llamadas a doDump en función de si el tipo de copia pasado como
parámetro al script es de tipo completo o incremental.
Al final también hacemos una especie de control de errores consideramos que si
aunque solo una sola copia parcial de uno de los repositorios ha sido errónea la
salida de la ejecución de todo el script debe ser errónea.
#!/bin/sh
#### backup_svnDump.sh #####
## Actualizamos el acceso a las carpetas de ejecutables de este
script. PATH=/bin:/sbin:/usr/bin:/usr/sbin
## Nos guardamos la fecha en la que se lanza el backup. FECHA=`date +%Y%m%d`
## Establecemos el directorio home. export HOME="/opt1/home/backup"
## Directorios para el backup BACKUP_BIN_DIR=$HOME"/bin" BACKUP_DIR=$HOME"/backup" BACKUP_TYPE="" PATRON=".dump"
## Variables necesarias para la gestión de copias del SVN. REPOSITORIO_LIST=( "PruebaRepo01" "PruebaRepo02" "PruebaRepo03‖ ) REPOSITORIES_DIR="/opt1/Repositorio" REPOSITORY="" REVISION01="" REVISION02="" LAST_REVISION="" FILENAME=$BACKUP_DIR"/"
## Funciones
## getParametros; recogemos los parámetros que se pasan como getParametros() { for ARGUMENTO in $* do PARAMETRO=‖${ARGUMENTO%=*} VALOR=‖${ARGUMENTO#*=}‖ Case ―$PARAMETRO‖ in ―--repositories_dir‖) REPOSITORIES_DIR=$VALOR
;; ―—-backup_type‖) BACKUP_TYPE=$VALOR ;; Esac Done }
## checkRepositorio; comprueba si el repositorio existe o es
correcto. checkRepositorio () { REPOSITORIO="" REPOSITORIO=$1
if [ -s $REPOSITORIO ] then svnlook author $REPOSITORIO if [ $? = 1 ] then return 1 fi return 0 else return 1 fi }
## checkRevision; Comprueba si la revisión es correcta. checkRevision () { REVISION=‖‖ REVISION=$1 shift REPOSITORIO=$1 checkRepositorio $REPOSITORIO if [ $? = 1 ] then return 1 fi svnlook info -r $REVISION $REPOSITORIO if [ $? = 1 ] then return 1 fi }
## getUltimaRevisionBackup; Obtiene la ultima revisión indicada en
el nombre del fichero de backup. getUltimaRevisionBackup() { ## recogemos los parametros de entrada de las funciones. FILENAME=$1 Shift REPOSITORIO=$1
SEPARADORREVISION=‖:‖ REVISION=${FILENAME#*$SEPARADORREVISION}
CheckRevision $REVISION $REPOSITORIO if [ $? = 1 ] then return 1 fi R2=$REVISION }
doDump() {
BACKUP_TYPE=$1 shift REPOSITORIO=$1 shift FILENAME=$1 shift REVISION01=$1
checkRepositorio ―$REPOSITORIO‖
if [ $? = 1 ] then return 1
fi
REVISION02=`svnlook youngest ―$REPOSITORIO‖`
if [ "$BACKUP_TYPE" != "full" -a "$BACKUP_TYPE" != "inc" ] then return 1 fi
if [ $BACKUP_TYPE = "inc" ] then checkRevision ―$REVISION01‖ ―$REPOSITORIO‖ if [ $? = 1 ] then return 1 fi
#Si la revisión es mayor o igual que la ultima revisión del
repositorio, o hay algo incorrecto o no ha habido revisiones
nuevas. No hacemos nada. if [ REVISION01 –ge $REVISION02 ] then exit 1 fi
REVISION01=` expr REVISION01 + 1 ` else REVISION01=‖0‖ fi # Actualizamos el nombre del fichero con los números de las
revisiones. $FILENAME=$FILENAME$REVISION01‖:‖$REVISION02
if [ "$BACKUP_TYPE" = "full" ] then svnadmin dump $REPOSITORIO -r $REVISION01 :
$REVISION02 > $FILENAME elif [ "$BACKUP_TYPE" = "inc" ] then svnadmin dump $REPOSITORIO -r $REVISION01 :
$REVISION02 --incremental > $FILENAME
}
## Cuerpo del script ## 1 Obtenemos los parámetros de entrada. ## 2 Recorremos la lista de repositorios. ## 3 Creamos el nombre del fichero de backup, sin especificar el
numero de revisiones. ## 4 En función del tipo de backup full/inc se llama al método
DoDump especificando la revisión inicial o no. ## 5 control de la salida de ejecución de las instrucciones.
getParametros $*
cd $BACKUP_DIR ERROR=0
for REPOSITORY in ${REPOSITORIO_LIST[*]} do
FILENAME_BACKUP=$BACKUP_DIR‖/‖$FECHA‖_‖$HOSTNAME‖_‖$REPOSITO
RY‖_‖$BACKUPTYPE‖.dump‖$
if [ ―$BACKUP_TYPE‖ = ―inc‖ ] then
FILE_BACKUPS=(`ls -tc *$REPOSITORY*`) REVISION02=`svnlook youngest $REPOSITORIO` LAST_FILE_BACKUP=‖${FILE_BACKUPS[0]}‖ REVISION01=‖‖ getUltimaRevisionBackup $LAST_FILE_BACKUP
$REPOSITORIO doDump $BACKUPTYPE $REPOSITORIO $FILENAME_BACKUP
$REVISION01 else
doDump ―$BACKUPTYPE‖ ―$REPOSITORIO‖
―$FILENAME_BACKUP‖ fi
unset REVISION01 unset REVISION02 unset FILENAME_BACKUP
if [ $? = 1 ] then
ERROR=1 fi
done
if [ $ERROR = 1 ] then exit 1 fi
exit 0 Para finalizar; Esto es un “pseudo” script, lo que quiero decir es
que debéis tomarlo como un script de referencia, no me puedo asegurar que lo copiéis tal cual, lo ejecutéis y no os de errores;
Sin embargo la idea y la organización del mismo si que la he
probado y creo que os puede ser útil o por lo menos interesante.
Este script, por ejemplo lo podríamos copiar en una carpeta
relacionada con el ejemplo descrito: “/opt1/backup/bin”.
Después de darle los permisos pertinentes podríamos llamar al comando de las siguientes maneras.
Para las copias completas.
backup_svnDump.sh --repositories_dir=‖/opt1/Repositories‖ –
backup_type=‖full‖ Para las copias incrementales.
backup_svnDump.sh --repositories_dir=‖/opt1/Repositories‖ –
backup_type=‖inc‖ Automatización.
Ahora solo nos falta incluirlo en un fichero cron, con el comando “crontab –e” y dejar algo parecido a esto:
SHELL=/bin/bash PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt1/home/backup/bin 00 22 * * 1 backup_svnDump.sh --
repositories_dir=‖/opt1/Repositories‖ –backup_type=‖full‖ 00 22 * * 2-5 backup_svnDump.sh --
repositories_dir=‖/opt1/Repositories‖ –backup_type=‖inc‖ 00 22 * * 6 /opt1/home/cesbackup/bin/delete_files.sh --
backup_dir="/opt1/home/cesbackup" El lunes se llama al script para hacer una copia completa.
De martes a viernes, se llama al script para hacer una copia
incremental.
El Sábado se llama a método que borra los ficheros de la semana para reiniciar el ciclo de copias la semana siguiente.
Referencias:
Linux Shell Scripting with Bash, Sams publising, isbn: 0-672-
32642-6.
Copias de seguridad de un repositorio subversion.
Categorías
CES OpenSouce/Java
Tema Varios
Autor Kike Muro Arbizu
Mes Diciembre
Año 2008
Boletín 12
Título Collection: Extensión para Mediawiki
Texto Introducción
Mediawiki es un motor para wikis bajo licencia GPL y programado
en PHP, además también hace uso de MySQL sobre Apache.
Una de las principales ventajas de Mediawiki es el soporte de
extensiones, que permiten añadir funcionalidades que no vienen
dentro de Mediawiki o integrarlo con otros sistemas. Estas
extensiones dan la oportunidad al administrador y a los usuarios
finales de adaptar la wiki a sus necesidades específicas.
Las extensiones son compilaciones de código PHP que añaden o
mejoran las funcionalidades del núcleo principal de la Mediawiki.
Dependiendo del objetivo que se pretenda conseguir se pueden
utilizar distintos tipos de extensiones:
Extender los formatos que utiliza Mediawiki para editar los
artículos: mirar las categorías “Parser function extensions” y “Parser extensions”.
Presentación de informes y añadir nuevas capacidades administrativas, mirar la categoría “Special page
extensions”.
Cambiar el “look and feel” de Mediawiki, mirar las
categorías: “Galery of user styles” y “User interface extensions”.
Mejorar la seguridad a través de mecanismos de autentificación, mirar la categoría “Authentication and
authorization extensions”.
A la hora de buscar una extensión se puede buscar por
categorías o directamente en un listado de todas las extensiones
existentes. Si la extensión que necesitas no está escrita todavía,
la puedes escribir tu mismo y añadirla a la lista de las ya
existentes.
Comprobación de las extensiones instaladas
Solo si se tiene acceso como administrador al servidor se pueden
instalar extensiones para mediawiki, el resto de los usuarios solo
pueden chequear que extensiones están activas en una instancia
de Mediawiki.
Instalando una extensión
Mediawiki esta lista para aceptar extensiones nada más terminar
de instalarla.
En este caso vamos a ver el proceso de instalación de la
extensión Collection sobre Red Hat. Esta extensión nos permite
organizar una selección de páginas en una colección. Con esta
extensión también se puede:
editar y estructurar mediante capítulos
exportar como documento PDF
exportar como documento de texto ODF
exportar como DocBook XML
La extensión Collection ha sido desarrollada bajo la GNU General
Public License por PediaPress GMBH, en colaboración con
Wikimedia Foundation y la Commonwealth of Learning. Esta
extensión se ha testeado con Mediawiki 1.11 y posteriores, con
lo que no se asegura que vaya a funcionar para versiones
anteriores.
Para añadir una extensión se deben seguir los siguientes pasos:
1.- Antes de empezar:
Algunas extensiones de Mediawiki requieren la
instalación de un parche, aunque en nuestro caso no es
necesario. La mayoría de las extensiones ofrecen instrucciones
de instalación (usando comandos de Unix), en las cuales se suele
especificar si es necesaria la instalación de algún parche. Para
introducir estos comandos es necesario tener acceso a la Shell.
2.- Descargar e instalar ExtensionFunctions.php :
Algunas extensiones, especialmente las más nuevas,
necesitan este fichero de ayuda. El
fichero ExtensionFunctions contiene una serie de funciones que
permiten modularizar las extensiones separándolas del núcleo
básico de Mediawiki. La mejor forma de instalar este fichero es
descargando la última versión del repositorio. Una vez
descargado, se copia el fichero ExtensionFunctions.php en el
subdirectorio de Mediawiki $IP/extensions/.
Importante: Solo se debe instalar este fichero si se tiene algún
problema sin él, ya que muchas extensiones no lo necesitan.
3.- Descargar la extensión:
Las extensiones generalmente se distribuyen como
paquetes modulares y normalmente, van en el
subdirectorio $IP/extensions/. Se puede encontrar una lista de
todas las extensiones documentadas en la página de
MediaWiki.org en la sección extensión matrix, además en esta
misma página se tiene una lista de todas las extensiones
guardadas en el repositorio, desde donde se puede descargar la
última versión de ellas, el repositorio se encuentra
en svn:trunk/extensions
La versión actual de la extensión que vamos a instalar se obtiene
del repositorio de subversión oficial de Mediawiki, que se
encuentra enhttp://svn.wikimedia.org/svnroot/mediawiki/. Su
instalación se realiza mediante los siguientes comandos:
cd extensions/
svn con
http://svn.mediawiki.org/svnroot/mediawiki/trunk/extensions/Collectio
n
También se puede descargar desde:
http://www.mediawiki.org/wiki/Extension:Collection
4.- Instalar la extensión
Antes de empezar con la instalación y configuración de la
extensión se comprueba si se cumplen los prerrequisitos
necesarios para que funcione. En este caso es necesario tener
instalado PHP con soporte cURL. En este artículo no vamos a
meternos con la instalación de PHP, pero si se quiere más
información se puede consultar el manual de PHP.
También requiere tener htmldoc, gcc y gcc-c++. Primero nos
aseguramos de que esta, por ejemplo gcc-c++:
yum search gcc-cc++
Si no está instalado, se instala:
yum install gcc-cc++
Por otro lado, la generación de ficheros PDF y ZIP se hace a
través de un servidor que puede estar corriendo independiente
de la instalación de la Mediawiki y puede ser compartido por
varias wikis
Si se tiene poco tráfico en la Wiki se puede usar el servidor de
renderización público. Para ello, solo se debe mantener el valor
por defecto de la variable de
configuración $wgCollectionMWServeURL.
Una vez comprobado que se cumplen todos los prerrequisitos se
pasa a la instalación de la extensión propiamente dicha.
Generalmente, se añade al final del fichero LocalSettings.php la
siguiente sentencia:
require_once
"$IP/extensions/extension_name/extension_name.php";
Esta línea fuerza al interprete de PHP a leer el fichero de la
extensión, y así hacerlo accesible a Mediawiki.
En nuestro caso la sentencia quedaría:
require_once("$IP/extensions/Collection/Collection.php");
Después de esto se revisan las demás variables de configuración
del fichero. A continuación se comentan las más relevantes:
$wgCollectionMWServeURL (string)
Se le da la URL del servidor de renderización. Su valor por
defecto es "http://tools.pediapress.com/mw-serve/" que es un
servidor público para Mediawiki con poco tráfico.
Importante: Hay que tener en cuenta que la Mediawiki debe
ser accesible por el servidor, por tanto si se tiene la Mediawiki
detrás de un Firewall no se puede usar el servidor público que
se indica por defecto. En este caso lo que se hace es crear un
servidor de rederización y configurarlo. Para ello instalamos
las librerías mwlib y mwlib.rl. La mwlib es una librería hecha
en Python para parsear los artículos XML de la wiki, y la
mwlib.rl es también una librería hecha en Python que permite
escribir documentos PDF de los artículos de la wiki que han
sido parseados por la librería mwlib.
Para instalar mwlib se requiere tener instalado con
anterioridad: Python, Perl 5, setuptools, PIL (Python Image
Library), g++, odfpy 0.7.0.
Nos aseguramos de que tenemos todos los requisitos
instalados:
yum install g++ perl python python-dev python-setuptools
python-imaging
easy_install odfpy==0.7.0
Y después solo queda instalar mwlib:
easy_install mwlib && rehash
Para poder instalar mwlib.rl debe estar instalada ya la mwlib,
por eso se procede primero a instalar mwlib, y una vez está
instalada se pasa a realizar la instalación de mwlib.rl.
easy_install mwlib.rl
Con esto, ya estaría instalado y configurado el servidor de
renderización.
$wgCollectionFormats
En esta variable tenemos un array en el cual se mapean los
mwlib_writers con los nombres de los formatos de salida. Por
defecto solo esta habilitada la transformación a PDF:
array(
'rl' => 'PDF',
)
Por ejemplo, si se quiere añadir el paso a OpenDocument
Text, se debería añadir una línea más a la variable, quedando
de la siguiente manera:
$wgCollectionFormats = array(
'rl' => 'PDF',
'odf' => 'ODT',
);
En el servidor de renderización público tools.pediapress.com
están disponibles los siguientes writers:
- docbook: DocBook XML
- odf: OpenDocument Text
- rl: PDF
- xhtml: XHTML 1.0 Transitional
Si se está usando otro servidor propio, la lista de writers
disponibles se puede obtener con el siguiente comando de
mwlib:
mw-render --list-writers
Aunque se han comentado estas dos variables, el fichero
LocalSettings.php contiene otras muchas, así que para saber
más acerca de la configuración de estas variables se recomienda
leer el fichero Readme de la extensión.
Por último creamos un directorio llamado Collection en el
directorio extensions de la instalación de Mediawiki:
mkdir /opt2/mediawiki-xxx/extensions/Collection
Copiamos en el directorio Collection que acabamos de crear
estos 5 ficheros: Collection.php, Collection.body.php,
Collection.alias.php, Collection.i18n.php y Version.php.
Estos ficheros los tenemos en el paquete de la extensión que nos
hemos descargado.
También debemos dar permisos:
chown -R apache:apache /opt2/mediawiki-
xxx/extensions/Collection
Con esto ya tenemos instalada la extensión y lista para ser
usada.
Problemas que pueden surgir
Es posible que si se accede a la wiki desde una IP externa nos de
un error al redireccionar el fichero PDF. Para solucionar esto se
pone directamente en la línea de comandos la IP del servidor:
mw-serve -d -i "IP servidor"
El parámetro –d se pone para transformarlo en demonio, de esta
forma se ejecutara en un segundo plano y así poder atender las
peticiones.
Otro problema que nos podemos encontrar es que al cerrar la
ventana en la que se ven los logs nos de un error porque no
encuentra por donde sacarlos. Para solucionar esto añadimos un
parámetro más a la línea de comandos indicándoles la ruta del
fichero donde debe guardar estos logs:
mw-serve -d -i "IP servidor" -l /var/log/mw-serve.log
Nota: Tener en cuenta que los comandos facilitados en este
artículo son válidos para la instalación de la extensión sobre Red
Hat.
ENLACES DE INTERES:
http://www.mediawiki.org/wiki/Manual:LocalSettings.php
http://enciclopedia.us.es/index.php/MediaWiki
http://www.mediawiki.org/wiki/Manual:Extensions
http://en.wikipedia.org/wiki/Special:Version
http://www.wadooa.com/doku.php/wiki_en_las_empresas
http://www.siteground.com/mediawiki.htm
http://code.pediapress.com/wiki/wiki/mwlib-install
http://code.pediapress.com/wiki/wiki/mwlib.rl-install
http://code.pediapress.com/wiki/wiki
http://code.pediapress.com/wiki/wiki/mwlib
Categoría
s
CES OpenSouce/Java
Tema Desarrollo
Autor Elena Gadea Aransay
Mes Diciembre
Año 2008
Boletín 12