<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7054543485166290590</id><updated>2024-09-09T07:16:37.236-07:00</updated><category term="technology"/><category term="tool"/><category term="REST"/><category term="others"/><category term="ubuntu"/><category term="research"/><category term="scalability"/><category term="Java"/><category term="http"/><category term="javascript"/><category term="web"/><category term="node.js"/><category term="service"/><category term="service orchestration"/><category term="LaTeX"/><category term="ajax"/><category term="prezi"/><category term="CFP"/><category term="Chrome"/><category term="asynchronous processing"/><category term="bootstrap"/><category term="browser"/><category term="client"/><category term="concurrency"/><category term="cookie"/><category term="css"/><category term="erlang"/><category term="event-driven programming"/><category term="firefox"/><category term="function"/><category term="mashup"/><category term="printer"/><category term="programming"/><category term="proxy"/><category term="resource"/><category term="s3"/><category term="service composition"/><category term="time"/><category term="201"/><category term="303"/><category term="503"/><category term="API"/><category term="Canada"/><category term="China"/><category term="EPS"/><category term="Eclipse"/><category term="Express.js"/><category term="GET"/><category term="GFCI"/><category term="GoogleApp"/><category term="IE"/><category term="JSON"/><category term="JUnit"/><category term="POST"/><category term="PS"/><category term="PUT"/><category term="String"/><category term="URL"/><category term="UofS"/><category term="XSS"/><category term="apple"/><category term="application"/><category term="aptana"/><category term="authorization"/><category term="availability"/><category term="benchmarking"/><category term="big data"/><category term="binding"/><category term="blog"/><category term="c10k"/><category term="ca"/><category term="cache"/><category term="caching"/><category term="calendar"/><category term="cloud"/><category term="cloud computing"/><category term="comet"/><category term="comic"/><category term="computational baton"/><category term="consistency"/><category term="content"/><category term="decentralized"/><category term="distributed"/><category term="donate"/><category term="earthquake"/><category term="email"/><category term="epics"/><category term="fake"/><category term="flash"/><category term="git"/><category term="gossip"/><category term="heavy tail"/><category term="idempotence"/><category term="jetty"/><category term="landscape"/><category term="latex2html"/><category term="livestamp"/><category term="load"/><category term="long tail"/><category term="middleware"/><category term="moment"/><category term="multi-process"/><category term="multithreading"/><category term="multiton"/><category term="mvc"/><category term="netpbm"/><category term="network"/><category term="nitrogen"/><category term="non-blocking I/O"/><category term="nvidia"/><category term="optiplex"/><category term="p2p"/><category term="pattern"/><category term="pdflatex"/><category term="pdftex"/><category term="performance"/><category term="perl"/><category term="pipe"/><category term="power"/><category term="power law"/><category term="problem"/><category term="process"/><category term="pyepics"/><category term="python"/><category term="rc10k"/><category term="refactoring"/><category term="release"/><category term="reload"/><category term="request"/><category term="sanitizer"/><category term="security"/><category term="stream"/><category term="switch"/><category term="synchronization"/><category term="tail"/><category term="testing"/><category term="traveler"/><category term="typeahead"/><category term="virtualbox"/><category term="visio"/><category term="visualization"/><category term="wideFinder"/><category term="windows"/><category term="xkcd"/><category term="zipf"/><title type='text'>Serenity Leads to a Further Vision</title><subtitle type='html'>Notes on research, technology, and life</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>116</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-168197432851050999</id><published>2022-06-11T13:33:00.001-07:00</published><updated>2022-06-11T13:33:58.703-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>RFC 9110 HTTP Semantics - a refresh and must-read for everyone working on HTTP based applications</title><content type='html'>&lt;p&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#&quot; target=&quot;_blank&quot;&gt;RFC 9110 HTTP Semantics&lt;/a&gt;, authored by Fielding,&amp;nbsp;Nottingham, and Reschke, was just published. This is a great refreshing summary of what we learned about the web protocol evolving over three versions. As it states, the core semantics of HTTP did not change. That core and the ideas behind it were described by Fielding in &lt;a href=&quot;https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm&quot; target=&quot;_blank&quot;&gt;his thesis&lt;/a&gt;, and known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Representational_state_transfer&quot; target=&quot;_blank&quot;&gt;REST&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The document is long, and I just went through it once. It is a great technical document in terms of precision and self-descriptive.&amp;nbsp;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The Hypertext Transfer Protocol (HTTP) is a family of stateless, application-level, request/response protocols that share a generic interface, extensible semantics, and self-descriptive messages to enable flexible interaction with network-based hypertext information systems.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;What a precise description.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/168197432851050999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2022/06/rfc-9110-http-semantics-refresh-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/168197432851050999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/168197432851050999'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2022/06/rfc-9110-http-semantics-refresh-and.html' title='RFC 9110 HTTP Semantics - a refresh and must-read for everyone working on HTTP based applications'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-242385666092242560</id><published>2018-08-01T00:36:00.002-07:00</published><updated>2018-08-01T00:37:48.762-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="idempotence"/><category scheme="http://www.blogger.com/atom/ns#" term="programming"/><category scheme="http://www.blogger.com/atom/ns#" term="sanitizer"/><category scheme="http://www.blogger.com/atom/ns#" term="XSS"/><title type='text'>Idempotentise String Sanitizers</title><content type='html'>I just made up a word idempotentise — to make a given function idempotent. I am neither an XSS expert, nor an expert sanitizer user, but had to deal with sanitizers on both server side and client side in order to either prevent the injection or fix an injection incident. I did a little research before writing this note, and found &lt;a href=&quot;http://webblaze.cs.berkeley.edu/papers/bek.pdf&quot;&gt;idempotence is a standard characteristics for sanitizers&lt;/a&gt;. However, the sanitizer in your system(either home brewed or )might not be idempotent when your regular work or sleep is interrupted by a P0 security incident that a smart hacker with her/his robots just carefully constructs a new test string that is not in the &lt;a href=&quot;https://github.com/cure53/H5SC&quot;&gt;cheatsheets&lt;/a&gt;.&lt;br /&gt;
&lt;h4&gt;
What is idempotence anyway?&lt;/h4&gt;
&lt;div class=&quot;graf graf--p graf-after--h3&quot; id=&quot;d1b0&quot; name=&quot;d1b0&quot; style=&quot;--baseline-multiplier: 0.17; line-height: 1.58; margin-top: 8px;&quot;&gt;
&lt;span style=&quot;font-feature-settings: &amp;quot;liga&amp;quot;, &amp;quot;salt&amp;quot;;&quot;&gt;I stopped having difficulty to spell the work correctly until I read &lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Idempotence&quot; style=&quot;font-feature-settings: &amp;quot;liga&amp;quot;, &amp;quot;salt&amp;quot;;&quot;&gt;the wikipedia page&lt;/a&gt;&lt;span style=&quot;font-feature-settings: &amp;quot;liga&amp;quot;, &amp;quot;salt&amp;quot;;&quot;&gt; and realized that idem means identical or the same, and potence is the power. So in mathematical notation, it means the output does not change no matter how many times a function is applied to the input, or&lt;/span&gt;&lt;br /&gt;
&lt;i&gt;f(x) = f(f(x)),&lt;/i&gt;&lt;/div&gt;
where &lt;i&gt;f &lt;/i&gt;is the function, and &lt;i&gt;x &lt;/i&gt;is the input. Obviously, if the output does not change when the function is applied twice, it will not when the function is applied more times.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h4&gt;
Idempotence issue for string sanitizers&lt;/h4&gt;
Different from mathematical functions whose idempotence can be proved, we are very difficult to &lt;b&gt;prove&lt;/b&gt; the idempotence of a sanitizer by &lt;b&gt;testing&lt;/b&gt;. It is because we construct the sanitizers in a case-based way. We will only see the idempotence issue when the problem string instance is inputed. The idempotence issue become more complicated when a string goes through several sanitizers from user input to browser rendering.&lt;/div&gt;
&lt;div&gt;
&lt;h4&gt;
Idempotentise it&lt;/h4&gt;
The solution turns out to be really simple. We just need a wrapper function for any given sanitizer such that&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;w(w(s(x))) = w(s(x)),&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
where &lt;i&gt;s&lt;/i&gt; is the sanitizer function, and w is the wrapper function. For any sanitizer, the wrapper can be implemented by apply &lt;i&gt;s&lt;/i&gt; recursively up to &lt;i&gt;k&lt;/i&gt; times:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;w(x) = s(s(s(…s(x))))&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
such that &lt;i&gt;s(w(x)) = w(x)&lt;/i&gt;. If &lt;i&gt;s(w(x)) != w(x)&lt;/i&gt;, then let &lt;i&gt;w(x)&lt;/i&gt; = empty string. We will want to log the input string, and harden the sanitizer function so that it converges within &lt;i&gt;k&lt;/i&gt; steps.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/242385666092242560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2018/08/idempotentise-string-sanitizers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/242385666092242560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/242385666092242560'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2018/08/idempotentise-string-sanitizers.html' title='Idempotentise String Sanitizers'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-1404554611626676660</id><published>2017-11-02T22:13:00.000-07:00</published><updated>2017-11-02T22:14:41.156-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ajax"/><category scheme="http://www.blogger.com/atom/ns#" term="concurrency"/><category scheme="http://www.blogger.com/atom/ns#" term="multiton"/><category scheme="http://www.blogger.com/atom/ns#" term="proxy"/><category scheme="http://www.blogger.com/atom/ns#" term="scalability"/><title type='text'>The multiton pattern on client side</title><content type='html'>A while ago, I wrote a post about &lt;a href=&quot;http://dongnotes.blogspot.com/2016/02/resource-representation-generation-on.html&quot; target=&quot;_blank&quot;&gt;the multiton pattern on server side&lt;/a&gt; that utilizes a fixed amount of computation resource to serve a large number of concurrent requests for the same representation that needs to be generated on demand. The pattern helps to avoid racing condition that the concurrent request processing could lead to.&lt;br /&gt;
&lt;br /&gt;
Each resource instance in the registry or map works like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Proxy_pattern&quot; target=&quot;_blank&quot;&gt;proxy&lt;/a&gt;. The proxy&#39;s interface is the same for the consumers no matter it arrives early or late. Inside the proxy, the representation is produced on demand, or retrieved from a cache or a persistent copy.&lt;br /&gt;
&lt;br /&gt;
A client side multition can reduce not only client side load but also the server side load and the traffic between client and server. Consider a client side JS component that depends on a remote resource. When loading, the component will issue an AJAX call to the remote resource and render when the resource representation is available. A problem arises when there are many such component instances on a page targeting the same remote resource. The server side will see multiple concurrent requests of the same resource representation when the client loads such a page. That will make backend services busy for a while. While a server side multition can help, a client side multition will further reduce the client side load on a page and also reduce the connection numbers and network traffic to the server.</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/1404554611626676660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2017/11/the-multiton-pattern-on-client-side.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/1404554611626676660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/1404554611626676660'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2017/11/the-multiton-pattern-on-client-side.html' title='The multiton pattern on client side'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-5563078441005416356</id><published>2016-11-01T17:30:00.000-07:00</published><updated>2016-11-03T07:17:24.654-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="computational baton"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="service composition"/><category scheme="http://www.blogger.com/atom/ns#" term="service orchestration"/><title type='text'>YouTube video play across linked devices and RESTful service composition</title><content type='html'>When I was asked to explain &lt;a href=&quot;http://homepage.usask.ca/~dol142/Files/rsc.pdf&quot; target=&quot;_blank&quot;&gt;RESTful service composition&lt;/a&gt;, and what I mean by &lt;a href=&quot;https://docs.google.com/presentation/d/1YXICJhtcEcScdiIDrIwo7uv8iDIG-TrT6p8RPkV4xiA/pub?start=false&amp;amp;loop=false&amp;amp;delayms=3000&quot; target=&quot;_blank&quot;&gt;staged computation and baton passing&lt;/a&gt;, I always need to find a concrete example instead of using abstract terms. &lt;a href=&quot;https://support.google.com/youtube/answer/3230451&quot; target=&quot;_blank&quot;&gt;Watching YouTube video across devices&lt;/a&gt; is a good case for this purpose.&lt;br /&gt;
&lt;br /&gt;
A user can find a video on YouTube on her/his mobile device, and then continue play it on any other TV devices linked to YouTube. S/he can also stop/resume/skip on the mobile device or the current playing device. It is also easy to switch between linked active TV devices.&lt;br /&gt;
&lt;br /&gt;
We can think the video play as a service composition. The goal of the composition is to play the video to the audience. The composition is composed of YouTube, the mobile device, and linked devices. The audience is the consumer of the composition.&lt;br /&gt;
&lt;br /&gt;
Watching video across devices can be viewed as a RESTful service composition. The computation is the play of the video from user starting to watch one to stopping it. It can have several &lt;b&gt;stages&lt;/b&gt;, each of which the video is played on one device.&lt;br /&gt;
&lt;br /&gt;
When the video play is switched from one device to the other, the &lt;b&gt;computational baton&lt;/b&gt; is passed. The next device continued the computation according to where the play should start from, and perhaps audience&#39;s other&amp;nbsp;preferences. But the computation has to be adjusted according to the current device&#39;s resource, like screen size and bandwidth. &amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
So is there a central conductor service? Maybe user&#39;s mobile device works like a conductor, but the playing of the video is in fact carried out by the linked devices and the mobile device itself. The linked device can also stop/resume/skip anytime.</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/5563078441005416356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/11/youtube-video-play-across-linked.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5563078441005416356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5563078441005416356'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/11/youtube-video-play-across-linked.html' title='YouTube video play across linked devices and RESTful service composition'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-7552627444270795083</id><published>2016-10-13T19:48:00.000-07:00</published><updated>2016-10-14T07:40:59.293-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="application"/><category scheme="http://www.blogger.com/atom/ns#" term="content"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>From User Generated Contents to User Defined Applications</title><content type='html'>In short: I think an interesting thing we can achieve on the Web is user-defined applications. The technologies, services, and most importantly, capability of web users will enable it.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
You can&#39;t connect the dots looking forward; you can only connect them looking backwards.&lt;/blockquote&gt;
&lt;div style=&quot;text-align: right;&quot;&gt;
---Steve Jobs, &lt;i&gt;You&#39;ve got to find what you love&lt;/i&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
We drive into the future using only our rearview mirror.&lt;/blockquote&gt;
&lt;div style=&quot;text-align: right;&quot;&gt;
---Marshall McLuhan&lt;/div&gt;
&lt;h3&gt;
We learned how to use email instantly &lt;/h3&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ray_Tomlinson&quot; target=&quot;_blank&quot;&gt;Ray Tomlinson&lt;/a&gt; will be remembered by his invention of email in 1971. @ became one of the most well known special characters used everyday. Although email at first &lt;a href=&quot;http://doi.acm.org/10.1145/354384.354390&quot; target=&quot;_blank&quot;&gt;imitated the paper mails&lt;/a&gt;, it has been eliminating paper mails, and furthermore changed the way of human communication. Still, in 1994, about 20 years after the invention of email, I knew nothing about email and did not know how to send an email when going to a university of about 50 thousand students. As I knew, at that time, there were only two computers that can be used to send emails on the campus. About one year after &lt;a href=&quot;https://en.wikipedia.org/wiki/CERNET&quot; target=&quot;_blank&quot;&gt;CERNET&lt;/a&gt; became available on campus, every students I met in computer classrooms had at least two email addresses. Everyone knew how to use email in a night. An interesting story is that the creator of a popular &lt;a href=&quot;https://en.wikipedia.org/wiki/Foxmail&quot; target=&quot;_blank&quot;&gt;Foxmail &lt;/a&gt;email client in China, Zhang Xiaolong, recently leaded the development of &lt;a href=&quot;https://en.wikipedia.org/wiki/WeChat&quot; target=&quot;_blank&quot;&gt;WeChat &lt;/a&gt;application.&lt;br /&gt;
&lt;h3&gt;
People know how to surf the Web&lt;/h3&gt;
The first thing I would do was to open a browser when I spent booked computer time (in Chinese universities, it was called &quot;machine time&quot;.). There were not too many websites in China at that time. I needed to write several popular domain names and interesting links on my paper notebook, and took it with me when I went to the computer classrooms. There were no real &quot;personal&quot; computers. Yahoo and other similar websites were big helpers for users like me at that time. Later when Google was known, all the portal web sites started their dying process. The new generation of web users in China, like my father, started to surf the web on their smart phones.&lt;br /&gt;
&lt;h3&gt;
We like sharing&lt;/h3&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Apache_HTTP_Server&quot; target=&quot;_blank&quot;&gt;httpd&lt;/a&gt; was the starting point of the whole &lt;a href=&quot;https://en.wikipedia.org/wiki/Apache_Software_Foundation&quot; target=&quot;_blank&quot;&gt;Apache foundation&lt;/a&gt;, and somehow the same for the open source movement. httpd gave individuals and organizations an online space to share contents about themselves. Almost everyone in academic research community got a personal homepage where they talked about their teaching, research, and personal life. They easily got attention from colleagues and students.&lt;br /&gt;
&lt;br /&gt;
Blogs and wikis soon emerged as tools to encourage sharing, discussion, and collaboration. At the beginning, only experienced computer users knew how to set up blogs or wikis on organization servers or personal computers connected to research or commercial networks. There were so many blog software, and the &lt;a href=&quot;http://blosxom.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;one I tried&lt;/a&gt; was in Perl. Blog services soon kicked the blog software out and they became the major players. Most bloggers migrated their blogs from their personal or organization server to blog services. Blog readers, like&amp;nbsp;&lt;a href=&quot;https://www.google.com/reader/about/&quot; target=&quot;_blank&quot;&gt;Google reader&lt;/a&gt;,&amp;nbsp;came to the hype when bloggers generated a big amount of blog daily. &amp;nbsp;The reader was later killed by Google in order to give way to Google&#39;s leading social platform Google+. Google+ was the Google&#39;s answer for the emerging social content and networking market led by Facebook. &lt;br /&gt;
&lt;h3&gt;
We create media&lt;/h3&gt;
If writing blogs and wikis takes a while, just wording a sentence, taking a picture or recording a video seems easier. The success of youtube and iPhone generated an explosion of user-created media. That also very naturally makes&amp;nbsp;sharing digital media the No.1 feature of&amp;nbsp;the new generation of social applications. The traditional way of content distribution saw an end of its life. Newspapers and magazines become pale when every normal people can publish and distribute their &lt;b&gt;stories&lt;/b&gt; in the media. The reception of media, no matter positive or negative, makes everyone how deeply they are connected to the rest of world.&lt;br /&gt;
&lt;h3&gt;
We created applications on the Web&amp;nbsp;&lt;/h3&gt;
An application helps its users to perform a task. &lt;a href=&quot;https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm&quot; target=&quot;_blank&quot;&gt;Hypermedia&lt;/a&gt; itself is an application. When creating hypermedia, we already created applications. &lt;br /&gt;
&lt;br /&gt;
We put links in our pages to guide the readers to explore related concepts and stories. When a page is carefully crafted, the author knows where the readers will land.&lt;br /&gt;
&lt;br /&gt;
We add forms in the pages to accept readers&#39; inputs. Forms provide a way to start interactions between peers including the one who initially created the content. Comments and ratings are all forms.&lt;br /&gt;
&lt;h3&gt;
Can we do more&lt;/h3&gt;
Yes, people can do much more on the web. And in fact, we already started. We can easily define personal web sites including blogs and wikis. We can easily embed contents created and hosted on different providers. We can easily stream media from web across devices. We can easily set up personal shops without worrying about payments and shipping details. Can we go even further?&lt;br /&gt;
&lt;h3&gt;
&lt;b&gt;What is user defined application&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
A user-defined web application is a web application generated by a platform from user defined specifications. It provides both basic graphical user interface (GUI) and application program interface (API). Ideally, the platform provides an environment for users to compose and test the application specifications. A user defines&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;the structure and types of data that application will store,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;the representation of the data for human and other applications,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;the generation of new data and corresponding new representations that will be triggered by user or application interaction, and&amp;nbsp;&lt;/li&gt;
&lt;li&gt;who or what applications can interact with the application.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;p1&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/7552627444270795083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/10/from-user-generated-contents-to-user.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/7552627444270795083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/7552627444270795083'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/10/from-user-generated-contents-to-user.html' title='From User Generated Contents to User Defined Applications'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-3370403381635333986</id><published>2016-09-26T20:38:00.000-07:00</published><updated>2016-09-26T20:42:43.025-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="asynchronous processing"/><category scheme="http://www.blogger.com/atom/ns#" term="event-driven programming"/><category scheme="http://www.blogger.com/atom/ns#" term="non-blocking I/O"/><category scheme="http://www.blogger.com/atom/ns#" term="scalability"/><title type='text'>Observations of Netflix&#39;s Zuul 2 experience</title><content type='html'>An engineering team from Netflix just published&amp;nbsp;&lt;a href=&quot;http://techblog.netflix.com/2016/09/zuul-2-netflix-journey-to-asynchronous.html&quot; target=&quot;_blank&quot;&gt;their experience of building a new Zuul from synchronous blocking I/O to asynchronous non-blocking I/O&lt;/a&gt;. Here are interesting observations from my perspective.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;1. Asynchronous programming is hard.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Concurrency might be one of the most challenging problem in programming. In Java, multithreading already became the norm for concurrency. The&amp;nbsp;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html&quot; target=&quot;_blank&quot;&gt;Java concurrency package&lt;/a&gt;&amp;nbsp;made programmers&#39; life easier than it really is. But it did not change the nature of concurrency. Testing and debugging of multithreading programs are difficult. Reproducing racing, locking, and suspicious execution sequences are tricking.&lt;br /&gt;
&lt;br /&gt;
If a programmer likes to be the boss in her/his program, then she/he will be disappointed by asynchronous programming. No executing sequence is garnered by the sequence in code. The routines say &quot;&lt;b&gt;do not call me, I&#39;ll call you.&lt;/b&gt;&quot; right after the initial call. The way to testing and debugging is different from that for synchronous code. The biggest challenge is that programmers have to change their mindset&amp;nbsp;&lt;a href=&quot;http://wiki.jvmlangsummit.com/images/a/ab/HickeyJVMSummit2009.pdf&quot; target=&quot;_blank&quot;&gt;from sequential to concurrent&lt;/a&gt;, from a single player game to a multiple player game, and accept the fact that&amp;nbsp;&lt;a href=&quot;http://queue.acm.org/detail.cfm?id=2745385&quot; target=&quot;_blank&quot;&gt;there is no NOW&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. For CPU-intensive independent jobs, there is no big performance difference between multi-threading sync blocking and event-based async non-blocking.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
When discussing performance and scalability, we should first identify the bottleneck. We should see only one bottleneck at one time, and will see a new one emerging when the current one disappears. When the jobs are CPU-intensive, the bottleneck is the CPU, and the throughput is decided by the CPU. Although whether the I/O is blocking or non-blocking will affect how long the processes will wait, its impact on the throughput is shrouded by the processing time in CPU. However, it will definitely have an impact on the memory. I hope the Netflix team could compare the memory profiles.&lt;br /&gt;
&lt;br /&gt;
In fact, multi-threading does not help accelerate CPU-intensive jobs. In theory, multiprocess with a non-blocking I/O feeding to the job queue will do the best, where the number of processes is decided by the number of cores. Timeout might be required when a job takes too long to finish.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. Async is the natural way to work with non-blocking I/O (or async I/O), and it introduces capacity gain for I/O intensive jobs.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
For I/O intensive jobs, the time to finish the I/O will be significant. If resources are occupied while waiting for the I/O to finish, they are wasted and do not contribute to the throughput but results in a high utilization. On the contrary, async frees resources when I/O is going on, and recapture them only when I/O is done and data is available.&amp;nbsp;&lt;a href=&quot;http://www.kegel.com/c10k.html&quot; target=&quot;_blank&quot;&gt;The C10K problem&lt;/a&gt;&amp;nbsp;is the best resource discussing this on the OS level.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;4. Contention and Coherency of a system&amp;nbsp;are decided by both the characteristics of jobs and the system&#39;s implementation.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In the&amp;nbsp;&lt;a href=&quot;http://www.perfdynamics.com/Manifesto/USLscalability.html&quot; target=&quot;_blank&quot;&gt;Universal Scalability Law (USL)&lt;/a&gt;, the contention is the coefficient of the first order penalty for concurrency, and the coherency is that of the second order. The contention represents the part of job that cannot be paralleled. When the number of requests in a system is N, the contention will affect (N-1) of the rest requests. The coherency represents the part of each job that can result in the generation of a new contention, which results in the second order effect.&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Thrashing_(computer_science)&quot; target=&quot;_blank&quot;&gt;Thrashing&lt;/a&gt;&amp;nbsp;occurs typically when coherency penalty is dominant. &amp;nbsp;One example of coherency occurs when a request resulting in updating a DB record finds its copy of data is stale. Obviously, the request needs to wait and try again when its copy gets refreshed. The refreshment time can potentially be affected by all other requests that update the DB. This can also occur to shared memory of multiple threads.&lt;br /&gt;
&lt;br /&gt;
In order to achieve better scalability, we need to design the system so that the contention and coherency can be reduced. Async can do nothing about the contention on CPU, but it can reduce the contention and coherency on I/O.</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/3370403381635333986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/09/observations-of-netflixs-zuul-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3370403381635333986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3370403381635333986'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/09/observations-of-netflixs-zuul-2.html' title='Observations of Netflix&#39;s Zuul 2 experience'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-5023961559794550546</id><published>2016-06-01T19:44:00.002-07:00</published><updated>2016-06-01T19:44:36.036-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><category scheme="http://www.blogger.com/atom/ns#" term="mvc"/><category scheme="http://www.blogger.com/atom/ns#" term="refactoring"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>Refactoring an express.js application without IDE</title><content type='html'>Refactoring is scaring.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://img.pandawhale.com/post-10513-Code-Refactoring-Cat-in-Bathtu-U295.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://img.pandawhale.com/post-10513-Code-Refactoring-Cat-in-Bathtu-U295.gif&quot; height=&quot;279&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Without support from an IDE, it becomes even difficult. However, git can help. I have done refactoring for an &lt;a href=&quot;http://expressjs.com/&quot;&gt;express.js&lt;/a&gt; web application several times recently. I summaries the following rules to make the process less scaring.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Helvetica Neue, Arial, Helvetica, sans-serif;&quot;&gt;Rule 1. branch before refactoring&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
If the refactoring is not going well, you can always go back. &amp;nbsp;In most cases, I have to touch the model, the view, and the controller. If you named your files according to RESTful resource name, it is very likely you need to update the file names.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Helvetica Neue, Arial, Helvetica, sans-serif;&quot;&gt;Rule 2. rename files and then commit before touch the codes inside files&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
After add new files and rm old ones, git will recognized the changes are renaming files. If you make changes inside a renamed file, then the renaming will be lost in git history.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Helvetica Neue, Arial, Helvetica, sans-serif;&quot;&gt;Rule 3. refactoring model first, then the views and client side JavaScript files, and finally the controller&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
In this sequence, you will be able to have a testable application after each code modification. When M-V-C are updated, and all tests passed, you can merge the refactoring branch back to the original one.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/5023961559794550546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/06/refactoring-expressjs-application.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5023961559794550546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5023961559794550546'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/06/refactoring-expressjs-application.html' title='Refactoring an express.js application without IDE'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-6937357007811316093</id><published>2016-05-17T20:58:00.001-07:00</published><updated>2016-05-18T06:08:59.954-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="API"/><category scheme="http://www.blogger.com/atom/ns#" term="POST"/><category scheme="http://www.blogger.com/atom/ns#" term="PUT"/><category scheme="http://www.blogger.com/atom/ns#" term="resource"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>PUT to update a resource state</title><content type='html'>It is common that a resource&#39;s behavior changes according to its state. A online survey is such a resource. A survey can have two states: idle and active. In idle state, a survey does not accept user inputs, and it does in active state. &amp;nbsp;The state transition can be seen in the follow figure.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzf-uOJtt5eWMAOLYtowTrtXjLVOn-88r1TXQbENZ2bDgy_6U3Xduom6weJEvyuzDKeF0Mv85mf-Afoe6niMEVKKcc4qTrBsZ64GcazsteES-zD_o0Wr0dW2zme-BEgtWr1XBmOqdjkoM/s1600/survey+state+%25281%2529.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;130&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzf-uOJtt5eWMAOLYtowTrtXjLVOn-88r1TXQbENZ2bDgy_6U3Xduom6weJEvyuzDKeF0Mv85mf-Afoe6niMEVKKcc4qTrBsZ64GcazsteES-zD_o0Wr0dW2zme-BEgtWr1XBmOqdjkoM/s320/survey+state+%25281%2529.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Survey state transition diagram&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Now if we want to expose the status of the survey as a resource, then we will have the following interfaces.&lt;br /&gt;
&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/surveys/:id/state&lt;/td&gt;
&lt;td&gt;Get the current state of a survey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;/surveys/:id/state&lt;/td&gt;
&lt;td&gt;Set the state of a survey&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
The PUT request can have the state to set in the request body. The response can be 200 with the set result. The response can also be 4xx, if the client has erred. The error can be 403 forbidden, 404 survey not found, or 409 conflict if the state to set is not supported.&lt;br /&gt;
&lt;br /&gt;
I am surprised to see that POST was used to set survey state in &lt;a href=&quot;https://developers.google.com/consumer-surveys/v2/reference/&quot; target=&quot;_blank&quot;&gt;Google&#39;s survey API&lt;/a&gt;. The interfaces are:&lt;br /&gt;
&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;/surveys/:id/start&lt;/td&gt;
&lt;td&gt;set the survey to be active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;/surveys/:id/stop&lt;/td&gt;
&lt;td&gt;set the survey to be idle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
This design has two problems compared to the previous design:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;/surveys/:id/start and&amp;nbsp;/surveys/:id/stop are not resources; and&lt;/li&gt;
&lt;li&gt;POST is not&amp;nbsp;idempotent, but PUT is.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/6937357007811316093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/05/put-to-update-resource-state.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6937357007811316093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6937357007811316093'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/05/put-to-update-resource-state.html' title='PUT to update a resource state'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzf-uOJtt5eWMAOLYtowTrtXjLVOn-88r1TXQbENZ2bDgy_6U3Xduom6weJEvyuzDKeF0Mv85mf-Afoe6niMEVKKcc4qTrBsZ64GcazsteES-zD_o0Wr0dW2zme-BEgtWr1XBmOqdjkoM/s72-c/survey+state+%25281%2529.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4562350114352354143</id><published>2016-03-23T19:33:00.000-07:00</published><updated>2016-03-23T19:33:52.535-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Express.js"/><category scheme="http://www.blogger.com/atom/ns#" term="function"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="middleware"/><title type='text'>An Express middleware to check if a resource exists</title><content type='html'>After writing a similar code snippets for tens of times, I decided to write &lt;a href=&quot;https://gist.github.com/dongliu/8c3fe98661924a565c30&quot; target=&quot;_blank&quot;&gt;a middleware&lt;/a&gt; to handle it. The scenario is quite common. You want to check if an resource exists before going to the next step of processing. In my applications, &amp;nbsp;it is a query in the MongoDB.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;/**
 * A middleware to check if id exists in collection
 * app.get(&#39;/resources/:id/&#39;, exist(&#39;id&#39;, collection), function(req, res){...}}
 * @param  {String} id            the parameter name of item id in req object
 * @param  {Model} collection     the collection model
 * @return {Function}             the middleware
 */
function exist(id, collection) {
  return function (req, res, next) {
    collection.findById(req.params[id]).exec(function (err, item) {
      if (err) {
        console.error(err);
        return res.send(500, &#39;Something wrong!&#39;);
      }

      if (!item) {
        return res.send(404, &#39;item &#39; + req.params[id] + &#39; not found&#39;);
      }
      
      req[req.params[id]] = item;
      next();
    });
  };
}
&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4562350114352354143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/03/an-express-middleware-to-check-if.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4562350114352354143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4562350114352354143'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/03/an-express-middleware-to-check-if.html' title='An Express middleware to check if a resource exists'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-6943325467634631826</id><published>2016-03-23T19:19:00.000-07:00</published><updated>2016-03-23T19:19:55.185-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="prezi"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>Introduction to REST prezi updated </title><content type='html'>I updated &lt;a href=&quot;http://prezi.com/z7mli7zzz9js/?utm_campaign=share&amp;amp;utm_medium=copy&quot; target=&quot;_blank&quot;&gt;the prezi of an introduction to REST&lt;/a&gt;&amp;nbsp;for next week&#39;s MSU Web Dev Cafe meeting next week. The major change was to add a case study to tell of a design is RESTful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;

&lt;iframe id=&quot;iframe_container&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot; width=&quot;550&quot; height=&quot;400&quot; src=&quot;https://prezi.com/embed/z7mli7zzz9js/?bgcolor=ffffff&amp;amp;lock_to_path=1&amp;amp;autoplay=0&amp;amp;autohide_ctrls=0&amp;amp;landing_data=bHVZZmNaNDBIWnNjdEVENDRhZDFNZGNIUE43MHdLNWpsdFJLb2ZHanI5Z1RMalNkQS9sNDVEejM5MkRPREJna3B3PT0&amp;amp;landing_sign=2Pnzib6Ii8AqjusuFA12iKjplIstmKUfhNmsiuP9m9U&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/6943325467634631826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/03/introduction-to-rest-prezi-updated.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6943325467634631826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6943325467634631826'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/03/introduction-to-rest-prezi-updated.html' title='Introduction to REST prezi updated '/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-8571887732763832107</id><published>2016-03-15T05:03:00.000-07:00</published><updated>2016-03-15T07:44:32.830-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ajax"/><category scheme="http://www.blogger.com/atom/ns#" term="cache"/><category scheme="http://www.blogger.com/atom/ns#" term="release"/><category scheme="http://www.blogger.com/atom/ns#" term="reload"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Forcing clients to reload on new application releases</title><content type='html'>Many Web application clients get data via Ajax. The resources including the CSS and JavaScript files &amp;nbsp;are never reloaded once loaded in this way. However, some new features require the clients to retrieve the updated resources from the server. This can be achieved by maintaining a service generated release number on the client, and compare that number with the current release number on the server via Ajax. If the release number is updated, then run&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;window.location.reload(true);&lt;/span&gt;&lt;/blockquote&gt;
You will need to either disable the the cache for the resource of release number, or set cache to &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;b&gt;false&lt;/b&gt;&lt;/span&gt; of the Ajax GET request. </content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/8571887732763832107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/03/forcing-clients-to-reload-on-new.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/8571887732763832107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/8571887732763832107'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/03/forcing-clients-to-reload-on-new.html' title='Forcing clients to reload on new application releases'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4055687541392532118</id><published>2016-02-24T17:00:00.000-08:00</published><updated>2016-08-18T17:29:59.858-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="asynchronous processing"/><category scheme="http://www.blogger.com/atom/ns#" term="concurrency"/><category scheme="http://www.blogger.com/atom/ns#" term="event-driven programming"/><category scheme="http://www.blogger.com/atom/ns#" term="GET"/><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="multithreading"/><category scheme="http://www.blogger.com/atom/ns#" term="node.js"/><category scheme="http://www.blogger.com/atom/ns#" term="pattern"/><category scheme="http://www.blogger.com/atom/ns#" term="resource"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="synchronization"/><title type='text'>Resource representation generation on first GET retrieval</title><content type='html'>&lt;h3&gt;
Intent&lt;/h3&gt;
Provide a resource type that has a new resource representation instance generated on the first retrieval. In HTTP, this is often a GET request to a URL that the resource provides.&lt;br /&gt;
&lt;br /&gt;
Also Known As&lt;br /&gt;
&lt;br /&gt;
The closest pattern I can find is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Multiton_pattern&quot; target=&quot;_blank&quot;&gt;Multiton&lt;/a&gt; pattern.&lt;br /&gt;
&lt;h3&gt;
Motivation&lt;/h3&gt;
I have implement this pattern in two scenarios:&lt;br /&gt;
&lt;br /&gt;
An application provides the remote control of a specific CCD detector. The detector scans a sample and acquires an image on every scan spot. The native image format is not &lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support&quot; target=&quot;_blank&quot;&gt;supported by Web browsers&lt;/a&gt;. The users can view the scan process on a page. The page will retrieve a new image once the acquisition finishes on a scan spot. The image is then converted to PNG, and sent to the client. The PNG image is saved, and all later requests will be served directly without conversion. The challenge, in this scenario, is that the application should convert the image to PNG only once. That implies that all the requests of that image before the PNG file is available are served together.&lt;br /&gt;
&lt;br /&gt;
In the other scenario, a client retrieves a user&#39;s thumbnail photo from an application. The application gets a photo from an Active Directory when the photo is requested from the first time. The application saves the photo and serves it locally thereafter. Similar to the first scenario, the application should retrieve the photo from the AD service only once.&lt;br /&gt;
&lt;h3&gt;
Design&lt;/h3&gt;
When a resource representation is requested but is not available in the local file system, the request is put into an array storing all the requests for the same representation. Such an array needs to be put into a hash table, and its key is the resource&#39;s identifier. When a resource is requested for the first time, the resource is not available in the local file system, and the corresponding key is not in the hash table. Then the key will be created in the hash table, and the first request is pushed into the array. All following requests of the same resource are pushed into the array when the application is generating the resource representation. When the representation is ready, it is saved in the file system, and the key is removed from the hash table. All the requests in the array are served in a batch. The design can be implemented in various programming languages. There is a big difference between the implementation in a non-event-driven programming language like Java and that in an event-driven programming language like node.js.&lt;br /&gt;
&lt;h4&gt;
Challenge 1: synchronization of the hash table&lt;/h4&gt;
Adding and removing similar resource requests into the hash table have to be synchronized.&lt;br /&gt;
&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;th&gt;node.js&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;We will have to use a concurrent util class like j&lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html&quot; target=&quot;_blank&quot;&gt;ava.util.concurrent.ConcurrentHashMap&lt;/a&gt;(String, List).&lt;/td&gt;
&lt;td&gt;A simple object like {&quot;resource-identifier&quot;: []} will work.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h4&gt;
Challenge 2: asynchronous processing&lt;/h4&gt;
When the application is generating or retrieving the resource, we want the thread previously allocated to the request to be freed, and the handling of the request &lt;b&gt;continues&lt;/b&gt; when the resource is finally available. Before &lt;a href=&quot;https://community.oracle.com/docs/DOC-983211&quot; target=&quot;_blank&quot;&gt;Servlet 3&lt;/a&gt;, we will have to use something like &lt;a href=&quot;https://wiki.eclipse.org/Jetty/Feature/Continuations&quot; target=&quot;_blank&quot;&gt;Jetty continuation&lt;/a&gt; for this in Java. On the contrary, because node.js by nature has only a single thread, the processing is by default asynchronous.&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;th&gt;node.js&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;We will need put a Jetty continuation or a Servlet 3&amp;nbsp;&lt;a href=&quot;https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html&quot; target=&quot;_blank&quot;&gt;AsyncContext&lt;/a&gt; into the hash map, and write the response from there when the resource is available.&lt;/td&gt;
&lt;td&gt;Just put the standard &lt;a href=&quot;https://nodejs.org/dist/latest-v4.x/docs/api/http.html#http_class_http_serverresponse&quot; target=&quot;_blank&quot;&gt;http.ServerResponse&lt;/a&gt; instances in the array.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
In order to improve performance, we can also add cache control to these generated resources in addition to the copy in the file system.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4055687541392532118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2016/02/resource-representation-generation-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4055687541392532118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4055687541392532118'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2016/02/resource-representation-generation-on.html' title='Resource representation generation on first GET retrieval'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-7522373838266220816</id><published>2015-07-08T20:12:00.001-07:00</published><updated>2015-07-08T20:12:19.452-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><category scheme="http://www.blogger.com/atom/ns#" term="virtualbox"/><category scheme="http://www.blogger.com/atom/ns#" term="windows"/><title type='text'>SSH connection drop issue for Ubuntu virtualbox guest on Windows 7 host with NAT network</title><content type='html'>The problem was really annoying when I need to ssh to some Linux servers from the Ubuntu virtualbox guest. As I remember, it started after I updated the&amp;nbsp;VirtualBox platform to 4.3.26 or updated the guest from 12.04 to 14.04. The fix was surprisingly simple. See&amp;nbsp;https://forums.virtualbox.org/viewtopic.php?f=6&amp;amp;t=66086#p313103&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
With the default Intel adapter, ssh connections were always dropped. When switching to the first non-Intel adapter, the issue was gone.&amp;nbsp;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/7522373838266220816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/07/ssh-connection-drop-issue-for-ubuntu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/7522373838266220816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/7522373838266220816'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/07/ssh-connection-drop-issue-for-ubuntu.html' title='SSH connection drop issue for Ubuntu virtualbox guest on Windows 7 host with NAT network'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-1381164983999512818</id><published>2015-04-30T19:04:00.000-07:00</published><updated>2015-05-09T18:16:19.937-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bootstrap"/><category scheme="http://www.blogger.com/atom/ns#" term="css"/><category scheme="http://www.blogger.com/atom/ns#" term="typeahead"/><title type='text'>Update twitter typeahead to 0.11</title><content type='html'>Overall, I think the &lt;a href=&quot;https://github.com/twitter/typeahead.js&quot; target=&quot;_blank&quot;&gt;bloodhound/typeahead&lt;/a&gt;&#39;s API gets much better from 0.10 to 0.11. The limit option was moved from bloodhound to typeahead because it is more related to display than indexing and query. The DOM element classes were changed, and I update the styles after reading the &lt;a href=&quot;https://github.com/twitter/typeahead.js/blob/master/src/typeahead/www.js&quot; target=&quot;_blank&quot;&gt;www.js&lt;/a&gt; file. The javascript and CSS code is on &lt;a href=&quot;https://gist.github.com/dongliu/86bf41348b725315c637&quot; target=&quot;_blank&quot;&gt;gist&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/dongliu/86bf41348b725315c637.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/1381164983999512818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/04/update-twitter-typeahead-to-0112.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/1381164983999512818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/1381164983999512818'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/04/update-twitter-typeahead-to-0112.html' title='Update twitter typeahead to 0.11'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-2744069934443843167</id><published>2015-03-29T13:30:00.000-07:00</published><updated>2015-03-29T13:30:05.775-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apple"/><category scheme="http://www.blogger.com/atom/ns#" term="GFCI"/><category scheme="http://www.blogger.com/atom/ns#" term="power"/><title type='text'>Macbook power problem and GFCI outlet</title><content type='html'>This was what I experienced a while ago. I worked on my macbook on the dinner table, and plugged the power adapter to the closest wall outlet in the kitchen. In a few minutes, I noticed that the battery percentage turned very low. It was not charging at all, but discharging. I turned the macbook around, and found the back was really hot. I then shut it down.&lt;br /&gt;
&lt;br /&gt;
I asked Google what could be the problem, and found &lt;a href=&quot;https://discussions.apple.com/thread/5188510?start=0&amp;amp;tstart=0&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt;. I followed the instruction to reset SMC. But it did not solve the problem.&lt;br /&gt;
&lt;br /&gt;
I found the outlet was a&amp;nbsp;&lt;a href=&quot;http://simple.wikipedia.org/wiki/GFCI&quot; target=&quot;_blank&quot;&gt;GFCI&lt;/a&gt;&amp;nbsp;outlet. It has a green led and two buttons. I suspected the outlet could be the problem, and plug the power adapter to a normal outlet. The problem was fixed. I still do not know if it was caused by that outlet, or all GFCI outlets are not safe for the macbook adapter. </content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/2744069934443843167/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/03/macbook-power-problem-and-gfci-outlet.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/2744069934443843167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/2744069934443843167'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/03/macbook-power-problem-and-gfci-outlet.html' title='Macbook power problem and GFCI outlet'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-2105985694489915790</id><published>2015-03-17T19:02:00.000-07:00</published><updated>2015-03-17T19:02:00.049-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="503"/><category scheme="http://www.blogger.com/atom/ns#" term="authorization"/><category scheme="http://www.blogger.com/atom/ns#" term="client"/><category scheme="http://www.blogger.com/atom/ns#" term="cookie"/><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="pipe"/><category scheme="http://www.blogger.com/atom/ns#" term="proxy"/><category scheme="http://www.blogger.com/atom/ns#" term="request"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="service"/><category scheme="http://www.blogger.com/atom/ns#" term="stream"/><title type='text'>request.get(...).pipe(response) is not RESTful</title><content type='html'>&lt;a href=&quot;https://github.com/request/request&quot; target=&quot;_blank&quot;&gt;Request&lt;/a&gt;&amp;nbsp;is a nice package for HTTP client programming on node.js. It provides API&#39;s that can directly stream. So you can do things like&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;request(&#39;http://google.com/doodle.png&#39;).pipe(fs.createWriteStream(&#39;doodle.png&#39;))&lt;/span&gt;&lt;/blockquote&gt;
When programming a service proxy, it is straightforward to do something like&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;app.get(&#39;/proxied/service&#39;, function (req, res) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; request.get(&#39;http://the/real/service&#39;).pipe(res);&lt;br /&gt;})&lt;/span&gt;&lt;/blockquote&gt;
It is so neat and powerful. However, it is not RESTful, and in some cases it could cause errors that is hard to figure out. When the client of the proxied service gets the response, it always expects some representation from your service not the real service. When the representation of &#39;http://the/real/service&#39; was sent to the client, the client will be confused. For example, the authorization header, the cache-control header, or the cookie header can all have side effects.&lt;br /&gt;
&lt;br /&gt;
In an application I develop, I had a proxied service just like the above. It provided a list in JSON that was fetched from the other service hosted by a different application. It ran well in test and first a few month after released, until one day a user complained that he cannot access the list that should appear in the suggestion of a text input. I found the service was down. However, the user should still be able to type in whatever s/he thought was right, and then save it on the application. But the issue was that the application rejects such a request for authentication reason if the user&#39;s hand was not &lt;i&gt;&lt;b&gt;quick&lt;/b&gt;&lt;/i&gt; enough. &amp;nbsp;It took me a while trying to reproduce the problem on my browser, and then figure it out.&lt;br /&gt;
&lt;br /&gt;
The problem was that the remote service was down, and the client&#39;s browser got a &quot;net::ERR_EMPTY_RESPONSE&quot; error that basically means the server closed the connection without sending any data. That was only first half of the story. Such an error will force the browser to clean up the cookies set by the application, and all following requests from the same page will be rejected. So the proper way is still to provide a timeout, and an error handler like&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;request({&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; url: service.device.url,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; timeout: 30 * 1000,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ...&lt;br /&gt;&amp;nbsp; &amp;nbsp; }, function (err, response, resBody) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; if (err) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return res.json(503, ...);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;...&lt;br /&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/blockquote&gt;
&lt;br /&gt;
500, 502, 503 are good codes for this case.</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/2105985694489915790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/03/requestgetpiperesponse-is-not-restful.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/2105985694489915790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/2105985694489915790'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/03/requestgetpiperesponse-is-not-restful.html' title='request.get(...).pipe(response) is not RESTful'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4734067958424692455</id><published>2015-02-04T17:00:00.000-08:00</published><updated>2015-12-17T12:12:10.938-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bootstrap"/><category scheme="http://www.blogger.com/atom/ns#" term="css"/><title type='text'>Bootstrap progress bar with markers and labels</title><content type='html'>Here is a gist to generate a progress bar with markers and labels. It results a progress bar like the following.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVFMoRSUdHeMjNQUqlvmGE0xh0riU9qZU7a1-o5yylimiZS2hoZS7wn-ddWQqRB-iCymYiymQjrLRIomHuMmgUy0_JitKxvr4y2rzVydA0kdCSVh-F-ZI89rWKQhM4xMD5mdLGOrnNxg8/s1600/progress_marker_label.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;40&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVFMoRSUdHeMjNQUqlvmGE0xh0riU9qZU7a1-o5yylimiZS2hoZS7wn-ddWQqRB-iCymYiymQjrLRIomHuMmgUy0_JitKxvr4y2rzVydA0kdCSVh-F-ZI89rWKQhM4xMD5mdLGOrnNxg8/s1600/progress_marker_label.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The &lt;a href=&quot;https://gist.github.com/dongliu/ba9e635da7e1570b6f22&quot; target=&quot;_blank&quot;&gt;code snippets&lt;/a&gt; are on github. It is based on &lt;a href=&quot;http://stackoverflow.com/questions/24928665/bootstrap-progressbar-vertical-marker-with-title&quot; target=&quot;_blank&quot;&gt;an answer on stackoverflow&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;300&quot; src=&quot;http://jsfiddle.net/dliu/tcsagLnv/1/embedded/result,js,html,css,resources&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4734067958424692455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/02/bootstrap-progress-bar-with-markers-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4734067958424692455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4734067958424692455'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/02/bootstrap-progress-bar-with-markers-and.html' title='Bootstrap progress bar with markers and labels'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVFMoRSUdHeMjNQUqlvmGE0xh0riU9qZU7a1-o5yylimiZS2hoZS7wn-ddWQqRB-iCymYiymQjrLRIomHuMmgUy0_JitKxvr4y2rzVydA0kdCSVh-F-ZI89rWKQhM4xMD5mdLGOrnNxg8/s72-c/progress_marker_label.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-779913873149698801</id><published>2015-02-01T10:52:00.000-08:00</published><updated>2015-02-01T17:45:09.805-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mashup"/><category scheme="http://www.blogger.com/atom/ns#" term="scalability"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Shipping status checking and its implementations </title><content type='html'>It is the holiday season, and everyone hopes their shippings arrive on time. Checking shipping status is a basic service for online retailers. For the two largest retailers, checking shipping status is implemented as a service that returns a block of HTML including JavaScript. I assume the main rationale behind this design is the easiness for front end integration. So the implementation is a typical server-side mashup. The user clicks on the link/button, the request is sent to the mashup service, the mashup service sends a request or several requests to the shipping carrier services, then the mashup service generates the HTML snippet and sends it back. At the same time, the mashup service can log the data from both the user and the carrier services.&lt;br /&gt;
&lt;br /&gt;
There must be more details in the real implementation. One interesting aspect is the carrier API&#39;s Service Level Agreement (SLA). Request throttling policy is one of the most critical pieces in SLA. A carrier service normally restricts the maximum number of service request from a client in a sliding time window and the maximum request rate, and some even have penalties (not just refusing the requests) if the client violates the restrictions. This can make the retailer&#39;s mashup service complicate when it tries to behave nicely in the face of a carrier&#39;s service and fulfills large amount of user requests.&lt;br /&gt;
&lt;br /&gt;
There is an easier solution other than the server-side mashup approach. If the carrier&#39;s service allows &lt;a href=&quot;http://en.wikipedia.org/wiki/Cross-origin_resource_sharing&quot; target=&quot;_blank&quot;&gt;cross-origin resource sharing&lt;/a&gt;, a client-side mashup can be implemented. Or simply a link to the carrier website with the tracking number in URL will work. What if the retailer wants to track the clicks? Then a link hosted by the retailer with the carrier&#39;s link encoded in the query like those in Google search results will work.</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/779913873149698801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2015/02/checking-shipping-status-and-its.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/779913873149698801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/779913873149698801'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2015/02/checking-shipping-status-and-its.html' title='Shipping status checking and its implementations '/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-3359097893546750452</id><published>2014-11-02T08:20:00.000-08:00</published><updated>2014-11-02T08:20:15.126-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Chrome"/><category scheme="http://www.blogger.com/atom/ns#" term="cookie"/><category scheme="http://www.blogger.com/atom/ns#" term="distributed"/><category scheme="http://www.blogger.com/atom/ns#" term="firefox"/><category scheme="http://www.blogger.com/atom/ns#" term="IE"/><category scheme="http://www.blogger.com/atom/ns#" term="time"/><title type='text'>IE and Chrome/Firefox see different expiration date on cookies</title><content type='html'>I was puzzled recently by a cookie problem that can only reproduced on IE and cannot be seen on Firefox and Chrome. The cookies generated by a testing server were set on Firefox and Chrome, but were rejected by IE. This resulted in that a user cannot access any protected resource right after logged in. After struggling with both the server and the client for abouting three hours, I read the messages line by line with wide open eyes and found a fresh cookie was already expired when it was sent to a client. The root reason was the test server&#39;s system time is about 24 hours behind.&lt;br /&gt;
&lt;br /&gt;
The browsers I used to test are all the latest stable versions, but I assume it reflects how these three browsers check whether a cookie is expired. Since the expiration date/time in the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc6265&quot; target=&quot;_blank&quot;&gt;Set-Cookie&lt;/a&gt;&lt;/span&gt; header is indeed expired when the client sees it in the response, IE&#39;s approach seems valid at the first sight. This approach is also straightforward to be implemented.&lt;br /&gt;
&lt;br /&gt;
We all know that any system&#39;s clock is inaccurate, and the clocks of a server and a client are not synchronized in most cases. If the clocks are not too off the &quot;true&quot; time, and the cookie&#39;s age is not too short, and the cookie delivery latency is not too long, the approach of judging expiration by &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Expires&lt;/span&gt; header should work fine. However, what if these assumptions are wrong?&lt;br /&gt;
&lt;br /&gt;
In such cases, Firefox and Chrome use more information to tell if a cookie is expired. The other piece of information is the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc2616#section-14.18&quot; target=&quot;_blank&quot;&gt;Date&lt;/a&gt;&lt;/span&gt; header. &amp;nbsp;With both &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Date&lt;/span&gt; and &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Expires&lt;/span&gt; headers, the client interprets how long the cookie can be valid from the local &quot;now&quot; on. &amp;nbsp;I think this is a more realistic approach to handle the problem in a distributed environment.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/3359097893546750452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/11/ie-and-chromefirefox-see-different.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3359097893546750452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3359097893546750452'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/11/ie-and-chromefirefox-see-different.html' title='IE and Chrome/Firefox see different expiration date on cookies'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4661500497398582106</id><published>2014-10-25T12:04:00.000-07:00</published><updated>2014-10-25T12:04:35.119-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="big data"/><category scheme="http://www.blogger.com/atom/ns#" term="cloud computing"/><category scheme="http://www.blogger.com/atom/ns#" term="email"/><category scheme="http://www.blogger.com/atom/ns#" term="scalability"/><title type='text'>When email box sizes were increased 10 years ago </title><content type='html'>Ten years ago, all the major email service providers increased their free storage size as a response to Google&#39;s 1GB offer. &amp;nbsp;Microsoft increased to 250MB, and Yahoo increased to 100MB. Obviously, &lt;a href=&quot;http://dongnotes.blogspot.com/2004/10/big-is-beautiful.html&quot; target=&quot;_blank&quot;&gt;the bigger seemed beautiful&lt;/a&gt;. The size in fact reflected the technology capability that each company can handle and also the vision for the future at that moment. All these in turn determined how they performed in the area of cloud computing and big data in the last 10 years. It is so interesting to look backward and connect the dots. </content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4661500497398582106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/10/when-email-box-sizes-were-increased-10.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4661500497398582106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4661500497398582106'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/10/when-email-box-sizes-were-increased-10.html' title='When email box sizes were increased 10 years ago '/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4773113863464476743</id><published>2014-09-10T18:00:00.000-07:00</published><updated>2014-09-10T18:00:00.361-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="201"/><category scheme="http://www.blogger.com/atom/ns#" term="303"/><category scheme="http://www.blogger.com/atom/ns#" term="ajax"/><category scheme="http://www.blogger.com/atom/ns#" term="browser"/><title type='text'>201 or 303</title><content type='html'>&lt;a href=&quot;http://en.wikipedia.org/wiki/Post/Redirect/Get&quot; target=&quot;_blank&quot;&gt;Post/Redirect/Get&lt;/a&gt; is a common pattern to create a new resource and then load the representation of the new resource in browser. An advantage of this pattern is to prevent duplicate form submission when a user clicks the browser refresh button after the Post request. The response code of the Post request is 303 if the request is successful. The browser will always redirect to the Location link in the 303 response header.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, this behavior can be confusing when the Post request is sent via AJAX. That means JavaScript will see response code 200 directly instead of 303 when the Post is successful. The AJAX engine inside a browser&lt;a href=&quot;http://www.w3.org/TR/XMLHttpRequest/#infrastructure-for-the-send()-method&quot; target=&quot;_blank&quot;&gt; has no way&lt;/a&gt; to stop at the 303 and prevent the redirection. Furthermore, JavaScript will normally see the HTML representation of the new resource in the response. This can be confusing when you want to control what the page to do when the new resource is created and load the JSON representation of the new resource. In this case, 201 is obviously better than 303, because the JavaScript can decide how to notify the user and also furthermore how to load the new resource into the current page or redirect. </content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4773113863464476743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/09/201-or-303.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4773113863464476743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4773113863464476743'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/09/201-or-303.html' title='201 or 303'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-5953314579382185262</id><published>2014-08-12T05:30:00.000-07:00</published><updated>2014-08-12T06:30:07.088-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="livestamp"/><category scheme="http://www.blogger.com/atom/ns#" term="moment"/><title type='text'>Make the moment live with livestamp</title><content type='html'>&lt;a href=&quot;http://mattbradley.github.io/livestampjs/&quot;&gt;Livestamp.js&lt;/a&gt; is a neat library that update your &lt;a href=&quot;http://momentjs.com/&quot;&gt;moment&lt;/a&gt;().fromNow() timestamps with js timer. By default, the update interval is one second, which is too short considering that the from now time shows update every minute. If you set the interval by $.livestamp.interval(int), then you will need to call $.livestamp.resume() in order to prevent the delay on a newly created live timestamp. An example is &lt;a href=&quot;http://jsfiddle.net/dliu/gtzLqvx3/&quot;&gt;here&lt;/a&gt;. 
&lt;iframe width=&quot;100%&quot; height=&quot;300&quot; src=&quot;http://jsfiddle.net/dliu/gtzLqvx3/embedded/&quot; allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/5953314579382185262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/08/make-moment-live-with-livestamp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5953314579382185262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/5953314579382185262'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/08/make-moment-live-with-livestamp.html' title='Make the moment live with livestamp'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-3083339794879556222</id><published>2014-03-27T20:13:00.001-07:00</published><updated>2014-09-15T09:33:42.822-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="traveler"/><title type='text'>Traveler application user manual</title><content type='html'>Since last year, I have been working on a web application to create, manage, and share forms and travelers. A traveler contains a sequence of actions defined by a user in a form. An action requires a person to verify the action is performed and/or collect data as defined in the form. The traveler application records who collects what data at what time before the traveler is completed. It is quite similar to &lt;a href=&quot;https://www.youtube.com/watch?v=xEY10Ub-k-U&quot; target=&quot;_blank&quot;&gt;Google Form&lt;/a&gt; that used Google speadsheet as the data storage and management interface in that the user defines the form in a WYSIWYG way. The major difference is that Google form is implemented for a survey-like application that collects multiple pieces of data for each form, while the traveler is designed for a workflow-like application that collects one piece of data with revision history from multiple users. The full user manual page is now on &lt;a href=&quot;http://rawgithub.com/dongliu/traveler/master/public/about.html&quot; target=&quot;_blank&quot;&gt;github (via rawgithub)&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The form builder&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;embed height=&quot;600&quot; pluginspage=&quot;http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash&quot; quality=&quot;low&quot; src=&quot;http://rawgit.com/dongliu/traveler/master/public/images/compose.swf&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;800&quot; wmode=&quot;transparent&quot;&gt;&lt;/embed&gt;

The traveler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;embed height=&quot;600&quot; pluginspage=&quot;http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash&quot; quality=&quot;low&quot; src=&quot;http://rawgit.com/dongliu/traveler/master/public/images/work.swf&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;800&quot; wmode=&quot;transparent&quot;&gt;&lt;/embed&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/3083339794879556222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/03/traveler-application-user-manual.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3083339794879556222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/3083339794879556222'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/03/traveler-application-user-manual.html' title='Traveler application user manual'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-6999132840238502038</id><published>2014-01-21T18:37:00.000-08:00</published><updated>2014-01-21T18:37:48.494-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="node.js"/><category scheme="http://www.blogger.com/atom/ns#" term="prezi"/><title type='text'>Introducing node.js to Java developers prezi</title><content type='html'>A prezi developed for my colleagues. &lt;/br&gt;

&lt;iframe src=&quot;http://prezi.com/embed/i3dtp1haqeca/?bgcolor=ffffff&amp;amp;lock_to_path=0&amp;amp;autoplay=0&amp;amp;autohide_ctrls=0&amp;amp;features=undefined&amp;amp;disabled_features=undefined&quot; width=&quot;550&quot; height=&quot;400&quot; frameBorder=&quot;0&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/6999132840238502038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/01/introducing-nodejs-to-java-developers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6999132840238502038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/6999132840238502038'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/01/introducing-nodejs-to-java-developers.html' title='Introducing node.js to Java developers prezi'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7054543485166290590.post-4191501201833039767</id><published>2014-01-03T17:30:00.000-08:00</published><updated>2014-01-03T17:30:00.325-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="prezi"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>REST introduction prezi</title><content type='html'>I developed a new version of REST introduction presentation with a case study of RESTful design and development process.
&lt;iframe frameborder=&quot;0&quot; height=&quot;400&quot; src=&quot;http://prezi.com/embed/z7mli7zzz9js/?bgcolor=ffffff&amp;amp;lock_to_path=0&amp;amp;autoplay=0&amp;amp;autohide_ctrls=0&amp;amp;features=undefined&amp;amp;disabled_features=undefined&quot; width=&quot;550&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://dongnotes.blogspot.com/feeds/4191501201833039767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dongnotes.blogspot.com/2014/01/rest-introduction-prezi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4191501201833039767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7054543485166290590/posts/default/4191501201833039767'/><link rel='alternate' type='text/html' href='http://dongnotes.blogspot.com/2014/01/rest-introduction-prezi.html' title='REST introduction prezi'/><author><name>D. Liu</name><uri>http://www.blogger.com/profile/12777478092719343060</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>