tag:blogger.com,1999:blog-97519632009-04-30T08:33:46.570+02:00Va y avoir du sport !(Some muscle flexing is coming on...)Christophe Grandnoreply@blogger.comBlogger29125tag:blogger.com,1999:blog-9751963.post-1160339886978903132006-10-08T22:22:00.000+02:002006-10-08T22:38:10.406+02:00IE Png Transparency Fix FixI was experiencing a random bug of shrinking pictures with pngfix.js. I finally tackled it! You have to wait til the image is loaded (img.complete == true) before measuring it. So if the image isn't yet loaded, wait for img.onload before fixing the transparency.<br /><a href="http://www.cgrand.net/misc/pngfixfixed/pngfix.js">Download pngfix.js!</a><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-116033988697890313?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1152142064082497142006-07-06T00:12:00.000+02:002006-07-06T01:27:44.150+02:00Architecture web moderne, le nerf de la guerre : les données (1)Bon, suite de mes visions architecturales pacorabiennes. Cette fois cela va être plus concret. Le sujet du jours est : les données.<br /><br />Ma position est assez simple : la couche d'accès aux données (DAL) doit être directement exposée sur le web. (Ce qui ne signifie qu'elle doit être en lecture/écriture ni même en lecture pour tout le monde ; l'authentification étant gérée par ce que fournissent http et https en standard, pas de sessions.)<br /><br />Chaque ressource correspond à un objet, une entité (biffez la mention inutile). Les relations sont remplacées par des hyperliens.<br /><br />Pour les cas simples, une table/une vue peut directement être mappée sous le chemin : <code>/nom-de-la-table/valeur-de-la-clé-primaire</code> et renvoyer (après négociation du type de la représentation, ici du javascript) : <pre>{<br /> id: 'abcdef',<br /> champ1: 3.14,<br /> champ2: Date(1978, 10, 11),<br /> relation1: Ref('table2/189'),<br /> relation2: [Ref('table1/234'), Ref('table1/567')]<br />}</pre><br /><br />Le minimum nécessaire de types de représentations est : html, xml et js pour permettre, respectivement, de : <ul><li>naviguer à travers les données depuis un simple navigateur,</li><li>maximiser l'interopérabilité (à part le locomotive basic tous les environnements de dev sont à présents équipés de librairies correctes pour traiter ce bon vieil xml)</li><li>accélérer le développement d'applis webs modernes.</li></ul><br /><br />Bon, bien sûr cela ne couvre que la lecture (et encore je n'ai pas insité sur le fait que les headers ETag et Last-Modified <em>doivent</em> être utilisés). Pour l'écriture distinguons les 3 opérations de modification : création, modification et suppression. Mais je ne vais pas mapper ces opérations sur les méthodes http PUT, POST et DELETE. Pas complètement. Je n'aime pas la sémantique du PUT qui consiste à laisser l'initiative du nommage au client ce qui est, pour moi, une abération.<br />Donc pas de PUT mais un POST de la ressource sur une url "factory" (typiquement <code>/nom-de-la-table</code>) qui redirigera (header "Location:") sur l'adresse de la ressource nouvellement créée après avoir répondu <a herf="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">201 Created</a>.<br />Reste le DELETE qui est parfait mais ne fait pas partie de <a href="http://pluralsight.com/blogs/dbox/archive/2006/03/18/20235.aspx">Lo-Rest</a>, il faut donc penser à proposer au client une alternative Lo-Rest au DELETE de la ressource. C'est facile, il est juste nécessaire d'y penser).<br />NB : En Ajax il est possible d'être Hi-Rest. PUT et DELETE sont disponibles.<br />Pour les simples modifications, il suffit de poster une représentation de la ressource modifiée à sa propre adresse.<br /><br />Facile n'est-il pas ? <br /><br />Pour les prochains billets il me reste à couvrir : comment imposer les intégrités métiers (sous-titre : "Sur le web personne ne sait que vous êtes une procédure stockée.") et comment gérer les transactions.<br />Ensuite, c'en sera fini de la couche d'accès aux données et je traiterai de la couche logique et de la couche présentation.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-115214206408249714?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1151082947496179462006-06-23T18:56:00.000+02:002006-06-23T19:18:48.296+02:00L'UTF m'a tuerJ'ajoute un feed (<a href="http://fr.wikipedia.org/wiki/Atom">atom 1.0</a>) pour suivre les commentaires de ce blog bifide, généré à partir d'un petit <a href="http://fr.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a> en <a href="http://www.python.org">Python</a>, construit en se basant sur une <a href="http://www.atomenabled.org/">source sûre</a>. J'itère jusqu'à ce que mon système fonctionne et que le feed <a href="http://www.feedvalidator.org">valide</a>.<br /><br />Tout va bien tout est en <a href="http://fr.wikipedia.org/wiki/Utf-8">UTF-8</a>. Facile. Je suis trop fort.<br /><br />Tiens, on me signale que mon flux s'affiche mal avec des problèmes sur les accents. Allons donc ! Ca marche très bien ! D'ailleurs ça valide... pas !<br /><br />Le titre d'une entrée de blog est obligatoire et le formulaire de saisie n'a pas de champ titre. J'avais choisi de tronquer le commentaire à 20 caractères pour créer le titre... seulement la chaîne fournie par le <a href="http://docs.python.org/lib/module-cgi.html">module CGI</a> est une chaîne de caractères codés sur un octet ! Donc mes caractères accentués tenaient sur deux "caractères" (oui ça commence à ressembler du Raymond Devos) et un caractère malheureux se trouvait à cheval sur la frontière du 20ème octet. Chlac ! Coupé en deux le caractère. Du coup, ça ne veut plus rien dire, ce n'est plus de l'utf-8 et les lecteurs de feed retombent en mode "ascii" (un caractère par octet) montrant deux caractères cabalistiques à la place de mes jolis caractères accentuées...<br /><br />J'ai alors patiemment protégé toutes mes entrées en les décodant selon utf-8 et toutes mes sorties en les encodant (en utf-8 toujours). Comme ça, entre les deux, je manipule des chaînes de caractères et pas des chaînes d'octets qui se prennent pour des caractères ! Quelle vilaine vieille habitude de confondre octet et caractère...<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-115108294749617946?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1150926440215626822006-06-21T23:18:00.000+02:002006-06-21T23:47:20.250+02:00Architecture web moderne : introductionJ'initie une série d'articles, en français !, sur ma perception actuelle de comment doit se construire une application web -- dans le but avoué de rationaliser mes idées.<br /><br />Le web est avant tout un grand référentiel. Personne ne se marche sur les pieds grâce aux noms de domaines qui permettent de partitionner l'espace des références.<br /><br />Un nombre limité d'opérations est possible sur ce référentiel et deux seulement d'entre elles sont d'usage courant (GET et POST) que l'on peut résumer à, respectivement, consultation et modification.<br /><br />Le web est prévu pour l'intermédiation : entre deux machines, il peut toujours y en avoir une troisième<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-115092644021562682?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1149622398116051452006-06-06T21:28:00.000+02:002006-06-06T21:33:18.143+02:00And now for something completely differentSwitching to another blog : <a href="http://cgrand.net/lasoupe/">la soupe à la grimace</a>.<br /><br /><i>Bon appétit !</i><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-114962239811605145?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1140559516880724502006-02-21T22:58:00.000+01:002006-06-18T02:34:45.710+02:00If I were a golden fish, I would stick PostIt notes everywhereTo connect to a full remote X desktop using cygwin: go and copy /usr/X11R6/bin/startxdmcp.bat and change SET REMOTE_HOST=10.0.0.1 to match the server you try to connect to.<br />If all you get is a grey screen, ssh to your server go and set "Enable=true" in the [xdmcp] section of /etc/gdm/gdm.conf (if you're not using gdm, rtfm).<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-114055951688072450?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1139345354669625872006-02-07T20:22:00.000+01:002006-02-07T22:01:04.213+01:00Web apps and scopesIn a classical webapp (ie not using <a href="http://en.wikipedia.org/wiki/Continuation">continuations</a>) there's three scopes : application, session and request. The application scope is not that much used except for architectural purposes and the request scope is only useful for data only relevant to the computation at hand. We are left with one single choice: session scope. Consequently we cram in session scope everything not worth of being stored in database.<br />Too much shared state!<br />Okay, alternatively you can store the state in the client. There's two ways:<br /><ul><li>on the one hand, hidden fields and form submissions (through post methods); as far as I know, it's the current design of JSF and Asp.Net (<a href="http://eleguehennec.wordpress.com">Eric</a>, <a href="http://odalet.wordpress.com/">Olivier</a>? someone to confirm?).</li><li>on the other hand, use the url stupid!... and end up passing 3k urls to the user.</li></ul><br /><h3>So what?</h3>Here, on <a href="http://eleguehennec.wordpress.com/">Eric</a>'s request, I'm going to document an idea of design which I've never written down: <span style="font-weight: bold;">webthreads</span>.<br /><br />A webthread is the succession of pages accessed through forward browsing. A webthread can be forked (eg by opening a link in a new tab).<br />First of all, I have to offer a solution to the state storage responsibility problem. The state is stored on the server but an id identifying the state is stored in the browser. Where to store this id? Url or hidden form field? (Cookies aren't an option, they are shared between windows.) My bet goes on the url.<br />So I propose to use pathes of the following form: /[state-id]/[applicative-path].<br /><br />My core design idea is to link states and, hence, when looking up for a property you have to look in the current state and, if the property is not found, you follow the links 'til you find the property... or the end of the webthread.<br /><br />How to link the states? Two solutions: use the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36">Referer</a> http header or rewrite (client-side or server-side) all the urls in the served page.<br />(I'll let the reader to develop the url rewriting way-of-link as an exercise.)<br /><br /></applicative-path></state-id><h3>Synopsis</h3>The client is at /0001/home where (0001 is the state id) and requests /preferences.<br />The server receives the /preferences request with /0001/home as a referer. The server creates a new state (0002), and links 0002 to 0001 (0002 "inherits" all the properties of 0001). The server redirects (<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3">302</a>) the client to /0002/preferences.<br />The client requests /0002/preferences.<br />The server does the actual requested processing and returns its response.<br /><span style="font-style: italic;">Rinse and repeat.<br /></span><br />(If you request /preferences from /0001/home again, you'll be redirected to /0003/preferences)<br /><br />Et voilà! You've got webthreads and their associated scopes!<br /><br /><h3>Ajax (and other page refreshing solutions) bonus</h3>The internal processing (ajax and reloads) of a page can benefit from having a persistent state associated to the page: all such requests take place in the same state, internal requests MUST NOT spawn new states.<br /><br />Here you go Eric, you've got the "interactive page" scope we talked about some times ago!<br /><br />Now it's up to the implementer to refine this mechanism and to define a state invalidation policy (a hard job).<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113934535466962587?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1139263152186368462006-02-06T21:55:00.000+01:002006-02-07T14:11:10.606+01:00Double Proxy PatternHere, I document a behavioral pattern which I often (not <span style="font-style: italic;">very</span> often but quite often) use and <a href="http://c2.com/cgi/fullSearch?search=proxy">which doesn't seem to be much used</a> except by me. Maybe it's an anti-pattern.<br />Here is a classical implementation of the lazy-loading proxy:<br /><pre>interface Banana {<br /> boolean isRipe();<br />}<br />interface BananaLoader {<br /> Banana loadBanana();<br />}<br />class LazyBanana implements Banana {<br /> private Banana banana;<br /> private BananaLoader bananaLoader;<br /> <br /> LazyBanana(BananaLoader bananaLoader) {<br /> this.bananaLoader = bananaLoader;<br /> }<br /><br /> boolean isRipe() {<br /> if (banana == null) {<br /> banana = loader.loadBanana();<br /> }<br /> return banana.isRipe();<br /> }<br />}</pre><br />Which i use like this:<br /><pre>Banana bananaNumberOne = new LazyBanana(new BananaLoader() {<br /> Banana loadBanana() {<br /> return myBananaStore.getById(1);<br /> }<br />});<br />print(bananaNumberOne.isRipe()); // loading occurs now<br />print(bananaNumberOne.isRipe()); // no more loading</pre><br />(The use of a <a href="http://c2.com/cgi/wiki?LexicalClosure">closure</a>-style anonymous class instance is, maybe, not so classical.)<br />The <span style="font-weight: bold;">if</span> bugs me (it feels awkward and out of place) so I found a way to code around it:<br /><pre>interface BananaReference {<br /> Banana getBanana();<br /> void setBanana(Banana banana);<br />}<br />class BananaLazySwapper implements Banana {<br /> private BananaLoader bananaLoader;<br /> private BananaReference bananaReference;<br /><br /> BananaLoader(BananaLoader bananaLoader, BananaReference bananaReference) {<br /> this.bananaLoader = bananaLoader;<br /> this.bananaReference = bananaReference;<br /> }<br /><br /> boolean isRipe() {<br /> Banana banana = bananaLoader.loadBanana();<br /> bananaReference.set(banana);<br /> return banana.isRipe();<br /> }<br />}<br /><br />class LazyBanana2 implements Banana, BananaReference {<br /> private Banana banana;<br /><br /> LazyBanana2(BananaLoader bananaLoader) {<br /> this.banana = new BananaLazySwapper(bananaLoader, this)<br /> }<br /><br /> boolean isRipe() {<br /> return banana.isRipe();<br /> }<br /><br /> Banana getBanana() {<br /> return banana;<br /> }<br /> void setBanana(Banana banana) {<br /> this.banana = banana;<br /> }<br />}</pre><br />(In such a case (lazy loading), BananaReference can be avoided using anonymous inner class)<br /><br />So I've got rid of the if which bugged me. I've traded it for a method lookup. I prefer that <a href="http://c2.com/cgi/wiki?CodeSmell">code smell</a>.<br />Basically, the front-end proxy is a brain-dead proxy whose the proxied object can be replaced at runtime. The back-end proxy performs the actual loading and replace itself (in the first proxy) by the loaded value.<br />Over-architecturing?<br />The Double Proxy Pattern goes far beyond lazy objects. I've first used it while writting a <a href="http://en.wikipedia.org/wiki/Recursive_descent_parser">recursive descent parser</a> with <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a> (à la <a href="http://pdos.csail.mit.edu/%7Ebaford/packrat/thesis/">Packrat</a> plus fixed point detection).<br />Now, I'm using it to manage the lifecycle of persistent objects. A persistent object can have several states: transient, untouched, dirty, stub. There's two distinct lifecycles (depending on whether the object was already persistent) :<br /><ul><li>transient -> untouched -> dirty -> untouched -> dirty -> ...</li><li> stub -> untouched -> dirty -> stub -> untouched -> dirty -> ...</li></ul> (In stub state the persistent object can not be in memory yet (lazy loading) or anymore (reclaimed by the garbage collector).)<br />For managing these lifecycles I use the DDP with four back-end proxies (one per state).<br /><br />This is some actual muscle flexing!<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113926315218636846?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1139256097540184882006-02-06T20:58:00.000+01:002006-02-06T21:02:39.836+01:00Yeah!Today, the postman brought me <a href="http://www.amazon.com/gp/product/B00008R104/sr=1-2/qid=1139255936/ref=sr_1_2/002-8353008-2771261?%5Fencoding=UTF8">this</a>. More info, <a href="http://www.youngbloodbrassband.com/site/site_index.html">here</a> (beware: flash required).<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113925609754018488?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1138748635077594612006-01-31T23:55:00.000+01:002006-02-01T00:06:50.623+01:00What if...Having just explained to a friend what I have learnt using the Spring framework, one thought came to me: an object's methods are divided into three groups. These groups are:<ul><li>methods which are part of an interface implementation,</li><li>methods which express depedencies,</li><li>ancillary methods which matter only for the inner workings of the object at hand (and of its hierarchy).</li></ul>So, what if these three groups where used instead of the classic visibility modifiers (public, protected, private, package etc.)?<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113874863507759461?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1138582256559687822006-01-30T01:33:00.000+01:002006-01-30T01:50:56.570+01:00Inspecting javascript (Rhino) closuresI've spent some time unraveling the <a href="http://www.mozilla.org/rhino/">Mozilla Rhino</a> codebase. My goal was to know how to serialize closures without resorting to java.io.Serializable.<br />I was biased toward static analysis and it took me some time to realize that there was no such thing (static analysis of dynamic programs is, by definition, a though problem -- sometimes you know something but you don't understand it until it jumps right to your face, sigh!).<br />So it's quite simple (I'm just talking for Mozilla Rhino in interpreted mode but my findings hint that compiled mode must not be different): I have to serialize the scopes stack, that's from where the values are coming. So, given a closure (which, being a Javascript object, implements Scriptable), you juste have to serialize closure per se, closure.getParentScope(), closure.getParentScope().getParentScope(), etc. up to top-level.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113858225655968782?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1137105881440264552006-01-12T22:58:00.000+01:002006-01-12T23:44:56.550+01:00Really Simple Decentralized Code Management , part 2Okay, I left the thoughest part: handling conflicts.<br />Set up: three repostories (Alice's, Bob's and Clint's), Clint's polls changes from Bob's and Bob's from Alice's and Alice's from Bob's.<br /><br />Scenario:<br />Alice commits a patch "A" to her repository. Bob commits a patch "B" to his.<br />Bob syncs his repo with Alice's. Applying "A" to his repo fails ("A" and "B" are conflicting). Bob solves the conflict, it results in the patch "C". (Using an informal formalism, I may write C = A / B -- not sure it makes sense.)<br />What must Bob publish as his patch feed? B then A? B then C?<br />If he publishes "B then A" then Clint's repository will fails to apply A after B in the same way Bob's had.<br />If he publishes "B then C" then how to tell Alice not to apply C after having applied B ?<br /><br />I have to read <a href="http://abridgegame.org/darcs/manual/node8.html">this</a> again, it may shed light on the problem at hand.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113710588144026455?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1137021618784341502006-01-11T23:45:00.000+01:002006-01-12T00:37:04.883+01:00Really Simple Decentralized Code ManagementRecipe:<ol><li><b>on the server repository:</b>use CVS/SVN triggers to generate ATOM/RSS patches feeds (one feed per branch) in an apache-served directory.</li><br /><li><b>on the client repository:</b>create an agent which polls the incoming feeds from the server repositories, try to apply the incoming patches on an up-to-date working copy (need to maintain a list of the already applied patches, &lt;atom:id&gt; element would be handful for this purpose). If the patches had been applied successfully then commit the modifications else notify the repository maintainer to fix it.</li><br /><li>set up the other way round (the client becomes the server and the server becomes the client)</li><br /><li>set up more repositories</li><br /><li>profit!</li></ol><br />Caveats:<ul><li>after applying a patch from another repository, the local repository MUST use the original id for this patch when putting it in the patch feed, this to allow repositories to detect already applied patches and, hence, to not apply them again.</li><br /><li>must define when to remove a patch from the patch list or a way for the client repository to request the server repository for all patches since last poll. Clever use of HTTP headers may be useful: ETag, Vary and If-None-Match would allow to generate feed containing at least all the requested patches and to efficiently use caches.</li><br /><li>must agree on the patch format</li></ul><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113702161878434150?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1137018063877573892006-01-11T22:49:00.000+01:002006-01-11T23:21:03.896+01:00Metcalfe's lawStumbling once again over the <a href="http://en.wikipedia.org/wiki/Metcalfe's_Law">Metcalfe's law</a> (the value of a network equals approximately the square of the number of users of the system) made me rethink of a pet pondering: at work, we once chose <a href="http://www.cps-project.org/">CPS</a> over <a href="http://www.plone.org/">Plone</a>.<br />I was a Plone proponent, I made it clear but I didn't try to argue further because I wasn't going to work on this project and one of those who were was feeling far more comfortable with CPS (partly because of a greater amount of documentation written in French -- <a href="http://www.nuxeo.com">Nuxeo</a>, the company behind CPS is french). <br />CPS and Plone features are very similar. To close to tell. So I was left with only three arguments: community size, community size and community size. So, according to Metcalfe's law, the gap between the communities is wider cause the communities "have to be squared". <br /><br />...And, by the way, the Plone's default ui is far better than CPS's one!<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113701806387757389?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com1tag:blogger.com,1999:blog-9751963.post-1136584534666561782006-01-06T22:38:00.000+01:002006-01-06T23:00:20.643+01:00digest: Starbucks Economics<i>The British supermarket Tesco has a "value" line of products with infamously ugly packaging, not because good designers are unavailable but because the supermarket wants to scare away customers who would willingly spend more.</i><br /><br />Emile Dupuit's quote: "It is not because of the few thousand francs which would have to be spent to put a roof over the third-class carriage or to upholster the third-class seats that some company or other has open carriages with wooden benches ... <i>What the company is trying to do is prevent the passengers who can pay the second-class fare from traveling third class;<br />it hits the poor, not because it wants to hurt them, but to frighten the rich ...</i> And it is again for the same reason that the companies, having proved almost cruel to the third-class passengers and mean to the second-class ones, become lavish in dealing with first-class customers. Having refused the poor what is necessary, they give the rich what is<br />superfluous."<br />("Ce n’est pas pour les quelques milliers de francs qu’il serait nécessaire de dépenser pour mettre un toit sur les wagons de troisième classe ou pour mettre des coussins sur les sièges que telle ou telle compagnie a des wagons ouverts avec des bancs de bois. <i>Ce que les compagnies essaient de faire est d’empecher les passagers pouvant se payer un billet de seconde de voyager en troisième. On frappe les pauvres non pas parce qu’on souhaite les frapper, mais pour effrayer les riches.</i> Et c’est le meme raisonnement qui conduit ces mêmes compagnies à être très généreuses sur l’équipement de la première classe. Ayant refusé aux pauvres ce qui était nécessaire, elles offrent aux riches ce qui est superflu.")<br /><br /><i>Starbucks' gambit is much simpler and more audacious: Offer the cheaper product but make sure that it is available only to those customers who face the uncertainty and embarrassment of having to request it specifically. Fortunately, the tactic is easily circumvented: If you'd like a better coffee for less, just ask.</i><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113658453466656178?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1135296006980399622005-12-23T00:07:00.000+01:002005-12-23T01:06:05.630+01:00the web is the computer, intro<em>aka "The network is the computer, redux"</em><br /><br />One of the first thing I alaways say when I'm trying and explain HTTP is to think of the web as of an associative array indexed on URLs. Repetition made me realize that HTTP was some kind of a bus between one CPU and some memory -- that, somehow, the Web was some kind of post-Von Neumann computer. I put this thought aside in a corner of my mind labeled "crap decantation area".<br /><br />Recently, I had started using del.icio.us and thinking (as many others) about tags.<br /><br />And the former idea kept popping up and bugging me.<br /><br />It took me a while to see a relation between del.icio.us and the "the web is the computer" thesis because I was focusing on some too low-level stuff. Del.icio.us is not some part of the computer, it's a component of the operating system. <strong>Del.icio.us is the file system.</strong><br /><br />Meanwhile, the Atom folks (and to somee extent, some Microsofties led by Ray Ozzie and the Googlers behind Base) seem to be working on a better storage facility. An <a href="http://www.xml.com/pub/a/2005/09/21/atom-store-web-database.html">Atom store</a> would be a data store accessed via the Atom Publishing Protocol.<br /><br />So here is what we get: a file system and a storage facility. My question is: is it time to build other services on top of such? Imagine a webmail (or a photosharing or...) which would store mails where you want (by an Atom store provider) and classify them using del.icio.us?<br />Or may be it rephrased: is it time to start commoditizing web apps? Hence, you would be able to switch some webapp provider for another while keeping your data safe in del.icio.us and in your atom store.<br /><br /><em>Yet more stuff for the "crap decantation area".</em><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113529600698039962?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1135033234557613882005-12-19T23:48:00.000+01:002005-12-21T00:57:18.076+01:00Firefox 1.5 segfaulting, flash not playing? Here's a solutionMy firefox installation sometimes segfaulted at startup and flash wasn't recongized any more (I kept trying and install the flash plugin, both automatically and manually, to no avail). I tried to run firefox with :<br /><pre><br />firefox -ProfileManager -safe-mode<br /></pre><br />and created a new profile. What did I get? Flash and no segfault.<br /><br />So i started uninstalling extensions until it worked and, in my case, the culprit seems to have been <a href="http://www.foxytunes.org/firefox/">Foxytunes</a>.<br /><br />Hope this help.<br /><br /><b>edit:</b> a very old version of sessionsaver (it wasn't even listed in the installed extensions) was messing my firefox and happily segfaulting it.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113503323455761388?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1134080327878521032005-12-08T23:08:00.000+01:002006-02-06T21:11:59.566+01:00I'm back!Okay, I've just finished the redesign of this blog.<br /><br />I've chosen a very classic Blogger design with some minor (CSS) tweaks but also with major (or less minor) tweaks.<br /><br />This Blogger blog is now a <a href="http://en.wikipedia.org/wiki/Mashup_%28web_application_hybrid%29">mini mashup</a>: I use the <a href="http://del.icio.us">del.icio.us</a> services to categorize my posts. (See the "Filed under:" field below some titles and the categories cloud on the right, just below the archives links.)<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-113408032787852103?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com2tag:blogger.com,1999:blog-9751963.post-1119473295909537972005-06-22T22:48:00.000+02:002005-06-22T22:50:47.656+02:00Misc. links<p class="mobile-post">On design (pragmatism vs perfectionism):<br /><a href="http://www.shirky.com/writings/evolve.html">http://www.shirky.com/writings/evolve.html</a><br />On geeks cultures, by Neal Stephenson:<br /><a href="http://www.nytimes.com/2005/06/17/opinion/17stephenson.html">http://www.nytimes.com/2005/06/17/opinion/17stephenson.html</a></p><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-111947329590953797?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1112429782061343102005-04-02T10:08:00.000+02:002005-04-02T14:42:11.706+02:00Tim Bray searching for ChansonniersYou can find used copies of "La fabuleuse histoire de Mister Swing" by Michel Jonasz <a href="http://www.priceminister.com/navigation?action=search&category=search_music&keyword=mister+swing&noicon=false&ss=15&x=0&y=0">here</a> [priceminister.com], some of the vendors will certainly ship to Canada.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-111242978206134310?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1109186243908832292005-02-23T20:16:00.000+01:002005-02-23T20:17:23.906+01:00IFrame or XmlHTTPRequest<span style="font-style: italic;">I mentioned before that there was some advantage to be had by using a hidden IFrame over making direct XMLHttp requests. One of these is that the IFrame's state affects the back button. So every time you do a search, it creates a new history entry. This creates an excellent user experience, because pressing the back button always takes you back to the last major action you performed (and the forward button works just as well).</span><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-110918624390883229?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1106924989590616182005-01-28T16:09:00.000+01:002005-12-08T21:38:06.270+01:00I'm my best friend!Today, I was painfully trying to reactivate a 8-month-old prototype feature (a change in the way a project interfaces to Crystal Reports). The problems were the same we have previously encountered -- but, at the time, I was not on the project, just giving some technical hints and support. The most helpful messages thread I found in the Business Objects forums is a 8-month-old reply from me to me on these problems.<br /><br />So, I'm my best friend.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-110692498959061618?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1106684589780328432005-01-25T21:07:00.000+01:002005-01-25T21:23:09.780+01:00The bretzel saved my lifeLuckily I wasn't eating a bretzel while reading <a href="http://www.spiegel.de/netzwelt/netzkultur/0,1518,338441,00.html">this</a> (the most interesting part is quoted and translated below) otherwise I would still be choking. <br /> <br /><span style="font-style: italic;font-family:Verdana,Arial,Helvetica,Geneva,sans-serif;font-size:85%;" > Nach Erhebungen des Browser-Fachdienstes WebSideStory wurde der Firefox Anfang Januar von 4,6 Prozent aller Internet-Nutzer eingesetzt. Bis Mitte des Jahres wird ein Anstieg auf rund zehn Prozent für möglich gehalten. Der Marktanteil des Internet Explorers ist bereits von 95,5 Prozent im Juni 2004 auf 90,6 Prozent zurückgegangen. In Deutschland dürften diese Zahlen jedoch krass anders aussehen: Die Log-Statistiken von SPIEGEL ONLINE (beruhend auf über 200 Millionen Seitenaufrufen im Monat) sehen <span style="font-weight: bold;">die Mozilla-Browser inzwischen bei rund 27 Prozent Marktanteil, der Internet Explorer ist derweil auf knapp 65 Prozent abgesackt</span>.</span> <br /> <br />Courtesy of google language tools: <br /><span style="font-style: italic;font-family:Verdana,Arial,Helvetica,Geneva,sans-serif;font-size:85%;" >After collections of the Browser specialty service WebSideStory the Firefox at the beginning of was used January by 4,6 per cent of all InterNet users. To center of the yearly a rise is considered possible approximately ten per cent. The market share of the InterNet Explorers already decreased/went back from 95,5 per cent in June 2004 on 90,6 per cent. In Germany these numbers might look however glaringly differently: The log statistics of MIRROR ON-line ONE (being based on over 200 million side calls in the month) see <span style="font-weight: bold;">the Mozilla Browser in the meantime with approximately 27 per cent market share, the InterNet Explorer are meanwhile on scarcely 65 per cent sagged</span>.</span><span style="font-size:85%;"> <br /></span> <br />If you don't know Der Spiegel, think Time or Newsweek. Wow! Is the web ignited? <br /><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-110668458978032843?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1106682379628827662005-01-25T19:47:00.000+01:002005-01-25T23:50:06.800+01:00Second System SyndromThe buzz is starting again about richer web interfaces (think GMail, Google Suggest), <a href="http://developers.slashdot.org/article.pl?sid=05/01/24/125236">slashdot amplifying it</a>. <br />I've got my lot of thoughts to share about this because I'm currently incubating the <a href="http://c2.com/cgi/wiki?SecondSystemEffect">Second System Syndrom</a> as my current task is coming to an end. I've been busy designing and building a JSP taglib and some strutseries using JavaScript, JSON and XMLHttpRequest. <br /> <br /><span style="font-weight: bold;font-size:130%;" >Shared Model</span> <br />The model has to be shared between the client and the server. Or, to be more precise, the model has to be replicated: currently, I'm sending far more data through background HTTP requests that what I need (and because of the new UI opportunities offered by the taglib the forms tend to be more complex and hence contain more data). <br />I'm currently envisioning two ways of doing it: <br /><ul> <li>directly mapping the Java Beans to replication enabled JS Objects.</li> <li>using server-side Javascript (Mozilla Rhino) with native objects and proxy objects on the client-side. <br /></li> </ul> I'm afraid of the second solution being overkill (and I've not even thought how to make it fit with Struts) but I've got the intuition that useful things can be achieved with such a design but I can't say which. Anybody? <br /> <br /><span style="font-size:130%;"><span style="font-weight: bold;">Toward more RESTful web applications</span></span> <br />It may seem counter-intuitive but, given (ideally taglib-enforced) guidelines, enhanced web UIs make the web more RESTful. My main arguments are : <br />better HTTP support (PUT, DELETE methods) <br />less cluttered URL-space: far less technical URLs (some things can be entirely done client-side and the others can all be crammed into a couple of endpoints -- not that I support such extremities), so far more URLs really represent business processes and business objects. <br /> <br />Concerning REST, the major impediment (in my ecosystem) is Struts (mantra: must, not, abandon, Struts). Concerning business objects, I certainly could benefit from embedding the hibernate IDs into my URLs but I didn't really thought about it yet. <br /><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-110668237962882766?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0tag:blogger.com,1999:blog-9751963.post-1106431041084657062005-01-22T13:32:00.000+01:002005-01-22T22:57:21.083+01:00Java 5 annotations **sigh!**I haven't yet use them that I run into their limits. (I'm not saying they will not help in managing the complexity; just that I already envision impossible applications.) <br />I just checked the Jsr 175 and the <a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/mirror/com/sun/mirror/util/package-summary.html">com.sun.mirror package</a> over and sadly apt doesn't give access to the AST of the java class being compiled. What I've got in mind while browsing this stuff was to check whether it would be feasible to implement annonations inference (à la Hindley-Milner).<span class="down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"></span><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9751963-110643104108465706?l=cgrand.net%2Fblog%2Findex.html'/></div>Christophe Grandnoreply@blogger.com0