<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Delegate Pattern</title>
	
	<link>http://blog.masto.com</link>
	<description>Musings on Software Development</description>
	<lastBuildDate>Fri, 18 Dec 2009 16:53:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<feedburner:info uri="delegatepattern" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://blog.masto.com/feed/" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://blog.masto.com/feed/" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Fblog.masto.com%2Ffeed%2F" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><item>
		<title>Catalyst and Hudson, Sitting in a Tree</title>
		<link>http://feedproxy.google.com/~r/DelegatePattern/~3/NLXe7pGCWZ4/</link>
		<comments>http://blog.masto.com/2009/12/catalyst-and-hudson-sitting-in-a-tree/#comments</comments>
		<pubDate>Fri, 18 Dec 2009 16:35:43 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Programming Languages]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[catalyst]]></category>
		<category><![CDATA[hudson]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://blog.masto.com/?p=39</guid>
		<description><![CDATA[Introduction
I&#8217;ve been developing Web applications for a long time.  Over the past few years, I&#8217;ve become a fan of the Catalyst Web Framework.  Much like Perl itself, it makes the easy things easy and the hard things possible.  One of Catalyst&#8217;s biggest strengths is that it does not attempt to provide a [...]]]></description>
			<content:encoded><![CDATA[<h1>Introduction</h1>
<p>I&#8217;ve been developing Web applications for a long time.  Over the past few years, I&#8217;ve become a fan of the <a href="http://www.catalystframework.org/">Catalyst Web Framework</a>.  Much like Perl itself, it makes the easy things easy and the hard things possible.  One of Catalyst&#8217;s biggest strengths is that it does <strong>not</strong> attempt to provide a complete package in the way that, for example, Rails does.  Catalyst&#8217;s &#8220;bring your own modules&#8221; approach allows it to concentrate on the core concerns of a Web framework: requests and responses, URL dispatching, component interaction, session handling, security, etc.  Even in those areas, Catalyst is well-designed and delegates responsibility to plugins.</p>
<p>But this article isn&#8217;t about Catalyst per se; it&#8217;s about automation.  I&#8217;ve built quite a few Catalyst-based applications, and in another similarity to Perl, I&#8217;ve found that it&#8217;s quite possible to learn it in stages.  The first projects got up and running quickly, but didn&#8217;t pay much attention to how Catalyst applications are built and deployed &#8212; it&#8217;s very easy to simply check out the source code, point Apache at it, and be off and running.  Once deployed, there&#8217;s rarely time to go back and reexamine the fundamentals, so I&#8217;ve tried to use the opportunity afforded by starting new projects to learn.  Each time I start from a clean slate, I resolve to do at least one thing better than last time: figuring out how to unit-test that tricky module, refactoring common code, fitting Rose::DB::Object into Catalyst&#8217;s Model layer.  Recently, I&#8217;ve dug in to one of the more basic yet easily ignored facets of a Catalyst application: the build system.</p>
<h1>The Plan</h1>
<p>Having spent a year doing some Java development at work, I&#8217;ve become familiar with the <a href="http://en.wikipedia.org/wiki/Continuous_integration">&#8220;continuous integration&#8221;</a> world.  A lot of this stuff is the way I&#8217;ve always worked, since I&#8217;ve been on relatively small teams with decent source control practices and no need to work so independently that integration ever become an issue.  I do think, however, that build automation is a highly worthwhile pursuit.  When I think about build automation, a few principles come to mind:</p>
<ul>
<li>One command for builds</li>
<li>Automated tests</li>
<li>Automated deployment if tests pass</li>
<li>Archived, versioned build artifacts</li>
</ul>
<p>I&#8217;ve used <a href="http://cruisecontrol.sourceforge.net/">CruiseControl</a> and <a href="http://hudson-ci.org/">Hudson</a> in the Java world, and vastly prefer Hudson.  CruiseControl does the job, but it&#8217;s a huge pain to set up and administer compared to Hudson.  Hudson also has an active plugin ecosystem.  And it looks so good, you almost wouldn&#8217;t guess it&#8217;s Java&#8230;</p>
<p>So here&#8217;s what I want to accomplish: when code is checked in, Hudson automatically runs the build command, then it runs the tests, and if the tests pass, it packages the application and deploys it to a server where the latest bleeding-edge version is always available for access by QA.</p>
<h1>PAR Boiled</h1>
<p>The first issue that came up was that a build needs to produce &#8220;artifacts&#8221;, to borrow a Javaism.  In a more traditional application, that&#8217;s the executable or installer or .tar.gz file.  Up until now, I&#8217;d just been treating the directory full of source code as my application, relying on source control tags to keep track of version numbers and releases.  But for automation it would be a lot better to produce a single file at the end, allowing me to take advantage of Hudson&#8217;s archiving and fingerprinting features.  If that single file were also, effectively, an &#8220;executable&#8221;, it could even simplify deployment.</p>
<p>In the Java world, they have WAR files, which are essentially Web applications packaged in a standard format, inside a self-contained ZIP file.  Java-based web servers know what to do when you hand them one of these files, making application deployment sim&#8230; well, nothing&#8217;s simple in the Java world, but at least the packaging is straightforward.  And it turns out that this idea has been adapted for Perl, in the form of <a href="http://par.perl.org/wiki/Main_Page">PAR</a>.</p>
<p>It seemed like this was the solution to the artifact problem: the build should produce a PAR file at the end, which contains everything needed to run the application.  I just ship that off to the web server and I&#8217;m done.  And Catalyst even has support built in for packaging an application as a PAR file.  Is it really that simple?  Read on&#8230;</p>
<h1>Cue Mr. Hudson</h1>
<p>Hudson has a very straightforward hook to non-Ant build systems: a text box into which you enter shell commmands.  That&#8217;s just fine with me.  So &#8220;make catalyst_par&#8221; is all I need to do, right?  Not exactly.  I ran into a few problems, one of which the set of files chosen to put in the PAR file.  Module::Install::Catalyst doesn&#8217;t provide much, if any control over the build process.  It makes an attempt to do the right thing, but unfortunately it ends up including a lot of .svn directories, while missing some SQL files I had added.  In the end, I settled on a somewhat awkward process of using &#8220;make distdir&#8221; to get a clean copy of the right files, changing into that directory, and continuing the build from there.  I also had to relocate my SQL files to &#8220;lib&#8221; for now, which is a bit ugly.  Here&#8217;s the guts of the script Hudson runs.  I&#8217;ve highlighted two lines that I&#8217;ll be referring to in the following discussion.</p>
<pre>[ -e Makefile ] &amp;&amp; make realclean
perl Makefile.PL --skipdeps
make distdir
<span style="color: red">cd MyProj-*</span>
cp lib/MyProj/DB.pm-dist lib/MyProj/DB.pm
cp myproj.conf-dist myproj.conf
echo "build hudson b$BUILD_NUMBER r$SVN_REVISION" &gt;&gt; myproj.conf
perl Makefile.PL
make
<span style="color: red">make testjunit</span>
make catalyst_par
mv myproj.par ..
./script/db_upgrade.pl myproj_dev myproj
cp myproj.par /opt/myproj
/sbin/service myproj restart
</pre>
<h1>Getting the Version Number</h1>
<p>MyProj.pm has a version number.  Module::Install helpfully finds that version number and uses it in the generated Makefile.  It forms, for example, the directory name generated by &#8220;make distdir&#8221;.  I wanted to add a target to the Makefile that would just print the version number, but I found that adding targets when using Module::Install is complicated and difficult.  Thus began the arduous part of my journey.  Of course, writing this after it&#8217;s all over, I know how to do it and I know there are plenty of other, possibly better ways to accomplish the same goal, so I&#8217;ll be returning to this script soon to fix it.</p>
<h1>It Gets a Bit Messy</h1>
<p>This may be worth reading at this point: <a href="http://stackoverflow.com/questions/73889/which-framework-should-i-use-to-write-modules/214717#214717">http://stackoverflow.com/questions/73889/which-framework-should-i-use-to-write-modules</a>.  Catalyst used to generate Build.PL, but apparently it caused problems and now it&#8217;s been switched to using only Module::Install.</p>
<p>So now that we know how to build a PAR file, let&#8217;s back up a step and address testing.  The Perl community, in my opinion, has high standards when it comes to testing.  Catalyst makes sure to set up a test infrastructure for your application, and provides plenty of support for doing the kinds of things that could be tricky to test in a Web app.  So testing is good, and we want to make sure to run our tests automatically.  &#8220;make test&#8221; actually handles that perfectly well out of the box.  But there&#8217;s an even better way.</p>
<p>Justin Mason has already been through the Hudson+Perl dance, and wrote a very nifty script to <a href="http://github.com/jmason/tap-to-junit-xml">convert Perl&#8217;s test output to JUnit</a>.  The win here is that Hudson knows how to parse JUnit output, so instead of just &#8220;build failed&#8221;, now it can tell you which tests failed and where.  I&#8217;ll take it!</p>
<p>At about the same time, I also got interested in Devel::Cover.  I decided to start adding this stuff to the Makefile so that I wouldn&#8217;t have to maintain a lot of external scripts to run these natural parts of the build process.  There&#8217;s a <a href="http://search.cpan.org/dist/ExtUtils-MakeMaker-Coverage/">module on CPAN</a> for including a Devel::Cover target in your Makefile.  So I added it that first.  Then I decided to add another target of my own to run the tests and do the JUnit conversion.  This is where I found out that ExtUtils::MakeMaker only has one usable extension point &#8212; and it was taken.  Also, excuse me not being much of a Makefile wizard.</p>
<pre># ExtUtils::MakeMaker::Coverage redefines MY::postamble
# This is a hack to be able to define a new target.
push @ExtUtils::MakeMaker::MM_Sections, "testjunit";
push @ExtUtils::MakeMaker::Overridable, "testjunit";
sub MY::testjunit {
  return &lt;&lt;END_TESTJUNIT
testjunit :
\t\$(MKPATH) testxml
\t-\$(MAKE) TEST_VERBOSE=1 test 2&gt;&amp;1 | tee make_test.log
\t\$(PERL) script/tap-to-junit-xml.pl --input make_test.log "make test" testxml/make_test
END_TESTJUNIT
}

makemaker_args clean => { FILES => [qw/make_test.log testxml/] };
</pre>
<p>There are two problems here.  One is that I failed to see in the documentation for ExtUtils::MM::Coverage that there&#8217;s a much simpler way to call its postamble from your own.  My mistake, which I&#8217;ll be fixing shortly.  I&#8217;m showing my mistake here because I do think it illustrates just how messy MakeMaker is.</p>
<p>The second problem is that, in the end, is a small chunk of code.  But getting to that small chunk of code took forever.  I&#8217;ve already rambled on and on, so I&#8217;m not writing down all the false starts and dead ends I went through.  In doing so, I read most of the code for Module::Install and MakeMaker.  They&#8217;re almost the polar opposite of Catalyst, in that their behavior is hard-coded and not built to be extended.  Certainly not in ways the original authors didn&#8217;t expect.  It&#8217;s not that the code is necessarily bad, but I think a build system for the future should be more flexible.  I haven&#8217;t looked at Module::Build much other than skimming the documentation, so maybe it&#8217;s not the perfect solution, but it looks like it would work better.  Maybe I&#8217;ll end up porting this stuff to it.  The problem is that Catalyst does provide some hooks for Module::Install, and I don&#8217;t have the time right now to reimplement things like the PAR building.</p>
<h1>Conclusion</h1>
<p>Continuous integration and build automation are worthwhile pursuits.  I think the Perl community should pay more attention to tools like Hudson.  Catalyst would benefit from improved build and deployment tools &#8212; something that the Rails community seems to be very actively working on these days.  MakeMaker has got to do, and given that Module::Install is tightly coupled to it, I&#8217;m not sure it&#8217;s the right solution.  In the end, I&#8217;m happy to have it all working, but I look forward to the next clean-slate opportunity when I&#8217;ll get  a chance to do it again using what I&#8217;ve learned.</p>
<img src="http://feeds.feedburner.com/~r/DelegatePattern/~4/NLXe7pGCWZ4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.masto.com/2009/12/catalyst-and-hudson-sitting-in-a-tree/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.masto.com/2009/12/catalyst-and-hudson-sitting-in-a-tree/</feedburner:origLink></item>
		<item>
		<title>Groovy, Man</title>
		<link>http://feedproxy.google.com/~r/DelegatePattern/~3/6o9i764sPOc/</link>
		<comments>http://blog.masto.com/2009/11/groovy-man/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 21:52:34 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Programming Languages]]></category>

		<guid isPermaLink="false">http://blog.masto.com/?p=34</guid>
		<description><![CDATA[This is what happens with Perl if you try to connect to a database and don’t have the driver installed:
Chris@Chris-Mastos-iMac:~$ perl -MDBI -e '$db = DBI-&#62;connect("dbi:Oracle:MM_WH")'
install_driver(Oracle) failed: Can't locate DBD/Oracle.pm in @INC (@INC contains: /opt/local/lib/perl5/site_perl/5.10.1/darwin-2level /opt/local/lib/perl5/site_perl/5.10.1 /opt/local/lib/perl5/vendor_perl/5.10.1/darwin-2level /opt/local/lib/perl5/vendor_perl/5.10.1 /opt/local/lib/perl5/vendor_perl /opt/local/lib/perl5/5.10.1/darwin-2level /opt/local/lib/perl5/5.10.1 .) at (eval 3) line 3.
Perhaps the DBD::Oracle perl module hasn't been fully installed,
or [...]]]></description>
			<content:encoded><![CDATA[<p>This is what happens with Perl if you try to connect to a database and don’t have the driver installed:</p>
<p><code>Chris@Chris-Mastos-iMac:~$ perl -MDBI -e '$db = DBI-&gt;connect("dbi:Oracle:MM_WH")'<br />
install_driver(Oracle) failed: Can't locate DBD/Oracle.pm in @INC (@INC contains: /opt/local/lib/perl5/site_perl/5.10.1/darwin-2level /opt/local/lib/perl5/site_perl/5.10.1 /opt/local/lib/perl5/vendor_perl/5.10.1/darwin-2level /opt/local/lib/perl5/vendor_perl/5.10.1 /opt/local/lib/perl5/vendor_perl /opt/local/lib/perl5/5.10.1/darwin-2level /opt/local/lib/perl5/5.10.1 .) at (eval 3) line 3.<br />
Perhaps the DBD::Oracle perl module hasn't been fully installed,<br />
or perhaps the capitalisation of 'Oracle' isn't right.<br />
Available drivers: DBM, ExampleP, File, Gofer, Proxy, SQLite, Sponge.<br />
at -e line 1</code></p>
<p>This is what happens with Groovy:</p>
<p><code>Chris@Chris-Mastos-iMac:proj/grails/3p_report_config$ grails run-app<br />
Welcome to Grails 1.2-M2 - http://grails.org/<br />
Licensed under Apache Standard License 2.0<br />
Grails home is set to: /usr/local/grails</p>
<p>Base Directory: /Users/Chris/proj/grails/3p_report_config<br />
Running script /usr/local/grails/scripts/RunApp.groovy<br />
Environment set to development<br />
 [groovyc] Compiling 1 source file to /Users/Chris/.grails/1.2-M2/projects/3p_report_config/classes<br />
Running Grails application..<br />
Nov 12, 2009 4:27:54 PM org.apache.coyote.http11.Http11Protocol init<br />
INFO: Initializing Coyote HTTP/1.1 on http-8080<br />
Nov 12, 2009 4:27:54 PM org.apache.catalina.core.StandardService start<br />
INFO: Starting service Tomcat<br />
Nov 12, 2009 4:27:54 PM org.apache.catalina.core.StandardEngine start<br />
INFO: Starting Servlet Engine: Apache Tomcat/6.0-snapshot<br />
Nov 12, 2009 4:27:54 PM org.apache.catalina.startup.ContextConfig defaultWebConfig<br />
INFO: No default web.xml<br />
Nov 12, 2009 4:27:54 PM org.apache.catalina.core.ApplicationContext log<br />
INFO: Initializing Spring root WebApplicationContext<br />
loading security config ...<br />
2009-11-12 16:28:00,188 [main] ERROR context.ContextLoader  - Context initialization failed<br />
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3961)<br />
 at org.apache.catalina.core.StandardContext.start(StandardContext.java:4456)<br />
 at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)<br />
 at org.apache.catalina.core.StandardHost.start(StandardHost.java:722)<br />
 at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)<br />
 at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)<br />
 at org.apache.catalina.core.StandardService.start(StandardService.java:515)<br />
 at org.apache.catalina.core.StandardServer.start(StandardServer.java:708)<br />
 at org.apache.catalina.startup.Tomcat.start(Tomcat.java:252)<br />
 at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:107)<br />
 at grails.web.container.EmbeddableServer$start.call(Unknown Source)<br />
 at _GrailsRun_groovy$_run_closure5_closure11.doCall(_GrailsRun_groovy:157)<br />
 at _GrailsRun_groovy$_run_closure5_closure11.doCall(_GrailsRun_groovy)<br />
 at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:274)<br />
 at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)<br />
 at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)<br />
 at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)<br />
 at _GrailsRun_groovy.runInline(_GrailsRun_groovy:115)<br />
 at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)<br />
 at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)<br />
 at RunApp$_run_closure1.doCall(RunApp.groovy:33)<br />
 at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)<br />
 at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)<br />
 at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)<br />
 at gant.Gant.withBuildListeners(Gant.groovy:344)<br />
 at gant.Gant.this$2$withBuildListeners(Gant.groovy)<br />
 at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)<br />
 at gant.Gant.dispatch(Gant.groovy:334)<br />
 at gant.Gant.this$2$dispatch(Gant.groovy)<br />
 at gant.Gant.invokeMethod(Gant.groovy)<br />
 at gant.Gant.processTargets(Gant.groovy:495)<br />
 at gant.Gant.processTargets(Gant.groovy:480)<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1225)<br />
 at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:880)<br />
 ... 32 more<br />
