Planning poker: il metodo Delphi al servizio di Scrum

Il problema

Immaginate di prepararvi per uno Sprint Planning Meeting: l’efficacia della pianificazione dipenderà, tra le altre cose, dalla qualità della stima dell’effort per ogni backlog item. La stima può essere effettuata discutendo insieme sui dettagli dell’attività e arrivando ad un valore condiviso ma questo percorso, apparentemente tranquillo, è pieno di trappole.
Ogni membro del team può avere una percezione differente dell’entità del lavoro necessaria e, nonostante questo, si può affidare al parere di un collega più esperiente, che ha inserito la storia nel product backlog dopo una telefonata col cliente o, semplicemente, non si  esprime perchè non è sufficientemente coinvolto.  Il risultato è che in una discussione “tradizionale” si possono perdere delle informazioni che, seppur presenti nel gruppo, non contribuiscono al processo decisionale.

Esistono dei metodi formali per approcciare il processo decisionale in modo ottimale e questi, aldilà delle valutazioni scientifiche, partono da un concetto ben noto: “due teste sanno più cose di una sola”. In un team ci sono tante teste, vediamo quindi come sfruttarle bene!

Il metodo

Il planning poker è una metodologia per la stima dell’effort necessario per portare a completamento una attività. Ogni partecipante al processo di stima è in possesso di una serie di carte simili a quelle da gioco (di cui il nome) rappresentanti la scala numerica della serie di Fibonacci o una sua approssimazione (ad.es: 0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100). Presentato l’argomento, ognuno sceglie una singola carta con valore adeguato alla propria stima personale e la depone sul tavolo coperta. Non appena tutti hanno fatto la propria scelta, le carte vengono scoperte contemporaneamente. Se i valori sul tavolo sono discordanti, viene avviata una discussione volta a identificare le ragioni delle differenze. Esaurita la discussione viene effettuata una nuova selezione delle carte, analogamente a quanto fatto inizialmente. Il processo viene ripetuto fino a quando non si ha una convergenza su un valore condiviso.

Il planning poker, molto diffuso tra gli utilizzatori di processi agili di sviluppo software, è  una applicazione del metodo Delphi messo a punto dalla RAND Corporation negli anni ’50 ed oggetto di studi e approfondimenti successivi. Il metodo Delphi è caratterizzato da:

  • Anonimità dell’opinione
  • Feedback controllato in un processo iterativo
  • Analisi della risposta statistica del gruppo

L’anonimità dell’opinione è richiesta per limitare l’effetto della presenza di individui dominanti nel processo decisionale. Il loro effetto è tipicamente quello di annullare il contributo di alcuni partecipanti, che adeguano la loro posizione sulla base dell’autorevolezza di qualcun’altro. Ciò riduce l’efficacia del processo: una divergenza iniziale è sempre un contributo alla qualità della decisione finale. Nel planning poker ciò è ottenuto presentando le stime contemporaneamente, a garanzia dell’autonomia della decisione dei partecipanti.

La revisione dei risultati di ogni interazione rappresenta il feedback controllato sul processo decisionale. La divergenza di opinioni può nascere, oltre che dal diverso grado di esperienza dei partecipanti, anche dalla sottovalutazione (o sopravvalutazione) della complessità dell’argomento o da una comprensione differente dello scope dell’attività oggetto di discussione. La discussione avviata alla fine di ogni iterazione permette lo scambio di informazioni tra i partecipanti col risultato di una definizione più precisa dei contorni dell’argomento discusso e una migliore valutazione delle implicazioni nascoste. L’effetto è che ad ogni iterazione si ha una sempre più definita convergenza verso un valore condiviso.

Il metodo Delphi, e quindi anche il planning poker, in virtù dello scambio di informazioni  e del valore attribuito al contributo di ogni partecipante, permette non solo di mantenere la discussione stimolante e interessante per tutti ma anche di rassicurare il gruppo sulla qualità delle decisioni assunte. Infatti, l’uso di una procedura sistematica e formale, attribuisce oggettività ai risultati.

Una nota sulle carte

L’utilizzo della serie numerica di Fibonacci o un suo adattamento non è casuale. E’ una garanzia ulteriore della qualità della stima effettuata: la sequenza numerica ha numeri progressivamente più distanti al crescere del loro valore, il che equivale a dire che più tempo  immagino di impiegare per portare a termine il lavoro, maggiore sarà l’imprecisione della stima. Ciò porta naturalmente alla frammentazione delle storie sul product backlog troppo ad alto livello in un insieme di storie minori che portano comunque un valore tangibile.
Oltre alle carte numeriche possono essere presenti due carte aggiuntive: una col punto interrogativo ed una con una tazza da caffè.
La prima indica che non si è in grado di fare nessuna stima. Se viene usata da uno o più  partecipanti significa che l’argomento di discussione non è ben definito o è troppo vasto: va chiarito o frammentato.
La carta con la tazza di caffè, che credo sia stata aggiunta da Crisp, rappresenta un concetto semplice: pausa! Questa carta è più efficace se, a dispetto delle regole, ci si mette d’accordo per avere la convergenza di opinione!

Se voleste approfondire la conoscenza del metodo Delphi, vi suggerisco The Delphi Method – An Experimental Study of Group Opinion, liberamente scaricabile dal sito della RAND Corporation.  Se invece siete alla ricerca delle carte da planning poker, vi raccomando un mazzo come quello che ho fotografato. Potete acquistarlo da Crisp a soli 6.38 euro+IVA, spedizione inclusa.

Pubblicato in Project management | Contrassegnato , , , , , , , | 2 commenti

Windows Azure Web Role in PHP

Windows Azure non è solo per chi sviluppa soluzioni utilizzando Visual Studio ed i framework .NET. Infatti, è possibile creare ed erogare dei cloud service anche utilizzando Java, Ruby o PHP. In questo post vedremo quanto è semplice usare PHP per muovere i primi passi su Windows Azure.

Per seguire questo tutorial è necessario predisporre  Eclipse per lavorare su Windows Azure. Maggiori dettagli sui prerequisiti e sulla preparazione dell’ambiente di sviluppo sono disponibili nel post Anatomia e sviluppo di una soluzione cloud per Windows Azure. Inoltre, sebbene faccia riferimento all’utilizzo di Visual Studio, è consigliabile dare una lettura ai due post della serie Hello Web Role! per familiarizzare con i web role e gli storage service.

