Sto per pubblicare una versione del ws-discovery nuova di zecca su codeproject, ovviamente corredandola con un articolo.
Sono a buon punto con la traduzione, mentre per quel che riguarda l'implementazione:
- il servizio diventa 'scopribile' a runtime via configurazione:
<extensions>
<behaviorExtensions>
<add name="serviceDiscoverableBehavior"
type="Masieri.ServiceModel.WSDiscovery.Behaviors.DiscoveryBehaviorSection,
Masieri.ServiceModel.WSDiscovery, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=18ad931e67d285bd" />
</behaviorExtensions>
</extensions>
<services>
<service behaviorConfiguration="serviceDiscoverable" name="ServiceTest.Istanza">
...
</service>
</services>
e il behavior
<behavior name="serviceDiscoverable">
<serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080/Mex" />
<serviceDiscoverableBehavior scopesMatchBy="http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc2396">
<scopes>
<add url="http://myscope.tempuri.org/"/>
</scopes>
</serviceDiscoverableBehavior>
</behavior>
</serviceBehaviors>
- Ha i log nel formato classico del Service Model.
<system.diagnostics>
<sources>
<source name="System.ServiceModel.WSDiscovery" switchValue="Warning, Error">
<listeners>
<add initializeData="InfoServiceDebug.e2e"
type="System.Diagnostics.XmlWriterTraceListener, System,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModel Listener"
traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging" switchValue="Warning, Error" >
<listeners>
<clear />
<add type="System.Diagnostics.DefaultTraceListener" name="Default"
traceOutputOptions="None" />
<add initializeData="MessageLog.e2e"
type="System.Diagnostics.XmlWriterTraceListener, System,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="MessageLogging Listener"
traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack" />
</listeners>
</source>
</sources>
<sharedListeners>
<add type="System.Diagnostics.DefaultTraceListener"
name="Default" />
</sharedListeners>
</system.diagnostics>
- Il client è implementato con la sola interfaccia senza dover far ereditare una classe DiscoveryClient<IServiceSample>()
- Ha la funzionalità di fault tollerance a livello di chiamata per scalare su altri servizi compatibili, ossia quando esistono piu servizi con la stessa interfaccia se la chiamata su un servizio fallisce, il client ripete automaticamente la chiamata sull'altro
- funziona solo senza il discovery proxy. La parte di discovery proxy l'implementerò in futuro
Spero al più presto di averlo on line
WS-Discovery è un protocollo WS-* compatibile che permette di utilizzare un servizio, senza specificarne l'indirizzo e il tipo di trasporto.
Di recente ho dovuto implementarlo con WCF e ho pensato di riportare su questo blog le principali problematiche.
Innanzitutto WS-Discovery, al contrario di WCF, si basa sul contratto e non sulla terna ABC del mondo WCF. Infatti il contratto è l'unico parametro necessario per la ricerca del servizio. Binding e Address saranno determinati dopo la fase di discovery.
La cosa che mi sarebbe piaciuto fare è un DiscoveryBindingElement che utilizzato da configurazione permettesse di definire il solo contratto. Questo aspetto non è un cambiamento da poco dal punto di vista della configurazione di WCF e ho dovuto abbandonare questa strada in quanto l'ho trovata non percorribile.
<
system.serviceModel>
<extensions>
<bindingExtensions>
<add name="discoveryBinding" type="WSDiscovery.Configuration.DiscoverySection, WS-Discovery, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bindingExtensions>
</extensions>
<client>
<endpoint address="discovery://239.255.255.250:3702" binding="discoveryBinding" bindingConfiguration="discovery" contract="SampleInterface.IBorsa" name="DiscoveryBinding.Proxy" />
</client>
<bindings>
<discoveryBinding>
<binding name="discovery">
<discoveryBinding>
<scopes mathcBy="http://schemas.xmlsoap.org/ws/2004/10/discovery/rfc2396">
<scope>http://mieiscopes.org/urlmioscope</scope>
</scopes>
</discoveryBinding>
</binding>
</discoveryBinding>
</bindings>
</system.serviceModel>
L'alternativa è un proxy che utilizza WS-Discovery implementato tramite una classe astratta, ma anche concreta, del tipo DiscoveryClientBase<TChannel> che ha un costruttore con parametro e un costruttore con un array di scopes (uri che specificano parametri aggiuntivi utili per la scelta del servizio a parità di contratto).
Un componente di infrastruttura deve essere assolutamente trasparente. Sulle orme della classe ClientBase di WCF ho utilizzato un sistema a proxy dinamici per esporre una proprietà Channel del tipo <TChannel> che permette in modo analogo alla classe ClientBase dirigere tutte le chiamate ai metodi dell'interfaccia ad una funzione "asso piglia tutto" che imbusta fisicamente il messaggio soap e lo invia. Una buona descrizione del metodo dei proxy dinamici è disponibile qui.
Le cose da tenere conto lato client sono due:
- Ogni entry recuperata dal campo va memorizzata in una global storage con tutti i contratti recuperati o pervenuti dal campo (grazie ai messaggi di Hello)
- Se dovesse comparire un DiscoveryProxy i meccanismi di trasporto cambiano completamente (unicast->multicast e viceversa) e quindi bisogna essere pronti all'evenienza
Il client ha funzioni analoghe quando lavora con la presenza di un discovery proxy e quando questo non è presente: utilizzare uno State (Gof) con una classe astratta che tiene le funzioni comuni e due classi concrete, DiscoveryProxyState e DiscoveryNoProxyState, agevola parecchio il lavoro.
Una funzione interessante che ho aggiunto nel client è che memorizzando le risposte dal campo è in grado di fornire piu servizi che implementano un contratto. La scelta del contratto da utilizzare è realizzata tramite un componente di riordino, che utilizzando logica di processo esegue il suo lavoro. Nel momento in cui invio un messaggio al servizio e dovessi avere un Fault nella comunicazione la classe proxy automagicamente prova ad usare un'altro servizio presente in lista cancellando quello irraggiungibile. Una funzione banale ma di ottima efficacia e totale trasparenza per l'utilizzatore, avendo tutto mascherato attraverso il dynamic proxy.
Dedicherò un successivo post per esprimere le mie considerazioni per il lavoro fatto lato servizio.
Innanzitutto questo è il mio primo post sul mio primo blog e scusate se sono un po impacciato.
Ho appena terminato uno sviluppo per hobby su un webcontrol. Da quando ho iniziato a sviluppare con HTML ASP & C. ho sempre trovato difficoltà nell'utilizzo di javascript. Per quanto sia da un lato semplicistico, dall'altro è estremamente complicato in quanto ha sempre avuto difficoltà nella portabilità da un browser all'altro (almeno quello che scrivevo io).
La cosa che ho apprezzato per prima di ASP.NET è poter fare a meno di javascript.
ASP.NET in realtà usa javascript nascondendolo all'utente, utilizzandolo per lo stretto necessario (postback, validazioni ...). La potenza di javascript talvolta però mi sorprende.
Di recente vedevo tra i progetti di sourceforge il controllo http://www.activewidgets.com/ che devo dire è fatto in maniera splendida.
Sebbene sembri un activex è fatto interamente in javascript (e per questo mi tolgo il cappello), il problema che da asp.net bisogna un po impegnarsi ogni qualvolta che si intende utilizzarlo.
L'ideale sarebbe stato avere una grid che esponga la stessa interfaccia della datagrid e che quindi con l'impostazione della datasource con un databind avessi tutto pronto.
La sfida è ottima e allora mi metto a lavorarci. In primis decido di ereditare mia classe dalla classe Datagrid, poi mi accorgo che non avrei finito entro l'anno con tutte le proprietà da agganciare e allora procedo con un nuovo WebControl implementando le interfacce INamingContainer e IPostBackEventHandler.
Il mio grosso gruccio non era realizzare il controllo ma evitare che un utente che usa tale controllo dovesse portarsi dietro i numerosi files css e js.
Una idea mi travolse. Perchè non embeddare tutti i filed js e css "statici" internamente al mio controllo e poi richiamarlo dalla reflection? Cosi ho provveduto a creare un HTTPModule che interrogato dalla request provvedesse a recuperare le risorse interne all'assemby.
In questa maniera il webcontrol può richiedere immagini js e css ad un url fittizzio generando link a risorse tipo Masieri.Web.Control.aspx?res=gird.js e cosi agganciare le risorse esterne.
Unica precauzione alla richiesta di caricamento del controllo ho dovuto controllare se l'HTTPModule esisteva e in caso negativo emettere un javascript in cui avviso lo sviluppatore di ricordarsi di aggiungere la dichiarazione dell'HttpModule al web.config.
Ecco il link con un esempio http://www.noise.it/activegrid/, penso che provvedero' ad aprire un apposito progetto su SourceForge dove depositare i sorgenti