Caused by: java.sql.SQLException: invalid arguments in call<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)<br />
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:236)<br />
 at oracle.jdbc.driver.PhysicalConnection.&lt;init&gt;(PhysicalConnection.java:441)<br />
 at oracle.jdbc.driver.T4CConnection.&lt;init&gt;(T4CConnection.java:165)<br />
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:35)<br />
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:801)<br />
 at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)<br />
 at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:294)<br />
 at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1247)<br />
 at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1221)<br />
 ... 33 more<br />
2009-11-12 16:28:00,209 [main] ERROR [localhost].[/]  - Exception sending context initialized event to listener instance of class org.codehaus.groovy.grails.web.context.GrailsContextLoaderListener<br />
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3961)<br />
 at org.apache.catalina.core.StandardContext.start(StandardContext.java:4456)<br />
 at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)<br />
 at org.apache.catalina.core.StandardHost.start(StandardHost.java:722)<br />
 at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)<br />
 at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)<br />
 at org.apache.catalina.core.StandardService.start(StandardService.java:515)<br />
 at org.apache.catalina.core.StandardServer.start(StandardServer.java:708)<br />
 at org.apache.catalina.startup.Tomcat.start(Tomcat.java:252)<br />
 at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:107)<br />
 at grails.web.container.EmbeddableServer$start.call(Unknown Source)<br />
 at _GrailsRun_groovy$_run_closure5_closure11.doCall(_GrailsRun_groovy:157)<br />
 at _GrailsRun_groovy$_run_closure5_closure11.doCall(_GrailsRun_groovy)<br />
 at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:274)<br />
 at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)<br />
 at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)<br />
 at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)<br />
 at _GrailsRun_groovy.runInline(_GrailsRun_groovy:115)<br />
 at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)<br />
 at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)<br />
 at RunApp$_run_closure1.doCall(RunApp.groovy:33)<br />
 at gant.Gant$_dispatch_closure4.doCall(Gant.groovy:324)<br />
 at gant.Gant$_dispatch_closure6.doCall(Gant.groovy:334)<br />
 at gant.Gant$_dispatch_closure6.doCall(Gant.groovy)<br />
 at gant.Gant.withBuildListeners(Gant.groovy:344)<br />
 at gant.Gant.this$2$withBuildListeners(Gant.groovy)<br />
 at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)<br />
 at gant.Gant.dispatch(Gant.groovy:334)<br />
 at gant.Gant.this$2$dispatch(Gant.groovy)<br />
 at gant.Gant.invokeMethod(Gant.groovy)<br />
 at gant.Gant.processTargets(Gant.groovy:495)<br />
 at gant.Gant.processTargets(Gant.groovy:480)<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 ... 32 more<br />
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (invalid arguments in call)<br />
 at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1225)<br />
 at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:880)<br />
 ... 32 more<br />