Un po’ di teoria

Windows Azure supporta il modulo FastCGI di Internet Information Server 7.0 consentendo la realizzazione di web role che eseguono applicazioni scritte in linguaggi interpretati o in codice nativo. Per creare un web role che esegue una applicazione FastCGI è necessario innanzitutto verificare che l’impostazione  del flag enableNativeCodeExecution sul file ServiceDefinition.csdef sia true (è il valore di default sugli ultimi SDK):

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="Simple"
xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="WebRole" enableNativeCodeExecution="true">
    <ConfigurationSettings>
    </ConfigurationSettings>
    <InputEndpoints>
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
  </WebRole>
</ServiceDefinition>

Inoltre è necessario includere il file opzionale web.roleConfig sulla radice del progetto. In questo file va specificato il percorso assoluto all’interprete o all’applicazione in codice nativo. Per specificare il percorso assoluto viene utilizzata la variabile d’ambiente %RoleRoot%. Questa ritorna il percorso assoluto alla radice dell’albero di directory in cui è in esecuzione il web role (dove sono presenti i file web.config e web.roleConfig). Un esempio di file web.roleConfig è:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.webServer>
    <fastCgi>
      <application fullPath="%RoleRoot%\approot\php\php.exe"/>
    </fastCgi>
    </system.webServer>
</configuration>

Un handler specifica quale risorsa di IIS gestisce la risposta per un certo tipo di richiesta. Configurando opportunamente l’handler è possibile indicare che sarà l’interprete PHP a gestire le richieste al server che fanno riferimento a file con estensione php. Tutte le richieste che collimano con il filtro path sulla definizione dell’handler verranno girate a php.exe (vedi MSDN). La configurazione degli handler viene effettuata nell’elemento handlers dell’elemento system.webServer sul file web.config, come segue:

<system.webServer>
…
  <handlers>
  …
    <add name="PHP via FastCGI" path="*.php" verb="*"
        modules="FastCgiModule"
        scriptProcessor="%RoleRoot%\approot\php\php-cgi.exe"
        resourceType="Unspecified" />
  </handlers>
</system.webServer>

Affinchè il role funzioni correttamente è necessario includere l’interprete nel progetto. Questo sarà compatibile con Windows Azure solo se è possibile effettuarne il deployment attraverso un semplice xcopy. La posizione dell’interprete deve combaciare con quella specificata con l’attributo fullPath dell’elemento application nel file webRole.config.

Sviluppare il web role con Eclipse

Muoverei primi passi verso lo sviluppo di un web role in PHP con Eclipse è estremamente facile. Vedremo infatti che la procedura guidata per la creazione del progetto permette di selezionare le feature da utilizzare e genera dei file php con degli esempi d’uso molto utili. Apriamo Eclipse, lanciamo la procedura guidata per la creazione di un nuovo progetto Windows Azure PHP Project e inseriamo il nome:

Windows Azure PHP ProjectPHP Azure Project NameAl terzo passo è possibile iniziare la configurazione del web role, a partire dal nome. Il checkbox Enable Windows Azure App Fabric support for this project inserisce nel progetto le classi per l’utilizzo dell’Access Control Service e il Service Bus. Visto il livello introduttivo del tutorial non approfondiremo l’argomento in proposito, ma vi consiglio di dargli un’occhiata. Il tab Data Storage Options permette di abilitare l’utilizzo dei servizi di storage e configurare le credenziali di accesso. Nel nostro caso useremo il development storage. Opzionalmente è possibile configurare anche le credenziali per l’accesso ad un database SQL Azure. Prima del deployment sarà possibile cambiare le impostazioni editando il file ServiceConfiguration.cscfg.

Windows Azure PHP Web RoleIl tab PHP Runtime permette di scegliere tra l’interprete PHP 5.3.4 compreso nel kit di sviluppo o una versione specifica installata sulla macchina.

Windows Azure PHP web role PHP runtimeSu Diagnostics è possibile attivare Windows Azure Diagnostics e specificare lo storage in cui inserire le informazioni diagnostiche, il livello di logging e l’intervallo di trasferimentio dei dati. Su Windows Azure Drives è possibile configurare l’utilizzo di eventuali Drives.

Windows Azure PHP web role diagnosticsCliccando su Finish viene creato il progetto. Le impostazioni di cui abbiamo parlato in precedenza dovrebbero essere già corrette. Dopo aver avviato il Compute Emulator e lo Storage Emulator, clicchiamo sul file index.php e selezioniamo Run/Run As/PHP Windows Azure Page. Dopo alcuni istanti viene aperta la pagina contenente il tipico output della funzione phpinfo() di PHP:

Windows Azure PHP runningIn cima alla pagina possiamo notare due link che permettono di effettuare il test delle operazioni sui blob e sulle table. Come dicevo all’inizio le pagine relative sono generate automaticamente e permettono di comprendere come effettuare delle operazioni sugli storage service. Non esiste un esempio riguardo le code, ma sulla documentazione del Windows Azure SDK for PHP è possibile trovare tutte le informazioni necessarie.

Dando un’occhiata al file BlobSample.php vediamo che innanzi tutto vengono inclusi i riferimenti alle librerie dell’SDK:

/**
 * Add PHP Azure SDK to the PHP include_path. This SDK is avilable in Application Root directory.
 * This can be done by adding following set_include_path statement in
 * every PHP file refering PHP Azure SDK.
 *
 * Alternatively user can update the include_path in PHP.ini file.
 */
set_include_path(get_include_path() . PATH_SEPARATOR . $_SERVER["RoleRoot"] . "\\approot\\");

/**
 * Refer PHP Azure SDK library files for Azure Storage Services Operations
 */
require_once 'Microsoft/WindowsAzure/Storage.php';
require_once 'Microsoft/WindowsAzure/Storage/Blob.php';

All’interno del file Storage.php è definita la classe Microsoft_WindowsAzure_Storage, mentre in Blob.php è definita la classe Microsoft_WindowsAzure_Storage_Blob, che estende la prima.

Proseguendo con la lettura del file BlobSample.php, vediamo che viene creato lo storage client:

  $blobStorageClient = createBlobStorageClient();

  if(!isBlobServiceAccessible($blobStorageClient))
  {
    return;
  }

La funzione createBlobStorageClient() è definita più in basso come segue:

function createBlobStorageClient()
{
  if (azure_getconfig('UseDevelopmentStorage') == 'false')
  {
    $host = Microsoft_WindowsAzure_Storage::URL_CLOUD_BLOB;
    $accountName = azure_getconfig('StorageAccountName');
    $accountKey = azure_getconfig('StorageAccountKey');
    $usePathStyleUri = false;

    $retryPolicy = Microsoft_WindowsAzure_RetryPolicy_RetryPolicyAbstract::retryN(10, 250);

    $blobStorageClient = new Microsoft_WindowsAzure_Storage_Blob(
                                    $host,
                                    $accountName,
                                    $accountKey,
                                    $usePathStyleUri,
                                    $retryPolicy
                                    );
  }
  else
  {
    $blobStorageClient = new Microsoft_WindowsAzure_Storage_Blob();
  }
  return $blobStorageClient;
}

come possiamo vedere, nel caso in cui non venga utilizzato il development storage, vengono recuperate le credenziali e creato il client facendo riferimento a queste ultime. L’oggetto Microsoft_WindowsAzure_RetryPolicy_RetryPolicyAbstract permette di specificare il numero di tentativi e la distanza tra questi (vedi il file Microsoft/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php).  Come è possibile vedere in Storage.php, non specificando le credenziali, il client viene creato per il development storage.
Il passo successivo è quello di recuperare la lista di container usando il metodo listContainers dello storage client. La funzione listContainers($blobStorageClient), definita come segue, stampa i nomi dei container trovati.

function listContainers($blobStorageClient)
{
  echo "<strong>List of Containers:</strong>";
    $containers = $blobStorageClient->listContainers();
    foreach ($containers as $container)
    {
        echo "- $container->Name </br>";
    }
    echo "</br>";
}

Viene creato un nuovo container con un nome unico prefissato dalla string “testcontainer” (ad es. testcontainer4d641216454b0) utilizzando il metodo createContainer dello storage client.

  $containerName = uniqid("testcontainer");
  $result = $blobStorageClient->createContainer($containerName);
  echo "New container with name <strong>'" . $result->Name . "'</strong> is created.";

Dopo aver ristampato la lista dei container, per poter verificare che il container sia stato effettivamente creato, vengono creati due blob con nome differente dall’immagine WindowsAzure.jpg presente nella root del progetto usando il metodo putBlob:

$azureLogoFile = ".\\WindowsAzure.jpg";
$result = $blobStorageClient->putBlob($containerName, 'WindowsAzure.jpg', $azureLogoFile);
echo "New blob with name '<b>" . $result->Name . "'</b> is created. <br/><br/>";

Dopo aver stampato l’URL per il primo blob creato, viene stampata la lista di tutti i blob contenuti nel container usando la funzione listBlobs, costruita intorno al metodo listBlobs dello storage client:

function listBlobs($blobStorageClient, $containerName)
{
    echo "<b>List of blobs in container '" . $containerName . "': </b><br/>";
    $blobs = $blobStorageClient->listBlobs($containerName);
    foreach ($blobs as $blob)
    {
        echo " - $blob->Name " . "<br/>";
    }
    echo "<br/>";
}

A questo punto viene dimostrata la funzionalità di impostazione e lettura dei metadati sui blob con i metodi getBlobMetadata e setBlobMetadata. Vengono impostati i valori ‘createdby’ = ‘PHPAzure’ e ‘FileType’ = ‘jpg’.

/**
* Set metadata on blob "WindowsAzure.jpg" in $containerName container
*/
$blobStorageClient->setBlobMetadata($containerName, 'WindowsAzure.jpg',
array('createdby' => 'PHPAzure', 'FileType' => 'jpg'));
echo "Set metadata for '<b>WindowsAzure.jpg</b>' blob!<br/><br/>";
/**
* Read metadata on blob "WindowsAzure.jpg" in $containerName container
*/
$metaData = $blobStorageClient->getBlobMetadata($containerName, 'WindowsAzure.jpg');
echo "metadata for '<b>WindowsAzure.jpg</b>' blob:<br/>";
var_dump($metaData);
echo "<br/><br/>";

Il metodo getBlob dello storage client permette di effettuare il download del blob su un file temporaneo in locale:

$temp_file_name = tempnam(sys_get_temp_dir(), 'WindowsAzure');
$blobStorageClient->getBlob($containerName, 'WindowsAzure.jpg', $temp_file_name);
echo "Downloaded WindowsAzure.jpg blob to <b>" . $temp_file_name . "</b><br/><br/>";

Per dimostrare le funzionalità relativa all’impostazione del livello di accesso ai container viene impostato quello finora utilizzato come pubblico. Successivamente viene creato un nuovo container e impostato come privato. In esso viene creato un blob e generata una shared signature con validità di 5 minuti:

  /**
   * By default, blob storage containers on Windows Azure are protected from public viewing. If
   * any user on the Internet should have access to a blob container, its ACL can be set to public.
   * Note that this applies to a complete container and not to a single blob!
   */
  $blobStorageClient->setContainerAcl($containerName, Microsoft_WindowsAzure_Storage_Blob::ACL_PUBLIC);
  echo "Made container '$containerName' <b>public!</b> <br/><br/>";

  /**
   * Create shared signature for specified 'WindowsAzure.jpg' blob in private container
   */
  /**
   * First create a private container with uniq name and add 'WindowsAzure.jpg' blob file
   */
  $privateContainerName = uniqid("privatecontainer");
  $result = $blobStorageClient->createContainer($privateContainerName);
  $blobStorageClient->setContainerAcl($privateContainerName, Microsoft_WindowsAzure_Storage_Blob::ACL_PRIVATE);
  $result = $blobStorageClient->putBlob($privateContainerName, 'WindowsAzure.jpg', $azureLogoFile);

  $startTime = time();
  $endTime = $startTime + 5*60;  // Timestamp after 5 minutes

  $sharedSignatureURL = $blobStorageClient->generateSharedAccessUrl(
                $privateContainerName,
                "WindowsAzure.jpg",
            	'b',
            	'r',
            	isoDate($startTime),
            	isoDate($endTime)
          );

  echo "<b>SharedSignature for '$privateContainerName/WindowsAzure.jpg' with 5 minutes validity:</b><br/>" .
    '<a href="' . $sharedSignatureURL . '">' . $sharedSignatureURL . '</a><br/><br/>';

