viernes, 13 de septiembre de 2013

Gestión de versiones en el GAC

Cuando se desarrolla una librería para ser distribuida públicamente es normal que esta pueda tener actualizaciones de versiones. Bien porque corrige algunos fallos o porque introduce nueva funcionalidad. Sobre el sistema de versionados ya hablé en un post anterior.

En esta ocasión me quiero centrar en las actualizaciones automáticas de estos ensamblados de las librerías en el GAC. Me explico con un ejemplo. Supongamos que hemos publicado una librería (v1.0.0.0) para que pueda ser usada por múltiples aplicaciones y componentes. Sacamos una nueva versión que corrige algunos errores (v1.0.0.1) y que mantiene la compatibilidad con la versión anterior.

Al ir firmadas con un Strong Name, las aplicaciones ignoran la nueva versión por completo, obligando a que estas deban ser recompiladas haciendo referencia a la nueva versión. Estamos dejando en manos de los desarrolladores que usan nuestras librerías la tarea de actualizar sus aplicaciones a la nueva versión. Si el número de aplicaciones es grande o el problema solucionado es grave (supongamos un agujero de seguridad), el escenario no es muy halagüeño.

Lo ideal es que las aplicaciones se adapten automáticamente a las nuevas versiones de ensamblados sin que a priori se necesite la re-compilación de las aplicaciones. Y digo a priori porque deben ser los desarrolladores de las aplicaciones los que establezcan si realmente quieren este comportamiento o no.

Redirección de versiones de ensamblado

.Net nos permite este tipo de políticas a través de su redirección de ensamblados. Esto es, podemos indicar a una aplicación que use versiones más modernas de un ensamblado.

Este tipo de políticas se pueden realizar en 3 niveles:

  • A nivel de aplicación. Cada aplicación decide su política de actualizaciones. Es decir, que versión van a usar en caso de que haya varias versiones del mismo ensamblado, o si realmente no quieren que esta política se lleve a cabo.
  • A nivel de máquina. Este es poco frecuente de usar. Normalmente el administrador de la máquina decide si se debe usar una versión concreta o no de un ensamblado en las aplicaciones instaladas que la usan.
  • A nivel de proveedor. Los proveedores de las librerías definen en el GAC la política de versiones a usar. De manera que cuando deciden que dos versiones son compatibles, las aplicaciones usarán automáticamente la versión más moderna. Aun así, se puede indicar que una aplicación no siga esta política y que continúe usando la versión de ensamblado con la que fue creado.

En los tres niveles, el esquema para definir la política de redirección se realiza de la misma manera a través de XML:

image