Caused by: java.sql.SQLException: invalid arguments in call<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)<br />
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)<br />
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:236)<br />
 at oracle.jdbc.driver.PhysicalConnection.&lt;init&gt;(PhysicalConnection.java:441)<br />
 at oracle.jdbc.driver.T4CConnection.&lt;init&gt;(T4CConnection.java:165)<br />
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:35)<br />
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:801)<br />
 at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)<br />
 at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:294)<br />
 at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1247)<br />
 at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1221)<br />
 ... 33 more<br />
2009-11-12 16:28:00,256 [main] ERROR core.StandardContext  - Error listenerStart<br />
2009-11-12 16:28:00,460 [main] ERROR core.StandardContext  - Context [] startup failed due to previous errors<br />
Server running. Browse to http://localhost:8080/</code></p>
<img src="http://feeds.feedburner.com/~r/DelegatePattern/~4/6o9i764sPOc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.masto.com/2009/11/groovy-man/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.masto.com/2009/11/groovy-man/</feedburner:origLink></item>
		<item>
		<title>Yet another attempt to replace Java</title>
		<link>http://feedproxy.google.com/~r/DelegatePattern/~3/XPOrd50tzA8/</link>
		<comments>http://blog.masto.com/2009/07/fan-programming-language/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 00:50:51 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Programming Languages]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[languages]]></category>

		<guid isPermaLink="false">http://blog.masto.com/?p=27</guid>
		<description><![CDATA[Which language will achieve critical mass?  Will it be Groovy, Scala, or Clojure?  Maybe Jruby or Jython?  Could it be JavaFX Script?  Or maybe Fan is finally the answer. All they seem to have in common is &#8220;let&#8217;s fix Java&#8221;.  With C# closing in on one side and Ruby on [...]]]></description>
			<content:encoded><![CDATA[<p>Which language will achieve critical mass?  Will it be Groovy, Scala, or Clojure?  Maybe Jruby or Jython?  Could it be JavaFX Script?  <a href="http://fandev.org/">Or maybe Fan is finally the answer.</a> All they seem to have in common is &#8220;let&#8217;s fix Java&#8221;.  With C# closing in on one side and Ruby on the other, the biggest threat to Java may be having too many potential successors with no clear and obvious choice.  Still, it&#8217;s interesting to watch language design evolve.</p>
<p><a href="http://fandev.org/">http://fandev.org/</a></p>
<img src="http://feeds.feedburner.com/~r/DelegatePattern/~4/XPOrd50tzA8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.masto.com/2009/07/fan-programming-language/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.masto.com/2009/07/fan-programming-language/</feedburner:origLink></item>
		<item>
		<title>WYSIWYG HTML Editors</title>
		<link>http://feedproxy.google.com/~r/DelegatePattern/~3/z7GHyTQx0kA/</link>
		<comments>http://blog.masto.com/2009/07/wysiwyg-html-editors/#comments</comments>
		<pubDate>Sun, 12 Jul 2009 19:06:44 +0000</pubDate>
		<dc:creator>chris</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://blog.masto.com/?p=7</guid>
		<description><![CDATA[A Comparison of HTML Editing Controls
I&#8217;m working on a project that includes a lot of content management features.  In the past, I&#8217;ve always avoided WYSIWYG HTML editors, because they&#8217;ve been ugly, buggy, and incompatible with many browsers.  But things have changed a lot since I last looked at them, and I&#8217;m trying to keep up [...]]]></description>
			<content:encoded><![CDATA[<h1>A Comparison of HTML Editing Controls</h1>
<p>I&#8217;m working on a project that includes a lot of content management features.  In the past, I&#8217;ve always avoided WYSIWYG HTML editors, because they&#8217;ve been ugly, buggy, and incompatible with many browsers.  But things have changed a lot since I last looked at them, and I&#8217;m trying to keep up with the times.  The non-technical users this project is aimed at will definitely appreciate being able to edit their pages without resorting to Markdown, so I decided to round up the most promising candidates and take another look.</p>
<p>My criteria for what makes a good editor are based on my own opinions and the needs of my current project, so my conclusions may not be applicable to everyone.  Generally speaking, what I&#8217;m looking for is a true &#8220;WYSIWYG&#8221; experience (the user doesn&#8217;t have to mess with the HTML directly).  I also want it to look as pretty as possible &#8212; working on a Mac, I have an immediate and visceral reaction to ugly software.  Users are going to want links and pictures, so it would be nice to have something more than just &#8220;Enter Image URL&#8221;.  I&#8217;m going to have to plug in some custom code to handle uploading and hooking into my image library, so the more support the editor gives for this, the better.  And, painful as cross-browser support is, it&#8217;s going to have to work in IE6+, Firefox, and Safari.</p>
<p>On to the editors&#8230;</p>
<h2 style="font-size: 1.5em;">markItUp!</h2>
<p><a href="http://markitup.jaysalvat.com/">http://markitup.jaysalvat.com/</a></p>
<p><img class="alignright" style="border: 0px initial initial;" title="markitup" src="http://blog.masto.com/wp-content/uploads/2009/07/markitup.jpg" alt="markitup" width="250" height="133" /></p>
<p>I included markItUp! because it looks very nice as a code-level editor.  But it is specifically not designed to do WYSIWYG &#8212; while you can open a preview, you&#8217;re editing the HTML (or Markdown, or many others) directly, with some assistance from the toolbar buttons.  It also has a fairly pretty &#8220;OS X&#8221; skin available.  I am, however, in the market for a WYSIWYG editor, so I can&#8217;t fairly include markItUp! in the comparison.</p>
<h2>HtmlBox</h2>
<p><a href="http://remiya.com/cms/projects/jquery-plugins/htmlbox/">http://remiya.com/cms/projects/jquery-plugins/htmlbox/</a></p>
<p><img class="size-full wp-image-14 alignright" title="HtmlBox" src="http://blog.masto.com/wp-content/uploads/2009/07/HtmlBox-2.8-Demonstration-Full-1.jpg" alt="HtmlBox" width="250" height="192" /></p>
<p>The looks: Not terribly ugly.</p>
<p>The good: Seems to support assigning CSS classes.  The demo has some nice examples of float-right callout boxes, etc.</p>
<p>The bad: Suffers a bit from too many buttons, and I don&#8217;t know why they put those fake drag handles on the toolbars.  It&#8217;s not immediately obvious from the web site what kind of configuration options, themes, etc. might or might not be available.  Images and links are only supported by &#8220;Enter URL&#8221; popups.</p>
<p>Conclusion: Not especially compelling.</p>
<h2>WYMeditor</h2>
<p><a href="http://www.wymeditor.org/">http://www.wymeditor.org/</a></p>
<p><img class="size-full wp-image-15 alignright" title="WYMeditor" src="http://blog.masto.com/wp-content/uploads/2009/07/WYMeditor.jpg" alt="WYMeditor" width="250" height="127" /></p>
<p>The looks: Almost actually kind of pretty.</p>
<p>The good: Rather than being directly WYSIWYG, it has a nice structural view of the document, with boxes around block elements.  I like the way this works a lot.  Even more compelling is the way it uses CSS classes to assign style.  If you want a word processor-like editor, where the user can change the font and color of every character individually, this may not be the control for you.  But in my application, it would work very well if the users were provided with a handful of pre-defined classes to choose from, so they could just mark a paragraph as &#8220;important&#8221;, rather than messing with the font and color settings.  Nice table editor, as well.</p>
<p>The bad: The method of assigning containers and classes is confusing and doesn&#8217;t fit in with the otherwise attractive look of the editor toolbar.  I know it&#8217;s trying to do the right thing, but the user experience seems to suffer somewhat.  No real help for uploading images and setting up links, pretty standard popup window with URL fields for that.</p>
<p>Conclusion: Very tempting, but I think I&#8217;d have to hack on it a bunch to get everything I want.</p>
<h2 style="font-size: 1.5em;">FCKeditor</h2>
<p><a href="http://www.fckeditor.net/">http://www.fckeditor.net/</a></p>
<p><img class="alignright" style="border: 0px initial initial;" title="FCKeditor" src="http://blog.masto.com/wp-content/uploads/2009/07/FCKeditor-Demo.jpg" alt="FCKeditor" width="250" height="91" /></p>
<p>The looks: Seems to be copied from Word 95.  This is the editor that set the standard for massive, ugly, cluttered toolbars.</p>
<p>The good: It seems to work ok.  I really can&#8217;t get past the interface to consider it any further.</p>
<p>The bad: Terribly unfortunate name.  Terribly unfortunate design.  And it&#8217;s not just looks: a lot of those buttons pop up confusing and badly laid-out dialogs, which fits in with the Windows theme.</p>
<p>Conclusion: Functionality&#8217;s there, but I need something that looks better.</p>
<h2>TinyMCE</h2>
<p><a href="http://tinymce.moxiecode.com/">http://tinymce.moxiecode.com/</a></p>
<p><img class="size-full wp-image-9 alignright" title="TinyMCE" src="http://blog.masto.com/wp-content/uploads/2009/07/editor.gif" alt="TinyMCE" width="250" height="181" /></p>
<p>The looks: Holy button overload, Batman.</p>
<p>The good: TinyMCE is the editor I&#8217;m writing this in, as it&#8217;s the one used by WordPress.  It has a lot of users and is therefore likely to be very well supported and tested across lots of browsers.  It doesn&#8217;t look terrible, and to be fair, one doesn&#8217;t have to enable all of those buttons.  When you&#8217;re inserting images and links, there are nice popup dialogs that make the process easier.  And it integrates with jQuery, my Javascript library of choice.</p>
<p>The bad: I&#8217;ve seen some visual glitches while writing this, but that may be due to the oddball version of Safari I&#8217;m using.</p>
<p>Conclusion: Definitely a contender.</p>
<h2>YUI Editor</h2>
<p><a href="http://developer.yahoo.com/yui/editor/  ">http://developer.yahoo.com/yui/editor/</a></p>
<p><img class="alignright" style="border: 0px initial initial;" title="YUI Editor" src="http://blog.masto.com/wp-content/uploads/2009/07/editor.jpg" alt="YUI Editor" width="250" height="161" /></p>
<p>The looks: Pleasant enough.</p>
<p>The good: YUI Editor is simple and clean, with an extensible plugin architecture that I could definitely see making use of.  It also has nice dialogs for images and links.  There&#8217;s a video right on the YUI Editor home page that explains the design and extension principles, which really sold me on it.</p>
<p>The bad: YUI is big, and I&#8217;d already made the decision to go with jQuery.  I&#8217;ve never been a fan of the button style either, it looks a bit like KDE or something, definitely out of place on the Mac.</p>
<p>Conclusion: I think YUI Editor is my top choice, design-wise.</p>
<h1>Wrap Up and Final Conclusion</h1>
<p>It&#8217;s definitely down to either TinyMCE or YUI Editor.  It&#8217;s a very difficult decision.  TinyMCE definitely has more features and a very active community behind it.  YUI Editor is more to my liking aesthetically, both from a frontend look and a code perspective.  I&#8217;m having so much trouble making the final call, I&#8217;m going to have to try to integrate them both and see which one gives me less trouble.  I&#8217;ll be starting with YUI Editor, so expect a followup article soon documenting the experience.</p>
<p>Feel free to chime in in the comments if you have any experience integrating an HTML editor, especially if it&#8217;s one of these.</p>
<img src="http://feeds.feedburner.com/~r/DelegatePattern/~4/z7GHyTQx0kA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.masto.com/2009/07/wysiwyg-html-editors/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.masto.com/2009/07/wysiwyg-html-editors/</feedburner:origLink></item>
	</channel>
</rss>