Infine viene cancellato il container pubblico col metodo deleteBlob e verificata l’effettiva cancellazione con il metodo containerExists:

  /**
   * Delete "WindowsAzure.jpg" blob from the $containerName container
   */
  $blobStorageClient->deleteBlob($containerName, 'WindowsAzure2.jpg');
  echo "Deleted blob <b>'WindowsAzure2.jpg'</b> from container <b>'$containerName'</b><br/><br/>";

  /**
   * Check if container exists
   */
  if ($blobStorageClient->containerExists($containerName))
  {
    echo "Container <b>'$containerName'</b> exists<br/><br/>";
  }
  if ($blobStorageClient->containerExists($privateContainerName))
  {
    echo "Container <b>'$privateContainerName'</b> exists<br/><br/>";
  }

Prima di concludere questo post vorrei indicarvi un utilizzo del table storage molto interessante per una applicazione PHP eseguita su Windows Azure. Quando l’applicazione è eseguita su almeno due istanze (a garanzia dello SLA), è necessario che lo stato delle sessioni sia condiviso tra le macchine virtuali. A tale scopo l’SDK per PHP mette a disposizione la classe Microsoft_WindowsAzure_SessionHandler  che permette di utilizzare il Table Storage di Windows Azure come session handler. Maggiori dettagli sono disponibili sulla documentazione del Windows Azure SDK for PHP.  Alla prossima!

Pubblicato in Programmazione, Windows Azure Tutorial | Contrassegnato , , , , , , , , | Lascia un commento

Hello Web Role! – Parte 2: Alla scoperta dello storage service

Nel post precedente abbiamo visto come realizzare la nostra prima soluzione cloud con un web role e come controllare il suo funzionamento utilizzando Windows Azure Diagnostics. In questo post vedremo come è possibile utilizzare lo storage service per memorizzare e pubblicare dati ed informazioni. I web role ed i worker role vengono eseguiti su istanze di macchine virtuali che mettono a disposizione uno spazio per la memorizzazione locale di file (local storage) volatile e quindi non utilizzabile per la memorizzazione persistente di file. Per la memorizzazione stabile di informazioni è possibile usare lo storage service o un database relazionale come SQL Azure.

Gli storage services di Windows Azure forniscono spazio di memorizzazione per dati testuali e binari, messaggi e dati strutturati.

I servizi offerti includono:

  • Il Blob service, per la memorizzazione di file
  • Il Queue service, per la memorizzazione di messaggi in una coda
  • Il Table service, per la memorizzazione di dati in forma strutturata non relazionale
  • I Windows Azure Drives, che permettono il mount di volumi NTFS da parte di una singola istanza

I servizi sono accessibili attraverso API REST che permettono l’accesso da una soluzione in esecuzione su Windows Azure o direttamente da qualunque applicazione in grado di inviare richieste e ricevere risposte HTTP/HTTPS su internet. L’accesso agli storage service avviene attraverso l’account. L’account è il livello più elevato nello spazio dei nomi per l’accesso ai servizi ed è alla base del processo di autenticazione. Nelle API REST l’account è esposto dai servizi come una risorsa.

Il blob service

Il Blob service fornisce lo storage per la memorizzazione di entità come file binari e di testo.Le API REST del blob service espongono due risorse: i container e i blob. Un container è un set di blob e ogni blob appartiene ad un container. Il blob service definisce due tipi di blob: i block blob, ottimizzati per lo streaming, ed i page blob. Questi ultimi sono ottimizzati per le operazioni di lettura e scrittura random, permettendo la scrittura di un range di byte in un blob. Sia ai container che ai blob è possibile associare metadati come coppie nome-valore definiti dall’utente.

Sebbene la struttura sia essenzialmente orizzontale, con i blob contenuti in un container, è possibile creare una struttura gerarchica simile ad un file system. I nomi dei blob possono contenere la codifica della gerarchia usando un separatore del percorso configurabile. Ad esempio i nomi di blob Gruppo/Blob1 e Gruppo/Blob2 implicano una organizzazione gerarchica virtuale. Ad agevolare questo genere di approccio, le API espongono dei metodi che permettono la navigazione delle gerarchie in modo simile a come siamo soliti fare sui file system consentendo, ad esempio, il recupero dei blob appartenenti ad un gruppo. Sull’MSDN è disponibile la reference delle API del blob service.

Il queue service

Il queue service fornisce un sistema affidabile per lo scambio di messaggi all’interno di un servizio e tra servizi differenti. Le API REST del queue service espongono due risorse: code e messaggi. Le code supportano la definizione di metadati come coppie nome-valore. Ogni account può contenere un numero illimitato di code di messaggi con nome univoco. Ogni coda può contenere un numero illimitato di messaggi con dimensione massima pari a 8 KB.

Quando un messaggio viene letto dalla coda, viene reso invisibile agli altri consumatori per un certo periodo di tempo. Se il messaggio non viene cancellato entro la scadenza dell’intervallo, viene ripristinata la sua visibilità in modo da permettere ad un altro consumatore di processarlo. Per approfondire l’argomento sulle code, la reference sulle API del queue service disponibile sull’MSDN è un ottimo inizio.

Il table service

Il table service fornisce uno storage strutturato sotto forma di tabelle che supporta le API REST ed è compatibile con le API REST di ADO.NET Data Services. All’interno dello storage account è possibile creare tabelle con nome univoco, all’interno delle quali vengono memorizzate informazioni come entità. Le entità sono collezioni di proprietà con i relativi valori, in modo similare ad una riga. Le tabelle sono partizionate al fine di supportare il load balancing tra i nodi e ogni tabella ha la prima proprietà utilizzata come chiave (partition key) per identificare la partizione a cui appartiene. La seconda proprietà identifica la riga (row key) che identifica un’identità all’interno della partizione. La combinazione tra la partition key e la row key forma la primary key che identifica l’entità in modo univoco all’interno della tabella. La tabella non impone nessuno schema, di conseguenza è lo sviluppatore a implementare e imporre lo schema lato client. Come per gli altri servizi, anche le API del table service sono ben descritte in una sezione dell’MSDN.

Al lavoro!

Adesso che abbiamo fatto amicizia con gli storage service, riprenderemo il progetto del post precedente e vedremo come è possibile effettuare l’upload di un file sul local storage e la sua successiva memorizzazione sullo storage service. Parallelamente, memorizzeremo un messaggio che notifica l’avvenuto upload.