PublicherPolicyFile.xml

  • <assemblyBinding> define el bloque de redirección y sólo puede haber uno por fichero de configuración.
  • <dependantAssembly> puedes tener tantos bloques como necesites aunque cada uno sólo debe contener un elemento <assemblyIdentity>.
  • <assemblyIdentity> define el nombre del ensamblado a redireccionar y su publicKeyToken para poder ser identificado sin que haya ninguna ambigüedad.   
  • <bindingRedirect> define la redirección de la versión. en la parte de oldVersion soporta tanto una versión en concreto (como es el caso del ejemplo) como un rango de versiones.

    No es necesario indicar de manera individual cada una de las versiones que queremos redirigir. También podemos indicar rangos de versiones y comodines:

  • image

    Si queremos usar la política de actualizaciones a nivel de aplicación, basta con poner la configuración en el fichero de configuración de la aplicación tal y como se muestra en el código XML anterior.

    En caso de ser nosotros los que distribuyamos las librerías, necesitaremos crear un fichero de políticas de versionado a nivel de proveedor. A continuación se explica cómo hacerlo.

    Crear un fichero de políticas de proveedor

    La creación de este fichero de políticas pasa por 3 sencillos pasos:

    1. Crear el fichero XML con la configuración de la política. Básicamente como se hizo en el ejemplo anterior.
    2. Crear el ensamblado con la configuración de la política.
    3. Añadir el ensamblado con la política de redirección al GAC.

    El paso 1 es básicamente crear el fichero tal y como se define en el XML del ejemplo, donde para el ensamblado ArriviSoft.RedirectingAssembllies.Library se ha definido que todos aquellas aplicaciones que referencien a la versión 1.0.0.0, pueden utilizar la versión 1.0.0.1

    Para crear el ensamblado que contendrá la política de redirección debemos usar la herramienta Assembly Linker (al.exe) que viene con Visual Studio.

    Ejecutamos el siguiente comando:

    al.exe /link:PublisherPolicyFile.xml  /out:Policy.1.0.ArriviSoft.RedirectingAssemblies.MyLibrary.dll  /keyfile:ArriviSoft.snk  /platform:x86

    donde:

    • /link: Indica el fichero xml con la política de redirección de ensamblados.
    • /out: Indica el ensamblado que se va a generar y que contendrá la política de redirección. Atención con el formato de nombre de fichero pues debe ser:
      "Policy.majorVersion.minorVersion.NombreEnsamblado.dll"
    • /keyfile: Fichero con el par de claves que además debe ser el mismo con el que se firme el ensamblado.
    • /platform: Permite definir la plataforma de compilación (x86, amd64, ia64, o MSIL) para afinar mejor.
      Desgraciadamente no podremos generar el mismo ensamblado de políticas si definimos distintas plataformas dado que el nombre del ensamblado resultante sería el mismo. Para solucionarlo bastaría con indicar la plataforma dentro del propio nombre de ensamblado.

    Finalmente, una vez tengamos el ensamblado con la política de redirección, este ya puede ser usado en los equipos clientes. El instalador de nuestra librerías debe registrar en el GAC la nueva versión de ensamblados junto con el ensamblado de políticas usando el comando Global Assembly Cache tool (Gacutil.exe)

    miércoles, 6 de febrero de 2013

    Enseñando licenciamiento de software con .NET a mi abuela

    Mi abuela es una señora muy adelantada a su tiempo y con muchas inquietudes sobre las nuevas tecnologías. No hace mucho le dio por empezar a programar en C# y a veces las dudillas que le surgen me las pregunta.

    El otro día tuvimos una interesante conversación sobre el licenciamiento y me he decidido a intentar transcribir nuestra conversación en una entrada. Puede ser un poco larga pero de seguro que merece la pena.

    - Pues mira nieto, el otro día hice una aplicación para gestionar residencias de ancianos. Parece que tiene buena acogida y que gusta, pero con el esfuerzo que me ha supuesto, no quisiera que se pusieran a copiársela entre las residencias. Que esos son muy listos para aprovecharse de los pobres ancianos. ¿Cómo puedo hacer para evitar que se lo copien mi aplicacioncita de unos a otros?

    - Claro que si abuela. Me imagino lo mal que se tiene que sentir uno al encontrarse sus cosas en el emule, torrents o RapidShares de turno…

    - Niño, ¿de que me hablas? Yo me refería que se lo mandan por mail o los copian en CDs…

    - Claro que si abuela. De hecho .NET tiene modelos que te permiten proteger, o mejor dicho, licenciar tus aplicaciones de manera muy sencilla y flexible. Si quieres te explico cómo se hace.

    - Claro que si nieto. Pero explícamelo sencillito que tu ya sabes que a mi me cuesta un poco con estas edades…

    - Bueno, bueno. A ver abuela. ¿Sabes de lo que hablo si digo atributos de clase?

    - Pues no estoy del todo segura. Anda, recuérdamelo. Y mejor si es con un ejemplillo.

    - Bueno abuela. Los atributos de clase son unos modificadores que se colocan justo antes de definir las clases. Y se colocan entre corchetes. Hay muchos atributos de clase, de hecho, puedes usar varios para una misma clase. Por ejemplo, este atributo sirve para indicar que una clase es serializable.

       1:  [Serializable]
       2:  class Foo
       3:  {
       4:     //...
       5:  }



    Como ya te he comentado, abuela, hay muchos más. Pero el que a nosotros nos interesa para el licenciamiento se llama System.ComponentModel.LicenseProvider.


    - O sea, que las protecciones se hacen usando ese atributo de clase, ¿no?


    - Exactamente abuela. Cada vez que usemos este atributo en una clase, significará que esta estará protegida por una licencia.


    - Vale. Entonces la clase que yo quiera proteger debe quedar más o menos así, ¿no?


       1:  [System.ComponentModel.LicenseProvider]
       2:  public class Foo
       3:  {
       4:     // ...
       5:  }



    Bien, nietecito. Hasta aquí lo entiendo. Aunque me temo que lo que sigue a partir de ahora será más complicado, ¿verdad?


    - Que va abuela. Mira. Verás que sencillo. Cuando una clase viene con ese atributo, significa que va a recibir un objeto licencia. Esta licencia es una instancia de la clase System.ComponentModel.License y es muy sencillita:


       1:  namespace System.ComponentModel
       2:  {
       3:      public abstract class License : IDisposable
       4:      {
       5:          protected License();
       6:          public abstract string LicenseKey { get; }
       7:          public abstract void Dispose();
       8:      }
       9:  }



    Lo interesante de esta clase es que es abstracta, que implementa la interfaz IDisposable y su propiedad LicenseKey es de sólo lectura.


    - Ya veo. Entonces voy a recibir una instancia de "algo" que hereda de System.ComponentModel.License. Porque como dices, es una clase abstracta. Lo que no me queda claro es quién crea esa instancia ni cómo lo hace.


    - Ahora vamos a eso, abuela. El sistema de licenciamiento que tiene .NET funciona de la siguiente manera: La clase que quieres proteger va a recibir un objeto que hereda de License. Este objeto License es generado por una entidad proveedora de licencias a través del gestor de licencias que tiene el framework de .NET.


    Explicado de otra manera. Cuando la clase que quieres licenciar se esta creando, le pide al gestor de licencias (System.ComponentModel.LicenseManager) una licencia. Este entregará dicha licencia haciendo uso del generador o proveedor de licencias (System.ComponentModel.LicenseProvider)


    - O sea,que LicenseManager va a retornar la licencia que a su vez retorne LicenseProvider a la clase a proteger. Aquí hay gato encerrado o hay algo que todavía no me has contado. La clase License es abstracta, asi que me falta una clase License que en cualquier caso, parece que siempre la devuelve LicenseProvider. Con lo cual, no veo que hay de bueno en todo esto.


    - Claro abuela, te falta añadirle toda la lógica del sistema de licenciamiento. El framework de .NET te da la base y tu la completas.


    - Ay madre, que ya me asustas. ¿Cómo es eso de que yo tengo que poner la lógica a todo esto?


    - Que no es para tanto, abuela. Lo que hay que hacer es crearse una clase que permita devolver las licencias que queramos usando la lógica que necesitemos. Y por supuesto, la licencia que esta devuelva podrá ser como queramos siempre y cuando herede de System.ComponentModel.License.


    La clase generadora de licencias se le pasará como argumento al atributo de clase System.ComponentModel.LicenseProvider. De manera que el proveedor pueda invocar a nuestra clase generadora para obtener la licencia correspondiente.


    - Oye. ¿Y es muy difícil construir una clase generadora de licencias de estas? Me vas a tener que enseñar un ejemplo de esto que me estás contando, ¿eh?


    - Bueno, tan difícil como queramos hacerlo nosotros. Microsoft de hecho nos dejó una creada muy sencillita. De hecho es tan sencillita que se aconseja no usarla para nuestros desarrollos. Esta clase es System.LicenseProvider.LicFileLicenseProvider.


    Te enseño un ejemplo de cómo se usaría este generador:


       1:  [System.ComponentModel.LicenseProvider(typeof(System.ComponentModel.LicFileLicenseProvider))]
       2:      public class Foo
       3:      {
       4:          private System.ComponentModel.License _license = null;
       5:   
       6:          public Foo()
       7:          {
       8:              _license = System.ComponentModel.LicenseManager.Validate(this.GetType(), this);
       9:          }
      10:      }



    Concretamente LicFileLicenseProvider funciona de la siguiente manera:


    1. En la carpeta donde se encuentra el ensamblado con la clase a proteger, busca un fichero con extensión .lic y nombre igual que el nombre del namespace con la clase a proteger, un punto y el nombre de la clase a proteger.


    2. El fichero .lic debe tener el siguiente contenido de texto: "Namespace+clase is a licensed component.", donde Namespace + clase es el nombre del namespace de la clase que queremos proteger.


    - Entiendo, entiendo. Pero no veo por qué no quieres que se use ese tipo de proveedor de licencias.


    - ¡Abuela! Cualquiera que supiese que estas usando ese licenciamiento puede crearse manualmente el fichero .lic y… adiós protección.


    - Bueno, bueno. Por cierto, ¿qué pasa si el torpe de turno va y borra o modifica el contenido de ese fichero?


    - En ese caso, la llamada en el constructor de tu clase a Validate() fallará y lanzará una excepción de tipo System.ComponentModel.LicenseException. Si no la capturas, el licenciamiento habrá funcionado, puesto que no se ha creado ninguna instancia de la clase protegida, pero posiblemente el mensaje de error que lance la aplicación no será muy descriptivo para el usuario.


    Las excepciones, cuando se producen, suelen ser bastante lentas, así que si únicamente quieres saber si existe licencia, puedes usar el método IsValid, en lugar de Validate. Este te retornará un booleano indicando si la licencia es valida o no. Pero no te devolverá ninguna licencia.


    - Entonces, realmente no necesito ninguna clase License. Me podría bastar con llamar a IsVaild y así saber si se puede ejecutar o no la aplicación, ¿no?


    - Bueno, abuela, de eso se trata. El modelo es así de flexible. Si no lo necesitas, no lo uses. Pero fíjate lo que se puede llegar a montar. Imagínate que a pesar de no tener una licencia buena, quisieras permitir que la aplicación se ejecutara. Por ejemplo, como una versión de prueba. O que dependiendo de la licencia, se pudieran ejecutar unas u otras opciones de la aplicación.


    - ¡Pues claro! Así si que sería fácil, por ejemplo, vender por separado el modulo de gestión de enfermeros y por otro lado la gestión de las medicinas… ¡Realmente interesante!


    - Exacto abuela. Y en la propiedad License.LicenseKey podrías poner el identificador del cliente.


    Resumiendo. Tenemos 4 actores en esto de la gestión de licencias: La clase que queremos licenciar, el tipo de licencia, el proveedor de licencias y el gestor de licencias.


    - Si, lo voy entendiendo. Pero, ¿cómo podría crearme mi propio proveedor de licencias?


    - No es difícil, abuela. Basta con crearse una clase que herede de LicenseProvider y otra que herede de License:


       1:  using System.ComponentModel;
       2:   
       3:  /// My License Provider class
       4:  class MyLicenseProvider : LicenseProvider
       5:  {
       6:      public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       7:      {
       8:          return new MyLicense();
       9:      }
      10:  }
      11:   
      12:  // My License class
      13:  class MyLicense : License
      14:  {
      15:      public override string LicenseKey
      16:      {
      17:          get { return "Some license key"; }
      18:      }
      19:  }



    Este es un ejemplo muy sencillo. Realmente no estamos haciendo ninguna comprobación de nada. Siempre devuelve una licencia buena.


    - Ya creo que me voy enterando. De todas maneras no estoy muy segura de la propiedad LicenseKey de la licencia. Si es de sólo lectura y toda la lógica para la generación de la licencia reside en el proveedor. Entonces le tendré que pasar el valor del LicenseKey a través del constructor, ¿no? Sino, no lo veo yo. Luego dependiendo de ese LicenseKey la aplicación permitiría o no hacer determinadas acciones. ¿Cierto?


    Más o menos esta sería mi clase Licencia, que recibiría el License Key a través del constructor:


       1:  class MyLicense : License
       2:  {
       3:      private string _licenseKey = null;
       4:   
       5:      public MyLicense(string licenseKey)
       6:      {
       7:          _licenseKey = licenseKey;
       8:      }
       9:   
      10:      public override string LicenseKey
      11:      {
      12:          get { return _licenseKey; }
      13:      }
      14:   
      15:      public override void Dispose()
      16:      {
      17:      }
      18:  }



    Mi clase Proveedora de licencias que, a modo de ejemplo, devuelve una licencia siempre con la misma clave:


       1:  using System.ComponentModel;
       2:   
       3:  class MyLicenseProvider : LicenseProvider
       4:  {
       5:      public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       6:      {
       7:          return new MyLicense("Full license");
       8:      }
       9:  }




    Y por último mi clase protegida. En este caso quiero proteger la llamada a DoSomething chequeando el valor de la clave de licencia:


       1:  using System.ComponentModel;
       2:   
       3:  [LicenseProvider(typeof(MyLicenseProvider))]
       4:  public class Foo
       5:  {
       6:      private License _license = null;
       7:   
       8:      public Foo()
       9:      {
      10:          _license = LicenseManager.Validate(this.GetType(), this);
      11:      }
      12:   
      13:      public void DoSomething()
      14:      {
      15:          if (_license.LicenseKey != "Full license")
      16:          {
      17:              throw new LicenseException(this.GetType(), this, 
      18:                             "You don´t have a valid license to invoke this method.");
      19:          }
      20:   
      21:          // Do something here ...
      22:      }
      23:  }



    - Bueno abuela, claro que lo podrías hacer así. Pero te estarías cargando toda la filosofía del modelo de licencias. Si en cualquier momento quisieras cambiar la forma de proteger tu aplicación, tendrías que modificar en todos los sitios de la clase protegida donde se comprueba el valor de la clave de licencia.


    Realmente a la aplicación, o en tu ejemplo, a la clase protegida, le da absolutamente igual el valor de LicenseKey. El hecho de que se haya podido llamar a la función ya significa que hay una licencia valida pues el constructor ya la ha validado.


    - Ah! Entonces también la lógica de saber si las claves de licencia son buenas o no, de si se puede ejecutar cierta función y demás, se hace también en el proveedor. ¿Cierto?


    - Exactamente. Piensa en tu licencia como un contrato o un permiso de utilización de la clase protegida. Si tiene licencia, funciona, sino, ni siquiera puede existir. Además, si fuera necesario, podría indicar determinadas limitaciones.


    - Comprendo, comprendo. Es como tu carnet de conducir, ¿no? Si tienes licencia, puedes llevar el coche, y si no, no. Pero además, tu carnet también te limita los vehículos que puedes conducir, ¿verdad?


    - Eso es abuela, muy buen ejemplo. Me lo tengo que apuntar para cuando se lo tenga que explicar a otras personas. Jejeje.


    - Entonces el ejemplo que he dicho antes no sirve. Mejor sería poner algo así:


    Mi licencia es capaz de indicar si se está en modo demostración. En este modo habrá operaciones que no se pueden ejecutar:


       1:  class MyLicense : License
       2:  {
       3:      private string _licenseKey = null;
       4:   
       5:      public MyLicense(string licenseKey, bool isDemoVersion)
       6:      {
       7:          _licenseKey = licenseKey;
       8:          this.IsDemoVersion = isDemoVersion;
       9:      }
      10:   
      11:      public override string LicenseKey
      12:      {
      13:          get { return _licenseKey; }
      14:      }
      15:   
      16:      public bool IsDemoVersion { get; private set; }
      17:   
      18:      public override void Dispose()
      19:      {
      20:      }
      21:  }



    Mi clase proveedora de licencias determina si la licencia a generar es de tipo demostración o no. En mi ejemplo, simplemente es de demostración a partir de 2013:


       1:  class MyLicenseProvider : LicenseProvider
       2:  {
       3:      public override MyLicense GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       4:      {
       5:          // Demo version since 2013
       6:          if (DateTime.Now.CompareTo(new DateTime(2013, 1, 1)) > 0)
       7:          {
       8:              return new MyLicense("Full license", true);
       9:          }
      10:          else
      11:          {
      12:              return new MyLicense("Full license", false);
      13:          }
      14:      }
      15:  }



    Por último, la clase que estoy protegiendo ya no tiene que tener ninguna lógica respecto a la licencia. Sólo chequear que no está en modo demostración:


       1:  [LicenseProvider(typeof(MyLicenseProvider))]
       2:  public class Foo
       3:  {
       4:      private MyLicense _license = null;
       5:   
       6:      public Foo()
       7:      {
       8:          _license = LicenseManager.Validate(this.GetType(), this) as MyLicense;
       9:      }
      10:   
      11:      public void DoSomething()
      12:      {
      13:          // Demo version cannot execute this code!!!
      14:          if (_license.IsDemoVersion)
      15:          {
      16:              throw new LicenseException(this.GetType(), this, 
      17:                             "You don´t have a valid license to invoke this method.");
      18:          }
      19:   
      20:          // Do something here ...
      21:      }
      22:  }



    - Mucha mejor pinta, abuela. Tampoco podemos olvidar que la llamada a Validate nos ofrece otros argumentos la mar de interesantes. Vamos a verlos.



    • LicenseContext context. Este objeto nos da el contexto en el cual se va a usar la clase a proteger. Quizás su propiedad más importante sea UsageMode, el cual nos va a permitir saber si estamos ejecutando una aplicación normal de escritorio o bien si estamos usando la clase en modo diseño dentro de Visual Studio. Por ejemplo, para licenciar componentes o controles gráficos para otros desarrolladores.
    • Type type. Nos da el tipo de la clase a licenciar. Así podemos usar el mismo proveedor de licencias para proteger varias clases.
    • object instance. Si no fuese sufieciente saber el tipo, tambien tenemos a nuestra disposición la instancia de la clase a proteger.
    • bool allowExceptions. Nos indica si debemos lanzar una excepción LicenseException en caso de no cumplir con la licencia. Y esto tiene sentido pues acuerdate de la llamada IsValid que no lanza excepciones. Básicamente Validate e IsValid usan la llamada a esta función. Una permite excepciones y la otra no.

    Sin meternos en historias de dar soporte a distintos tipos de clases a proteger ni nada de eso, tu clase proveedora de licencias quedaría mejor así:


       1:  class MyLicenseProvider : LicenseProvider
       2:  {
       3:      public override MyLicense GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions)
       4:      {
       5:          // Full license until 2013
       6:          if (DateTime.Now.CompareTo(new DateTime(2013, 1, 1)) < 0)
       7:          {
       8:              return new MyLicense("Full license", false);
       9:          }
      10:          // Demo version between 2013 - 2014
      11:          else if (DateTime.Now.CompareTo(new DateTime(2014, 1, 1)) < 0)
      12:          {
      13:              return new MyLicense("Demo version", true);
      14:          }
      15:          // No license from 2014
      16:          else
      17:          {
      18:              if (allowExceptions)
      19:              {
      20:                  throw new LicenseException(type, instance, "Invalid license.");
      21:              }
      22:              else
      23:              {
      24:                  return null;
      25:              }
      26:          }
      27:      }



    Y así nuestra clase protegida puede usar IsValid en lugar de Validate sin preocuparse de si se van a producir excepciones.


    Es muy  importante que te quede claro que toda la lógica de la licencia debe quedar dentro de la clase de generadora de licencias. Esta puede hacer todo lo que se te ocurra. Desde leer un fichero de texto como hace LicFileLicenseProvider, hasta conectarse a un servidor remoto en otro continente para obtener la licencia.


    - Pues si nietecito, ya tengo más o menos claro todo esto de las licencias. Ya puedo estar segura de que nadie se va a copiar mi programa de residencias de ancianos.


    - Me alegro de haberte ayudado abuela. De todas maneras, siempre habrá alguien que encuentre la manera de saltarse la licencia. Pero todo dependerá de lo importante que sea lo que tengamos que proteger y el tiempo que le queramos dedicar poniendo trampas y demás dificultades a estos individuos. Recuerda, no hace falta crear la super licencia que protege los sistemas del Pentágono. La mayoría de las personas lo dará por imposible sólo al ver que ya tiene licencia. Y eso es lo importante.

    sábado, 18 de febrero de 2012

    El nuevo logotipo de Windows 8

    Microsoft ha hecho público en su página de The Windows Blog lo que parece que será el diseño del nuevo logotipo de Windows 8.

    Parece que el resultado no ha dejado indiferente al personal y corren ríos de posts comentando el nuevo diseño.

    Logotipo Windows 8

    Han querido infundirle el espíritu de su nueva interfaz gráfica Metro, dotándole de líneas simplistas, planas y monocromáticas.

    A mi desde luego no me gusta. Recuerda mucho al logotipo de Windows 1.0, aunque desde mi punto de vista, no lo mejora.

    Logotipo de Windows 1.0

    Creo que lo han hecho demasiado simple. Han querido condensar demasiado significado en 4 cuadrados con algo de perspectiva. Lo cual me hace recordar una celebre frase:

    Everything should be made as simpler as possible, but not simpler.

    Albert Einstein

    De hecho, una de las responsables de la empresa encargada de los diseños de logos para Windows hizo esta sencilla pregunta: “your name is Windows. Why are you a flag?”

    domingo, 5 de junio de 2011

    Primeras impresiones de Windows 8

    Microsoft ha presentado hace muy pocos días lo que va a ser su siguiente generación de sistemas operativos. Por ahora se llama Windows 8 y se estima que saldrá a lo largo de 2012.
    Está inspirado en la interfaz de usuario de su nuevo sistema operativo para moviles Windows Phone 7 (WP7). Lo que le permite tener más información de numerosas fuentes en una sola pantalla (HUBs).
    El nuevo Windows 8 está preparado tanto para funcionar sobre plataformas clásicas de escritorio Intel de 32 y 64 bits como sobre plataformas ARM. Mejor adaptadas a dispositivos pequeños como tabletas y micro-portátiles. Interesante ver que durante el video, no se nombra a los dispositivos tablets (tabletas), sino a los slates, que viene a significar pizarras. Lo malo es que parece que las aplicaciones clásicas de Windows no serán compatibles con la versión de la plataforma ARM. Lo cual significa que en muchos casos será necesario sacar dos versiones de las aplicaciones. Y esperemos que sea tan facil como cambiar en el IDE de desarrollo una opción de plataforma, porque sino…
    Hay que tener muy claro que el video es una demo. Los que hayan preparado alguna vez una demo sabrán a lo que me refiero. Normalmente lo que se muestra está preparado. Y muchos de los botones sean figurantes, que no tengan funcionalidad, o con una probabilidad alta de fallos. Y no creo que sea una casualidad que hayan sacado esto con tan pocos días de antelación a la presentación de Apple de su Sistema Operativo Lion.
    Tras ver el video, mi primera impresión al ver el video fue el de estar viendo una pantalla de movil con Windows Phone 7 de tamaño gigante. El movimiento por las pantallas es fluido y el contenido de cada cuadro preciso. De esta manera no es necesario abrir toda la aplciación para ver si hay algún cambio en su estado. Personalmente me parece una buena apuesta anti-Apple. Habrá que saber si el gran público la adopta de buena gana.
    En cualquier caso, Windows 8 puede funcionar tambien con el escritorio clásico de Windows. Por las pocas pantallas que aparecen en el video, se parece bastante a Windows 7 con algún retoquito estético.
    Si me ha defraudado un poco el que digan que las aplicaciones “táctiles” estén basadas en HTML5+JavaScript. No puedo entender que Microsoft, teniendo una plataforma de desarrollo como es .NET y Silverlight, no base las aplicaciones de su nueva interfaz en estos lengajes. Aunque es cierto que he leido por ahí (no recuerdo exactamente donde) que los harían compatible.
    En definitiva, sólo hemos visto un video de una demo. Veremos lo que nos deparan los días y otras demos que se hagan sobre el nuevo sistema operativo de Microsoft.

    martes, 15 de febrero de 2011

    YAML. Un lenguaje estructurado pero no de marcado

    YAML, cuyas iniciales dicen YAML Ain't Markage Language, es un lenguaje que me ha sorprendido. Lo descubrí a raíz de un artículo en el que comentaban su uso en la introducción de estructuras de datos por personas que no estaban relacionadas con el desarrollo software.
    Y es que es cierto que estamos muy acostumbrados a usar lenguaje XML o derivados, del que .NET tiene muchas librerias, o incluso JSON para otras plataformas.
    Xml no está pensado realmente para ser leido por una persona. Aunque para ficheros pequeños y con no demasiadas estructuras anidadas, podamos apañarnos bien.

    YAML es un lenguaje estructurado, pero sin etiquetas. Lo cual lo hace extremadamente fácil de leer para una persona. Definido a partir de una serie de reglas muy símples que hace que escribir un documento con este formato sea realmente fácil.

    Las andaduras de este lenguaje comenzaron en Mayo de 2001 por Clark Evans. Y actualmente está en la versión de especificación 1.2. La página de referencia la podeis encontrar aquí. Y que precisamente tiene todo su contenido escrito con estructura YAML.

    Una pequeña lista de sus características son:

    • YAML tiene un formato de lectura sencillo para las personas.
    • YAML es conciso y compacto.
    • YAML es expresivo y extensible.
    • YAML no es un lenguaje de marcado!

    Si esta lista la quisiera expresar con XML necesitaría algo así:

    <ul>
       <li>YAML tiene un formato de lectura sencillo para las personas.</li>
       <li>YAML es conciso y compacto.</li>
       <li>YAML es expresivo y extensible.</li>
       <li>YAML no es un lenguaje de marcado!</li>
    <ul>

    En YAML se definiría de la siguiente manera:

    - YAML tiene un formato de lectura sencillo para las personas.
    - YAML es conciso y compacto.
    - YAML es expresivo y extensible.
    - YAML no es un lenguaje de marcado!

    Otros ejemplos de estructuras que pueden hacerse con YAML:

    Diccionarios:

    ---
    Clave 1: Valor de la clave 1.
    Clave 2: Valor de la clave 2.
    Clave 3: Valor de la clave 3.

    Listas de diccionarios anidados:

    ---
    - Clave 1: Valor de la clave 1.
    - Clave 2:
         Subclave 1: Valor de la Clave 2, Subclave 1.
         Subclave 2: Valor de la Clave 2, Subclave 2.

    Mapas:

    ---
    Seat Leon: {Color: Rojo, Carburante: Diesel, Velocidad Máxima: 185 Km/h, Kilometros: 25000}
    Nissan Micra: {Color: Blanco, Carburante: Hibrido, Velocidad Máxima: 172 Km/h, Kilometros: 12000}
     

    Existen implementaciones de este formato en muchos lenguajes, incluyendo .NET, el cual dispone de una implementación en Codeplex y que acompaña con un minitutorial de Yaml en 5 minutos.

    miércoles, 9 de febrero de 2011

    Versionado de ensamblados de .NET

    Uno de los temas que menos cuidado se le suele prestar en el desarrollo de software es el del versionado de componentes. Y es que gracias a las versiones, podemos saber exactamente cuando se compiló un ensamblado (e incluso saber la etiqueta de la versión de código fuente que la generó) y si hay otros ensamblados que son compatibles.

    En .NET, las versiones se establecen en el fichero AssemblyInfo.cs que se puede encontrar dentro de la carpeta de “Properties” del proyecto:

        1 using System.Reflection;

        2 using System.Runtime.InteropServices;

        3 

        4 // General Information about an assembly is controlled through the following

        5 // set of attributes. Change these attribute values to modify the information

        6 // associated with an assembly.

        7 [assembly: AssemblyTitle("Project Title")]

        8 [assembly: AssemblyDescription("Some description")]

        9 [assembly: AssemblyConfiguration("Debug")]

       10 [assembly: AssemblyCompany("My Company of Software")]

       11 [assembly: AssemblyProduct("Product Name")]

       12 [assembly: AssemblyCopyright("Copyright © My Company, 2011")]

       13 [assembly: AssemblyTrademark("")]

       14 [assembly: AssemblyCulture("")]

       15 

       16 // Setting ComVisible to false makes the types in this assembly not visible

       17 // to COM components.  If you need to access a type in this assembly from

       18 // COM, set the ComVisible attribute to true on that type.

       19 [assembly: ComVisible(false)]

       20 

       21 // The following GUID is for the ID of the typelib if this project is exposed to COM

       22 [assembly: Guid("f446dcb3-68fb-49d9-a057-fe50383ce4c3")]

       23 

       24 // Version information for an assembly consists of the following four values:

       25 //

       26 //      Major Version

       27 //      Minor Version

       28 //      Build Number

       29 //      Revision

       30 //

       31 // You can specify all the values or you can default the Build and Revision Numbers

       32 // by using the '*' as shown below:

       33 // [assembly: AssemblyVersion("1.0.*")]

       34 [assembly: AssemblyVersion("1.0.0.0")]

       35 [assembly: AssemblyFileVersion("1.0.0.0")]

    Las últimas líneas del fichero están relacionadas con la versión del ensamblado y con la versión del fichero.

    AssemblyFileVersion

    Su principal objetivo es el de identificar de manera uniequivoca a un ensamblado. A partir de los valores de la versión podemos saber la versión de código fuente que compiló el ensamblado. Tambien nos permite reconocer si se trata de una versión de “Service Pack” o de “Hotfix”.

    Las versiones de un fichero están basadas en un código de 4 números. El uso de cada uno de los valores depende de cada equipo de desarrollo. Personalmente, les doy el siguiente significado a los valores de versión:

    • Major Version: Versión principal de producto. No suele cambiar a lo largo de todo el ciclo de desarrollo salvo que el desarrollo requiera rehacer todo el código desde cero. Evidentemente no se mantiene ninguna compatibilidad con versiones anteriores.
    • Minor Version: Cambio importante en las interfaces del ensamblado que hace que se rompa la compatibilidad con versiones anteriores. Al igual que la versión principal, este valor se cambia de manera manual.
    • Build Number: Número calculado automáticamente por el proceso de compilación en orden creciente de manera que dos compilaciones seguidas no generen el mismo valor. Existen muchos esquemas de generación de este valor. Quizá el más conocido es el de basado en el número de días transcurridos desde el 1 de Enero de 2000.
    • Revision: Número tambien generado automáticamente por el sistema de compilación. Por ejemplo, si se generan varias versiones el mismo día, la versión de Build no cambia, por lo que está va incrementandose de uno en uno por cada compilación.

    AssemblyVersion

    Los ensamblados de .NET usan esta versión para enlazarse con otros ensamblados. Esto es, cuando por ejemplo el ensamblado A.dll referencia al ensablado B.dll con version “1.0.0.0”, si sustituimos este último ensamblado por la versión “1.0.0.1”, A.dll no será capaz de encontrarlo pues espera encontrar la versión con la que fue compilado. Por este motivo no se debe dejar a la ligera la opción de Visual Studio de autoincrementar la versión del ensamblado en cada compilación.

    El significado de los valores de la versión puede ser:

    • Major Version: Se actualiza de manera manual. Normalmente significa un cambio radical en la estructura del ensamblado. Por ejemplo porque se ha vuelto a reescribir gran parte del código. Normalmente implica rompe la compatibilidad con versiones anteriores.
    • Minor Version: Se han añadido nuevas funcionalidades al ensamblado. Y por tanto, puede suponer cambios en la interfaz pero tratando de mantener la compatibilidad con versiones anteriores. Su modificación es manual.
    • Build Number: Valor calculado automatiamente y que identifica el número de compilación realizado sobre el código fuente.
    • Revision: Normalmente identifican ligeros cambios sobre el código. Por ejemplo solución de hotfixes. Aquellos ensamblados donde únicamente varíe el número de revisión son totalmente intercambiables entre sí.

    AssemblyInformationalVersion

    Este es otro atributo que puede ser usado dentro de AssemblyInfo. Su objetivo es el de exponer una versión del ensamblado a nivel de producto, documentación, instalador, etc. En ningún momento esta información es usada por Windows o por otros ensamblados de .NET.

    Esta versión se identifica con tres valores:

    • Major Release: Normalmente asociada a la versión principal de producto y no suele variar en todo el ciclo de desarrollo del proyecto salvo que se tenga que rehacer todo el contenido del ensamblado.
    • Minor Release: Este número se asocia a incrementos en la funcionalidad del ensamblado.
    • Maintenance Release: Los incrementos en este valor suelen reflejar pequeños cambios normalmente asociados a correción de errores, rendimiento o estabilidad. Los clientes del ensamblado no aprecian un cambio en la funcionalidad importada.

    viernes, 22 de octubre de 2010

    Ver un PC virtual con VMWare Player desde la red

    Hoy ha sido un día un interesante de trabajo para mi compañero Miguel Ángel y para mí. Estamos preparando un sistema de integración continua para nuestro proyecto y nos habíamos decidido por probar la solución de TeamCity, la cual ofrece una versión gratuita. La idea era montar este servicio sobre una unidad virtual en uno de los ordenadores de la red para que el resto del equipo tuviese acceso también.

    Creamos la unidad virtual con VMWare Player v3.1.2 con Windows 7 Profesional e instalamos TeamCity sin complicaciones. La nueva unidad virtual puede ver sin problemas el resto de los ordenadores de la red así que configuramos TeamCity para que se conecte a los repositorios de código fuente. Todo perfecto… hasta aquí.

    El problema

    Si como he dicho antes la unidad virtual podía ver los ordenadores de la red, no era lo mismo al revés. Nadie, ni tan siquiera el host que albergaba a la unidad virtual era capaz de ver al equipo, y por tanto, acceder al servicio.

    Buscamos por Internet suponiendo que este problema sería bastante común y que fácilmente encontraríamos la manera de configurar el sistema para solucionarlo. El caso es que no es así, y que además, la manera de solucionarlo es bastante… “truqui del almendruqui”.

    Buscando la solución

    Lo primero que debemos hacer es configurar la unidad virtual con configuración de red NAT como se muestra en la imagen:

    clip_image002

    Con esto hacemos que la unidad virtual comparta la misma IP que nuestro equipo host. Pero atención, debemos tener en cuenta que cuando instalamos VMware Player, automáticamente te instala un par de adaptadores de red: VMnet1 y VMnet2. Puedes verlo haciendo una llamada a ipconfig desde la consola:

    clip_image003

    Para hacer que la unidad virtual utilice de manera correcta el adaptador el adaptador, en nuestro caso VMnet8, necesitamos usar la herramienta de VMware: Virtual Network Editor.

    Ya te puedes hinchar de buscar que no la vas a encontrar. Parece ser que no viene con la distribución gratuita de VMware Player. ¿O sí?

    Conseguir Virtual Network Monitor

    Por lo visto, a los chicos de VMware se les ha ocurrido una curiosa manera de “darte” esta aplicación, pero como si no existiera. En realidad la tienen oculta dentro del fichero de instalación. Para conseguirla tienes que seguir estos pasos:

    1. Extrae todo el contenido del instalador mediante la llamada:
    c:\> VMware-player-3.1.2.<versión>.exe /e vmware_temp

    2. Dentro de la carpeta vmware_temp encontrarás un fichero network.cab. Descomprimelo con WinRAR o 7zip y dentro encontrarás el ejecutable que buscas: vmnetcfg.exe

    3. Como este ejecutable tiene muchas dependencias con otras librerías, es aconsejable copiarlo a la carpeta donde esté instalado VMware Player.

    4. Listo para configurar.

    Configuración con Virtual Network Monitor

    Una vez arranquemos la herramienta, realizará una búsqueda de todos los adaptadores de redes virtuales que se encuentren en el PC host. Entre ellos encontrará activados VMnet1 y VMnet8. Nosotros nos centramos en VMnet8. El cual configuramos de la siguiente manera:

    clip_image005

    Debemos asegurarnos que también esté configurado como NAT y que la IP y la máscara de subred corresponden con lo mostrado en la configuración que muestra el host con el ipconfig que le hicimos en VMnet8.

    Con esto ya hemos conseguido configurar el adaptador de red que está asociado a nuestra unidad virtual.

    Dado que desde fuera del host, no se tiene acceso a esa subred, y por tanto a la unidad virtual, debemos configurar el NAT para que el host haga de puente entre la red y la unidad virtual. Para ello, asociamos un puerto del host con un puerto de la unidad virtual.

    Sabiendo que TeamCity lo que expone es un servicio web y por tanto se accede usando el navegador, configuramos el puerto 8080 del host para que se correspondiera con el puerto 80 de la unidad virtual:clip_image006

    Es interesante destacar que hay que hacerlo tanto para el protocolo TCP y UDP, sino, no funciona.

    Y listo. Cualquier ordenador de la red que acceda desde el navegador a la dirección 192.168.14.248:8080, realmente está accediendo a la subred privada 172.16.37.128:80

    A continuación la configuración de red de la unidad virtual:

    clip_image008