tag:blogger.com,1999:blog-60787062024-03-14T09:31:55.002-07:00codeWord - Thoughts on SoftwareMohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.comBlogger582125tag:blogger.com,1999:blog-6078706.post-726736402388400512013-10-18T10:02:00.003-07:002013-10-18T10:02:52.655-07:00LruCache in JavaJava collections has LinkedHashMap which is a combination of a HashMap and a linked list which allows for predictable iteration order. There are two options for iteration - Based on insertion order, or based on Access order.<br />
<br />
Now to create a Least Recently Used cache, the implementation need to use the access order for iteration and override the hook method removeEldestEntry() which should check the required size of the map to remove entries. I've posted the code in the example below.<br />
<br />
Also present in the example is how you should *not* use the map in an multithreaded fashion. The class is not thread-safe and you end up with non deterministic behavior and the size invariant and probably other things go wrong.
<br />
<br />
<script src="https://gist.github.com/rrevo/7043309.js"></script>
Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com2tag:blogger.com,1999:blog-6078706.post-30338738549574422172013-05-19T02:39:00.001-07:002013-05-19T02:39:10.071-07:00Functional Programming Principles in ScalaI just completed the Functional Programming Principles in Scala course at <a href="https://www.coursera.org/course/progfun">https://www.coursera.org/course/progfun</a> with my solutions at <a href="https://github.com/rrevo/progfun">https://github.com/rrevo/progfun</a> . It was a good experience learning both about Functional programming and Scala.<br />
<div>
</div>
<div>
Overall the new features compared to an OO language like Java were-</div>
<ol>
<li>Higher-order functions</li>
<li>Case classes and pattern matching</li>
<li>Immutable collections</li>
<li>Flexible evaluation strategies: strict/lazy/by name</li>
</ol>
<br />It was refreshing to try and solve problems without mutation. However this also has a higher performance cost. Most of my current work is in Java and so I can translate some of these ideas to my work. Preferring immutability is a good idea for concurrency anyways and this was also brought up by Effective Java and Concurrency in Practice.<br />
<br />
Scala as a language was also pretty interesting. Higher-order functions and the other features made code much more succinct than java. I did find myself trying to minify code into a single line at times. But at the same time this density meant that reading code at a later time was definitely much harder. Since types are also inferred, I did end up spending quite a bit of time trying to reason about the types of variables.<br />
<br />
Java 8 will have some form of higher-order functions. That should close the gap between the languages slightly. However scala also has many other features and libraries that also have immense value. Choosing between either still depends on many other factors.Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-60368466848899662592013-01-03T00:20:00.000-08:002013-01-03T00:20:47.937-08:00Spring vs Guice default scopesSpring and Guice are popular Inversion Of Control (IoC) frameworks. One of the most important aspects of IoC is how the framework creates new instances for value. This is the Scope. Some of the scopes are-<br />
<br />
<ol>
<li>Singleton (Spring default) - A single instance is created by the IoC container for the entire application lifetime.</li>
<li>Prototype (Guice default) - A new instance is created whenever a value is needed.</li>
</ol>
In addition there are other scopes valid in a web context and custom scopes can also be defined. For additional details on scopes see <a href="http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-scopes">Spring docs</a>.<br />
<br />
For stateless objects the programmer can choose either Singleton or Prototype. <a href="https://code.google.com/p/google-guice/wiki/Scopes">Guice docs</a> recommend that prototype scope be used for Stateless objects even though only a single instance should suffice really. The rationale is to optimize for speed instead since singleton scopes require synchronization for implementation and today's JVMs are good at garbage collection.<br />
<br />Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-52683142030434761552012-12-09T11:21:00.001-08:002012-12-09T11:21:27.764-08:00How does JDBC Connection Pooling work?
This post is an excerpt from the <a href="http://www.onyem.com/blog/how-does-dbcp-work.html">jtracer blog</a>.
<blockquote>
<h2>Overview</h2>
The Apache commons <a href="http://commons.apache.org/dbcp/">DBCP</a> component provides Connection pooling for JDBC connections. In this blog I am going to use JTracer to reveal how DBCP works internally.
<br />
<h2>JDBC Primer</h2>
To connect to a database using JDBC a java.sql.Connection is needed. For creating a Connection, a JDBC driver needs to be loaded. Connection parameters like url and credentials should also be set. Once a Connection is obtained you can create a Statement to execute queries. After the Statement and Connection are used, they should be closed to clean up the resources. The DataSource API as part of the javax.sql package provides an alternate way to create connections.<br />
<br />
Creation of Connections is an expensive process. It is better to use a connection pool that will re-use Connections for running queries rather than creating only new Connections. The DBCP project from Apache provides a BasicDataSource which also implements Connection pooling.
<br />
<br />
</blockquote>
<h2>Continue</h2>
Continue reading at the <a href="http://www.onyem.com/blog/how-does-dbcp-work.html">jtracer blog</a>.
Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-55233110402516541992012-11-04T01:39:00.001-07:002012-11-04T01:39:32.153-07:00Overview of OSGi<h3>What is OSGi?</h3>
OSGi is a module system and service platform for the Java platform.<br />
<br />
For the module system, it extends the code level visibility controls that Java provides via the private, protected, public and package-private access. An application is usually packaged as multiple jars in java. Some of these are external libraries. With OSGi it is possible to specify version for a jar and dependency information between jars.<br />
<br />
OSGi enables Service Oriented Architecture in a Virtual Machine. It provides APIs for providing a way to register services, discover and bind to them.<br />
<br />
<br />
<h3>Specifications</h3>
<br />
The <a href="http://www.osgi.org/">OSGi Alliance</a> is an industry backed non-profit organization that manages the OSGi <a href="http://www.osgi.org/Specifications/HomePage">specifications</a>. There are a many implementations like <a class="urllink" href="http://felix.apache.org/">Apache Felix</a>, <a href="http://www.eclipse.org/equinox">Eclipse Equinox</a> and <a class="urllink" href="http://www.knopflerfish.org/">Knopflerfish</a>. Building on top of the core OSGi framework are runtimes that provide additional services like <a href="http://karaf.apache.org/">Apache Karaf</a>.<br />
<br />
<h3>Architecture</h3>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEionzqCM6YKWUUrGjN2gw7lemO_1GC2uVnGtNtg5KH2J28IaQQ6_TNlgmNRtRT3ZJvnkLillpLYgHHrI3f_ryDjvRRvi8oIpo6jkJpglh1jFMCbeMENWBjoKLnqC9eGrdIHC8Pabg/s1600/OSGi.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="134" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEionzqCM6YKWUUrGjN2gw7lemO_1GC2uVnGtNtg5KH2J28IaQQ6_TNlgmNRtRT3ZJvnkLillpLYgHHrI3f_ryDjvRRvi8oIpo6jkJpglh1jFMCbeMENWBjoKLnqC9eGrdIHC8Pabg/s320/OSGi.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">OSGi Platform</td></tr>
</tbody></table>
<br />
The OSGi platform is composed of the Framework and Standard services. The framework is the runtime that implements and provides OSGi functionality. The standard services define reusable APIs for common tasks. <br />
<br />
<h4>
Core framework </h4>
The core framework itself can be broken down into 3 layers - Module, Lifecycle and Service. Each layer is dependent on the layer below. The Module layer has to be used by an application but the Lifecycle and Service layers are optional.<br />
<br />
1. Module Layer<br />
In OSGi the unit of modularity is called a Bundle. A Bundle is basically a jar file containing additional metadata in headers with an additional file - META-INF/MANIFEST.MF. <br />
<br />
Headers Bundle-SymbolicName and Bundle-Version are used to uniquely identify a Bundle and its version. Using the header Export-Package the Bundle can specify the packages that are visible to other packages. Import-Package and Require-Bundle are used to specify the dependencies of a Bundle.<br />
<br />
2. Lifecycle Layer<br />
<br />
There are 4 phases of a Bundle lifecycle<br />
<ol>
<li>Installation - The bundle jar has to be installed first from a URL. The framework automatically resolves its dependencies based on the Import-Package and Require-Bundle headers. Resolution happens transitively for all the dependent bundle as well.</li>
<li>Execution - When the Bundles classes are first accessed or the bundle is explicitly started, it moves to a starting state</li>
<li>Update - Bundles may be updated to a different version using the API</li>
<li>Removal - Finally bundles may be uninstalled. </li>
</ol>
Some of the important APIs that allow this functionality are-<br />
org.osgi.framework.BundleActivator<br />
org.osgi.framework.BundleContext<br />
org.osgi.frameword.Bundle<br />
<br />
3. Service Layer<br />
<br />
The OSGi framework allows registration of services, discovery and binding to them. Bundles may be installed and uninstalled allowing for dynamic services and possibly multiple implementations.<br />
<br />
The org.osgi.framework.BundleContext class provides APIs for this service layer.<br />
<br />
<h4>
Services</h4>
<h3 dir="ltr" id="internal-source-marker_0.3229076172933987">
</h3>
An OSGi platform provides core services some of which are-<br />
<ul>
<li>Package Admin (org.osgi.service.packageadmin) provides the ability to control and reflect over bundle and package level resolution</li>
<li>Start Level (org.osgi.service.startlevel) controls the relative order of bundle startup by assigning start levels to bundles</li>
<li>Service Hooks (org.osgi.framework.hooks.service) allows bundles to monitor and limit service registry events and access</li>
</ul>
Some of the optional services are-<br />
<ul>
<li>Log (org.osgi.service.log) for logging by bundles</li>
<li>HTTP (org.osgi.service.http) for a HTTP server with Servlet support</li>
<li>Configuration Admin (org.osgi.service.cm) Manages bundle configuration data storage and injection</li>
<li>Preferences (org.osgi.service.prefs) for management of Preferences</li>
<li>Event Admin (org.osgi.service.event) for publish-and-subscribe and topic-based event notification</li>
</ul>
<br />
<h3>Summary</h3>
OSGi does provides some valuable extensions to the core java language. Just being able to specify packages to external clients allows to design APIs without leaking unnecessary information. Classpath resolution is also well managed.<br />
The service layer further adds to the idea of well defined APIs.<br />
<br />
Being based on plain java classes the framework does not feel heavy like EJBs. However figuring out how to use a particular runtime is not an easy task.<br />
<br />
Integrating with existing code and converting them to bundles is also non-trivial. If jars are not well designed APIs then there is always a problem that there are some references that will never be cleaned causing a leak. Existing code also needs to be refractored to use the OSGi services of logging and configuration.<br />
<br />
In all OSGi has value but there is also a cost to using the framework.<br />
<br />
Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-90561090038172403712012-04-24T03:34:00.000-07:002012-04-24T03:34:34.716-07:00The Concurrency Revolution IIIn Dec 2004 Mohnish had posted <a href="http://codeword.blogspot.com/2004/12/concurrency-revolution.html">The Concurrency Revolution</a> based on Herb Sutter's article on the multi-core paradigm shift in processors.<br />
<br />
Well, it's time to read the follow up - <a href="http://herbsutter.com/welcome-to-the-jungle/">Welcome to the Jungle</a> . <br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-67101133490554306772012-03-28T16:30:00.001-07:002012-03-28T16:30:04.673-07:00Ubuntu TourI'm running a slightly old version of ubuntu .. trying to hold moving to the new UI. I wanted to have a peek at the current state.<br />
<br />
Opened the <a href="http://www.ubuntu.com/tour">Ubuntu Tour</a> and expected to see a few screenshots. But ..<br />
<br />
The tour is actually a working Ubuntu desktop built as an HTML page! Amazing. The dash works, the start bar works with date, volume etc and other basic controls. Some applications are just images which you can drag around. Thunderbird is more complex with demo emails. Firefox is the best because you can open other webpages.. recursive browsing.<br />
<br />
When drafting this blog, noticed that the layout reconfigures when the width of the page changes.<br />
<br />
HTML layouts have come a long way.<br />
<br />
Know what libraries are used for this?<br />
<br />Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-30961951633466642112012-01-19T13:40:00.000-08:002012-01-19T13:40:18.434-08:00Simplicity<blockquote>
Simplicity is the ultimate sophistication - Leonardo da Vinci</blockquote>
Rich Hickey presented a fantastic talk, <a href="http://www.infoq.com/presentations/Simple-Made-Easy">Simple made easy</a> - "Rich Hickey emphasizes simplicity’s virtues over easiness', showing that while many choose easiness they may end up with complexity, and the better way is to choose easiness along the simplicity path."<br />
<br />
Anders Hejlsberg mentioned <a href="http://www.artima.com/intv/simplexity.html">simplexity</a> in an interview a while back as well where he says "Let me first talk a little bit about how I view simplicity
in general. No one ever argues that simplicity isn't good, but people define
simplicity in a variety of ways. There's one kind of simplicity that I like to call
<i>simplexity</i>. When you take something incredibly complex and try to wrap
it in something simpler, you often just shroud the complexity. You don't actually
design a truly simple system. And in some ways you make it even more
complex, because now the user has to understand what was omitted that they
might sometimes need. That's simplexity. So to me, simplicity has to be true, in
the sense that the further down you go the simpler it gets. It shouldn't get more
complicated as you delve down.
"<br />
<br />
<br />
Over time I have seen quite a few systems with too much complexity. And mostly it was not needed. Just a lot of code added without too much thought to design. More thought should be put into design and refractoring and testing.<br />
<br />
It is also funny how this parallels to a sense of <a href="http://en.wikipedia.org/wiki/Minimalism">minimalistic design</a> for design and architecture.<br />
<br />Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-49826780125693436852011-11-22T19:37:00.001-08:002011-11-22T19:47:16.671-08:00Installing mercurial on CentOS or RHEL 4For mercurial/hg you need a relatively new version of python installed. Since that is not available as an rpm let's compile the source.<br />
<br />
From http://python.org/download/ download a python source version like http://python.org/ftp/python/2.7.2/Python-2.7.2.tgz<br />
<br />
From http://mercurial.selenic.com/release/ download a mercurial source drop like http://mercurial.selenic.com/release/mercurial-1.7.5.tar.gz<br />
<br />
Run the following commands as root:<br />
<pre>
#Install some dependencies
yum install zlib-devel.i386 ncurses-devel.i386 openssl-devel.i386 readline-devel.i386 bzip2-devel.i386
#Extract the python code
tar xvzf Python-2.7.2.tgz
cd Python-2.7.2
# Python will be installed in /opt/python27
make clean; ./configure --prefix=/opt/python27; make; make install
cd ..
# Extract the mercurial code
tar xvzf mercurial-1.7.5.tar.gz
cd mercurial-1.7.5
#Python will be installed in /opt/hg17
make install PYTHON=/opt/python27/bin/python PREFIX=/opt/hg17
#Add new python and mercurial to PATH
export PATH=/opt/python27/bin:/opt/hg17/bin:$PATH
</pre>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-18465766868285642302011-06-14T14:26:00.000-07:002011-06-14T14:26:41.729-07:00Java7 Automatic resource management<b>ARM:</b><br />
<br />
One of the useful features in Java7 is the Automatic resource management via the try-with-resources statement.<br />
<br />
In the olden days thou would (or atleast were supposed to) code like:<br />
<pre class="brush: java">void doSomething() throws IOException {
OutputStream out = null;
try {
out = new FileOutputStream("");
out.write(data);
} finally {
if (out != null) {
out.close();
}
}
}
</pre><br />
Which is error prone if you do forget to close the resources.<br />
<br />
The new way is:<br />
<br />
<pre class="brush: java">void doSomethingNew() throws IOException {
try (OutputStream out = new FileOutputStream("")) {
out.write(data);
}
}
</pre><br />
This is syntactically much more pleasing and does the right thing internally.<br />
<br />
<b>Going deep</b>:<br />
The try-with syntax works with any <a href="http://download.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html">AutoCloseable</a> implementation and you can have multiple resources as well.<br />
<br />
The compiler will transform the above code into something like:<br />
<br />
<pre class="brush: java">void doSomethingNew() throws IOException {
OutputStream out = null;
Throwable localThrowable1 = null;
try {
out = new FileOutputStream("");
out.write(blah);
} catch (Throwable localThrowable2) {
// We keep a reference to which can add a suppressed exception
localThrowable1 = localThrowable2;
throw localThrowable2;
} finally {
if (out != null) {
if (localThrowable1 != null) {
// Already have an exception thrown
try {
out.close();
} catch (Throwable localThrowable3) {
// Add the suppressed exception during close
localThrowable1.addSuppressed(localThrowable3);
}
} else {
// Potentially throw an IOException due to failure when close'ing
out.close();
}
}
}
}
</pre><br />
The pattern is:<br />
<br />
<ul><li>If there are no exception - close() in the finally block OR</li>
<li>If there are no exception during operation but there is an exception during close() then throw it OR</li>
<li>If there is an exception thrown then throw it after closing the resource OR</li>
<li>If there is an exception1 thrown then throw it .. and if there is an exception2 closing the resource.. then exception1.addSuppressed(exception2). This is new in the<a href="http://download.oracle.com/javase/7/docs/api/java/lang/Throwable.html#addSuppressed(java.lang.Throwable)">Throwable</a> class</li>
</ul><br />
<b>Sidenote</b>:<br />
There was a huge effort to get closures into Java7 but none of the solutions was light on syntax/complexity. After the backlash over Generics a smaller but better step has been taken.<br />
<br />
<b>Sidenote++</b>:<br />
For scala see <a href="http://stackoverflow.com/questions/2207425/what-automatic-resource-management-alternatives-exists-for-scala/2219494#2219494">using</a>. You do not need to wait for language changes to make syntactically pleasing APIs because of curry-ing and other fancy stuff.<br />
<br />
<br />
<b>More References</b>:<br />
<a href="http://blogs.oracle.com/darcy/entry/project_coin_updated_arm_spec">ARM spec</a><br />
<a href="http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html">Article on ARM</a>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com3tag:blogger.com,1999:blog-6078706.post-5417650248773645092011-02-23T11:51:00.000-08:002011-02-23T11:51:03.457-08:00Beautiful software<moody><br />
I feel a deep sense of happiness when I use Google products like gmail and chrome. Every now and then they update the applications and things just work. They make things look so simple but they must be super complex in the back. That's great software. <br />
<br />
Great software can only be built by great teams. That means everyone from engineers to managers. Give enough freedom to the team to execute. Treat them like people. Understand software.<br />
<br />
No software methodology can save you. Let the team define its processes. Let common sense prevail. And then greatness will follow.<br />
</moody>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com1tag:blogger.com,1999:blog-6078706.post-84068905578603468912011-01-04T10:54:00.009-08:002011-01-04T11:14:02.395-08:00Where did the class get loaded from?<div>As with most enterprise apps gone crazy you have a bunch of duplicate jars or some jars at places you don't even know were in the classpath. And once a while you need to know from which jar a particular class file was loaded. To find this out run java with the -verbose:class flag. For each class loaded, the VM will print out the folder or jar where the class was found.</div><br /><br /><pre><br />~ $ java -verbose:class HelloWorld<br />...<br />[Loaded HelloWorld from file:/C:/work/cygwin/home/rrevo/]<br />Hello World<br />[Loaded java.lang.Shutdown from shared objects file]<br />[Loaded java.lang.Shutdown$Lock from shared objects file]<br /></pre><br /><br /><div>Read <a href="http://download.oracle.com/javase/6/docs/technotes/tools/windows/java.html">java options</a> for more info.<br /><div><br />PS: It's time to start blogging again.<br /></div></div>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-22126268198464033342009-11-04T11:40:00.002-08:002009-11-04T11:51:24.262-08:00Linux on the desktop<p>Recently I performed some upgrades on my laptop and the sound broke once again. Sound has been flaky for a while now and I've had to use windows to use skype. This was a pretty useful tutorial which helped me sort out the issues - <a href="http://ubuntuforums.org/showthread.php?t=789578">http://ubuntuforums.org/showthread.php?t=789578</a></p><p>But it got me thinking more about experience on Linux once again. Applications like OpenOffice, Gimp are inferior in usability to MS Office and Photoshop. Its painful when I need a newer version of software but the distro is older and so only an older version is available in the repo. Maybe its time to move back to Windows...</p>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-63530770733949713142009-10-01T11:48:00.005-07:002009-10-01T12:02:04.170-07:00An Overview of SpringSpring is a very popular framework in the Java space and quickly becoming an alternative to JEE. Spring started of as an implementation of the ideas in the book "Expert One-on-One J2EE Design and Development" in 2002 by Rob Johnson. Today the project has grown into to a large framework with multiple modules to assist with different areas of serverside programming. Spring provides a lightweight container and uses POJOs. This makes code much simpler to reason about and also test. No starting a mega EJB container anymore with remoting when you don't really need it.<br /><br /><b>Inversion Of Control (IoC)</b><br />At its core, Spring is an IoC container. Generally at some point in code you use 'new' instances. If you are coding against an interface you end up with code like:<br /><pre><br />class Bar {<br /> Foo foo;<br /> void Bar() {<br /> foo = new FooImpl();<br /> }<br />}<br /></pre><br />Now the class Bar has a dependency on Foo and its implementation FooImpl. Now you could remove this dependency by using the ServiceLocator pattern but then you end up with a dependency to the ServiceLocator class and your code is not that testable anymore.<br />In Spring you instead inject the actual implementations at runtime and rules about which instances are to be returned are written in an XML configuration file (or via Annotations). So code becomes like:<br /><pre><br />class Bar {<br /> Foo foo;<br /> void Bar(Foo foo) {<br /> this.foo = foo;<br /> }<br />}<br /></pre><br />with an instance of FooImpl being returned where Foo is expected. Now Bar depends only on the interface Foo but not the implementation of Foo which is simply passed to it. Hence the name Inversion of Control as Bar does not depend on FooImpl but gets it.<br /><br />More importantly the code can be tested very easily. If I wanted to test the Bar class I can very easily provide a mock implementation of the Foo instance. Writing testable code is difficult and a major part of the problem is breaking dependencies. You should probably never need to new objects again but just get them injected.<br /><br />In the above example Foo is set in the constructor. This is an example of a Constructor based injection. Another way to inject dependencies is via setter methods. However I prefer Constructor based injection as it allows for final fields which should be the default to help with immutability.<br /><br />Another important idea is how instances are created. This is known as scopes. By default instances are created only once and that reference will be passed everywhere similar to a Singleton. So one FooImpl instance is created and passed wherever a Foo is needed. A lot of code can be written in a style where there is no local state allowing for Singletons. Again since the Singleton pattern is not explicitly needed, code remains testable. Another scope is prototype in which a new instance of FooImpl will be created whenever a Foo needs to be injected. For web programming other scopes like Request, Session and Application also exist.<br /><br />The complete lifetime of instances can be managed and there are many other features but thats another post. If you just need an IoC container then there are alternatives like Google Guice.<br /><br /><b>Aspect Oriented Programming</b><br /><br />AOP allows for crosscutting code to be written external to the main business logic. This allows cleaner code in areas like logging. The implementation of AOP in Spring is based on Proxy based interceptors which happen at runtime. AspectJ integration is also provided in some form.<br />One of the major uses of AOP is defining transactions which will be covered soon.<br /><br /><b>Data Access and Transaction management</b><br /><br />Most serverside code needs to talk to a database. Spring provides neat abstractions over JDBC and other frameworks to simplify code to a great extent.<br /><br /><div>A DataSource is usually configured and injected.<br />JDBC code is wrapped with classes like the SimpleJdbcTemplate. Queries become like:<br /><pre><br />String daString = getSimpleJdbcTemplate().queryForObject(<br /> "SELECT daString FROM DaStringTable WHERE daStringId = ?",<br /> String.class, daStringId);<br /></pre><br />Thats one line of code for what would have been multiple lines of JDBC like getting a connection, then statement, then query and result parsing. And there are no SQLExceptions. All of those are wrapped into a DataAccessException instance which is a RuntimeException. Create a generic handler in your code for exceptions because thats what you'll mostly be interested in. You can easily provide mapper classes to map from db values to object instances for custom types. Also variations exist for returning mutiple rows and other SQL CRUD operations.<br />And I almost forgot to mention that there are also named parameters for queries.<br /><br />Using Hibernate is even easier. Use HibernateTemplate instead and get access to a hibernate API. Or JPATemplate if you want to use JPA. All of this code is obviously going to be tucked under a DAO tier!<br /><br />Transaction management is also dead simple with usage of Java annotations. You declare the Transaction rules for a method invocation like Isolation level (Read uncommitted, Read committed, Repeatable Read, Serializable), Propagation (Required, New), timeout and exceptions for rollback. An AOP proxy is created over the method which enforces the Transaction rules over the underlying datastore.<br /><br /><b>Web MVC</b><br /><br />Built on the core IoC functionality Spring has a Web MVC framework as well. Like Struts it is a request or action based framework. The DispatcherServlet class is a FrontController and acts as the entry point to the MVC framework. Typically request URIs are mapped to Controller classes. Controllers compute the Model and then render a View based on the Model for the request. A general multi tier architecture for applications is another topic.<br /><br /><b>Even more</b><br /><br />There are quite a few other projects under the Spring banner. WebFlow extends Web MVC for richer work flows in webapps. Spring WebServices for WebService (duh). Spring IDE is a bunch of Eclipse plugins to help with Spring development.<br /><br />Spring Framework 3 is going to be released. It adds better Java 5 support across various APIs. There is also a lot of annotation usage.<br /><br /><b>The company and future</b><br /><br />Spring started off as a simple open source project and has now morphed into a complete stack framework backed by a company.<br />The core framework helps building applications. Further they added Groovy language and Grails. Mostly spring apps need a web container like Tomcat or Jetty to run. SpringSource also provides a custom Tomcat build known as tcServer with additional capabilities. Another environment is the dmServer which adds OSGi capabilities. For management and diagnostics they have additional tools. All of these play together to provide a really strong stack against the usual JEE standard running in an application container.<br /><br />VMWare bought SpringSource which means that soon Cloud-deployed apps based on Spring are going to be dead simple to make. This means that VMWare will now compete with the likes of Google App Engine and Microsoft Azure as well by providing virtualized application frameworks.<br /><div><br /></div><div><b>Links</b></div><div><a href="http://www.infoq.com/presentations/Spring-Framework-3.0-Juergen-Hoeller">Spring 3 presentation</a></div><div><a href="http://www.springsource.org/">Spring Community project</a></div><div><a href="http://www.springsource.com/">SpringSource</a></div><div><br /></div></div>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-79525661597122313522009-08-18T22:01:00.003-07:002009-08-18T22:09:11.310-07:00List of countries and timezonesI'm pasting a csv file containing a list of timezone data of some of the cities from <a href="http://www.timeanddate.com/worldclock/custom.html?sort=1">timeanddate.com</a>. I was unable to find such a list and eventually had to do a lot of manual work. Do comment if there are any errors.<br /><br /><pre><br />Afghanistan - Kabul,Asia/Kabul,Afghanistan Time,UTC/GMT +4:30<br />Algeria - Algiers,Africa/Algiers,Central European Time,UTC/GMT +1:00<br />Argentina - Buenos Aires,America/Argentina/Buenos_Aires,Argentine Time,UTC/GMT -3:00<br />Australia - Australian Capital Territory - Canberra,Australia/Canberra,Eastern Standard Time (New South Wales),UTC/GMT +10:00<br />Australia - New South Wales - Sydney,Australia/Sydney,Eastern Standard Time (New South Wales),UTC/GMT +10:00<br />Australia - Northern Territory - Darwin,Australia/Darwin,Central Standard Time (Northern Territory),UTC/GMT +9:30<br />Australia - Queensland - Brisbane,Australia/Brisbane,Eastern Standard Time (Queensland),UTC/GMT +10:00<br />Australia - South Australia - Adelaide,Australia/Adelaide,Central Standard Time (South Australia),UTC/GMT +9:30<br />Australia - Victoria - Melbourne,Australia/Melbourne,Eastern Standard Time (Victoria),UTC/GMT +10:00<br />Australia - Western Australia - Perth,Australia/Perth,Western Standard Time (Australia),UTC/GMT +8:00<br />Austria - Vienna,Europe/Vienna,Central European Time,UTC/GMT +1:00<br />Bahamas - Nassau,America/Nassau,Eastern Standard Time,UTC/GMT -5:00<br />Bangladesh - Dhaka,Asia/Dhaka,Bangladesh Time,UTC/GMT +6:00<br />Belarus - Minsk,Europe/Minsk,Eastern European Time,UTC/GMT +2:00<br />Belgium - Brussels,Europe/Brussels,Central European Time,UTC/GMT +1:00<br />Bolivia - La Paz,America/La_Paz,Bolivia Time,UTC/GMT -4:00<br />Brazil - São Paulo - Sao Paulo,America/Sao_Paulo,Brasilia Time,UTC/GMT -3:00<br />Bulgaria - Sofia,Europe/Sofia,Eastern European Time,UTC/GMT +2:00<br />Canada - Alberta - Edmonton,America/Edmonton,Mountain Standard Time,UTC/GMT -7:00<br />Canada - British Columbia - Vancouver,America/Vancouver,Pacific Standard Time,UTC/GMT -8:00<br />Canada - Manitoba - Winnipeg,America/Winnipeg,Central Standard Time,UTC/GMT -6:00<br />Canada - Newfoundland and Labrador - St. John's,America/St_Johns,Newfoundland Standard Time,UTC/GMT -3:30<br />Canada - Nova Scotia - Halifax,America/Halifax,Atlantic Standard Time,UTC/GMT -4:00<br />Canada - Ontario - Toronto,America/Toronto,Eastern Standard Time,UTC/GMT -5:00<br />Canada - Quebec - Montreal,America/Montreal,Eastern Standard Time,UTC/GMT -5:00<br />Chile - Santiago,America/Santiago,Chile Time,UTC/GMT -4:00<br />China - Shanghai,Asia/Shanghai,China Standard Time,UTC/GMT +8:00<br />Colombia - Bogota,America/Bogota,Colombia Time,UTC/GMT -5:00<br />Croatia - Zagreb,Europe/Zagreb,Central European Time,UTC/GMT +1:00<br />Cuba - Havana,America/Havana,Cuba Standard Time,UTC/GMT -5:00<br />Czech Republic - Prague,Europe/Prague,Central European Time,UTC/GMT +1:00<br />Denmark - Copenhagen,Europe/Copenhagen,Central European Time,UTC/GMT +1:00<br />Dominican Republic - Santo Domingo,America/Santo_Domingo,Atlantic Standard Time,UTC/GMT -4:00<br />Egypt - Cairo,Africa/Cairo,Eastern European Time,UTC/GMT +2:00<br />El Salvador - San Salvador,America/El_Salvador,Central Standard Time,UTC/GMT -6:00<br />Estonia - Tallinn,Europe/Tallinn,Eastern European Time,UTC/GMT +2:00<br />Ethiopia - Addis Ababa,Africa/Addis_Ababa,Eastern African Time,UTC/GMT +3:00<br />Fiji - Suva,Pacific/Fiji,Fiji Time,UTC/GMT +12:00<br />Finland - Helsinki,Europe/Helsinki,Eastern European Time,UTC/GMT +2:00<br />France - Paris,Europe/Paris,Central European Time,UTC/GMT +1:00<br />Germany - Berlin - Berlin,Europe/Berlin,Central European Time,UTC/GMT +1:00<br />Greece - Athens,Europe/Athens,Eastern European Time,UTC/GMT +2:00<br />Guatemala - Guatemala,America/Guatemala,Central Standard Time,UTC/GMT -6:00<br />Honduras - Tegucigalpa,America/Tegucigalpa,Central Standard Time,UTC/GMT -6:00<br />Hong Kong - Hong Kong,Asia/Hong_Kong,Hong Kong Time,UTC/GMT +8:00<br />Hungary - Budapest,Europe/Budapest,Central European Time,UTC/GMT +1:00<br />Iceland - Reykjavik,Atlantic/Reykjavik,Greenwich Mean Time,UTC/GMT +0:00<br />India - Delhi - New Delhi,Asia/Kolkata,India Standard Time,UTC/GMT +5:30<br />India - Maharashtra - Mumbai,Asia/Kolkata,India Standard Time,UTC/GMT +5:30<br />India - West Bengal - Kolkata,Asia/Kolkata,India Standard Time,UTC/GMT +5:30<br />Indonesia - Java - Jakarta,Asia/Jakarta,West Indonesia Time,UTC/GMT +7:00<br />Iran - Tehran,Asia/Tehran,Iran Standard Time,UTC/GMT +3:30<br />Iraq - Baghdad,Asia/Baghdad,Arabia Standard Time,UTC/GMT +3:00<br />Ireland - Dublin,Europe/Dublin,Greenwich Mean Time,UTC/GMT +0:00<br />Israel - Jerusalem,Asia/Jerusalem,Israel Standard Time,UTC/GMT +2:00<br />Italy - Rome,Europe/Rome,Central European Time,UTC/GMT +1:00<br />Jamaica - Kingston,America/Jamaica,Eastern Standard Time,UTC/GMT -5:00<br />Japan - Tokyo,Asia/Tokyo,Japan Standard Time,UTC/GMT +9:00<br />Jordan - Amman,Asia/Amman,Eastern European Time,UTC/GMT +2:00<br />Kazakstan - Almaty,Asia/Almaty,Alma-Ata Time,UTC/GMT +6:00<br />Kenya - Nairobi,Africa/Nairobi,Eastern African Time,UTC/GMT +3:00<br />Kiribati - Christmas Island - Kiritimati,Pacific/Kiritimati,Line Is. Time,UTC/GMT +14:00<br />Kuwait - Kuwait City,Asia/Kuwait,Arabia Standard Time,UTC/GMT +3:00<br />Lebanon - Beirut,Asia/Beirut,Eastern European Time,UTC/GMT +2:00<br />Madagascar - Antananarivo,Indian/Antananarivo,Eastern African Time,UTC/GMT +3:00<br />Malaysia - Kuala Lumpur,Asia/Kuala_Lumpur,Malaysia Time,UTC/GMT +8:00<br />Mexico - Federal District - Mexico City,America/Mexico_City,Central Standard Time,UTC/GMT -6:00<br />Morocco - Casablanca,Africa/Casablanca,Western European Time,UTC/GMT +0:00<br />Myanmar - Yangon,Asia/Rangoon,Myanmar Time,UTC/GMT +6:30<br />Nepal - Kathmandu,Asia/Kathmandu,GMT+05:45,UTC/GMT +5:45<br />Netherlands - Amsterdam,Europe/Amsterdam,Central European Time,UTC/GMT +1:00<br />New Zealand - Auckland,Pacific/Auckland,New Zealand Standard Time,UTC/GMT +12:00<br />New Zealand - Chatham Islands,Pacific/Chatham,Chatham Standard Time,UTC/GMT +12:45<br />Nicaragua - Managua,America/Managua,Central Standard Time,UTC/GMT -6:00<br />Nigeria - Lagos,Africa/Lagos,Western African Time,UTC/GMT +1:00<br />Norway - Oslo,Europe/Oslo,Central European Time,UTC/GMT +1:00<br />Pakistan - Karachi,Asia/Karachi,Pakistan Time,UTC/GMT +5:00<br />Paraguay - Asuncion,America/Asuncion,Paraguay Time,UTC/GMT -4:00<br />Peru - Lima - Lima,America/Lima,Peru Time,UTC/GMT -5:00<br />Philippines - Manila,Asia/Manila,Philippines Time,UTC/GMT +8:00<br />Poland - Warsaw,Europe/Warsaw,Central European Time,UTC/GMT +1:00<br />Portugal - Lisbon,Europe/Lisbon,Western European Time,UTC/GMT +0:00<br />Puerto Rico - San Juan,America/Argentina/San_Juan,Argentine Time,UTC/GMT -3:00<br />Romania - Bucharest,Europe/Bucharest,Eastern European Time,UTC/GMT +2:00<br />Russia - Anadyr,Asia/Anadyr,Anadyr Time,UTC/GMT +12:00<br />Russia - Kamchatka,Asia/Kamchatka,Petropavlovsk-Kamchatski Time,UTC/GMT +12:00<br />Russia - Moscow,Europe/Moscow,Moscow Standard Time,UTC/GMT +3:00<br />Russia - Vladivostok,Asia/Vladivostok,Vladivostok Time,UTC/GMT +10:00<br />Saudi Arabia - Riyadh,Asia/Riyadh,Arabia Standard Time,UTC/GMT +3:00<br />Serbia - Belgrade,Europe/Belgrade,Central European Time,UTC/GMT +1:00<br />Singapore - Singapore,Asia/Singapore,Singapore Time,UTC/GMT +8:00<br />South Africa - Johannesburg,Africa/Johannesburg,South Africa Standard Time,UTC/GMT +2:00<br />South Korea - Seoul,Asia/Seoul,Korea Standard Time,UTC/GMT +9:00<br />Spain - Madrid,Europe/Madrid,Central European Time,UTC/GMT +1:00<br />Sudan - Khartoum,Africa/Khartoum,Eastern African Time,UTC/GMT +3:00<br />Sweden - Stockholm,Europe/Stockholm,Central European Time,UTC/GMT +1:00<br />Switzerland - Zurich,Europe/Zurich,Central European Time,UTC/GMT +1:00<br />Taiwan - Taipei,Asia/Taipei,China Standard Time,UTC/GMT +8:00<br />Thailand - Bangkok,Asia/Bangkok,Indochina Time,UTC/GMT +7:00<br />Turkey - Ankara,Turkey,Eastern European Time,UTC/GMT +2:00<br />Turkey - Istanbul,Turkey,Eastern European Time,UTC/GMT +2:00<br />U.K. - England - London,Europe/London,Greenwich Mean Time,UTC/GMT +0:00<br />United Arab Emirates - Dubai - Dubai,Asia/Dubai,Gulf Standard Time,UTC/GMT +4:00<br />Uruguay - Montevideo,America/Montevideo,Uruguay Time,UTC/GMT -3:00<br />U.S.A. - Alaska - Anchorage,America/Anchorage,Alaska Standard Time,UTC/GMT -9:00<br />U.S.A. - California - Los Angeles,America/Los_Angeles,Pacific Standard Time,UTC/GMT -8:00<br />U.S.A. - Colorado - Denver,America/Denver,Mountain Standard Time,UTC/GMT -7:00<br />U.S.A. - Hawaii - Honolulu,Pacific/Honolulu,Hawaii Standard Time,UTC/GMT -10:00<br />U.S.A. - Illinois - Chicago,America/Chicago,Central Standard Time,UTC/GMT -6:00<br />U.S.A. - Michigan - Detroit,America/Detroit,Eastern Standard Time,UTC/GMT -5:00<br />U.S.A. - New York - New York,America/New_York,Eastern Standard Time,UTC/GMT -5:00<br />Uzbekistan - Tashkent,Asia/Tashkent,Uzbekistan Time,UTC/GMT +5:00<br />Venezuela - Caracas,America/Caracas,Venezuela Time,UTC/GMT -4:30<br />Vietnam - Hanoi,Asia/Vientiane,Indochina Time,UTC/GMT +7:00<br />Yemen - Aden,Asia/Aden,Arabia Standard Time,UTC/GMT +3:00<br />Zimbabwe - Harare,Africa/Harare,Central African Time,UTC/GMT +2:00<br /></pre>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com4tag:blogger.com,1999:blog-6078706.post-62744944492532498702009-08-07T00:00:00.004-07:002009-08-07T00:45:25.263-07:00FlexI had an opportunity to look into Flex a few weeks back. I've been mostly focused on Swing last couple of years and before that was fairly involved with web development (pre-ajax). So it's been a nice exercise to investigate what it offers and compare and contrast.<br /><br />Flex has a good out of the box widget set, similar to Swing. One of its major strengths is its UI designer - no real surprise there, given it grew out of flash. Actionscript, the ECMAScript compliant language, tries to be dynamic with optional static typing. This can be useful, especially during at development time with code completion. Debugging can be a challenge though, especially the mix between actionscript and mxml. End up falling back on Alerts which isn't the greatest thing (more so given that they don't block and you can end up with a flurry of alert boxes). I also found that there are multiple ways to do the same thing. This can be quite confusing especially when looking at samples on the web. You don't really know which is the best way or why. One other small thing - event names don't follow "on[event_name]" convention... it's very easy to confuse them with attributes in mxml. Overall, there's a big similarity with the html/js/css style of development - mxml/as/css.<br /><br />Flex demos really well. For creating small simple apps involving fetching data from a remote data source and rendering it, it is probably unrivaled from a productivity perspective. Drag a datagrid onto the canvas, construct an http service and attach a result event handler to bind the result to the datagrid. Simple. Done. I think the 'Bindable' attribute is a big part of why Flex is so successful for these types of apps. It takes care of all that plumbing of syncing model with view. It's interesting to note that JavaFX has introduced the 'bind' keyword in JavaFX Script.<br /><br />Most of what I've come across - articles/examples deal with small apps. I'm interested in how development in Flex would scale. It can be double edged sword. Given that it is so simple to just drag/drop and attach handlers, I would think it would require <span style="font-style: italic;">more</span> discipline and good coding practices. It would be all too easy to keep doing this all over without really giving much thought to overall design and code hygiene. I don't particularly like the <span style="font-style:italic;">script</span> tag in the MXML file. There should've been strict separation between the .mxml file and the .as file. I understand this was probably done to emulate the html/js model but why not force the separation as with separate .js files.<br /><br />Running a fairly complicated app - like TourDeFlex which chronicles the wonders of the various flex widgets - sucks up a lot of resources. Usability suffers. The experience feels sluggish. A similarly "heavy" "ajaxy" app doesn't. What's worse is that on more than one occasion, the browser has crashed running that demo. It's no surprise I haven't seen full fledged flex apps out on the 'open web'. Enterprises can, and have, embraced it since they can dictate the environment.<br /><br />There's a lot of revival in interest in web standards. Just as Adobe has AIR, Microsoft has Silverlight and Sun/Oracle has JavaFX, Google is embracing and evangelizing open web standards - HTML5/CSS/Javascript - as their RIA platform. GWT is the browser independent development platform with generated Javascript as the bytecode. I've started playing around with GWT recently and am quite impressed with its abilities - especially the development environment/cycle. <a href="http://code.google.com/events/io/sessions/GwtPreviewGoogleWebToolkit2.html">Version 2.0</a> promises more useful features.<br /><br />It'll be interesting to see where these platforms end up 3-4 years from now.Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com2tag:blogger.com,1999:blog-6078706.post-50936617910782331842009-07-24T14:37:00.003-07:002009-07-24T14:51:04.766-07:00TurboStringMap for GwtIn my previous post I did mention that Gwt has the basic Java collections emulated in Javascript. In the browser such rich interfaces may not be needed. There is a lightweight Map implementation where the keys are constrained to String types only called <a href="http://code.google.com/p/google-web-toolkit/source/browse/tags/1.7.0/user/src/com/google/gwt/user/client/ui/FastStringMap.java">FastStringMap</a>. It is <a href="http://code.google.com/p/google-web-toolkit/source/browse/tags/1.7.0/user/src/com/google/gwt/user/client/ui/FastStringMap.java#271">directly backed</a> by a Js array. Want to go even lighter then here is the derived TurboStringMap which even a thinner API over a Js array to act like a Map.<br /><br />The rest of the this post is Gwt Java code-<br /><br /><pre><br />package com.onyem.finance.client;<br /><br />import com.google.gwt.core.client.JavaScriptObject;<br /><br />class TurboStringMap<T> {<br /><br /> /*<br /> * Accesses need to be prefixed with ':' to prevent conflict with built-in<br /> * JavaScript properties.<br /> */<br />@SuppressWarnings("unused")<br />private JavaScriptObject map;<br /><br />public TurboStringMap() {<br /> init();<br />}<br /><br />public void clear() {<br /> init();<br />}<br /><br />private native void init() /*-{<br /> this.@com.onyem.finance.client.TurboStringMap::map = {};<br /> }-*/;<br /><br />// Prepend ':' to avoid conflicts with built-in Object properties.<br />public native T get(String key) /*-{<br /> return this.@com.onyem.finance.client.TurboStringMap::map[':' + key];<br /> }-*/;<br /><br />// Prepend ':' to avoid conflicts with built-in Object properties.<br />public native T put(String key, T value) /*-{<br /> key = ':' + key;<br /> var map = this.@com.onyem.finance.client.TurboStringMap::map;<br /> var previous = map[key];<br /> map[key] = value;<br /> return previous;<br /> }-*/;<br /><br />// Prepend ':' to avoid conflicts with built-in Object properties.<br />public native T remove(String key) /*-{<br /> key = ':' + key;<br /> var map = this.@com.onyem.finance.client.TurboStringMap::map;<br /> var previous = map[key];<br /> delete map[key];<br /> return previous;<br /> }-*/;<br /><br />// only count keys with ':' prefix<br />public native int size() /*-{<br /> var value = this.@com.onyem.finance.client.TurboStringMap::map;<br /> var count = 0;<br /> for(var key in value) {<br /> if (key.charAt(0) == ':') ++count;<br /> }<br /> return count;<br /> }-*/;<br /><br />public boolean isEmpty() {<br /> return size() == 0;<br />}<br /><br />public native String[] keys() /*-{<br /> var value = this.@com.onyem.finance.client.TurboStringMap::map;<br /> var keys = [];<br /> for(var key in value) {<br /> keys.push(key.substring(1));<br /> }<br /> return keys;<br />}-*/;<br />}<br /></pre>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-41427819851195633632009-07-08T11:29:00.007-07:002009-07-09T10:30:59.240-07:00GWTI've been working with <a href="http://code.google.com/p/google-web-toolkit/">Gwt</a> since a while and in this post will dump some of my learnings<br /><br /><b>What is Gwt</b><br />Gwt is a toolkit primarily to enable writing webapps in Java which will then be cross compiled into plain Javascript and Html. The core is based on this java to javascript compiler. Additionally they have libraries for purposes like web gui creation, dom manipulation, json and xml parsing, RPC calls with java server support and also unit testing. The Gwt guys are seem pretty obsessed with performance so a lot of the design choices are based on this.<br /><div><br /></div><div>To code in Gwt you use your standard Java tools. This is a great advantage as you get great tool/ide support and java's static checking for code. Java generics are also supported. To create UIs a SWT-like library has been created which can be used. The library contains both Widget classes for common controls as well as layout classes for positioning. There are some quirks which you have to be aware of but thats true for almost any new platform. The layout library is one of the first reasons which got me to Gwt as I was (and still am) simply too scared to use CSS for managing a layout. Using the UI libraries for building interfaces is a bit painful in Java just as it is in Swing and Swt. But more importantly it does not translate to the best Html/Css layout code. For example to place child elements horizontally next to each other you can use the HorizontalPanel and add elements to this. Now when this code is cross compiled the HorizontalPanel is converted to a Html table with a single tr and all the child elements will be placed in td elements. Which depending on your style may not look that good. In the next version of Gwt a new UI Binder feature is going to be added. This will allow declaring the UI in an XML format and then writing the actual event handlers in Java. I guess a lot of UI frameworks are moving to this style which really does make sense in this domain. Also the code generated may be closer to the layout code you would have written by hand (if you knew CSS). You can also create your own Widgets and Panels if you want to. I also almost forgot to mention that you have access to a subset of the Java library. The <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/1.6/index.html?overview-summary.html">API</a> can be read here. Basic java.lang and other important packages like some collections are supported.</div><div><br /></div><div>Now that you have written some Java code you compile it by using the GwtCompiler which can be invoked by an ant task. You get a whole bunch of files into your dist folder. Now these are plain html, js and css files. You dont need any Java support in your webserver to run these. Gwt will generate separate optimized files for each browser supported. A client using IE7 will get a file separate from a FF client and each file will manage the quirks of that browser. So a client downloads almost only what is needed. I lied a bit in this process though. When the browser first hits the entry point html file special js code will decide what browser is running the code. After that the client code for that browser is then loaded in another Http request. Search for more on deferred binding to learn more. Importantly most of the code generated can be cached with long expiration headers.</div><div><br /></div><div>The Gwt compiler essentially compiles Java (source code) to Js. But from where does it get the java.lang and other code of the standard library? The Gwt team has re-written supported Java libraries in a more Js friendly fashion. The String class is based on a normal Js string but the extended Java API for the string class is supported. Similarly the <a href="http://code.google.com/p/google-web-toolkit/source/browse/releases/1.6/user/super/com/google/gwt/emul/java/util/ArrayList.java">ArrayList</a> class is not something that Bloch wrote but a simpler Js friendly version. Just like the layout code generated some of this is overkill for Js. You dont really need the whole Collections API most of the time. You dont need the whole class hierarchy. Like I just needed a lame Map implementation with put and get and nothing else. And since some part of the Gwt library may use the collections in some way you end up using them anyways. See my <a href="http://code.google.com/p/google-web-toolkit/issues/detail?id=3451&can=4">bug</a> for an extended rant</div><div><br /></div><div>Atleast one compiler optimization has to mentioned at this point - dead code elimination. For example if you dont use the substing method in the String class.. that method will not be compiled to Js. Additionally Js code is minified and obfuscated which greatly helps in reduction of download size.</div><div><br /></div><div><b>Where should you use GWT</b></div><div>Gwt is great for Ajax apps like Gmail, Google calendar etc. As a Java developer I dont think I could do better/faster had I been coding Js/Html for <a href="http://onyem.com">Onyem</a>. There are deficiencies right now but things are improving and are already optimized enough.</div><div>I would not still recommend Gwt in sites that have a lot content like news sites or sites where you just want a small widget in one corner. Use jquery or yui for that.</div><div>Also there can be no substitute for knowing CSS and also how to build websites. With a desktop mindset you will end up just bulding really slow apps.</div><div><br /><b>More Features</b><br /></div><div>These may come in some other post-</div><div>Hosted mode to OOPHM</div><div>Async pattern</div><div>Jsni</div><div>RPC</div><div><div>Deferred binding </div><div>More Compiler optimizations</div><div><br /></div></div>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com3tag:blogger.com,1999:blog-6078706.post-30395151375437190202009-06-03T22:59:00.002-07:002009-06-03T23:04:14.843-07:00Open Source is Magic<dl><dt>"Any sufficiently advanced technology is indistinguishable from magic." - Sir Arthur C. Clarke<br /></dt></dl><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/hmZyyBVbkOQ&color1=0xb1b1b1&color2=0xcfcfcf&feature=player_embedded&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/hmZyyBVbkOQ&color1=0xb1b1b1&color2=0xcfcfcf&feature=player_embedded&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-24868555072991273472009-06-01T03:41:00.002-07:002009-06-01T03:49:14.896-07:00Anti-socialIn relation with normal human behaviour I am "anti-social". Personally I know I am just different-social which is great for me.<br /><br />I've been emailing a few people of late whom I don't know that well. And shockingly those gmail contacts started popping up in my gtalk list. The reason was gmails default setting of over-friendly behaviour. If you want more privacy then you need to disable the "Auto-add suggested contacts" feature in chat as described in this <a href="http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=29795">help page</a>Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-74473888202530123302009-05-21T13:19:00.011-07:002009-05-23T21:02:48.750-07:00Ubuntu RemixedI upgraded to Jaunty Jackalope (9.04) over the weekend. Technically it wasn't an upgrade, rather a clean install. My setup makes it convenient to do this... I have three partitions - Windows, Ubuntu and DATA which contains all my docs/pics/music/movies. I tend to skip every other release and when I do want to "upgrade", I just back up my home directory (which is on the Ubuntu partition) to DATA and do a clean install.<br /><br />Way back in Oct 2006, I <a href="http://codeword.blogspot.com/2006/10/ubuntu-606-dapper-drake-on-acer.html">wrote</a> about my first foray into the Ubuntu universe. Two and a half years and five releases later it's thrilling to see how far its come and how much better the experience has gotten on that same machine. All issues listed in that post are virtually non-existent. One of Jaunty's specific goals was improving startup time and they delivered superbly... < 25 seconds on my laptop. I like the consistent new notification system as well. With every new release, it gets more and more polished. <br /><br />Anyway, I thought I'd list the steps I take to get my setup going after every clean install... <ul><li>backup home directory: <span style="font-family:courier new;">cp -R /home/mohnish /media/data/mohnish</span><br /></li><li>clean install </li><li>install updates<br /></li><li>restore home directory: <span style="font-family:courier new;">cp -R /media/data/mohnish /home/mohnish</span></li><li>create sym links in home directory to media on DATA:<br />- <span style="font-family:courier new;">ln -s /media/data/Docs Docs</span><br />-<span style="font-family:georgia;"> <span style="font-family:courier new;">ln -s /media/data/Music Music</span></span><br />- <span style="font-family:courier new;">ln -s /media/data/Pics Pics</span> </li><li>install apps<br />- <a href="http://packages.ubuntu.com/jaunty/ubuntu-restricted-extras">media codecs</a> (audio/video playback, java, flash): <span style="font-family:courier new;">apt-get install ubuntu-restricted-extras</span><br />- <a href="http://do.davebsd.com/">gnome do</a> ('crazy delicious' once you're used to it): <span style="font-family:courier new;">apt-get install gnome-do gnome-do-plugins</span><br />- <a href="http://www.listen-project.org/">listen</a> (music during setup is essential): <span style="font-family:courier new;">apt-get install listen</span><br />- <a href="http://www.opera.com/">opera</a> (small, wicked fast and built-in mouse gestures... the best browser out there): grab .deb package from opera.com<br />- <a href="http://www.skype.com/">skype</a> (convenient, saves money for cheapies like me): grab .deb package from skype.com<br />- <a href="http://www.videolan.org/vlc/">vlc</a> (the 'everything' media player): <span style="font-family:courier new;">apt-get install vlc</span><br />- <a href="http://picasa.google.com/linux">picasa</a> (organize pics with nice web album integration): grab .deb package from goog<br />- <a href="http://earth.google.com/">googleearth</a> (spy on your neighbourhood) - grab .bin file from goog<br />- <a href="http://gnomad2.sourceforge.net/">gnomad2</a> (organize music on creative nomads/zens): <span style="font-family:courier new;">apt-get install gnomad2</span><br />- <a href="http://code.google.com/p/sopcast-player/">sopcast-player</a> (for those 5am Liverpool footy matches (with chinese commentary)): grab .deb package from goog code<br />- <a href="http://www.getmiro.com/">miro</a> (get some <a href="http://codeword.blogspot.com/2008/03/constant-learning-with-miro.html">gyan</a>): <span style="font-family:courier new;">apt-get install miro</span><br /><span style="text-decoration: underline;"></span></li></ul>All this <a href="http://blogs.sun.com/jonathan/entry/will_java_be_the_world">noise</a> about "apps stores" lately... the linux distro repos are the orignial and ultimate app store aren't they? Moreover, all the app settings are in my restored home directory. So they start up like nothing's changed... firefox/opera with their last sessions, miro with the same feed list, skype with my account.<br /><br /><span style="font-weight: bold;">Remix</span><br />I happened to have access to an Asus Eee 700 netbook. It comes with Xandros linux with a weird Windows XP like look and limited functionality ("basic mode" with no access to the terminal/can't install any app (only certain pre-selected ones)/etc...). <a href="http://www.canonical.com/projects/ubuntu/unr">Ubuntu Remix</a> targets these types of netbooks. It's a full ubuntu system with added packages for the UI specifically tuned for small screen sizes.<br /><br />With no cd/dvd drive, the way to install it is via a bootable usb drive which can easily be created using <a href="https://help.ubuntu.com/community/Installation/FromImgFiles">usb-imagewriter</a>. The installation process is the same good ol ubuntu wizard. It ended up sucking up ~2.5 gb of the 4gb harddisk. There are things to tune/optimize which should reclaim some of that space as well as speed things up. For now, I'm just happy to get a chance to play around with it :)Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com3tag:blogger.com,1999:blog-6078706.post-12053629296680611092008-09-03T16:21:00.001-07:002008-09-05T00:09:25.701-07:00Javascript RevisistedWhen starting an expedition with a new language, (most) people take time to fully understand and learn it before attempting to program with it. Except with Javascript... almost universally, it invites people to dive in without really knowing it. I'm paraphrasing <a href="http://javascript.crockford.com/" target="_blank">Douglas Crockford</a> here.<br /><br />I've found this to be very true. Almost 10 years back I started playing around with html and then CSS. The natural final progression led me to Javascript. I clearly remember going through <a href="http://www.webmonkey.com/tutorial/JavaScript_Tutorial" target="_blank">Thau's Javascript tutorials</a> over at WebMonkey trying to actually figure out what I was hacking. Those were exciting times at the height of the "Browser War" with the DOM coming into its own: MS introducing <span style="font-family:Courier;">document.all</span> and Netscape's <span style="font-family:Courier;">document.layers</span>. After IE won, nothing much happened for the next 5 years. And Javascript, already considered a toy language back then was more or less ignored.<br /><br />With the emergence of Ajax all that changed. People actually started taking a serious look at JS. Rahul found a series of videos - <a href="http://video.yahoo.com/video/play?vid=111593" target="_blank">The Javascript Programming Language</a> - by the above mentioned Douglas Crockford over at YUI threatre. They are by far the <b>best</b> material on revealing the <i>real</i> javascript and what a wonderful dynamic language it is. It is simple, yet, deceptively powerful. I took notes and here are some language highlights:<br /><br /><b>Overview</b><br /><ul><li>A real language - small, so easily approachable.</li><br /><li>Dynamic - "load and go"... i.e. <b>interpreted</b> + <b>loosely typed</b>.</li><br /><li>Objects are containers... i.e. HashMaps.</li><br /><li><b>Prototypical inheritance</b> - No classes. Inherit directly from objects.</li><br /><li><b>Lambda</b> support - Closures. "First lambda language to go mainstream".</li></ul><br /><b>Types</b><br /><ul><li>Numbers - 64-bit floats. <span style="font-family:Courier;">NaN</span> is result of erroneous operations.</li><br /><li>Strings - 16-bit chars. Immutable. == compares string values.</li><br /><li><span style="font-family:Courier;">null</span> - nothing.</li><br /><li><span style="font-family:Courier;">undefined</span> - default variable value. Missing object members.</li><br /><li>Booleans - <b>falsy</b>: <span style="font-family:Courier;">false</span>, <span style="font-family:Courier;">null</span>, <span style="font-family:Courier;">undefined</span>, empty string, 0, <span style="font-family:Courier;">NaN</span>. <b>truthy</b>:!(falsy)</li><br /><li>Objects - everything else. Including functions.</li><br /><li>Loosely typed != Untyped. Checks at runtime. Any type can be stored in any variable <b>including functions</b>.</li></ul><br /><b>Ops</b><br /><ul><li>== and != do type coersion. Use === and !== instead.</li><br /><li>&& and || return values.</li><br /><li><span style="font-family:Courier;"><b>foreach (i in object)</b></span> iterates over ALL properties. Use <b>hasOwnProperty</b> check.</li><br /><li><b>Scope</b> - blocks {} don't have scope. Only functions have scope. Variables inside function are not visible outside.</li></ul><br /><b>Objects</b><br /><ul><li>Inherit from other objects.</li><br /><li>Are an unordered Map.</li><br /><li>Object literals: <b>{}</b> (Basis of <b>JSON</b>: simple data exchange format)</li><br /><li>New empty object: new Object() == {} == object(Object.prototype)</li><br /><li>New members are added by simple assignment. No need for a new class.</li><br /><li>"Linkage" to "parent" object - Simple inheritance.</li><br /><li>When setting values, only affects current object. When getting values, access goes "up", if not found in current object.</li><br /><li>Pass by ref.</li><br /><li>=== is comparison on reference.</li><br /><li>Remove member: <span style="font-family:Courier;">delete object[name]</span></li></ul><br /><b>Arrays</b><br /><ul><li>Inherit from Object.</li><br /><li>Indexes are strings (numbers are converted).</li><br /><li>Array literals: <b>[]</b> (== new Array())</li><br /><li><span style="font-family:Courier;">delete array[index]</span> leaves a "hole" (value is <span style="font-family:Courier;">undefined</span>). Use <span style="font-family:Courier;">Array.prototype.splice</span> instead.</li></ul><br /><b>Functions</b><br /><ul><li>Are objects.</li><br /><li>Inherit from Object and <b>can store name/value pairs</b>!</li><br /><li>JS function == other language lambda.</li><br /><li><span style="font-family:Courier;">function foo() {}</span> is equivalent to <span style="font-family:Courier;">var foo = function() {}</span></li><br /><li>Can have inner functions with access to parent function scope even after parent function is invoked: <b>Closures</b>.</li></ul><br /><b>Misc</b><br /><ul><li>Can augment built-in types by modifying prototypes.</li><br /><li>Avoid type wrappers (added to be similar to Java).</li><br /><li>Browser 'window' is global object. Container for all global vars and built-in objects.</li><br /><li>Every object is a separate namespace.</li><br /><li><b>Browser apps are all single threaded.</b></li></ul><br /><b>DOM</b><br /><ul><li><b>id</b> - uniquely identifies element.</li><br /><li>Avoid <span style="font-family:Courier;">document.all</span>. Use <span style="font-family:Courier;">document.getElementById(id)</span>, <span style="font-family:Courier;">document.getElementsByName(name), </span><span style="font-family:Courier;"><node>.getElementsByTagName(tagName)</node></span>.</li><br /><li>Document Tree Structure: <b>document</b> is root. <b>document.documentElement</b> is HTML tag. <b>document.body</b> is BODY tag.</li><br /><li><b>Node</b>: child, sibling, parent pointers. childNodes list.</li><br /><li><b>Style</b>: <span style="font-family:Courier;">node.className</span>/<span style="font-family:Courier;">node.style.styleName</span></li><br /><li><b>Events</b>: Browser - event driven, single threaded, async programming model. Events targeted at particular nodes. Events cause invocation of event handlers. (Remove event handlers from nodes before deleting). <span style="font-family:Courier;">node["on" + type] = function</span> to add event handler. Event handlers send optional event object. Events bubble up from "specific" to "general".</li><br /><li><b>Dialogs</b>: alert, confirm, prompt all block async model. Avoid them in ajaxy apps.</li></ul><br /><br />Detailed slides:<br /><a href="http://yuiblog.com/assets/crockford/javascript.zip" target="_blank">The Javascript Programming Langauge</a><br /><a href="http://yuiblog.com/assets/crockford/advancedjavascript.zip" target="_blank">Advanced Javascript</a><br /><a href="http://yuiblog.com/assets/crockford/theory.zip" target="_blank">DOM</a>Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-49287657439577706462008-06-26T02:52:00.001-07:002008-06-26T03:00:21.397-07:00Eclipse Ganymede is out<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://ganymede-mirror1.eclipse.org/eclipse/downloads/drops/R-3.4-200806172000/whatsnew3.4/images/workingsets.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px;" src="http://ganymede-mirror1.eclipse.org/eclipse/downloads/drops/R-3.4-200806172000/whatsnew3.4/images/workingsets.png" border="0" alt="" /></a><br />.. and one hungry Indian dude wrote a section in the <a href="http://ganymede-mirror1.eclipse.org/eclipse/downloads/drops/R-3.4-200806172000/whatsnew3.4/eclipse-news-part1.html">New and Noteworthy</a>.Anonymoushttp://www.blogger.com/profile/09578376920748324933noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-2449836825649968902008-03-31T23:20:00.004-07:002008-04-01T10:37:30.307-07:00Immutable locksImmutability is something that's mentioned over and over when it comes to parallel programming. Part I of <a href="http://www.javaconcurrencyinpractice.com/" target="_blank">Java Concurrency In Practice</a> is all about composing objects that "play well" in the concurrent world and those that are immutable are the "best" citizens. No wonder <a href="http://www.defmacro.org/ramblings/fp.html" target="_blank">functional languages</a> are coming up in a big way. Anyone want to bet on them taking over the world in the next decade? Or if not taking over outright, at least succeeding in mutating our beloved imperative ones into unrecognisable functional beasts.<br /><br />Anyway, check out the method below:<br /><br /><span style="font-family:courier; font-size:80%; line-height:80%;">public class Foo {<br /> private Listener[] listeners;<br /><br /> public Foo() {<br /> listeners = new Listener[0];<br /> }<br /><br /> public void addListener(Listener listener) {<br /> synchronized (listeners) {<br /> Listener[] newListeners = new Listener[listeners.length + 1];<br /><br /> for (int i = 0; i < listeners.length; ++i) {<br /> newListeners[i] = listeners[i];<br /> }<br /><br /> newListeners[listeners.length] = listener;<br /><br /> listeners = newListeners;<br /> }<br /> }<br />}</span><br /><br />So here, the first thread to obtain the lock on <span style="font-family:courier;">listeners</span> reassigns it to a new Array object... <span style="font-family:courier;">newListeners</span>. Subsequent threads would continue to lock on the old <span style="font-family:courier;">listeners</span> array, while new threads would lock on the "new" listeners array, and potentially corrupt the data. So I guess there's an unwritten property about locks... they need to be immutable to avoid situations like the above.<br /><br />So given that locks need to be constant, there's no way to have a workable solution in the above code without using an additional object as the lock. The easy "lazy" thing to do is to just synchronize the method itself. The Foo instance (<span style="font-family:courier;">this</span>) would then be that "additional object". But that would be wasteful since it would prevent all other synchronized method calls, even ones that have nothing to do with listeners.<br /><br />(If instead of an Array, a List was used, the problem wouldn't have occurred given that the List itself wouldn't change. And I think, nowadays mostly the thinking when programming in managed languages is to go for Collections, rather than mess with manually managing Arrays. But I have still seen code like the above, so it's not totally contrived.)Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com0tag:blogger.com,1999:blog-6078706.post-26534428403822427832008-03-30T20:32:00.003-07:002008-12-09T12:10:05.920-08:00Constant Learning with MiroIn every field, it's important to continue to learn about one's discipline even after graduating from formal learning institutions. No where is this more pertinent than in the software industry where the only constant is (fast and furious) change. There's always books and articles to keep up with what's new, but videos have emerged in a big way as another avenue.<br /><br />And it seems like the developer communities at all the big guns... Microsoft, Sun, Google and Yahoo... have jumped on the bandwagon and are producing great content. Microsoft had started <a href="http://channel9.msdn.com/" target="_blank">channel 9</a> a few years back where they'd go around interviewing key engineers. Sun has videos through the <a href="http://blogs.sun.com/sdnchannel/" target="_blank">Sun Developer Network Channel</a>. Google has their <a href="http://www.google.com/url?sa=t&ct=res&cd=1&url=http%3A%2F%2Fvideo.google.ca%2Fvideosearch%3Fq%3DengEDU&ei=tGzwR9iZM6XopgSO-Ol4&usg=AFQjCNHuId79udc4xHMEAFcQg0ZB5m647Q&sig2=UMR-cIZ4qk0qpdE3-BBLsw" target="_blank">Tech Talks</a> series. Rahul introduced me to Yahoo's <a href="http://developer.yahoo.com/yui/theater/" target="_blank">YUI Theater</a> few months back.<br /><br />The great thing about a lot of the talks is that although they are presented in the context of the company's platform/language/technology, they tend to transcend them and are concepts and trends that apply to general software engineering. Microsoft's channel9 for instance, has had wonderful discussions with architects and designers on everything from <a href="http://channel9.msdn.com/ShowPost.aspx?PostID=374141" target="_blank">functional programming</a> to <a href="http://channel9.msdn.com/ShowPost.aspx?PostID=329808" target="_blank">garbage collection</a> to <a href="http://channel9.msdn.com/ShowPost.aspx?PostID=347531" target="_blank">concurrency</a>.<br /><br />In spite of all the great videos out there, it's a hassle to have to go to each of these different sites, see what's new and watch it. Enter <a href="http://www.getmiro.com/">Miro</a>. Miro is an amazing open source application designed specifically to consume vidcasts (it works with podcasts too). Here's a screenshot where I've subscribed to some of these "channels" (yes, I have a lot of catching up to do!).<br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvLCZQXubT1GlmLax0uC8VJJ8TDLJbP0OiiIBqartc5VCb-8cxnpECtf9fB7G0vgwjmAALZP5kVcpNLDdiHmjy3jiUYfEOx21iKb2NNhraongHzw42STO9MZmBPr2adz8mbFjVew/s1600-h/miro.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvLCZQXubT1GlmLax0uC8VJJ8TDLJbP0OiiIBqartc5VCb-8cxnpECtf9fB7G0vgwjmAALZP5kVcpNLDdiHmjy3jiUYfEOx21iKb2NNhraongHzw42STO9MZmBPr2adz8mbFjVew/s400/miro.png" alt="" id="BLOGGER_PHOTO_ID_5183771205816232834" border="0" /></a><br />You may be wondering if this really is a huge deal. I think it is. Miro simplifies and automates the process and makes it so simple and easy. Every time you start miro, it'll tell you of the latest content available and ask if you want to download it. And the quality is generally much better what with being able to view it full screen and it also has nifty <a href="http://www.getmiro.com/features/" target="_blank">features</a> like remembering where you left of in case you have to pause midway through etc... The one downside is that it has to download the video bits as opposed to streaming it through flash (as is common on many sites since the emergence of youtube). So for the bandwidth constrained it can be a bit of an issue. But then again, flash still has some issues on Linux and some of the videos don't play. Plus, looks like Microsoft recently converted to Silverlight which doesn't work on Linux (haven't looked into <a href="http://www.mono-project.com/Moonlight" target="_blank">Moonlight</a>). So most likely you'd end up having to download it anyway.<br /><br />I've only mentioned few channels, but as you can imagine, there's tons of them around the web. Miro has a guide built-in which showcases some of them. Apart from tech content, many universities like <a href="http://webcast.berkeley.edu/index.php" target="_blank">Berkeley</a> and <a href="http://www.princeton.edu/WebMedia/special/" target="_blank">Princeton</a> are broadcasting some of their lectures and events. So there's definitely no lack of content. Just need to make time!<br /><br />Overall, I love Miro and can't say enough good things about it. Kudos to the <a href="http://planet.getmiro.com/" target="_blank">guys</a> who've developed it.Mohnish Raohttp://www.blogger.com/profile/06259133405943378088noreply@blogger.com0