Come prima cosa assicuriamoci della correttezza della configurazione del local storage sul web role:

Annotiamo il nome del local storage, utilizzato per l’acquisizione della risorsa. Apriamo la pagina default.aspx ed inseriamo un campo fileupload ed un pulsante. Per evitare confusione lasciamo i nomi predefiniti. Andiamo sull’handler del click e inseriamo il codice per l’upload sul local storage:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.IO;

namespace WebRole1
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            LocalResource LocalStorage =
                  RoleEnvironment.GetLocalResource("LocalStorage1");
            if (FileUpload1.FileContent.Length > 0){
                FileUpload1.SaveAs(
                      LocalStorage.RootPath + FileUpload1.FileName);
            }
        }
    }
}

Utilizzando il metodo RoleEnvironment.GetLocalResource(“LocalStorage1″) (vedi MSDN) otteniamo l’accesso all’oggetto LocalResource che contiene i dettagli sul local storage LocalStorage1: Name, MaximumSizeInMegabytes e RootPath. Quest’ultimo contiene il percorso fisico alla directory assegnata al local storage, che utilizzeremo per salvare il file.

Lanciamo il progetto ed effettuiamo l’upload di un file. Se apriamo il l’interfaccia utente del Compute Emulator possiamo verificare l’effettivo upload. Basta cliccare col tasto destro sull’istanza (se sono due potremmo dover ritentare con l’altra) e scegliere “Open local store”. I file del LocalStorage1 sono contenuti in \directory\LocalStorage1.

Prima di passare alla scrittura del codice relativo alle operazioni sullo storage service, è necessario impostare il settaggio relativo alla stringa di connessione all’account. Nel nostro caso useremo il development storage e aggiungeremo, immediatamente sotto la stringa di connessione impostata per la memorizzazione dei dati diagnostici, la voce “BlobConnectionString” di tipo connection string con valore “UseDevelopmentStorage=true”.
Per maggiori informazioni sulla configurazione delle stringhe di connessione, c’è una sezione ad hoc sull’MSDN.

Riprendiamo l’handler dell’evento click di Button1 e iniziamo a scrivere il codice che ci permetterà di copiare il file su un blob. Per prima cosa è necessario creare un oggetto CloudStorageAccount inizializzato utilizzando la stringa di connessione che abbiamo inserito in precedenza. Per recuperarne il valore utilizziamo la classe RoleEnvironment che permette l’accesso all’ambiente di esecuzione del role e che abbiamo già utilizzato per avere accesso al local storage (vedi MSDN).
Adesso, disponendo dell’oggetto CloudStorageAccount e quindi l’indirizzo dell’endpoint Blob e delle credenziali, possiamo inizializzare il CloudBlobClient, l’oggetto che rappresenta il client di accesso al blob service:

CloudStorageAccount Storage = CloudStorageAccount.Parse(
RoleEnvironment.GetConfigurationSettingValue("BlobConnectionString"));
CloudBlobClient Client =
     new CloudBlobClient(Storage.BlobEndpoint, Storage.Credentials);

A questo punto possiamo passare alla definizione del container utilizzando il metodo GetContainerReference. E’ bene ricordare che il nome dei container deve eseguire le regole  per qualunque nome DNS valido:

  1. Il nome deve iniziare per lettera o numero e contenere solo lettere, numeri ed il carattere – (trattino).
  2. Il carattere – deve tassativamente essere preceduto e seguito da una lettera o da un numero.
  3. Tutte le lettere del nome devono essere minuscole.
  4. Il nome deve avere una lunghezza compresa tra 3 e 63 caratteri.

Il metodo SetPermissions permette di impostare il livello di accesso al container. Nel nostro caso il container non ha restrizioni d’accesso. Come di consueto, l’MSDN potrà fornirvi maggiori informazioni in proposito. L’ultima operazione da fare sul container è chiamare il metodo CreateIfNotExist, dal nome più che autodescrittivo.

CloudBlobContainer Container = Client.GetContainerReference("uploads");
Container.SetPermissions(new BlobContainerPermissions
 { PublicAccess = BlobContainerPublicAccessType.Container });
Container.CreateIfNotExist();

In modo analogo a come fatto in precedenza per il container, creiamo il riferimento al blob usando il metodo GetBlobReference del container. Come detto in precedenza, ai blob è possibile associare dei metadati sotto forma di coppie nome-valore. In questo caso creeremo il metadato “MIME” contenente il content type del file. Il metodo UploadFile del container ci permette di uploadare il contenuto, creando il blob o sovrascrivendo l’eventuale blob omonimo.

   CloudBlob Blob = Container.GetBlobReference(FileUpload1.FileName);
   Blob.Metadata["MIME"] = FileUpload1.PostedFile.ContentType;
   Blob.UploadFile(LocalStorage.RootPath + FileUpload1.FileName);

A questo punto, con un processo assolutamente analogo, definiamo una coda chiamata “filequeue”. E accodiamo un messaggio contenente il nome del file appena uploadato sul cloud storage. Maggiori informazioni sullla gestione delle code è disponibile in questa pagina dell’MSDN.

   CloudQueueClient QueueClient = Storage.CreateCloudQueueClient();
   CloudQueue Queue = QueueClient.GetQueueReference("filequeue");
   Queue.CreateIfNotExist();
   CloudQueueMessage Message = new CloudQueueMessage(FileUpload1.FileName);
   Queue.AddMessage(Message);

A questo punto non resta che lanciare il progetto e uploadare un file. Giocando con i breakpoint,  il Compute Emulator ed il Server Explorer è possibile vedere il file apparire prima sul local storage e poi sul blob service.

Alla prossima!

Pubblicato in Programmazione, Windows Azure Tutorial | Contrassegnato , , , , , , , , , | Lascia un commento

Hello Web Role! – Sviluppare la prima soluzione per Windows Azure.

Nel post precedente abbiamo visto quali sono i tool necessari per poter iniziare a sviluppare una soluzione per Windows Azure, quali sono gli elementi che la compongono e le funzionalità che i role possono utilizzare. In questo post realizzeremo una semplice soluzione utilizzante un web role e vedremo inseme quali sono le configurazioni ed i meccanismi in gioco.

Iniziamo con il lanciare Visual Studio e cliccare sul link nuovo progetto della pagina iniziale:


Selezionando le template Cloud viene proposto il modello del progetto Windows Azure. Specificando il nome e premendo OK, viene mostrata una finestra che permette di scegliere quali role utilizzare all’interno della soluzione:

La scelta possibile è tra:

  • Web Role ASP.NET
  • Web Role ASP.NET MVC2
  • Web Role Servizio WCF
  • Web Role CGI
  • Worker Role

I primi due permettono di pubblicare un servizio con front end web (nel secondo caso sviluppata usando MVC 2). Il Web Role Servizio WCF, come intuibile dal nome, permette di creare un Web Role preconfigurato per la pubblicazione di un servizio WCF. Il Web Role CGI permette di inserire un progetto per una web application FastCGI che utilizza codice nativo all’interno del Cloud Service. Il Worker Role, come visto nel post precedente, permette di creare un componente eseguito all’esterno del contesto IIS come, ad esempio, un processo per l’esecuzione di task in background. Nel nostro caso ci limiteremo ad inserire solo un web role. Premendo OK il progetto viene creato:

Osservando la soluzione è possibile notare che è composta da due progetti: il WindowsAzureProject1, con lo stesso nome della soluzione, contenente i file di configurazione del servizio e una cartella contenente i role e il progetto web WebRole1,  avente la stessa struttura di una comune web application eccettuata la presenza del file WebRole.cs.

Apriamo questo file:

vediamo che si tratta di una classe derivata dalla classe RoleEntryPoint. Questa classe, utilizzata da Windows Azure per comunicare con le istanze, possiede tre metodi di sicuro interesse:

  • public virtual bool OnStart()
  • public virtual void OnStop()
  • public virtual void Run()

Durante l’esecuzione del metodo OnStart l’istanza viene posta allo stato Busy ed è indisponibile alle richieste ricevute dal load balancer. Questo metodo è utile per effettuare tutte le inizializzazioni necessarie prima dell’avvio del servizio.
Se il metodo OnStart ritorna falso l’istanza viene immediatamente arrestata, in caso contrario viene avviata chiamando il metodo Run. Questo consiste essenzialmente in un thread in esecuzione continua. Nell’implementazione di default il thread va in sleep per un tempo infinito, bloccando il ritorno del metodo.
Il metodo OnStop, chiamato da Windows Azure allo shutdown dell’istanza, viene utilizzato per implementare il codice necessario per gestire l’arresto regolare. Il metodo OnStop deve ritornare entro un certo tempo, in caso contrario Windows Azure interromperà forzatamente l’esecuzione dell’istanza del role.

Come detto in precedenza nel progetto del cloud service è presente una cartella contenente i riferimenti ai role e i due XML file di configurazione ServiceConfiguration.cscfg e ServiceDefinition.csdef. Questi file contengono tutte le impostazioni del servizio e dei role in esso contenuti. Sebbene molte impostazioni siano configurabili solo attraverso la loro modifica  (vedi MSDN), quelle principali possono essere effettuate usando l’interfaccia grafica. Basta fare click con il tasto destro su uno dei role e selezionare Proprietà.


I permessi disponibili per un role dipendono dal livello di trust con cui il role è stato pubblicato. Sono disponibili due livelli di trust: partial trust e full trust. L’impostazione di default prevede l’esecuzione in full trust. Maggiori informazioni sul livello di trust sono disponibili sugli articoli dell’MSDN Hosting Web and Worker Roles Under Full Trust e Windows Azure Partial Trust Policy Reference.

Nella sezione Instances è possibile impostare il numero di istanze utilizzate dal role e la tipologia di virtual machine su cui devono essere eseguite. Al fine di rientrare nello SLA di Windows Azure, che prevede una disponibilità del compute service del 99.95%, è necessario attivare almeno due istanze per role.
Le dimensioni disponibili per le macchine virtuali sono:

Virtual Machine CPU Cores RAM Local Storage
ExtraSmall Shared 768 MB 20 GB
Small 1 1.75 GB 225 GB
Large 4 7 GB 1000 GB
ExtraLarge 8 14 GB 2040 GB

Per controllare il funzionamento dei role eseguiti in full trust (e valutare l’opportunità di scalare in alto o in basso) è possibile utilizzare Windows Azure Diagnostics che permette di campionare informazioni dai performance counters e memorizzarle sul cloud storage.
La voce di configurazione Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString contiene la stringa di connessione al cloud storage utilizzato a questo scopo o la stringa “UseDevelopmentStorage=true”, indicante l’utilizzo del Development Storage del kit di sviluppo.
Per configurare la raccolta dei dati andiamo sul metodo OnStart() della classe vista in precedenza e inseriamo il codice seguente:

In seguito all’avvio del progetto avremo modo di vedere che ci permetterà di monitorare il livello di occupazione di CPU per le istanze del web role campionando i valori ogni 10 secondi e memorizzando i valori sulla tabella WADPerformanceCountersTable del Development Storage ogni minuto. Maggiori dettagli sull’utilizzo di Windows Azure Diagnostics è disponibile sull’MSDN.

Come visto in precedenza, ogni istanza del web role può utilizzare uno spazio di storage locale la cui dimensione varia a secondo del tipo di virtual machine selezionata. Questo spazio può essere configurato, definendo delle risorse cui viene attribuito un nome e una dimensione, attraverso le impostazioni relative.

E’ bene ricordare che il local storage è da considerarsi volatile anche se non viene impostato per la cancellazione in caso di riciclo del role e pertanto va utilizzato solo per la memorizzazione di file temporanei.

E’ giunto il momento di avviare il nostro primo progetto ma, prima di lanciare il debug, vi consiglio di cliccare col tasto destro sul logo di Windows Azure sull’area di notifica (il Windows Azure Simulation Monitor) e selezionare Show Compute Emulator UI. In questo modo avrete modo di vedere l’effetto del deployment e, cliccando sulle singole istanze del web role, il susseguirsi degli eventi che portano all’avvio.

Ad avvio completato, viene lanciato il browser e caricata la pagina Default.aspx del web role. Il primo passo verso le nuvole è fatto, adesso non vi resta che dare un’occhiata ai dati diagnostici sul Development Storage utilizzando il Server Explorer di Visual Studio. Alla prossima!

Pubblicato in Programmazione, Windows Azure Tutorial | Contrassegnato , , , , , , , , | Lascia un commento

Anatomia e sviluppo di una soluzione cloud per Windows Azure

In questo post, che immagino come l’inizio di una serie con lo scopo di aiutare a muovere i primi passi nell’utilizzo di Windows Azure e SQL Azure, vedremo in dettaglio come è fatto un cloud service e cosa è necessario per iniziare a sviluppare. Nei prossimi post approfondiremo la conoscenza delle varie caratteristiche della piattaforma Azure e realizzeremo dei piccoli  progetti che ne illustrano l’utilizzo.

Anatomia di un cloud service di Windows Azure

Un’applicazione progettata per essere eseguita su Windows Azure è composta da un insieme di componenti scalabili (roles) e dai file di configurazione XML che configurano e definiscono il servizio.
Attualmente Windows Azure supporta tre tipologie di roles:

  • Web Role
    Un web role è un role eseguito all’interno di IIS che permette la programmazione di web applications,  dei front-end web based e di servizi SOAP/WCF in modo semplice e immediato. La sua struttura deriva direttamente da quella di una normale web application .NET.
  • Worker Role
    E’ il role utile per la gestione di processi in background, asincroni, con lunghi tempi di esecuzione o con esecuzione intermittente. Può essere anche utilizzato per realizzare servizi applicativi che non richiedono una user interface. I worker role possono essere in ascolto su una coda o ospitare un servizio TCP. A differenza dei web role, i worker role non sono eseguiti nel contesto di IIS ma sono degli eseguibili autonomi.
  • VM Role
    Il VM Role è un tipo speciale di role. Non è un’applicazione da eseguire su una macchina virtuale, ma la macchina virtuale stessa. Permette la massima libertà nel controllo del suo funzionamento ed è una buona soluzione per l’esecuzione su Windows Azure di applicazioni legacy: basta creare un disco immagine VHD di un sistema Windows Server 2008 R2 per il deployment.

Le tre tipologie di role possono essere combinate liberamente all’interno di un cloud service. Ogni istanza dei role (in genere ce ne sono almeno due, per garantire lo SLA) viene eseguita utilizzando una macchina virtuale con potenza di calcolo e spazio di storage locale configurabile tra Extra Small, Small, Medium, Large ed Extra Large. Lo storage locale, utilizzabile per il processamento di file temporanei, è volatile. Per la memorizzazione di dati e files si utilizzano gli storage services o database SQL Azure.
Gli storage services di Windows Azure, che forniscono le funzionalità per la  memorizzazione di dati binari, testi e dati strutturati comprendono:

  • Blob service (dati binari e testi)
  • Queue service (code di messaggi)
  • Table service (storage strutturato non relazionale)
  • Windows Azure drives (volumi NTFS)

SQL Azure Database è la piattaforma database relazionale cloud-based basata sulla tecnologia SQL Server. Usando SQL Azure è possibile utilizzare database relazionali sul cloud con le caratteristiche di alta disponibilità, scalabilità e sicurezza offerte da un data center distribuito.

I role possono inoltre utilizzare le funzionalità esposte dalla Windows Azure AppFabric:

  • AppFabric Service Bus
    Un servizio che esegue il relay bidirezionale sicuro di messaggi tra qualsiasi web service, indipendentemente dalla tipologia di dispositivo, computer o server che lo ospita e dal fatto che si trovi dietro un firewall o che venga utilizzato un NAT.
  • AppFabric Access Control
    E’ un servizio interoperabile che fornisce soluzioni di autenticazione ed autorizzazione federata claim-based per qualunque risorsa, indipendentemente dalla tipologia di dispositivo, computer o server che lo ospita.

Nei prossimi post, dopo aver mosso i primi passi, avremo modo di vedere il funzionamento degli storage service, di SQL Azure e delle funzionalità dell’App Fabric con degli esempi concreti.

I tool di sviluppo

Per iniziare a sviluppare per Windows Azure non è necessario disporre di un account sulla piattaforma di Microsoft. E’ possibile realizzare un progetto e lanciarlo in un ambiente simulato di sviluppo (Development Fabric) che consente la messa a punto della soluzione senza dover incorrere in costi aggiuntivi.
I principali ambienti di sviluppo per Windows Azure, a secondo se si vuole sviluppare utilizzando il framework .NET, Java o PHP, sono Visual Studio ed Eclipse.

Requisiti per lo sviluppo sul framework .NET

Requisiti per lo sviluppo in Java e PHP:

Adesso che abbiamo predisposto la macchina di sviluppo, nel prossimo post realizzeremo il primo progetto.

Pubblicato in Programmazione, Windows Azure Tutorial | Contrassegnato , , , , , , , , , , , , | Lascia un commento

La definizione del NIST e le offerte di cloud computing

Il NIST, l’istituto statunitense degli standard e della tecnologia derivato dal vecchio National Bureau of Standards, ha dato una definizione formale al cloud computing:

“Il cloud computing è un modello abilitante un accesso comodo ed on-demand ad un pool condiviso di risorse di calcolo configurabili che possono essere velocemente ottenute e rilasciate con minimo sforzo di gestione ed una limitata interazione con il fornitore di servizi.”

A questa definizione iniziale sono associate cinque caratteristiche essenziali:

  • Self service ed on-demand
    L’utilizzatore del servizio può richiedere ed utilizzare le risorse di calcolo e di storage secondo le sue necessità, senza richiedere nessuna interazione umana con il fornitore di servizi.
  • Accesso di rete aderente agli standard
    Le capacità fornite dal servizio sono disponibili sulla rete e disponibili attraverso meccanismi standard che permettono l’utilizzo ad applicazioni eseguite su piattaforme eterogenee.
  • Resource pooling
    Le risorse del fornitore di servizio sono raggruppate allo scopo di servire gli utilizzatori attraverso un modello di erogazione multi-tenant. Le risorse fisiche e virtuali sono assegnate dinamicamente secondo le esigenze degli utilizzatori. Questi ultimi non hanno visibilità dell’effettiva posizione fisica delle risorse utilizzate se non con un livello di astrazione molto elevato (nazione, stato o al massimo datacenter).
  • Elasticità
    Le risorse possono essere fornite in modo elastico, veloce e, a volte, automatico permettendo una veloce scalabilità verso l’alto e verso il basso. L’utilizzatore, potendo acquistare l’uso di risorse in qualsiasi quantità ed in qualunque momento, ha la percezione di una disponibilità potenzialmente infinita.
  • Costo commisurato all’effettivo utilizzo
    I sistemi cloud controllano ed ottimizzano automaticamente l’uso delle risorse facendo leva sulla capacità di misura dell’uso delle tipologie di servizio (es. storage, computing time, banda). L’utilizzo delle risorse può essere monitorato e controllato in modo trasparente per il fornitore di servizio e l’utilizzatore.

La definizione del NIST contiene anche una descrizione dei possibili modelli di servizio (SaaS, PaaS e IaaS) e dei modelli di deployment (private cloud, community cloud, public cloud ed hybrid cloud).

In questo post vorrei focalizzare l’attenzione sulla definizione dei cloud computing e delle caratteristiche che il NIST ritene, giustamente, essenziali. In questo momento esistono sul mercato delle offerte che, sebbene siano proposte come cloud computing in realtà sono solo delle applicazioni molto avanzate di virtualizzazione.

Sono tre le caratteristiche essenziali a dettare la differenza maggiore tra una offerta di cloud computing reale e quelle di virtualizzazione avanzata, sia dal punto di vista tecnologico che da quello strategico:

  • La possibilità di riconfigurare le risorse in modo semplice e veloce, senza richiedere interventi umani dal lato del fornitore di servizi.
  • La possibilità di modificare le caratteristiche del servizio e di scalare verso l’alto e verso il basso in modo elastico (dove per elastico intendo anche la possibilità di una variazione anche continua, di ora in ora).
  • La possibilità di pagare sulla base dell’effettivo utilizzo delle risorse.

Se queste caratteristiche sono assenti, l’offerta impone dei vincoli temporali (es. costi del servizio su base mensile) e le operazioni di attivazione, upgrade o downgrade del servizio, a causa della necessità di un intervento umano, non sono immediate. Il risultato è che l’infrastruttura di erogazione non può più seguire le necessità dell’utilizzatore esponendolo potenzialmente a costi operativi superiori rispetto ad una offerta di cloud computing reale.

C’è da dire che implementare queste caratteristiche su un servizio a grande scala è particolarmente complesso e richiede del tempo. Probabilmente alcune offerte di virtualizzazione avanzata pian piano si allineeranno alle caratteristiche di un reale servizio di cloud computing ma, nel frattempo, è bene stare attenti a cosa si acquista. Il termine cloud computing è di gran moda ed il marketing delle aziende non può non cedere alla tentazione di offrire come tale qualcosa di solo vagamente simile.

Pubblicato in Cloud computing | Contrassegnato , , , , | 2 commenti

Un mese con Windows Phone 7

In occasione dello SMAU 2010 ho avuto modo di vedere in anteprima il primo telefono Windows Phone 7 lanciato sul mercato italiano: l’LG Optimus 7.

LG Optimus 7

Una settimana dopo ne avevamo preso uno per effettuare i test dell’applicazione per WebSignage su cui stavamo lavorando, prima della pubblicazione sul marketplace :-).

Ho utilizzato molti dispositivi mobili con il sistema operativo Microsoft, sin dal Pocket PC 2000. Nel tempo il sistema operativo ed i dispositivi sono evoluti diventando più potenti, più pratici e affidabili, ma l’approccio è sempre stato un po’ “figlio del desktop”. Un adattamento di Windows (aggiungerei CE) ad uno scenario d’uso ed una categoria di utenti differente.

Quello che colpisce subito di Windows Phone 7 è l’interfaccia utente radicalmente diversa dai suoi predecessori. Il concetto di finestra è sparito e le applicazioni sembrano muoversi  in un ambiente vagamente tridimensionale, con sfondi e testi che escono fuori dallo spazio visibile sul display. L’interfaccia, molto reattiva, è arricchita da animazioni eleganti ma discrete. Tutto è molto intuitivo e immediato. Le configurazioni, come ad esempio quelle delle caselle di posta, per la prima volta hanno perso tutto il tecnicismo diventando semplicissime e alla portata di tutti. La varietà delle applicazioni disponibili, in particolare quelle gratuite, è ancora limitata ma decine di nuove applicazioni sono pubblicate ogni giorno.

Ho utilizzato il telefono in modo molto intensivo per più di un mese e, a parte l’autonomia della batteria (colpa mia) e la sensibilità del GPS un po limitata (probabilmente colpa di LG), non ho avuto problemi particolari. Windows Phone 7 ha dimostrato di essere un sistema operativo affidabile, nonostante la sua giovinezza. Manca il supporto per il copia e incolla, il tethering e il trasferimento di file tramite Bluetooth ma confido nel prossimo aggiornamento annunciato per l’inizio del 2011.

Cosa mi è piaciuto

  • L’ergonomicità della nuova interfaccia utente.
  • La fortissima integrazione con i social network e Facebook in particolare.
  • La gestione unificata dei calendari dei vari account. Era una cosa che desideravo!
  • I comandi vocali, molto ben progettati, non sbagliano un colpo neanche in presenza di rumori di fondo. Sono utilissimi quando si usa l’auricolare.
  • Internet Explorer 7.5 è un buon browser che garantisce una buona esperienza di navigazione anche su siti complessi e non progettati per essere visualizzati su un dispositivo mobile.

Cosa non mi è piaciuto

  • Sebbene sia molto “social” e permetta di condividere facilmente testi e fotografie, la condivisione dei video è praticamente impossibile se non tirando fuori il file con Zune.
  • Se il marketplace si blocca e chiude non è più possibile avviare ne lui ne l’hub Musica e video. Si deve riavviare il telefono. Un difetto di gioventù che si evidenzia solo se la connessione UMTS è davvero scadente e cade al momento sbagliato. Solo per gli sfortunati!
    EDIT: dopo qualche mese e un bel po di applicazioni in più sul marketplace la situazione è decisamente peggiorata. E’ difficile usare il marketplace un po’ più a lungo senza arrivare al blocco. Oggi (10 marzo 2011) ho effettuato l’aggiornamento del sistema operativo di Febbraio senza problemi. Speriamo che il vero aggiornamento, previsto per la seconda metà del mese, risolva il difetto…
  • L’assenza di Flash. Più che altro perchè a volte diventa un po’ complicato vedere pagine con video YouTube embedded.
  • La dipendenza da Zune per il trasferimento di musica e video, un po’ come per  l’iPhone che dipende da iTunes. Sarebbe bello vedere il telefono come disco USB.
  • L’impossibilità a trasferire file col Bluetooth.
  • L’assenza del copia e incolla.
Pubblicato in Information Technology | Contrassegnato , , , | Lascia un commento