<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1482979278030787271</id><updated>2012-01-30T23:06:16.185-08:00</updated><category term='ruby'/><category term='Twitter'/><category term='JPA'/><category term='markup languges'/><category term='wiki'/><category term='DemoCamp'/><category term='beanshell'/><category term='Hibernate'/><category term='AJAX'/><category term='UI'/><category term='Java 7'/><category term='Tasktop'/><category term='MDE'/><category term='WebKit'/><category term='EGit'/><category term='Confluence'/><category term='Profiler'/><category term='Git'/><category term='python'/><category term='MDD'/><category term='DSL'/><category term='Safari'/><category term='Career'/><category term='Textile'/><category term='Hudson'/><category term='SSL'/><category term='JUnit'/><category term='Spring'/><category term='Android'/><category term='EclipseCon'/><category term='scripting'/><category term='DITA'/><category term='Continuous Integration'/><category term='SoyLatte'/><category term='Subversion'/><category term='REST'/><category term='MediaWiki'/><category term='CVS'/><category term='Mylyn'/><category term='Java'/><category term='NetBeans'/><category term='RichFaces'/><category term='XCode'/><category term='GEF'/><category term='SWTBot'/><category term='JDK7'/><category term='GitHub'/><category term='View'/><category term='iPhone'/><category term='DocBook'/><category term='groovy'/><category term='WikiText'/><category term='Eclipse'/><category term='mac'/><category term='JSF'/><category term='Perspective'/><category term='TLS'/><category term='iPad'/><category term='testing'/><category term='SVN'/><category term='JSR-223'/><category term='Ant'/><category term='Java 6'/><title type='text'>Green's Opinion</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>70</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-7663561177960333024</id><published>2010-07-29T09:00:00.000-07:00</published><updated>2010-07-29T06:16:13.806-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>Testing REST Web Services with JPA and Spring</title><content type='html'>&lt;p&gt;REST Web Services can be particularly difficult to test, with the need for networking, a web container, multiple threads and transaction management creating extra complexity beyond your standard unit test.  In this article I demonstrate patterns designed to address this complexity while enabling complete testing of your REST web service stack.&lt;/p&gt;&lt;p&gt;The ideal web service unit test will use the same principles discussed in my &lt;a href="http://greensopinion.blogspot.com/2010/07/patterns-for-better-unit-testing-with.html"&gt;previous article&lt;/a&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;clean database with mocked data so that we can reliably test specific scenarios&lt;/li&gt;&lt;li&gt;rollback to avoid side-effects after our test&lt;/li&gt;&lt;li&gt;in-memory database so that no environment setup is required&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In addition, we&amp;#8217;ll need to run a web container for our unit test.  To maintain a zero-setup test environment, we&amp;#8217;ll have our unit test start a web container for the purpose of running our web service.   By having each test start and stop the web container, tests become very simple to run and we&amp;#8217;re guaranteed that our tests will have a clean environment with no external dependencies.&lt;/p&gt;&lt;h4 id="WebContainer"&gt;Web Container&lt;/h4&gt;&lt;p&gt;Normally starting a web container is difficult to do in a unit test, and container startup time can be a problem &amp;#8212; however with the right choice of web container we can overcome these problems.  &lt;a href="http://winstone.sourceforge.net/"&gt;Winstone&lt;/a&gt; is a small, fast web container that is designed to be embedded in Java programs.  By using Winstone, we&amp;#8217;ll be able to start and stop the web container as part of our unit test setup and tear-down with relative ease.&lt;/p&gt;&lt;p&gt;Starting Winstone is as simple as this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt; Map args = new HashMap();&lt;br /&gt; args.put("webroot", pathToWebRoot);&lt;br /&gt; args.put("httpPort", String.valueOf(port));&lt;br /&gt; Launcher.initLogger(args);&lt;br /&gt;&lt;br /&gt; // start winstone&lt;br /&gt; winstoneLauncher = new Launcher(args);&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Winstone only takes a couple of seconds to start up.  Shutting it down is as simple as this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt; if (winstoneLauncher.isRunning()) {&lt;br /&gt;  winstoneLauncher.shutdown();&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To hide the details of winstone from our unit tests, we can create a class &lt;code&gt;WebApplicationContainer&lt;/code&gt;, which is responsible for starting choosing a port, starting/stopping Winstone, and providing a base URL for our tests:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public class WebApplicationContainer {&lt;br /&gt;&lt;br /&gt; private static final Random random = new Random(System.currentTimeMillis());&lt;br /&gt;&lt;br /&gt; private Launcher winstoneLauncher;&lt;br /&gt;&lt;br /&gt; private File webRoot;&lt;br /&gt;&lt;br /&gt; private int port;&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * start the webserver, guarantees that the webserver is started upon return.&lt;br /&gt;  * @see #stop()&lt;br /&gt;  */&lt;br /&gt; @SuppressWarnings({ "unchecked", "rawtypes" })&lt;br /&gt; public void start() {&lt;br /&gt;  if (winstoneLauncher != null) {&lt;br /&gt;   throw new IllegalStateException("already started");&lt;br /&gt;  }&lt;br /&gt;  port = findAvailablePort();&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  Logger log = Logger.getLogger(WebApplicationContainer.class.getName());&lt;br /&gt;  log.fine("Starting web container on "+getBaseUrl());&lt;br /&gt;  &lt;br /&gt;  Map args = new HashMap();&lt;br /&gt;  try {&lt;br /&gt;   args.put("ajp13Port", "-1");&lt;br /&gt;   args.put("useJasper", "false");&lt;br /&gt;   args.put("webroot", webRoot.getAbsolutePath());&lt;br /&gt;   args.put("httpPort", String.valueOf(port));&lt;br /&gt;   Launcher.initLogger(args);&lt;br /&gt;&lt;br /&gt;   // start winstone&lt;br /&gt;   winstoneLauncher = new Launcher(args);&lt;br /&gt;&lt;br /&gt;   // wait for Winstone to finish starting up&lt;br /&gt;   // we do that by attempting to connect via socket&lt;br /&gt;   final int maxRetries = 150;&lt;br /&gt;   for (int x = 0; x &amp;lt; maxRetries; ++x) {&lt;br /&gt;    if (testForSuccessfulStartup()) {&lt;br /&gt;     break;&lt;br /&gt;    }&lt;br /&gt;    if (x == maxRetries - 1) {&lt;br /&gt;     throw new IllegalStateException(&lt;br /&gt;      String.format("Connection to localhost:%s failed."+&lt;br /&gt;       "  Did the web container start up successfully?"));&lt;br /&gt;    }&lt;br /&gt;    // wait and then try again&lt;br /&gt;    Thread.sleep(100L);&lt;br /&gt;   }&lt;br /&gt;   Logger.getLogger(WebApplicationContainer.class.getName()).&lt;br /&gt;    info("Started web container at "+getBaseUrl());&lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;   throw new IllegalStateException(e);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private int findAvailablePort() {&lt;br /&gt;  ...&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private boolean testForSuccessfulStartup() {&lt;br /&gt;  // test to see if the web container is listening on the address/port combo&lt;br /&gt;  try {&lt;br /&gt;   Socket socket = SocketFactory.getDefault().createSocket("localhost", port);&lt;br /&gt;   socket.close();&lt;br /&gt;   return true;&lt;br /&gt;  } catch (UnknownHostException e) {&lt;br /&gt;   throw new IllegalStateException(e);&lt;br /&gt;  } catch (IOException e) {&lt;br /&gt;   return false;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * stop the web container&lt;br /&gt;  * @see #start()&lt;br /&gt;  */&lt;br /&gt; public void stop() {&lt;br /&gt;  if (winstoneLauncher == null) {&lt;br /&gt;   throw new IllegalStateException();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  winstoneLauncher.shutdown();&lt;br /&gt;  winstoneLauncher = null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getBaseUrl() {&lt;br /&gt;  return String.format("http://localhost:%s/", port);&lt;br /&gt; }&lt;br /&gt; ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="RESTServiceUnitTest"&gt;REST Service Unit Test&lt;/h4&gt;&lt;p&gt;A complete test for our web service ends up looking like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@RunWith(SpringJUnit4ClassRunner.class)&lt;br /&gt;@Transactional&lt;br /&gt;public class BlogServiceClientTest extends BlogServiceTest {&lt;br /&gt; @Autowired&lt;br /&gt; private BlogServiceClient blogServiceClient;&lt;br /&gt;&lt;br /&gt; @Autowired&lt;br /&gt; private WebApplicationContainer webContainer;&lt;br /&gt;&lt;br /&gt; @Before&lt;br /&gt; public void before() {&lt;br /&gt;  webContainer.setWebRoot(computeWebRoot());&lt;br /&gt;  webContainer.start();&lt;br /&gt;&lt;br /&gt;  blogServiceClient.setBaseUrl(webContainer.getBaseUrl()+"api");&lt;br /&gt;  // use the client instead of the service directly&lt;br /&gt;  service = blogServiceClient;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @After&lt;br /&gt; public void after() {&lt;br /&gt;  if (webContainer.isStarted()) {&lt;br /&gt;   webContainer.stop();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You might be thinking "where are the &lt;code&gt;@Test&lt;/code&gt; methods?".  The beauty of our web service test is that it extends our service test: all of the test methods we were running before using direct method calls are now also run over our web service protocol.  By using inheritance, other than setup of our web service there&amp;#8217;s nothing else to do.  In other words, our service tests are run twice: once using Java method calls, and again over-the-wire using our web service.  This ensures that we see consistent behaviour in both modes of execution.&lt;/p&gt;&lt;p&gt;&lt;code&gt;BlogServiceClient&lt;/code&gt; is an implementation of &lt;code&gt;BlogService&lt;/code&gt; (our service interface under test) based on Spring&amp;#8217;s &lt;code&gt;RestTemplate&lt;/code&gt;.  It&amp;#8217;s a REST client for our service, implementing the same Java interface as our server-side service.  This enables us to substitute the REST client service implementation in our test.&lt;/p&gt;&lt;h4 id="TransactionManagementandJPA"&gt;Transaction Management and JPA&lt;/h4&gt;&lt;p&gt;Now that we&amp;#8217;re able to start our web service and run tests we&amp;#8217;re done, right?  Not quite: we still need to manage transactions and our test data.  This is somewhat more complicated with our web service: recall that we want our unit test to have the following qualities:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;clean database on startup&lt;/li&gt;&lt;li&gt;run in a transaction that rolls back after the test&lt;/li&gt;&lt;li&gt;mocks all of its own data&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Employing the techniques we&amp;#8217;ve already established in &lt;a href="http://greensopinion.blogspot.com/2010/07/patterns-for-better-unit-testing-with.html"&gt;Patterns for Better Unit Testing with JPA&lt;/a&gt; helps, but is not quite enough.  &lt;/p&gt;&lt;p&gt;Our service implementation is running in a web container.  The web container services incoming HTTP requests with threads in a thread pool.  Our service is pretty much guaranteed to be running on a distinct thread, not the same thread as that running our unit test method.  As a result our service bean will be running in it&amp;#8217;s own transaction context.  There are two problems that arise from this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;our service won&amp;#8217;t be able to see the data we&amp;#8217;ve mocked up in our test&lt;/li&gt;&lt;li&gt;our service method will commit its transaction&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Both of these things are undesirable.  To overcome this problem we&amp;#8217;ll apply a little trickery: we&amp;#8217;ll provide an entity manager that is shared by both the test thread and the web container thread servicing our HTTP requests, and we&amp;#8217;ll prevent any commit or rollback from occurring on that entity manager while it&amp;#8217;s in use in a unit test.  We do this by wrapping the entity manager factory in our test environment:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;bean class="greensopinion.restexample.test.jpa.TestEntityManagerFactory" &lt;br /&gt; id="blogDomain"&amp;gt;&lt;br /&gt; &amp;lt;property name="delegate"&amp;gt;&lt;br /&gt;  &amp;lt;bean&lt;br /&gt;   class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"&amp;gt;&lt;br /&gt;   &amp;lt;property name="dataSource" ref="dataSource" /&amp;gt;&lt;br /&gt;   &amp;lt;property name="persistenceXmlLocation" &lt;br /&gt;    value="classpath*:/persistence-test.xml"/&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt; &amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our &lt;code&gt;TestEntityManagerFactory&lt;/code&gt; employs the standard delegate pattern, with the exception that it changes the default behaviour of &lt;code&gt;createEntityManager()&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt; public EntityManager createEntityManager() {&lt;br /&gt;  if (theEntityManager == null) {&lt;br /&gt;   theEntityManager = delegate.createEntityManager();&lt;br /&gt;  }&lt;br /&gt;  return new TestEntityManager(theEntityManager);&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It creates and returns a singleton entity manager instance.  This allows us to share a single entity manager between our unit test and the web container.  We also wrap the entity manager so that we can control it&amp;#8217;s lifecycle (we prevent &lt;code&gt;close()&lt;/code&gt;) and that of it&amp;#8217;s transaction (we prevent &lt;code&gt;commit()&lt;/code&gt; and &lt;code&gt;rollback()&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;While we know that the &lt;code&gt;EntityManager&lt;/code&gt; is not thread safe, it&amp;#8217;s okay to share it between threads providing that no two threads access it &lt;em&gt;at the same time&lt;/em&gt;.  Since our REST client will block on any calls to the server, we&amp;#8217;re not at risk of concurrent access.&lt;/p&gt;&lt;h4 id="SourceCode"&gt;Source Code&lt;/h4&gt;&lt;p&gt;Complete, working source code demonstrating these patterns and others is available on GitHub:&lt;/p&gt;&lt;p&gt;&lt;a href="http://github.com/dgreen99/greensopinion.restexample"&gt;http://github.com/dgreen99/greensopinion.restexample&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The source contains two Eclipse projects: one for the web application, and one for unit tests.  The unit tests all run and pass, and the web application can be started and deployed with a MySQL database.&lt;/p&gt;&lt;h5 id="ServicePatterns"&gt;Service Patterns&lt;/h5&gt;&lt;p&gt;The service is split into the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Service interface &amp;#8211; defines the service contract&lt;/li&gt;&lt;li&gt;Service bean &amp;#8211; implements the service over JPA&lt;/li&gt;&lt;li&gt;Service controller &amp;#8211; exposes the service using Spring REST&lt;/li&gt;&lt;li&gt;Service client &amp;#8211; implements client end of the REST service, used in our tests&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This gives us a good separation of concerns, and client code need not always know if it&amp;#8217;s calling an in-process or out-of-process service.&lt;/p&gt;&lt;p&gt;JPA domain objects are exposed by the service interface.  This helps in a few ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The service is usable in other parts of the application that deal with the domain&lt;/li&gt;&lt;li&gt;It helps to reduce the amount of code that must be written&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;However it has drawbacks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It exposes the design of our domain, limiting our ability to change it in the future&lt;/li&gt;&lt;li&gt;Changes to the domain may inadvertently break the service interface for third-party clients&lt;/li&gt;&lt;li&gt;The service controller must be careful to expose shallow copies of our domain objects&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This approach may be feasible for some projects.  It&amp;#8217;s common to see transfer objects as well, which allows a service to be explicit about it&amp;#8217;s data model without exposing the domain implementation details.&lt;/p&gt;&lt;h5 id="MissingFunctionality"&gt;Missing Functionality&lt;/h5&gt;&lt;p&gt;Notably this sample code is missing two crucial aspects of a normal web application:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Exception handling should be built-in to our service controller so that errors can be propagated gracefully.&lt;/li&gt;&lt;li&gt;Data validation is not performed by the service bean.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;These aspects of the sample application are left unimplemented.&lt;/p&gt;&lt;h4 id="Summary"&gt;Summary&lt;/h4&gt;&lt;p&gt;Testing REST web services can be easy by following the following patterns:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;implement a web service client to exercise your service from unit tests&lt;/li&gt;&lt;li&gt;run the web service in your tests with a lightweight embedded web container&lt;/li&gt;&lt;li&gt;share the entity manager and transaction context between the service and test&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;By applying these straight-forward techniques it&amp;#8217;s possible to overcome the complexity inherent in testing a web service, enabling better coverage and higher quality applications.&lt;/p&gt;&lt;h4 id="References"&gt;References&lt;/h4&gt;&lt;p&gt;Technologies used in this project:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;REST services are implemented using &lt;a href="http://www.springsource.org/"&gt;Spring REST&lt;/a&gt; and &lt;a href="http://jackson.codehaus.org/"&gt;Jackson&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.eclipse.org/eclipselink/"&gt;EclipseLink&lt;/a&gt; is used as a JPA provider&lt;/li&gt;&lt;li&gt;&lt;a href="http://junit.org"&gt;JUnit&lt;/a&gt; is used for unit tests&lt;/li&gt;&lt;li&gt;&lt;a href="http://hsqldb.org"&gt;Hypersonic&lt;/a&gt; is used for an in-memory database&lt;/li&gt;&lt;li&gt;&lt;a href="http://winstone.sourceforge.net/"&gt;Winstone&lt;/a&gt; is used for an embedded web container&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-7663561177960333024?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/7663561177960333024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=7663561177960333024' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/7663561177960333024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/7663561177960333024'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/07/testing-rest-web-services-with-jpa-and.html' title='Testing REST Web Services with JPA and Spring'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-537892683420596602</id><published>2010-07-15T08:07:00.000-07:00</published><updated>2010-07-15T09:41:06.374-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Patterns for Better Unit Testing with JPA</title><content type='html'>&lt;p&gt;Over the years I&amp;#8217;ve run across many projects, which tend to fall into one of two categories: those projects that have great  confidence, energy and momentum, running hundreds (or thousands) of unit tests dozens of times every day; and those projects where the effort of writing and running unit tests is high enough that tests aren&amp;#8217;t maintained and as a result no one on the team really knows if their software works.  So how do we get our project into the first category?  Below I highlight a few patterns and practices that significantly lower the bar to creating a solid test suite, enabling your team to adopt a practice and culture of complete test coverage.&lt;/p&gt;&lt;h2 id="SetupandSpeed"&gt;Setup and Speed&lt;/h2&gt;&lt;p&gt;Configuration, setup and maintenance of environments creates a significant barrier to a well-exercised and maintained test suite.  Ideally we want to enable running tests with zero setup.  Data-intensive applications need a database, which are notoriously hard to configure and setup.  We&amp;#8217;ll use a database for our tests, but instead of connecting to an external one the database will start up and run as part of the unit test.  This gives us the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;zero setup: no installation, no shared machine, no maintenance, no JDBC urls, no passwords  &lt;/li&gt;&lt;li&gt;in-memory and fast&lt;/li&gt;&lt;li&gt;can run anywhere, even when not on a network&lt;/li&gt;&lt;li&gt;clean, with no rogue data that could affect our tests&lt;/li&gt;&lt;li&gt;no deadlocks&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;My first reaction when I saw this approach in use was &amp;#8220;how can tests be meaningful if they're not run on the same database software that is used in production&amp;#8221;?  The answer is two-sided.  We &lt;em&gt;should&lt;/em&gt; be running these tests on the same database software.  In fact, I encourage you to do that &amp;#8212; on your continuous integration build server.  The tests are meaningful in that they can make assertions and exercise your code.  Database platform-specific issues can be flushed out on the build server, which can run these same tests using a different configuration.&lt;/p&gt;&lt;p&gt;To set this up I recommend using &lt;a href="http://db.apache.org/derby/"&gt;Apache Derby&lt;/a&gt; or &lt;a href="http://hsqldb.org"&gt;Hypersonic&lt;/a&gt;.  Here&amp;#8217;s how it&amp;#8217;s done with Hypersonic and Eclipselink as our JPA provider:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;add hsqldb.jar to the test project classpath (this is for Hypersonic)&lt;/li&gt;&lt;li&gt;configure your JDBC connection to use an in-memory database URL as follows: &lt;code&gt;jdbc:hsqldb:mem:tests&lt;/code&gt;&lt;/li&gt;&lt;li&gt;configure your persistence.xml with the right SQL dialect.  For Eclipselink this is done by setting &lt;code&gt;eclipselink.target-database&lt;/code&gt; to &lt;code&gt;HSQL&lt;/code&gt; as follows:&lt;/li&gt;&lt;/ol&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" standalone="no"?&amp;gt;&lt;br /&gt;&amp;lt;persistence xmlns="http://java.sun.com/xml/ns/persistence" &lt;br /&gt; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" &lt;br /&gt; xsi:schemaLocation="http://java.sun.com/xml/ns/persistence&lt;br /&gt;  http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"&amp;gt;&lt;br /&gt;    &amp;lt;persistence-unit name="blogDomain" transaction-type="RESOURCE_LOCAL"&amp;gt;&lt;br /&gt;        &amp;lt;provider&amp;gt;org.eclipse.persistence.jpa.PersistenceProvider&amp;lt;/provider&amp;gt;&lt;br /&gt;        ... snip ...&lt;br /&gt;        &amp;lt;properties&amp;gt;&lt;br /&gt;            &amp;lt;property name="eclipselink.target-database" value="HSQL"/&amp;gt;&lt;br /&gt;            &amp;lt;property name="eclipselink.ddl-generation" value="create-tables"/&amp;gt;&lt;br /&gt;            &amp;lt;property name="eclipselink.ddl-generation.output-mode" &lt;br /&gt;                             value="database"/&amp;gt;&lt;br /&gt;            &amp;lt;property name="eclipselink.weaving" value="false"/&amp;gt;&lt;br /&gt;            &amp;lt;property name="eclipselink.logging.level" value="INFO"/&amp;gt;&lt;br /&gt;        &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;/persistence-unit&amp;gt;&lt;br /&gt;&amp;lt;/persistence&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this configuration the test suite will create a new in-memory database every time it&amp;#8217;s run, and Eclipselink will create the necessary tables to persist our data.  We&amp;#8217;ve managed to eliminate the need for any kind of setup or external database to run our tests.  That&amp;#8217;s great, but now we&amp;#8217;ve got an empty database, where does our data come from?&lt;/p&gt;&lt;h2 id="TestData"&gt;Test Data&lt;/h2&gt;&lt;p&gt;Data-intensive applications need data for their tests &amp;#8212; that&amp;#8217;s a given.  For our tests to run reliably and check specific scenarios every time, we need the data used by each test case to be the same every time tests are run.  The best way to do this is to have our tests mock up the data.  Mocking data can be cumbersome, however that can be alleviated by using mock factories for our domain objects.  Mock factories populate domain objects with values such that they&amp;#8217;re ready to use as-is.  Where needed, tests can alter the state of domain objects to create the desired scenario.  Here&amp;#8217;s an example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt; @Test&lt;br /&gt; public void testUpdateBlog() {&lt;br /&gt;  Blog blog = MockFactory.on(Blog.class).create(entityManager);&lt;br /&gt;  entityManager.flush();&lt;br /&gt;  entityManager.clear();&lt;br /&gt;  &lt;br /&gt;  blog.setName(blog.getName()+"2");&lt;br /&gt;  &lt;br /&gt;  Blog updatedBlog = service.updateBlog(blog);&lt;br /&gt;  assertNotNull(updatedBlog);&lt;br /&gt;  assertNotSame(updatedBlog,blog);&lt;br /&gt;  assertEquals(blog.getName(),updatedBlog.getName());&lt;br /&gt;  assertEquals(blog.getId(),updatedBlog.getId());&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this example the test verifies that the &lt;code&gt;Blog&lt;/code&gt; entity is properly updated by the service.  Notice that the test didn&amp;#8217;t have to populate the &lt;code&gt;blog&lt;/code&gt; object before persisting it: all of the required values were filled in by the &lt;code&gt;MockFactory&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;For this to work, we&amp;#8217;ll need a &lt;code&gt;MockFactory&lt;/code&gt; implementation.  This is what ours looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**&lt;br /&gt; * A factory for domain objects that mocks their data.&lt;br /&gt; * Example usage:&lt;br /&gt; * &amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;br /&gt; * Blog blog = MockFactory.on(Blog.class).create(entityManager);&lt;br /&gt; * &amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt; * @author David Green&lt;br /&gt; */&lt;br /&gt;public abstract class MockFactory&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt; private static Map&amp;lt;Class&amp;lt;?&amp;gt;,MockFactory&amp;lt;?&amp;gt;&amp;gt; factories = &lt;br /&gt;      new HashMap&amp;lt;Class&amp;lt;?&amp;gt;, MockFactory&amp;lt;?&amp;gt;&amp;gt;();&lt;br /&gt; static {&lt;br /&gt;  register(new MockBlogFactory());&lt;br /&gt;  register(new MockArticleFactory());&lt;br /&gt; }&lt;br /&gt; private static void register(MockFactory&amp;lt;?&amp;gt; mockFactory) {&lt;br /&gt;  factories.put(mockFactory.domainClass,mockFactory);&lt;br /&gt; }&lt;br /&gt; @SuppressWarnings("unchecked")&lt;br /&gt; public static &amp;lt;T&amp;gt; MockFactory&amp;lt;T&amp;gt; on(Class&amp;lt;T&amp;gt; domainClass) {&lt;br /&gt;  MockFactory&amp;lt;?&amp;gt; factory = factories.get(domainClass);&lt;br /&gt;  if (factory == null) {&lt;br /&gt;   throw new IllegalStateException(&lt;br /&gt;    "Did you forget to register a mock factory for "+&lt;br /&gt;      domainClass.getClass().getName()+"?");&lt;br /&gt;  }&lt;br /&gt;  return (MockFactory&amp;lt;T&amp;gt;) factory;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private final Class&amp;lt;T&amp;gt; domainClass;&lt;br /&gt;&lt;br /&gt; private int seed;&lt;br /&gt; &lt;br /&gt; protected MockFactory(Class&amp;lt;T&amp;gt; domainClass) {&lt;br /&gt;  if (domainClass.getAnnotation(Entity.class) == null) {&lt;br /&gt;   throw new IllegalArgumentException();&lt;br /&gt;  }&lt;br /&gt;  this.domainClass = domainClass;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Create several objects&lt;br /&gt;  * @param entityManager the entity manager, or null if the mocked objects&lt;br /&gt;  *            should not be persisted&lt;br /&gt;  * @param count the number of objects to create&lt;br /&gt;  * @return the created objects&lt;br /&gt;  */&lt;br /&gt; public List&amp;lt;T&amp;gt; create(EntityManager entityManager,int count) {&lt;br /&gt;  List&amp;lt;T&amp;gt; mocks = new ArrayList&amp;lt;T&amp;gt;(count);&lt;br /&gt;  for (int x = 0;x&amp;lt;count;++x) {&lt;br /&gt;   T t = create(entityManager);&lt;br /&gt;   mocks.add(t);&lt;br /&gt;  }&lt;br /&gt;  return mocks;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Create a single object&lt;br /&gt;  * @param entityManager the entity manager, or null if the mocked object&lt;br /&gt;  *        should not be persisted&lt;br /&gt;  * @return the mocked object&lt;br /&gt;  */&lt;br /&gt; public T create(EntityManager entityManager) {&lt;br /&gt;  T mock;&lt;br /&gt;  try {&lt;br /&gt;   mock = domainClass.newInstance();&lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;   // must have a default constructor&lt;br /&gt;   throw new IllegalStateException();&lt;br /&gt;  }&lt;br /&gt;  populate(++seed,mock);&lt;br /&gt;  if (entityManager != null) {&lt;br /&gt;   entityManager.persist(mock);&lt;br /&gt;  }&lt;br /&gt;  return mock;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Populate the given domain object with data&lt;br /&gt;  * @param seed a seed that may be used to create data&lt;br /&gt;  * @param mock the domain object to populate&lt;br /&gt;  */&lt;br /&gt; protected abstract void populate(int seed, T mock);&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; private static class MockBlogFactory extends MockFactory&amp;lt;Blog&amp;gt; {&lt;br /&gt;  public MockBlogFactory() {&lt;br /&gt;   super(Blog.class);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  @Override&lt;br /&gt;  protected void populate(int seed, Blog mock) {&lt;br /&gt;   mock.setName("Blog "+seed);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static class MockArticleFactory extends MockFactory&amp;lt;Article&amp;gt; {&lt;br /&gt;  &lt;br /&gt;  public MockArticleFactory() {&lt;br /&gt;   super(Article.class);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  @Override&lt;br /&gt;  protected void populate(int seed, Article mock) {&lt;br /&gt;   mock.setAuthor("First Last");&lt;br /&gt;   mock.setTitle("Article "+seed);&lt;br /&gt;   mock.setContent("article "+seed+" content");&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This implementation uses static methods and a static initializer.  It&amp;#8217;s not very Spring-like.  If you prefer you could get rid of all the statics and instead have Spring inject (autowire) your mock factories into your test classes.&lt;/p&gt;&lt;p&gt;In our implementation we use an integer &lt;code&gt;seed&lt;/code&gt; to help us produce values that vary.  This implementation is fairly trivial, however if needed we could apply more advanced techniques such as using data dictionaries to source data.  Such mock factories should populate enough values that the mocked domain object can be persisted: all not-null properties should have values.  To ensure that this is true over the lifespan of our project, it&amp;#8217;s important to have a test:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**&lt;br /&gt; * test that {@link MockFactory mock factories} are working as expected&lt;br /&gt; * &lt;br /&gt; * @author David Green&lt;br /&gt; */&lt;br /&gt;@RunWith(SpringJUnit4ClassRunner.class)&lt;br /&gt;@ContextConfiguration( { "/applicationContext-test.xml" })&lt;br /&gt;@Transactional&lt;br /&gt;public class MockFactoryTest {&lt;br /&gt;&lt;br /&gt; @PersistenceContext&lt;br /&gt; private EntityManager entityManager;&lt;br /&gt;&lt;br /&gt; @Test&lt;br /&gt; public void testCreateBlog() {&lt;br /&gt;  Blog blog = MockFactory.on(Blog.class).create(entityManager);&lt;br /&gt;  assertNotNull(blog);&lt;br /&gt;  assertNotNull(blog.getName());&lt;br /&gt;  entityAssertions(blog);&lt;br /&gt;  entityManager.flush();&lt;br /&gt; }&lt;br /&gt; @Test&lt;br /&gt; public void testCreateArticle() {&lt;br /&gt;  Blog blog = MockFactory.on(Blog.class).create(entityManager);&lt;br /&gt;  Article article = MockFactory.on(Article.class).create(null);&lt;br /&gt;  article.setBlog(blog);&lt;br /&gt;  entityManager.persist(article);&lt;br /&gt;  assertNotNull(article);&lt;br /&gt;  assertNotNull(article.getTitle());&lt;br /&gt;  assertNotNull(article.getContent());&lt;br /&gt;  entityAssertions(article);&lt;br /&gt;  entityManager.flush();&lt;br /&gt; }&lt;br /&gt; private void entityAssertions(AbstractEntity entity) {&lt;br /&gt;  assertNotNull(entity.getId());&lt;br /&gt;  assertNotNull(entity.getCreated());&lt;br /&gt;  assertNotNull(entity.getModified());&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key behind this approach is that each test mocks its own data.  Where data models are signifcantly more complex, utility functions can use these mock factories to create common scenarios.&lt;/p&gt;&lt;p&gt;Now that we&amp;#8217;ve got an easy way to produce data in our tests, how do we eliminate potential for side-effects between tests?  That part is easy: we ensure that transactions are rolled back after every test.  In our example we&amp;#8217;re using Spring to run our tests via &lt;code&gt;SpringJUnit4ClassRunner&lt;/code&gt;.  Its default behaviour is to roll back after each test is run, however if you&amp;#8217;re not using Spring you should have something like this in an abstract test case class:&lt;/p&gt;&lt;pre&gt;&lt;code&gt; /**&lt;br /&gt;  * Overriding methods should call super.&lt;br /&gt;  */ &lt;br /&gt; @After&lt;br /&gt; public void after() {&lt;br /&gt;  if (entityManager.getTransaction().isActive()) {&lt;br /&gt;   entityManager.getTransaction().rollback();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Summary"&gt;Summary&lt;/h2&gt;&lt;p&gt;We&amp;#8217;ve seen how we can make unit testing easy with a few simple patterns:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;in-memory database&lt;/li&gt;&lt;li&gt;mocked data&lt;/li&gt;&lt;li&gt;roll back transactions after each test&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;By employing these simple techniques, tests are easy to write and run.  Your team will get addicted to thorough test coverage as the number of tests being run increases and provides tangible feedback of their progress.&lt;/p&gt;&lt;p&gt;In my next article I&amp;#8217;ll post complete working source code and show how these techniques can be taken a step further in testing Spring REST web services.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-537892683420596602?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/537892683420596602/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=537892683420596602' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/537892683420596602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/537892683420596602'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/07/patterns-for-better-unit-testing-with.html' title='Patterns for Better Unit Testing with JPA'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2216816018375446196</id><published>2010-06-15T09:40:00.000-07:00</published><updated>2010-06-15T10:24:44.365-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><category scheme='http://www.blogger.com/atom/ns#' term='Tasktop'/><title type='text'>Joining the Tasktop Team</title><content type='html'>&lt;p&gt;&lt;img src="http://lh3.ggpht.com/_vw9l2nnub6c/TBev4R-mUDI/AAAAAAAAAOA/R519UGrpF2I/s800/tasktop-logo-graphic.png" align="left" style="padding: 5px 20px 5px 0px;border:none;"/&gt;My experience with Mylyn began shortly after it was released in 2006 as a way to access bug reports from my IDE. It didn’t take long for me to discover how radically my effectiveness and productivity were affected by Mylyn’s ability to track the context of each task and trigger my situational memory. Mylyn’s effect on my work was so profound that over the following years I was inspired to create several extensions for Mylyn, including &lt;a href="http://greensopinion.blogspot.com/2008/06/focus-your-work-week-even-more-beyond.html"&gt;automatic creation of task contexts for model-driven development (MDD) tools&lt;/a&gt;, &lt;a href="http://greensopinion.blogspot.com/2008/12/mylyn-context-driven-domain-diagram.html"&gt;context-driven domain diagrams&lt;/a&gt;, and &lt;a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText"&gt;WikiText&lt;/a&gt;. It was through these projects and others that I came to know a world-class team, the creators of Mylyn at &lt;a href="http://tasktop.com"&gt;Tasktop&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Mylyn’s frameworks and Tasktop’s products have become the hub of a new movement in software development, focused on integrating Agile methodologies and ALM tools to provide the groundbreaking productivity gains of the task-focused interface. It’s with great excitement that I join the team at Tasktop as Vice President of Engineering. I look forward to working with one of the most innovative software development companies that exists at the center of the Agile and ALM space and leading edge of the Eclipse community.&lt;/p&gt;&lt;p&gt;You can find out more by reading the &lt;a href="http://tasktop.com/about/press/david-green-vp-engineering.php"&gt;press release&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2216816018375446196?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2216816018375446196/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2216816018375446196' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2216816018375446196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2216816018375446196'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/06/joining-tasktop-team.html' title='Joining the Tasktop Team'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vw9l2nnub6c/TBev4R-mUDI/AAAAAAAAAOA/R519UGrpF2I/s72-c/tasktop-logo-graphic.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3855306247892385019</id><published>2010-05-12T08:45:00.001-07:00</published><updated>2010-05-12T09:58:46.065-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Lava Lamps, Android and Continuous Integration</title><content type='html'>&lt;p&gt;A few years ago I came across &lt;a href="http://mark.michaelis.net/Blog/BuildStatusUsingLavaLampsByKenNichols.aspx"&gt;this very cool setup&lt;/a&gt; by Ken Nichols where Lava Lamps are used to indicate build status.  Real, tangible feedback on your builds somehow has that cool factor that just can't be ignored.  With circa-2004 lava lamps as inspiration, the latest Hudson Helper for Android has been updated.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/S-rOmmZ8NbI/AAAAAAAAANE/_FfnoAhffyU/s1600/Screen+shot+2010-05-12+at+8.50.56+AM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 225px; height: 337px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/S-rOmmZ8NbI/AAAAAAAAANE/_FfnoAhffyU/s400/Screen+shot+2010-05-12+at+8.50.56+AM.png" border="0" alt="http://commons.wikimedia.org/wiki/File:Lavalampdark.jpg" id="BLOGGER_PHOTO_ID_5470411859934590386" /&gt;&lt;/a&gt;&lt;p&gt;Android devices have a multi-color LED that can be used for notifications.  On my Nexus One, the trackball doubles as the LED, resulting in a glowing orb.  Hudson Helper will monitor your builds and make the glowing orb throb when your builds change status to red, yellow or blue.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/S-rdOdxN7nI/AAAAAAAAANc/LoqLlm68bpc/s1600/Photo+on+2010-05-12+at+09.36+%236.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 390px; height: 241px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/S-rdOdxN7nI/AAAAAAAAANc/LoqLlm68bpc/s400/Photo+on+2010-05-12+at+09.36+%236.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5470427937973857906" /&gt;&lt;/a&gt;&lt;p&gt;Fix your build and the orb throbs blue.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/S-reLF8KJTI/AAAAAAAAANk/4BbFplCU4mE/s1600/Photo+on+2010-05-12+at+09.56+%234.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 358px; height: 205px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/S-reLF8KJTI/AAAAAAAAANk/4BbFplCU4mE/s400/Photo+on+2010-05-12+at+09.56+%234.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5470428979549316402" /&gt;&lt;/a&gt;&lt;p&gt;That and Hudson Helper for Android now has support for host aliases (a la &lt;tt&gt;/etc/hosts&lt;/tt&gt;), making integration with misconfigured Hudson servers and VPNs much easier.&lt;/p&gt;&lt;p&gt;To find Hudson Helper search for it in the market, or scan this QR code with your Android device:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/S-rcDoVjkLI/AAAAAAAAANU/I8SHPzl9HtQ/s1600/qrcode.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 175px; height: 175px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/S-rcDoVjkLI/AAAAAAAAANU/I8SHPzl9HtQ/s400/qrcode.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5470426652320436402" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3855306247892385019?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3855306247892385019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3855306247892385019' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3855306247892385019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3855306247892385019'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/05/lava-lamps-android-and-continuous.html' title='Lava Lamps, Android and Continuous Integration'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/S-rOmmZ8NbI/AAAAAAAAANE/_FfnoAhffyU/s72-c/Screen+shot+2010-05-12+at+8.50.56+AM.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1387559005709101925</id><published>2010-04-19T15:00:00.000-07:00</published><updated>2010-04-20T14:59:22.798-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Faster Builds: A Continuous Integration Build Strategy</title><content type='html'>&lt;p&gt;We all know that &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;Continuous Integration (CI)&lt;/a&gt; can deliver real, tangible benefits to our projects.  So why are the benefits so elusive?  Many software projects claim to be exercising CI, but have builds that run for 30 minutes or more, or worse, just have a nightly build.  Build times that exceed a few minutes are excessive; all too commonly we see build times reaching 20 minutes to an hour or two.  Real projects tend to have build times that gradually increase as the project evolves, resulting in a failure to reach the full potential that CI promises to deliver.  Luckily for many of these projects, there's a solution that can get your project back on track.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/S84iuYcBeXI/AAAAAAAAAM8/vKBe-d2lmp0/s1600/Screen+shot+2010-04-20+at+2.54.35+PM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 255px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/S84iuYcBeXI/AAAAAAAAAM8/vKBe-d2lmp0/s400/Screen+shot+2010-04-20+at+2.54.35+PM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5462341578276305266" /&gt;&lt;/a&gt;&lt;p&gt;I've seen many projects with this problem, and they all have the same pattern: a monolithic build that builds components in a serial fashion.  While many of these projects are modular, where modules can be built independently, dependencies between modules prevent them from being built in parallel.&lt;/p&gt;&lt;p&gt;So how do we get shorter build times?  We cheat.&lt;/p&gt;&lt;p&gt;By building modules in independent CI jobs, we can get faster feedback for independent modules of our project without reducing the total build time.  As we make discrete changes to the source code, only the affected modules will rebuild.&lt;/p&gt;&lt;p&gt;With this approach each module is built separately from the others.  As a result we get very fast feedback as to the impact of our changes, possibly even before downstream modules have finished building.  Furthermore, in many cases only part of the project will need building &amp;mdash; thus resulting in a reduction in total build time.  I know, I said that total build time is not reduced using this approach, but for many cases the total build time is in fact reduced since we don't always need to build the whole project!&lt;/p&gt;&lt;p&gt;So how do we employ this approach in practice?  These are the steps that I took on a recent project:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Identify logical modules of related functionality in your project.  For Eclipse projects, this could be related plug-ins.  For my project which involves about 100 Eclipse plug-ins (OSGi bundles) this meant splitting the bundles into about 7 modules.&lt;/li&gt;&lt;li&gt;Next, change your build process so that each module builds independently.  To do this I started with the module that would need to be built without any dependencies on other modules.  After creating a CI job to built it, I moved on to the next module, which could now download its dependencies from the first CI job.  Repeat this process until each module is built by an independent CI job that downloads its dependencies as binaries from other CI jobs.&lt;/li&gt;&lt;li&gt;After all modules were building on the CI server using binary dependencies, some minor tweaks were required to limit job concurrency and unnecessary thrashing on the build server.  This is where the power of &lt;a href="http://hudson-ci.org"&gt;Hudson&lt;/a&gt; really shines.&lt;ol&gt;&lt;li&gt;Configure each module's job under &lt;b&gt;Post-build Actions&lt;/b&gt; to trigger the next module in the series of dependant modules: check &lt;b&gt;Build other projects&lt;/b&gt; and list them there.&lt;/li&gt;&lt;li&gt;Using the &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Locks+and+Latches+plugin"&gt;Locks and Latches plugin&lt;/a&gt; ensure that all module jobs are using the same lock, preventing them from all building at once.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here's what I ended up with:&lt;/p&gt;&lt;p&gt;&lt;b&gt;Before:&lt;/b&gt; 1 job, 1100 tests, 30 minute build time.&lt;/p&gt;&lt;p&gt;&lt;b&gt;After:&lt;/b&gt; 7 jobs, 1075 tests, 20 minute 30 second build time, with a break down as follows in dependency order:&lt;/p&gt;&lt;table width="auto" style="width:auto;"&gt;&lt;tr style="border:1px;"&gt;&lt;td&gt;Job 1:&lt;/td&gt;&lt;td&gt;20 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 2:&lt;/td&gt;&lt;td&gt;26 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 3:&lt;/td&gt;&lt;td&gt;1 minute 31 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 4:&lt;/td&gt;&lt;td&gt;5 minutes 54 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 5:&lt;/td&gt;&lt;td&gt;5 minutes 45 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 6:&lt;/td&gt;&lt;td&gt;5 minutes 12 seconds&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Job 7:&lt;/td&gt;&lt;td&gt;1 minute 12 seconds&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;The net result is that most of the time, &lt;em&gt;we get feedback within 6 minutes&lt;/em&gt;, whereas we were having to wait 30 minutes.  Even if you can't parallelize your build, splitting your build into independent modules that build serially can produce measurable benefits such as faster feedback and shorter build times.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1387559005709101925?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1387559005709101925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1387559005709101925' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1387559005709101925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1387559005709101925'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/04/faster-builds-continuous-integration.html' title='Faster Builds: A Continuous Integration Build Strategy'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/S84iuYcBeXI/AAAAAAAAAM8/vKBe-d2lmp0/s72-c/Screen+shot+2010-04-20+at+2.54.35+PM.png' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3012086746901400832</id><published>2010-04-08T13:29:00.000-07:00</published><updated>2010-04-08T17:22:25.481-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><title type='text'>From iPhone to iPad: Creating a Universal Application</title><content type='html'>&lt;p&gt;I&amp;#8217;ve recently had the pleasure of porting an iPhone app to the iPad.  The process was remarkably easy.  This brief article covers the technical issues as well as the relatively minor UI design issues that came up when doing the port.&lt;/p&gt;&lt;p&gt;Apple&amp;#8217;s iPhone OS SDK is capable of producing &amp;#8216;Universal Applications&amp;#8217; which run on multiple devices, including the iPad and iPhone.  The &lt;a href="http://developer.apple.com/iphone/library/documentation/General/Conceptual/iPadProgrammingGuide/StartingYourProject/StartingYourProject.html#//apple_ref/doc/uid/TP40009370-CH9-SW1"&gt;recommended approach&lt;/a&gt; for existing iPhone app projects is to run an upgrade wizard, which worked beautifully for me.  After running the wizard I was initially concerned that nothing happened, because the wizard simply exited with no indication of what occurred &amp;#8212; but after comparing project files against the version in my VCS there were obviously some minor changes to the project files, and one new file, the iPad&amp;#8217;s main view &lt;tt&gt;MainWindow-iPad.xib&lt;/tt&gt;.&lt;/p&gt;&lt;p&gt;Immediately after running the upgrade wizard I was able to run the app in the iPad simulator.  Very impressive.  Having implemented the app with relative layouts, the whole UI scaled very nicely to the larger screen.  Running through some basic workflows in the app, I expected some things to break &amp;#8212; but no, everything worked perfectly!&lt;/p&gt;&lt;p&gt;There were two places in the code where visually things needed adjusting based on the size of the screen.  Initially I tried Apple&amp;#8217;s recommended approach of conditional coding based on &lt;code&gt;(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)&lt;/code&gt;, however I quickly discovered that it doesn&amp;#8217;t work for iPhone 3.1.x apps because &lt;code&gt;UIUserInterfaceIdiomPad&lt;/code&gt; is not defined.  Clearly Apple missed something here.  After thinking through some creative ways to make this work (see &lt;a href="http://iphonedevelopment.blogspot.com/2010/04/few-more-notes-on-creating-universal.html"&gt;Jeff LaMarche&amp;#8217;s blog&lt;/a&gt; for an approach that works) I realized that instead of trying to detect the platform, using the screen size was a better approach.  An easy way to do that is as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[[UIScreen mainScreen] bounds]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The iPad has some new UI paradigms which are well-described in the &lt;a href="http://developer.apple.com/iphone/library/documentation/General/Conceptual/iPadHIG/index.html"&gt;iPad HIG&lt;/a&gt;.  Most apps will benefit from following these guidelines, however I was surprised at how good the app looked in its current form on the iPad.&lt;/p&gt;&lt;p&gt;The moment of truth of course came when I deployed the app to an iPad.  Apart from having to download a new provisioning profile from Apple, the process was easy.  Within a few minutes it was up and running flawlessly on the device.  Kudos go to Apple for making it so easy to port apps from the iPhone to iPad.  It&amp;#8217;s hard to imagine it being any easier.  &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3012086746901400832?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3012086746901400832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3012086746901400832' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3012086746901400832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3012086746901400832'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/04/from-iphone-to-ipad-creating-universal.html' title='From iPhone to iPad: Creating a Universal Application'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5381195764072426330</id><published>2010-03-12T07:32:00.000-08:00</published><updated>2010-04-22T21:21:25.041-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='WebKit'/><category scheme='http://www.blogger.com/atom/ns#' term='EclipseCon'/><category scheme='http://www.blogger.com/atom/ns#' term='EGit'/><title type='text'>Crowdsourcing Documentation Made Easy</title><content type='html'>&lt;p&gt;Momentum is building around documentation best-practices at Eclipse.  &lt;a href="http://www.eclipse.org/egit"&gt;EGit&lt;/a&gt;, Eclipse's Git integration project, &lt;a href="http://dev.eclipse.org/mhonarc/lists/egit-dev/msg00936.html"&gt;recently became&lt;/a&gt; the latest project to adopt a crowdsourcing approach to documentation, and is reaping the benefits: in one day they've already seen the contribution of &lt;a href="http://wiki.eclipse.org/EGit/Git_For_Eclipse_Users"&gt;Git for Eclipse Users&lt;/a&gt; from Alex Blewitt.&lt;/p&gt;&lt;p&gt;They are able to do this by taking advantage a new feature of the latest &lt;a href="http://wiki.eclipse.org/Mylyn/WikiText"&gt;Mylyn WikiText&lt;/a&gt;, which can generate Eclipse help directly from a wiki.  While it was possible to process individual pages before, now Mylyn WikiText makes it easy to process multiple wiki pages at once while automatically downloading images, maintaining page cross-references, and building a unified table of contents.&lt;/p&gt;&lt;p&gt;You can learn more about crowdsourcing documentation at EclipseCon 2010, where Chris Aniszczyk  and I are co-presenting &lt;a href="http://www.eclipsecon.org/2010/sessions?id=1285"&gt;Documentation: Single-Sourcing, Crowd-Sourcing And Other Voodoo&lt;/a&gt;.  If you can't wait two weeks, you can find out how they're doing it at EGit from their &lt;a href="http://wiki.eclipse.org/EGit/Contributor_Guide#Documentation"&gt;contributor guide&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Updated 4/21/2010:&lt;/em&gt; Thanks to Chris, a working example with documentation and source is available at &lt;a href="http://wiki.eclipse.org/DocumentationGuidelines/CrowdSourcingExample"&gt;DocumentationGuidelines/CrowdSourcingExample&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5381195764072426330?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5381195764072426330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5381195764072426330' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5381195764072426330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5381195764072426330'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/03/crowdsourcing-documentation-made-easy.html' title='Crowdsourcing Documentation Made Easy'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3469662986004145069</id><published>2010-02-05T21:31:00.000-08:00</published><updated>2010-02-06T19:04:00.050-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GitHub'/><category scheme='http://www.blogger.com/atom/ns#' term='Git'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='EGit'/><title type='text'>Good Things Come In Threes</title><content type='html'>&lt;p&gt;Hundreds of developers at Eclipse.org are on the cusp of something great.  After &lt;a href="http://greensopinion.blogspot.com/2008/06/why-is-eclipseorg-still-on-cvs.html"&gt;lamenting the pains&lt;/a&gt; of the dark ages and thinking it wouldn't end, Eclipse's focus on best practices has resulted in a &lt;a href="https://bugs.eclipse.org/249745"&gt;decision&lt;/a&gt; to move to &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;.  With tooling integration being such a key factor, it's no surprise that everyone's eyes are on the &lt;a href="http://www.eclipse.org/egit/"&gt;EGit&lt;/a&gt; project.  I figured it was time to give it a spin after a &lt;a href="http://aniszczyk.org/2010/01/29/calling-eclipse-org-projects-interested-in-git/"&gt;recent call&lt;/a&gt; for project trials.  This is how my adventures with GitHub, EGit and Mylyn began.&lt;/p&gt;&lt;p&gt;To kick it off, reading the &lt;a href="http://progit.org/"&gt;progit book&lt;/a&gt; helped with the Git basics, and Lars Vogel's excellent &lt;a href="http://www.vogella.de/articles/EGit/article.html"&gt;EGit tutorial&lt;/a&gt; got me up to speed with EGit.  Knowing the only way to learn is to do it, I created an account on GitHub and kicked off a new project for the purpose.&lt;/p&gt;&lt;p&gt;Keeping focused on the issue at hand is key, so naturally I use &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; &amp;mdash; but wait!  There's even a GitHub connector for Mylyn!  To my disappointment after installing it, I realized that the connector was missing some key features, such as being able to edit and close tasks from within the Eclipse IDE.  Time to get hacking!  This is where the power of Git really kicked in.&lt;/p&gt;&lt;p&gt;Within moments I &lt;a href="http://github.com/dgreen99/org.eclipse.mylyn.github"&gt;cloned the GitHub Mylyn connector project&lt;/a&gt; and got to work.  Luckily the code was in great shape before I started, and within a few hours I had implemented a functional Mylyn task editor for GitHub.  It's trivial for the connector project committers to pull my contributions back into their project.&lt;/p&gt;&lt;p&gt;Though Git itself is not simple to use, it eliminates many barriers and eases community collaboration.  EGit integrates first-class support for Git into Eclipse, overcoming much of the complexity of using Git.&lt;/p&gt;&lt;p&gt;Eclipse's ability to collaborate within its project teams and with its wide user community will step into high gear this year.  A continued focus on best practices and adoption of enabling technologies such as Git and EGit will make it happen.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3469662986004145069?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3469662986004145069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3469662986004145069' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3469662986004145069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3469662986004145069'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/02/good-things-come-in-threes.html' title='Good Things Come In Threes'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5738821495883213064</id><published>2010-01-19T11:00:00.000-08:00</published><updated>2010-01-19T10:07:56.189-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='JDK7'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 7'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6'/><title type='text'>Eclipse 3.6 on OpenJDK on Mac OS X</title><content type='html'>&lt;p&gt;Eclipse 3.6 (aka Helios) will run on OpenJDK on Mac when it's released this year.  This will free us from Apple's &lt;a href="http://www.google.com/search?q=13949712720901ForOSX"&gt;oft-criticized&lt;/a&gt; and historically slow release cycle of the Java VM.&lt;/p&gt;&lt;p&gt;With a little persistence and help from some committers at Eclipse, the most glaring problems were resolved:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://bugs.eclipse.org/276564"&gt;bug 276564:&lt;/a&gt;Eclipse won't launch using JDK 7 because SWT native libraries don't load&lt;/li&gt;&lt;li&gt;&lt;a href="https://bugs.eclipse.org/276763"&gt;bug 276763&lt;/a&gt;: [launcher] launcher fails to use OpenJDK-based VM when -vm is specified&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you're interested in trying it out, you'll need to get an integration build of Eclipse (see &lt;a href="http://download.eclipse.org/eclipse/downloads/#3.6%20Stream%20Integration%20Build"&gt;3.6 Stream Integration Builds&lt;/a&gt;) I20100119-0800 or newer, or a nighly build N20100117-2000 or newer.  Prior to these builds, running Ecilpse on OpenJDK on a mac was only possible with &lt;a href="http://greensopinion.blogspot.com/2009/05/eclipse-35-galileo-on-jdk-7.html"&gt;some trickery&lt;/a&gt;.  You'll also need a working OpenJDK on your mac, which can be built from source using &lt;a href="http://wikis.sun.com/display/OpenJDK/Darwin9Build"&gt;these instructions&lt;/a&gt;, or installed using mac ports.&lt;/p&gt;&lt;p&gt;Many thanks to the Eclipse team for helping, notably Andrew Niefer, Mike Wilson, Kevin Barnes, Grant Gayed, Felipe Heidrich, Thomas Watson, Steve Northover and Silenio Quarti.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5738821495883213064?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5738821495883213064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5738821495883213064' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5738821495883213064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5738821495883213064'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2010/01/eclipse-36-on-openjdk-on-mac-os-x.html' title='Eclipse 3.6 on OpenJDK on Mac OS X'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-8223601082740286343</id><published>2009-12-31T16:30:00.000-08:00</published><updated>2009-12-31T16:46:18.503-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Continuous Integration at Eclipse.org</title><content type='html'>&lt;p&gt;As we near the end of the year it's a good time to look back at how we're doing things and to look for opportunities for improvement.  Last year Eclipse.org has taken some significant steps forward.  One notable change for the better is the increasing adoption of running builds on &lt;a href="https://build.eclipse.org/hudson/"&gt;a central build server&lt;/a&gt; (in this case, Hudson).  While red balls look great on the Christmas tree, they can really detract from the &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration#Advantages"&gt;benefits of continuous integration&lt;/a&gt;.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/Sz07zxM9HBI/AAAAAAAAAMw/d8YCUx7ZjNY/s1600-h/red_anime.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 32px; height: 32px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/Sz07zxM9HBI/AAAAAAAAAMw/d8YCUx7ZjNY/s400/red_anime.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5421555286990986258" /&gt;&lt;/a&gt;&lt;p&gt;At &lt;a href="http://www.maketechnologies.com"&gt;my work&lt;/a&gt; we've developed a culture that focuses on maintaining a fast, clean build.  It is light-hearted and fun: those that break the build are quick to respond and restore integrity.  This behavior is encouraged by good-humored ribbing by other team members.  This culture takes a small amount of effort to maintain, however the team recognizes that everyone benefits:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Integration problems are detected and fixed continuously&lt;/li&gt;&lt;li&gt;Early warning of broken/incompatible code&lt;/li&gt;&lt;li&gt;Early warning of conflicting changes&lt;/li&gt;&lt;li&gt;Immediate unit testing of all changes&lt;/li&gt;&lt;li&gt;Constant availability of a "current" build for testing, demo, or release purposes&lt;/li&gt;&lt;li&gt;Immediate feedback to developers on the quality, functionality, or system-wide impact of code they are writing&lt;sup&gt;[&lt;a href="http://en.wikipedia.org/wiki/Continuous_integration#Advantages"&gt;1&lt;/a&gt;]&lt;/sup&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;At Eclipse.org we can do the same thing: let's strive to eliminate broken builds entirely.  Builds should (almost) always be blue; when they're broken, those that caused the breakage should either roll back their changes or fix them.&lt;/p&gt;&lt;p&gt;Here's to a great 2010 at Eclipse.org, where continuous integration meets with &lt;a href="http://en.wikipedia.org/wiki/Continuous_improvement"&gt;continuous improvement&lt;/a&gt;!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-8223601082740286343?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/8223601082740286343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=8223601082740286343' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8223601082740286343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8223601082740286343'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/continuous-integration-at-eclipseorg.html' title='Continuous Integration at Eclipse.org'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/Sz07zxM9HBI/AAAAAAAAAMw/d8YCUx7ZjNY/s72-c/red_anime.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5973166493867970520</id><published>2009-12-12T23:20:00.000-08:00</published><updated>2009-12-12T23:33:02.512-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper for Android Update</title><content type='html'>&lt;p&gt;&lt;a href="http://greensopinion.blogspot.com/2009/06/hudson-helper-for-android.html"&gt;Hudson Helper for Android&lt;/a&gt; has been updated.  New in this version:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Support for untrusted SSL Certificates (Self-signed, or signed by an untrusted certificate authority)&lt;/li&gt;&lt;li&gt;Layout improvements, bug fixes&lt;/li&gt;&lt;/ul&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SySW8Nf1vbI/AAAAAAAAAMM/OsP3dIXHORk/s1600-h/build-detail2.png"&gt;&lt;img style="display:block; margin:0px auto; padding: 0px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px; border: 1px solid black;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SySW8Nf1vbI/AAAAAAAAAMM/OsP3dIXHORk/s400/build-detail2.png" alt=""id="BLOGGER_PHOTO_ID_5414618613165702578" /&gt;&lt;/a&gt;&lt;p&gt;Thanks to Jim Hague who assisted with providing an environment for testing untrusted SSL certificates.&lt;/p&gt;&lt;p&gt;To get Hudson Helper for Android, search for 'Hudson Helper' in the Google Market on your Android device, or install via &lt;a href="https://slideme.org/application/hudson-helper"&gt;SlideME&lt;/a&gt; if the Google Market doesn't support paid applications in your region.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5973166493867970520?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5973166493867970520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5973166493867970520' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5973166493867970520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5973166493867970520'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/hudson-helper-for-android-update.html' title='Hudson Helper for Android Update'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SySW8Nf1vbI/AAAAAAAAAMM/OsP3dIXHORk/s72-c/build-detail2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1498106675529936619</id><published>2009-12-09T22:31:00.000-08:00</published><updated>2009-12-09T23:08:49.833-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Profiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Ant Build Analysis Gets A Facelift</title><content type='html'>&lt;p&gt;Wondering why your Ant build is slow? Are your continuous integration builds taking too long?  Analysis of Ant builds has had a major facelift, with a new version of &lt;a href="https://antutility.dev.java.net"&gt;Ant Utility&lt;/a&gt; now fully integrated into the Eclipse UI.  With Ant Utility, you can run your build and get immediate feedback:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SyCXNMCqGvI/AAAAAAAAALc/_PDWndEgtFI/s1600-h/antutility-build-metrics.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 168px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SyCXNMCqGvI/AAAAAAAAALc/_PDWndEgtFI/s400/antutility-build-metrics.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5413493004925803250" /&gt;&lt;/a&gt;&lt;p&gt;As your build runs, Ant Utility measures how much time is spent in Ant tasks and targets.  When the build is complete, build metrics are presented in an Eclipse editor, showing exactly which parts of your build file are taking the most time.  Double-click on an entry to open the build file in the Ant editor, with problematic lines presented in varying degrees of red depending on the level to which the task or target contributes to the overall build time.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SyCYu7uqmzI/AAAAAAAAALk/wDJdtYqW0rc/s1600-h/antutility-buildfile-overlay.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 376px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SyCYu7uqmzI/AAAAAAAAALk/wDJdtYqW0rc/s400/antutility-buildfile-overlay.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5413494684174162738" /&gt;&lt;/a&gt;&lt;p&gt;Ant Utility is not designed to replace a full-fledged profiler, however it can give you fast insight into build file problems in the language of your build script.  Ant Utility has almost no overhead and is simple to configure.&lt;/p&gt;&lt;p&gt;Ant Utility can be installed into Eclipse via the following update site:&lt;br/&gt;&lt;tt&gt;https://antutility.dev.java.net/site/&lt;/tt&gt;&lt;/p&gt;&lt;p&gt;To enable Ant Utility on your build, add&lt;br/&gt; &lt;tt&gt;-listener net.java.dev.antutility.core.MetricsBuildListener&lt;/tt&gt; to the Arguments section of your Ant launch configuration:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/SyCa-4qMHWI/AAAAAAAAAL8/7f8HCRoetAw/s1600-h/antutility-launch-config1a.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 338px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/SyCa-4qMHWI/AAAAAAAAAL8/7f8HCRoetAw/s400/antutility-launch-config1a.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5413497157251243362" /&gt;&lt;/a&gt;&lt;p&gt;The Ant build must also be configured to run in the same VM as the Eclipse IDE:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/SyCbOdDC05I/AAAAAAAAAME/-k645sqotzg/s1600-h/antutility-launch-config2a.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 338px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/SyCbOdDC05I/AAAAAAAAAME/-k645sqotzg/s400/antutility-launch-config2a.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5413497424717206418" /&gt;&lt;/a&gt;&lt;p&gt;At this point you can run your build.  When your build completes successfully, build metrics will appear in Eclipse.  Hopefully you're off to the races, returning to snappy, healthy builds in no time flat!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1498106675529936619?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1498106675529936619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1498106675529936619' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1498106675529936619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1498106675529936619'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/ant-build-analysis-gets-facelift.html' title='Ant Build Analysis Gets A Facelift'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vw9l2nnub6c/SyCXNMCqGvI/AAAAAAAAALc/_PDWndEgtFI/s72-c/antutility-build-metrics.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-30343269158196815</id><published>2009-12-07T12:49:00.000-08:00</published><updated>2009-12-07T13:29:07.099-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>How To Be A Successful Developer</title><content type='html'>&lt;p&gt;I was recently asked for advice from a young student on how to become a successful software developer.  This is a complicated question.  I put some thought into it, and realized that every individual will become successful in different ways.  Here are some things that helped me:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Always strive to improve yourself and learn more.&lt;/li&gt;&lt;li&gt;Share information freely with others &amp;mdash; be generous.&lt;/li&gt;&lt;li&gt;Focus on developing good working relationships with your coworkers, both technical staff and others.&lt;/li&gt;&lt;li&gt;Effective communication, both written and spoken is crucial.&lt;/li&gt;&lt;li&gt;Get involved in open source.&lt;/li&gt;&lt;li&gt;Be precise.&lt;/li&gt;&lt;li&gt;Deliver on commitments, or if you need to renegotiate your commitments.&lt;/li&gt;&lt;li&gt;In everything that you do, do it with integrity.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Almost none of these have anything to do with knowledge of technology.  I believe that social aspects have far more impact on success than anything else.  Of course  being knowledgeable helps too, however what's more important than knowing a specific technology is being able to pick up the knowledge that you need, when you need it.&lt;/p&gt;&lt;p&gt;A few things that I missed in my response because I take them for granted:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Have passion for what you do.&lt;/li&gt;&lt;li&gt;Strive for excellence.&lt;/li&gt;&lt;li&gt;Avoid being self-righteous.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I'm sure that there are many things that contribute to being successful.   I'd love to hear from others: what do you think are key contributing factors to becoming a successful software developer?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-30343269158196815?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/30343269158196815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=30343269158196815' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/30343269158196815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/30343269158196815'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/how-to-be-successful-developer.html' title='How To Be A Successful Developer'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-155958830852420395</id><published>2009-12-02T09:01:00.000-08:00</published><updated>2009-12-02T09:24:17.004-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>iPhone Bluetooth Tethering Not Working?</title><content type='html'>&lt;p&gt;I love my iPhone and I love it's Bluetooth tethering capability.  I use it every day for at least two hours.  Much to my dismay, one day it stopped working without explanation.  On my mac the Bluetooth 'Connect to Network' menu item was disabled, and for the life of me I couldn't figure out what was wrong &amp;mdash; no error messages, no log entries, no clues to speak of. Yesterday I stumbled upon the problem by accident.  What follows is some basic instructions that helped me to get it working again.  These instructions are only applicable to software developers provisioning apps to their iPhone via XCode.&lt;/p&gt;&lt;p&gt;The underlying problem turned out to be some iPhone provisioning profiles that had expired.  If you've had expiring provisioning profiles on your device before, then you know that the iPhone will warn you many times as the expiry date approaches.  In the &lt;b&gt;Settings&lt;/b&gt; app of your iPhone, tap through &lt;b&gt;General -&gt; Profiles&lt;/b&gt;, select any expired profiles and press the &lt;b&gt;Remove&lt;/b&gt; button.  Once the rogue profiles are removed from your phone, start up XCode on your mac and from the menu select &lt;b&gt;Window -&gt; Organizer&lt;/b&gt;.  From the Organizer, select &lt;b&gt;Provisioning Profiles&lt;/b&gt; under &lt;b&gt;IPHONE DEVELOPMENT&lt;/b&gt;.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/SxairTMwufI/AAAAAAAAAKs/j95uHkDR2FI/s1600-h/organizer.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 273px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/SxairTMwufI/AAAAAAAAAKs/j95uHkDR2FI/s400/organizer.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5410690867104102898" /&gt;&lt;/a&gt;&lt;p&gt;Delete any expired provisioning profiles using the context menu.  After completing this step, exit XCode and you're done!  Your &lt;b&gt;Connect to Network&lt;/b&gt; menu item should now be enabled, and tethering should now be restored to its normal greatness.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-155958830852420395?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/155958830852420395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=155958830852420395' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/155958830852420395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/155958830852420395'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/iphone-bluetooth-tethering-not-working.html' title='iPhone Bluetooth Tethering Not Working?'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vw9l2nnub6c/SxairTMwufI/AAAAAAAAAKs/j95uHkDR2FI/s72-c/organizer.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-8697108991496671104</id><published>2009-12-01T08:55:00.000-08:00</published><updated>2009-12-01T09:22:18.958-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Encoding And Other Character Woes</title><content type='html'>&lt;p&gt;Ever have a troublesome file containing unprintable characters or other character encoding problems?  Ever wonder if your line delimiter (EOL) is correct for your platform?  Over the years I've seen many incarnations of such problems, hidden deeply within XML and other text files.  Due to the nature of such characters, being unprintable and sometimes occupying 0 space, these problems can be hard to find and diagnose -- until now.  I've created a tiny utility for Eclipse that provides a way to see the invisible: &lt;a href="https://textinspection.dev.java.net/"&gt;Text Inspection&lt;/a&gt; is a new project specifically designed to aid in looking at text in Eclipse.&lt;/p&gt;&lt;p&gt;&lt;a href="https://textinspection.dev.java.net/"&gt;Text Inspection&lt;/a&gt; does this by providing a view that displays the current workbench text selection, exposing unprintable characters as a &lt;a href="http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html"&gt;Java String literal&lt;/a&gt; using unicode escape sequences where appropriate.  All you have to do to see the text of interest is open your file in a text editor (any text editor will do) and select the area of interest.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SxVPEiT_3NI/AAAAAAAAAKU/s8_iOM8SRHI/s1600/text-inspector.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 189px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SxVPEiT_3NI/AAAAAAAAAKU/s8_iOM8SRHI/s400/text-inspector.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5410317466704272594" /&gt;&lt;/a&gt;&lt;p&gt;The project also augments the Java source editor with a paste command alternative for pasting text as a Java string literal, escaping characters as needed.  This can come in really handy when writing unit tests.  My unit tests often compare output to an expected value, which is often copy/pasted from the console.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SxVQgdWs1sI/AAAAAAAAAKc/dfriWZMQ39Q/s1600/paste-command.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 190px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SxVQgdWs1sI/AAAAAAAAAKc/dfriWZMQ39Q/s400/paste-command.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5410319045921396418" /&gt;&lt;/a&gt;&lt;p&gt;I invite you to try it out and provide feedback.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-8697108991496671104?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/8697108991496671104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=8697108991496671104' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8697108991496671104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8697108991496671104'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/12/encoding-and-other-character-woes.html' title='Encoding And Other Character Woes'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vw9l2nnub6c/SxVPEiT_3NI/AAAAAAAAAKU/s8_iOM8SRHI/s72-c/text-inspector.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4636987395876984802</id><published>2009-11-26T23:51:00.000-08:00</published><updated>2009-11-26T15:39:37.696-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='DemoCamp'/><title type='text'>Eclipse DemoCamp Vancouver 2009</title><content type='html'>&lt;p&gt;I had a great time at &lt;a href="http://wiki.eclipse.org/Eclipse_DemoCamps_November_2009/Vancouver"&gt;Eclipse DemoCamp Vancouver 2009&lt;/a&gt;.  There were some really interesting talks, and the pace was good with 9 minutes per presentation.  In particular I was really impressed by a presentation by Emerson Murphy-Hill titled &lt;cite&gt;Identifying Code Smells in Eclipse&lt;/cite&gt;.  Emerson demo'd a tool which augments the Java source editor with ambient &lt;a href="http://en.wikipedia.org/wiki/Code_smell"&gt;code smell&lt;/a&gt; decorations.  Neat stuff.&lt;/p&gt;&lt;p&gt;Anyone that is interested can find &lt;a href="https://textile-j.dev.java.net/CrowdsourcingDocumentation.pdf"&gt;my slides on Crowdsourcing Documentation here&lt;/a&gt;.  In a nutshell I presented on how Mylyn WikiText can enable the use of a wiki (such as &lt;a href="http://wiki.eclipse.org"&gt;EclipsePedia&lt;/a&gt;) for creating and maintaining documentation while still publishing to other formats, such as the Eclipse help system.  This idea is evolving within discussions by the Eclipse Architecture Council on &lt;a href="https://bugs.eclipse.org/283734"&gt;bug 283734: Documentation Best Practices&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4636987395876984802?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4636987395876984802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4636987395876984802' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4636987395876984802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4636987395876984802'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/11/eclipse-democamp-vancouver-2009.html' title='Eclipse DemoCamp Vancouver 2009'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-8516500984762439892</id><published>2009-11-13T09:10:00.000-08:00</published><updated>2009-11-13T09:27:14.686-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><title type='text'>NSLog Revisited</title><content type='html'>&lt;p&gt;Logging is an essential tool when developing for iPhone.  Keeping things simple and code readable is a major challenge when scattering logging code throughout your application.  A &lt;a href="http://stackoverflow.com/questions/969130/nslog-tips-and-tricks"&gt;few&lt;/a&gt; &lt;a href="http://iphoneincubator.com/blog/debugging/the-evolution-of-a-replacement-for-nslog"&gt;recent&lt;/a&gt; &lt;a href="http://www.karlkraft.com/index.php/2009/03/23/114/"&gt;posts&lt;/a&gt; on the topic have provided some great solutions, but if you're like me sometimes a dead-simple solution is exactly what you need.&lt;/p&gt;&lt;p&gt;This is what I use:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// in Implementation.m&lt;br /&gt;#define DLog NSLog&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;// I need to log something&lt;br /&gt;DLog(@"something interesting happended: %@",what)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When I want to disable logging in that file, I simply change the #define as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#define DLog // NSLog&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That's all!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-8516500984762439892?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/8516500984762439892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=8516500984762439892' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8516500984762439892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8516500984762439892'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/11/nslog-revisited.html' title='NSLog Revisited'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-9085719594655362661</id><published>2009-11-05T09:06:00.000-08:00</published><updated>2009-11-05T09:16:14.414-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><title type='text'>TreeViewer: Two Clicks To Edit</title><content type='html'>&lt;p&gt;TreeViewer is the foundation behind most trees and tree tables found in Eclipse.  It provides excellent extensibility and options for customization.  One feature, in-place editing of tree nodes, is very useful -- however the default implementation activates editing on the first click.  This can be frustrating and unintuitive to users who expect the first click to select a node, and the second click to edit.  This tip shows a simple way to cause TreeViewer to require two clicks to edit.&lt;/p&gt;&lt;p&gt;Since Eclipse 3.3, &lt;code&gt;ColumnViewerEditorActivationStrategy&lt;/code&gt; has been available as API to configure how a TreeViewer starts editing.  Our strategy will require the tree's current selection to be the node being clicked in order for a click to be considered an editing event.  We do this by extending ColumnViewerEditorActivationStrategy as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/**&lt;br /&gt; * an activation strategy that only allows editor activation if the item under the&lt;br /&gt; * event was already selected. This has the effect of requiring a click to first select&lt;br /&gt; * the item, then a second click to edit. &lt;br /&gt; */&lt;br /&gt;class SecondClickColumnViewerEditorActivationStrategy extends&lt;br /&gt;  ColumnViewerEditorActivationStrategy implements ISelectionChangedListener {&lt;br /&gt;&lt;br /&gt; private Object selectedElement;&lt;br /&gt;&lt;br /&gt; public SecondClickColumnViewerEditorActivationStrategy(ColumnViewer viewer) {&lt;br /&gt;  super(viewer);&lt;br /&gt;  viewer.addSelectionChangedListener(this);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; @Override&lt;br /&gt; protected boolean isEditorActivationEvent(&lt;br /&gt;   ColumnViewerEditorActivationEvent event) {&lt;br /&gt;  IStructuredSelection selection = (IStructuredSelection)getViewer().getSelection();&lt;br /&gt;  &lt;br /&gt;  return selection.size() == 1 &amp;&amp; super.isEditorActivationEvent(event) &amp;&amp; &lt;br /&gt;    selectedElement == selection.getFirstElement();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void selectionChanged(SelectionChangedEvent event) {&lt;br /&gt;  IStructuredSelection ss = (IStructuredSelection) event.getSelection();&lt;br /&gt;  &lt;br /&gt;  if (ss.size() == 1) {&lt;br /&gt;   selectedElement = ss.getFirstElement();&lt;br /&gt;   return;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  selectedElement = null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code relies on the order of events when comparing the tree's current selection against it's previous current selection.&lt;/p&gt;&lt;p&gt;Now all we have to do is install the strategy on our TreeViewer:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;viewer = new org.eclipse.jface.viewers.TreeViewer(parent, &lt;br /&gt;   SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);&lt;br /&gt;TreeViewerEditor.create(viewer,null,new SecondClickColumnViewerEditorActivationStrategy(viewer),&lt;br /&gt;   ColumnViewerEditor.DEFAULT);  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Voila!  We're done: our TreeViewer now edits on the second click instead of the first.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-9085719594655362661?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/9085719594655362661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=9085719594655362661' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9085719594655362661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9085719594655362661'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/11/treeviewer-two-clicks-to-edit.html' title='TreeViewer: Two Clicks To Edit'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-128181937759340447</id><published>2009-11-02T09:05:00.001-08:00</published><updated>2009-11-02T09:19:24.221-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Movember!</title><content type='html'>&lt;p&gt;Normally my wife would pay me to shave it off... now I've found the best excuse ever to grow a Mo!  I'm growing a Mo to &lt;a href="http://ca.movember.com/mospace/294472/"&gt;raise money and awareness&lt;/a&gt; for Prostate Cancer Canada.  Now to decide.... what'll it be?&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/Su8RlOW0PWI/AAAAAAAAAKM/lgIDF6PfCYY/s1600-h/moustache.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 364px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/Su8RlOW0PWI/AAAAAAAAAKM/lgIDF6PfCYY/s400/moustache.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5399553809447140706" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Please &lt;a href="http://ca.movember.com/mospace/294472/"&gt;donate&lt;/a&gt;, or better yet sign up and start raising too!  Want to find out more?  Check out the &lt;a href="http://ca.movember.com/"&gt;official Movember site.&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-128181937759340447?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/128181937759340447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=128181937759340447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/128181937759340447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/128181937759340447'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/11/movember.html' title='Movember!'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vw9l2nnub6c/Su8RlOW0PWI/AAAAAAAAAKM/lgIDF6PfCYY/s72-c/moustache.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-363912190097070724</id><published>2009-10-27T12:23:00.000-07:00</published><updated>2009-10-27T13:30:27.778-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Key Bindings in Eclipse Editors</title><content type='html'>&lt;p&gt;For tool writers, the Eclipse platform provides great facilities for binding keys to commands so that specific actions can be invoked with the right key combination.  In most cases hooking into these facilities is a simple matter of activating the right context via &lt;code&gt;IContextService&lt;/code&gt; and using a few extension points in your &lt;tt&gt;plugin.xml&lt;/tt&gt;.  For more complex editors, such as those with multiple Text or StyledText controls, it's not so easy.  This article examines the mechanisms that the Eclipse platform uses to determine how commands are handled, and outlines an approach for implementing editors that wish to alter command handling depending on the focus control.&lt;/p&gt;&lt;p&gt;Suppose we are building an editor that has more than one StyledText control and uses &lt;tt&gt;IOperationHistory&lt;/tt&gt; to support undo/redo.  We want CTRL+Z and CTRL+Y (Command-Z and Command-Y on a Mac) to cause undo/redo in the editor.  In this editor what we want is for these keys to manipulate the &lt;tt&gt;IOperationHistory&lt;/tt&gt;, however when a StyledText has focus in our editor we want to invoke undo/redo on the StyledText.  In other words, the user will expect undo/redo to behave differently depending on what they're doing.  This is consistent with how undo/redo work throughout the rest of the platform.&lt;/p&gt;&lt;p&gt;Eclipse uses the following abstractions to represent the concept  of keys invoking commands:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;KeyBinding - binds a sequence of keys to a logical command&lt;/li&gt;&lt;li&gt;Command - an abstract representation for some semantic behaviour&lt;/li&gt;&lt;li&gt;IHandler - an implementation of the behaviour for a specific command&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let's take (for example) CTRL+Z.  When key presses are made, they are matched with a &lt;tt&gt;KeyBinding&lt;/tt&gt;.  Eclipse knows which &lt;tt&gt;Command&lt;/tt&gt; to invoke based on the &lt;tt&gt;KeyBinding&lt;/tt&gt; (in this case the 'undo' command).  Eclipse then determines which &lt;tt&gt;IHandler&lt;/tt&gt; to use by looking up the handler using the logical command in &lt;tt&gt;IHandlerService&lt;/tt&gt;.  (There are many layers of indirection in the actual implementation -- this example is simplified for ease of understanding).&lt;/p&gt;&lt;p&gt;In our editor we want the operation history to be associated with undo/redo.  We hook it up as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;OperationHistoryActionHandler undoAction= new UndoActionHandler(site, moduleEditor.getOperationContext());&lt;br /&gt;PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);&lt;br /&gt;undoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);&lt;br /&gt;undoAction.setId(ITextEditorActionConstants.UNDO);&lt;br /&gt;&lt;br /&gt;IHandlerService handlerService = (IHandlerService) site.getService(IHandlerService.class);&lt;br /&gt;handlerService.activateHandler(undoAction.getActionDefinitionId(), new ActionHandler(undoAction));&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When we test our editor this works as expected: we can undo changes in the editor by pressing CTRL+Z.  After focusing on a StyledText in our editor however, we expect CTRL+Z to undo text that we type.  In our editor these text changes aren't in our operation history until after the text control loses focus.  So how do we set that up?  Here's how:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;TextViewer textViewer = // create the text viewer&lt;br /&gt;TextViewerSupport support = new TextViewerSupport(textViewer);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most of the work is done by this helper class:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt; protected class TextViewerSupport implements FocusListener, DisposeListener {&lt;br /&gt;&lt;br /&gt;  private final TextViewer textViewer;&lt;br /&gt;  private List&lt;IHandlerActivation&gt; handlerActivations = new ArrayList&lt;IHandlerActivation&gt;();&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;  public TextViewerSupport(TextViewer textViewer) {&lt;br /&gt;   this.textViewer = textViewer;&lt;br /&gt;   StyledText textWidget = textViewer.getTextWidget();&lt;br /&gt;   textWidget.addFocusListener(this);&lt;br /&gt;   textWidget.addDisposeListener(this);&lt;br /&gt;&lt;br /&gt;   if (textViewer.getTextWidget().isFocusControl()) {&lt;br /&gt;    activateContext();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  public void focusLost(FocusEvent e) {&lt;br /&gt;   deactivateContext();&lt;br /&gt;  }&lt;br /&gt;  public void focusGained(FocusEvent e) {&lt;br /&gt;   activateContext();&lt;br /&gt;  }&lt;br /&gt;  public void widgetDisposed(DisposeEvent e) {&lt;br /&gt;   deactivateContext();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected void activateContext() {&lt;br /&gt;   if (handlerActivations.isEmpty()) {&lt;br /&gt;    activateHandler(ISourceViewer.QUICK_ASSIST,ITextEditorActionDefinitionIds.QUICK_ASSIST);&lt;br /&gt;    activateHandler(ISourceViewer.CONTENTASSIST_PROPOSALS,ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);&lt;br /&gt;    activateHandler(ITextOperationTarget.CUT, ITextEditorActionDefinitionIds.CUT);&lt;br /&gt;    activateHandler(ITextOperationTarget.COPY, ITextEditorActionDefinitionIds.COPY);&lt;br /&gt;    activateHandler(ITextOperationTarget.PASTE, ITextEditorActionDefinitionIds.PASTE);&lt;br /&gt;    activateHandler(ITextOperationTarget.DELETE, ITextEditorActionDefinitionIds.DELETE);&lt;br /&gt;    activateHandler(ITextOperationTarget.UNDO, ITextEditorActionDefinitionIds.UNDO);&lt;br /&gt;    activateHandler(ITextOperationTarget.REDO, ITextEditorActionDefinitionIds.REDO);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;  protected void activateHandler(int operation, String actionDefinitionId) {&lt;br /&gt;   StyledText textWidget = textViewer.getTextWidget();&lt;br /&gt;   IHandler actionHandler = createActionHandler(operation, actionDefinitionId);&lt;br /&gt;   IHandlerActivation handlerActivation = handlerService.activateHandler(actionDefinitionId, actionHandler,new ActiveFocusControlExpression(textWidget));&lt;br /&gt;   &lt;br /&gt;   handlerActivations.add(handlerActivation);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private IHandler createActionHandler(final int operation, String actionDefinitionId) {&lt;br /&gt;   Action action = new Action() {&lt;br /&gt;    @Override&lt;br /&gt;    public void run() {&lt;br /&gt;     if (textViewer.canDoOperation(operation)) {&lt;br /&gt;      textViewer.doOperation(operation);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   };&lt;br /&gt;   action.setActionDefinitionId(actionDefinitionId);&lt;br /&gt;   return new ActionHandler(action);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  protected void deactivateContext() {&lt;br /&gt;   if (!handlerActivations.isEmpty()) {&lt;br /&gt;    for (IHandlerActivation activation: handlerActivations) {&lt;br /&gt;     handlerService.deactivateHandler(activation);&lt;br /&gt;     activation.getHandler().dispose();&lt;br /&gt;    }&lt;br /&gt;    handlerActivations.clear();&lt;br /&gt;   }&lt;br /&gt;  }  &lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Though &lt;tt&gt;TextViewerSupport&lt;/tt&gt; looks complicated, it's really just activating different command handlers whenever the textViewer gains focus.&lt;/p&gt;&lt;p&gt;When the text control has focus the editor now has two handlers for the undo command.  So how does Eclipse know which one to use?  Digging into the internals of Eclipse reveals that it uses the handler with highest priority.  But we don't set a priority no these handlers!  Eclipse calculates the priority of handlers automatically based on the enablement expression of the handler.  The priority of the enablement expression is based in part on the variables that the expression references.  This leads us to the last part of our solution, &lt;tt&gt;ActiveFocusControlExpression&lt;/tt&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/**&lt;br /&gt; * An expression that evaluates to true if and only if the current focus control is the one provided.&lt;br /&gt; * Has a very high priority in order to ensure proper conflict resolution.&lt;br /&gt; */&lt;br /&gt;public class ActiveFocusControlExpression extends Expression {&lt;br /&gt;&lt;br /&gt; private Control focusControl;&lt;br /&gt;&lt;br /&gt; public ActiveFocusControlExpression(Control control) {&lt;br /&gt;  focusControl = control;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void collectExpressionInfo(ExpressionInfo info) {&lt;br /&gt;  info.markDefaultVariableAccessed(); // give it a very high priority&lt;br /&gt;  info.addVariableNameAccess(ISources.ACTIVE_SHELL_NAME);&lt;br /&gt;  info.addVariableNameAccess(ISources.ACTIVE_WORKBENCH_WINDOW_NAME);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public EvaluationResult evaluate(IEvaluationContext context)&lt;br /&gt;   throws CoreException {&lt;br /&gt;  if (Display.getCurrent() != null &amp;&amp; focusControl.isFocusControl()) {&lt;br /&gt;   return EvaluationResult.TRUE;&lt;br /&gt;  }&lt;br /&gt;  return EvaluationResult.FALSE;&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In &lt;tt&gt;collectExpressionInfo&lt;/tt&gt; we ensure that the expression indicates that it uses the default variable.  This gives the expression a very high priority.  Since the expression is only enabled when our control has focus, we've ensured that undo is directed to our text control at the right time.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Credit:&lt;/em&gt; The approach described in this article is inspired by a class &lt;tt&gt;CommonTextSupport&lt;/tt&gt; (authored by Steffen Pingel) from the &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; project.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-363912190097070724?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/363912190097070724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=363912190097070724' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/363912190097070724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/363912190097070724'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/10/key-bindings-in-eclipse-editors.html' title='Key Bindings in Eclipse Editors'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4078015860959904004</id><published>2009-09-03T11:49:00.000-07:00</published><updated>2009-09-03T12:08:23.288-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper for iPhone Update</title><content type='html'>&lt;p&gt;&lt;a href="http://greensopinion.blogspot.com/2009/05/hudson-helper-hudson-on-your-iphone.html"&gt;Hudson Helper&lt;/a&gt; for iPhone and iPod Touch has been updated.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SqATMtKcrCI/AAAAAAAAAJU/EUeEqX_gYy8/s1600-h/screenshot-main.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SqATMtKcrCI/AAAAAAAAAJU/EUeEqX_gYy8/s400/screenshot-main.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5377319064083147810" /&gt;&lt;/a&gt;&lt;p&gt;New in this version:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Build controls (start/stop builds)&lt;/li&gt;&lt;li&gt;Build details with blame!&lt;/li&gt;&lt;li&gt;Build progress indicator&lt;/li&gt;&lt;li&gt;More compact layout&lt;/li&gt;&lt;li&gt;Trend graphs now display when using Hudson CI servers version 1.321 and greater&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can get Hudson Helper by searching for 'Hudson Helper' in the app store, or &lt;a href="itms://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=311514567&amp;mt=8&amp;s=143441"&gt;click here to get it with iTunes&lt;/a&gt;.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SqAQsLAWgaI/AAAAAAAAAJE/XfQMs4rUbb4/s1600-h/screenshot-build-with-progress.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SqAQsLAWgaI/AAAAAAAAAJE/XfQMs4rUbb4/s400/screenshot-build-with-progress.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5377316306134925730" /&gt;&lt;/a&gt;&lt;p&gt;If you're an Eclipse contributor &lt;a href="http://greensopinion.blogspot.com/2009/06/galileo-builds-on-your-iphone.html"&gt;my original offer&lt;/a&gt; still stands: you can get Hudson Helper for free.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4078015860959904004?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4078015860959904004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4078015860959904004' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4078015860959904004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4078015860959904004'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/09/hudson-helper-for-iphone-update.html' title='Hudson Helper for iPhone Update'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vw9l2nnub6c/SqATMtKcrCI/AAAAAAAAAJU/EUeEqX_gYy8/s72-c/screenshot-main.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-132544505299283721</id><published>2009-09-03T08:00:00.000-07:00</published><updated>2009-09-03T10:18:16.787-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>How I Lost My Annotations: JPA, Hibernate and FetchType.LAZY</title><content type='html'>&lt;p&gt;Many &lt;a href="http://en.wikipedia.org/wiki/Java_Persistence_API"&gt;JPA&lt;/a&gt; applications use Java annotations on their domain model classes in order to specify validation rules.  Runtime frameworks use reflection to discover these validation rules and enforce them at runtime.  This all works nicely until you realize that you need &lt;code&gt;FetchType.LAZY&lt;/code&gt; on some of your @ManyToOne associations.  After specifying lazy fetching you may find that some of your annotations disappear – only not all the time.  So what's going on here?  This article discusses the implications of &lt;code&gt;FetchType.LAZY&lt;/code&gt; and how to overcome this intermittent problem.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;In order to implement lazy fetching under the covers Hibernate enhances your classes.  What does this mean?  Basically, Hibernate extends your classes and overrides its methods at runtime.  It does this by using one of two bytecode manipulation libraries: &lt;a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/"&gt;Javassist&lt;/a&gt; or &lt;a href="http://cglib.sourceforge.net/"&gt;CGLib&lt;/a&gt;.  If you ask one of these enhanced classes for its name, you'll end up with something like this:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;// JPA entity class name&lt;br /&gt;greensopinion.Person&lt;br /&gt;&lt;br /&gt;// CGLib enhanced class name&lt;br /&gt;greensopinion.Person$$EnhancerByCGLIB$$4ad0592d&lt;br /&gt;&lt;br /&gt;// Javassist enhanced class name&lt;br /&gt;greensopinion.Person_$$_javassist_0&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Hibernate only does this when necessary.  So when you get an instance of your entity it might be enhanced, and it might not – depending on how it was loaded.  This can be confusing, since instances of a Person may not always be the same class.  For example, code like this can be problematic:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Person person1 = entityManager.find(Person.class,id1);&lt;br /&gt;Person person2 = entityManager.find(Person.class,id2);&lt;br /&gt;&lt;br /&gt;// bad: contrary to what we'd expect, this comparison may&lt;br /&gt;// be true sometimes, but not always&lt;br /&gt;if (person1.getClass() == person2.getClass()) {&lt;br /&gt;}&lt;br /&gt;// bad: this may not work as expected either&lt;br /&gt;if (person1.getClass().isAssignableFrom(person2.getClass())) {&lt;br /&gt;}&lt;br /&gt;// bad: again, sometimes true sometimes false&lt;br /&gt;if (person1.getClass().isAssignableFrom(Person.class)) {&lt;br /&gt;}&lt;br /&gt;// this is ok: comparison will always be true (assuming person1 is not null)&lt;br /&gt;if (person1 instanceof Person) {&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the above example comparisons that look straight-forward may behave differently depending on how and if these entities were already loaded by the same entity manager.  This can be tricky when implementing &lt;a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-3771"&gt;hashCode and equals&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Furthermore, we're in for more trouble if we declare Person as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@Entity&lt;br /&gt;public class Person {&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  @Required&lt;br /&gt;  public String getFirstName() { ... }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we're using an annotation to indicate to our validation framework that the firstName property is a required field (see &lt;a href="http://jcp.org/en/jsr/detail?id=303"&gt;JSR-303&lt;/a&gt;, &lt;a href="https://www.hibernate.org/412.html"&gt;Hibernate Validation&lt;/a&gt;, &lt;a href="https://springmodules.dev.java.net/"&gt;Spring Modules&lt;/a&gt;, &lt;a href="http://oval.sourceforge.net/"&gt;OVal&lt;/a&gt;).  Our validation framework may do something like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Class clazz = entity.getClass();&lt;br /&gt;&lt;br /&gt;// iterate on class accessors&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;if (method.getAnnotation(Required.class) != null) {&lt;br /&gt;  ...&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This may work well most of the time, and may work in all of our unit tests – however in the running application it may appear as if our class has lost its annotations!  Why is this happening?  Don't worry, your JVM isn't losing its head, or your annotations.  This code may not work as expected if our entity is an enhanced version since Hibernate's ProxyFactory enhancers &lt;a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-4112"&gt;don't consider annotations when overriding methods&lt;/a&gt; on your entity.&lt;/p&gt;&lt;p&gt;So why use &lt;code&gt;FetchType.LAZY&lt;/code&gt; at all?  Most non-trivial &lt;a href="http://en.wikipedia.org/wiki/Java_Persistence_API"&gt;JPA&lt;/a&gt; applications will have to make use of &lt;code&gt;FetchType.LAZY&lt;/code&gt; in order to avoid @ManyToOne associations from causing cascading loads.  Using &lt;code&gt;FetchType.LAZY&lt;/code&gt; can also reduce the number of joins when fetching collections, which in some cases greatly improves performance.&lt;/p&gt;&lt;p&gt;So how to we solve this problem?  Our reflection code must be aware of HibernateProxy.  Here's an example of how this can be fixed:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;Class clazz = PersistenceUtil.getEntityClass(entity);&lt;br /&gt;... now do reflection ...&lt;br /&gt;&lt;br /&gt;// in PersistenceUtil.java&lt;br /&gt;public static Class getEntityClass(Object entity) {&lt;br /&gt;   if (entity instanceof HibernateProxy) {&lt;br /&gt;       return ((HibernateProxy)entity).getHibernateLazyInitializer().getPersistentClass();&lt;br /&gt;   }&lt;br /&gt;   return entity.getClass();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The intermittent and in some cases occasional nature of the symptoms involved make this problem hard to reproduce.  Lazy fetching may seem like a panacea, however it's another case where &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky abstractions&lt;/a&gt; make our lives more challenging.  As with most things in software development, it's important to understand the implementation details in order to work effectively with JPA.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-132544505299283721?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/132544505299283721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=132544505299283721' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/132544505299283721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/132544505299283721'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/08/how-i-lost-my-annotations-jpa-hibernate.html' title='How I Lost My Annotations: JPA, Hibernate and FetchType.LAZY'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6836044614390565055</id><published>2009-09-02T16:37:00.000-07:00</published><updated>2009-09-02T16:44:29.372-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper for Android Update</title><content type='html'>&lt;p&gt;&lt;a href="http://greensopinion.blogspot.com/2009/06/hudson-helper-for-android.html"&gt;Hudson Helper for Android&lt;/a&gt; has been updated.  New in this version:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;background status monitoring&lt;/li&gt;&lt;li&gt;trend graphs now display when using Hudson CI servers &lt;a href="https://hudson.dev.java.net/changelog.html"&gt;version 1.321&lt;/a&gt; and greater&lt;/li&gt;&lt;li&gt;bug fixes&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To get Hudson Helper for Android, search for 'Hudson Helper' in the Google Market on your Android device.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6836044614390565055?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6836044614390565055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6836044614390565055' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6836044614390565055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6836044614390565055'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/09/hudson-helper-for-android-update.html' title='Hudson Helper for Android Update'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2210223067430316383</id><published>2009-08-14T14:03:00.000-07:00</published><updated>2009-09-03T10:27:35.308-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>Regular Expression Gotchas</title><content type='html'>&lt;p&gt;Mylyn WikiText uses &lt;a href="http://en.wikipedia.org/wiki/Regular_expression"&gt;regular expressions&lt;/a&gt; to parse wiki markup.  For &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; users, this means that your Bugzilla, JIRA and Trac bugs look good in the Eclipse UI.  With the Galileo release of Eclipse, Mylyn WikiText added automatic stack trace detection.&lt;br /&gt;Little did I know that the regular expression that I wrote would &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=283629"&gt;hang the Eclipse UI&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The intention was to format stack traces using a monospace font to improve layout:&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/SoXcSkLlrDI/AAAAAAAAAIg/IFpM-8KrwJw/s1600-h/Picture+6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 179px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/SoXcSkLlrDI/AAAAAAAAAIg/IFpM-8KrwJw/s400/Picture+6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5369940342217223218" /&gt;&lt;/a&gt;&lt;p&gt;Stack traces are detected by a regular expression.  The regex that I created for this purpose worked well in testing, however for some stack traces would pin the CPU at near 100%, hanging the Eclipse UI.  Here's what the regular expression looked like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;([A-Za-z][a-z0-9_$]*)+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Actually, the full regular expression is a lot bigger: &lt;/p&gt;&lt;pre&gt;&lt;code&gt;\s*((((Caused by:\s+)|(at\s+))?([a-z][a-z0-9]*)(\.([a-z][a-z0-9]*))*\.([A-Za-z][a-z0-9_$]*)+((:\s+\w.*)|(\.((\&lt;init\&gt;)|([a-zA-Z0-9_$]+))\(.*?\)))?)|(\.\.\.\s\d+\smore))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The regex segment attempts to match a camel-case Java class name.  What I didn't notice the first time around is that it contains nested greedy quantifiers.  For some input can cause the CPU spiral of death... not good news.&lt;/p&gt;&lt;p&gt;To fix the offending regular expression is simple: just eliminate any nested quantifiers.  My improved regular expression segment now looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[A-Za-z][a-zA-Z0-9_$]*&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Two things that I learned from this experience:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Never write a regular expression that nests greedy quantifiers.&lt;/li&gt;&lt;li&gt;Always ensure that testing involves sufficient diversity of input data.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2210223067430316383?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2210223067430316383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2210223067430316383' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2210223067430316383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2210223067430316383'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/08/regular-expression-gotchas-how-i-hung.html' title='Regular Expression Gotchas'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/SoXcSkLlrDI/AAAAAAAAAIg/IFpM-8KrwJw/s72-c/Picture+6.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5669151997714898459</id><published>2009-07-02T23:01:00.001-07:00</published><updated>2009-07-03T06:50:00.456-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Android versus iPhone Development: A Comparison</title><content type='html'>&lt;p&gt;A few months ago I ventured into the world of Mobile development and created an application (&lt;a href="http://greensopinion.blogspot.com/2009/05/hudson-helper-hudson-on-your-iphone.html"&gt;Hudson Helper&lt;/a&gt;) for both iPhone and Android.  This article is about my experiences, comparing Android and iPhone development with a focus on tools, platform and the developer experience.&lt;/p&gt;&lt;p&gt;Before going much further I should note that my comparison is with considerable bias.  I’ve spent the past 12+ years in Java development, having spent much of my career building developer tools.  Since January of 2004 I’ve been building plug-ins for &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt;, and before that plug-ins for &lt;a href="http://www.netbeans.org/"&gt;NetBeans&lt;/a&gt;.  This bias is somewhat tempered with several years of C and C++ development.  With this background I find that I’m very critical of developer tools.  Developer productivity is key — anything that takes away from the flow of a developer in the zone is a real problem.&lt;/p&gt;&lt;h4 id="LanguageProgrammingModelandPlatform"&gt;Language, Programming Model and Platform&lt;/h4&gt;&lt;h5 id="Language"&gt;Language&lt;/h5&gt;&lt;p&gt;The language of choice for iPhone development is &lt;a href="http://en.wikipedia.org/wiki/Objective-C"&gt;Objective-C&lt;/a&gt;.  Objective-C is a language based on C with extensions for object-oriented concepts, such as classes, inheritance, interfaces, messages, dynamic typing, etc.  The Java language is used when developing for Android (though it doesn’t actually get compiled to bytecode).  &lt;/p&gt;&lt;p&gt;Java is a no-brainer.  I have to say that it’s nice not to have to learn a new language to target mobile.  Existing skillsets don’t come easy — so reuse of expertise is worth a lot.&lt;/p&gt;&lt;p&gt;It took a little while to wrap my head around some of the language features available with Objective-C.  I soon discovered that I really loved certain language features, such as message passing (instead of calling methods), categories and named arguments.   I did find however that the syntax of Objective-C is cumbersome.  I’m still not used to ‘+’ and ‘-’ for static and member methods, too many parentheses are required, and in general I just felt like I had to type way to much to express a simple concept.  The IDE didn’t help much with this either (more on that later).&lt;/p&gt;&lt;p&gt;One thing that really became clear to me is that Objective-C, though it may have been visionary for its time, is really a language of the '80s.  Certain issues such as split header and implementation files and violation of &lt;a href="http://en.wikipedia.org/wiki/DRY"&gt;DRY&lt;/a&gt; are really time-wasters, and not small ones at that.  I found myself constantly switching back and forth between files, which not only has a cost in navigation (which file to open?) but with every file opened your sense of context must be recreated (where’s the caret, what’s selected, where am I in the file, how is this file organized).  &lt;/p&gt;&lt;p&gt;As far as DRY, must I really do 5 things to declare a property?? (declare in the class definition, again to declare getter/settter, initialize in the init method, @synthesize in the implementation, release in dealloc).  Here’s what I mean:&lt;/p&gt;&lt;p&gt;&lt;b&gt;Server.h&lt;/b&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@interface Server : Updatable {&lt;br /&gt;NSString *name;  &amp;lt;-- declare the property &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic,retain) NSString *name; &amp;lt;--- declare the property again&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Server.m&lt;/b&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@synthesize name;  &amp;lt;-- implement getter/setter&lt;br /&gt;&lt;br /&gt;-(void) dealloc {&lt;br /&gt;[name release];  &amp;lt;-- release memory&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you ask me, everything in Server.m should go away.  Another gotcha here is the positional relevance of @synthesize.&lt;/p&gt;&lt;p&gt;Java has a similar problem with properties, though not quite so bad — and the IDE helps you write your getter/setter.       &lt;/p&gt;&lt;p&gt;Pointers in Objective-C, though powerful, are also another time-waster.  This is where Java really shines with its garbage collection.  I found that I was constantly considering whether allocated objects were freed appropriately.  Code flow is poor since application logic is littered with memory management.  I only have so many brain cycles available — why do I have to think about this other cruft that’s not really a core concern of the application domain?  Of course this gets even worse when trying to figure out where things went wrong if you make a mistake.  Zombies help, but still don’t make it obvious if you’ve accessed something that was deallocated.   Other issues include deallocating something twice, autoreleasing something twice.  I also found it non-intuitive when to retain return values from methods.&lt;/p&gt;&lt;p&gt;Another annoyance of Objective-C is the patterns that must be followed: implementing correct init and dealloc methods is non-trivial.   @synthesized getters and setters for properties with retain should not be called in these methods.  So many conventions and rules to remember!&lt;/p&gt;&lt;p&gt;Though I understand why there’s a separation of alloc and init, it’s still overly wordy to specify &lt;code&gt;[[aloc Foo] initWithArg: arg]&lt;/code&gt;.  Why not just &lt;code&gt;[new Foo arg]&lt;/code&gt;?  Or how about &lt;code&gt;new Foo(arg)&lt;/code&gt; -- oh, wait, that’s just like Java!&lt;/p&gt;&lt;p&gt;Objective-C’s imports and forward-declarations (@class) are a pain.  Though these issues exist with Java development, Eclipse’s JDT is so good that I’ve almost forgotten what it’s like to write an import.  All you have to do is Ctrl+Space to auto-complete a class name or Ctrl+Shift+O to organize imports and voila!&lt;/p&gt;&lt;p&gt;Of course Java is not perfect either, however this fact is hidden from me due to the fact that I’ve been living in Java for a very long time.  Sometimes I wish that Java were more &lt;a href="http://en.wikipedia.org/wiki/Groovy_(programming_language)"&gt;Groovy&lt;/a&gt;-like, however I’m used to it and the tooling is so good.&lt;/p&gt;&lt;h5 id="Platform"&gt;Platform&lt;/h5&gt;&lt;p&gt;On Android I found that I could readily use the Java runtime classes.  Some, but not all, of the standard Java RT classes are available on Android.  I didn’t find this a problem, since most of the standard Java IO, network and regex libraries are available.  Android RT classes appear to be based on &lt;a href="http://harmony.apache.org/"&gt;Harmony&lt;/a&gt;, which has been around long enough to be stable. &lt;/p&gt;&lt;p&gt;With iPhone on the other hand, finding the functionality that I needed was painful.  Classes and methods are poorly organized.  When to look for a static method versus a class with members was not clear to me.  Also depending on the framework used, naming conventions and code organization would differ.  I suppose this is the legacy of an older platform.  Areas where functionality was lacking that I found painful were regular expressions, string handling and XML parsing.  I ended up using the excellent &lt;a href="http://regexkit.sourceforge.net/"&gt;Regex Kit Lite&lt;/a&gt; for regular expressions. For XML parsing I implemented a parser abstraction over &lt;a href="http://xmlsoft.org/"&gt;libxml&lt;/a&gt;, only to discover later that I may have had an easier time with &lt;code&gt;NSXMLParser&lt;/code&gt; which is a lot more like SAX. &lt;/p&gt;&lt;p&gt;On the iPhone when things didn’t work as expected I had to resort to Google and hope that others had encountered the same problem.  This technique was hampered by Apple’s earlier NDA policy, which meant that iPhone content is pretty thin on the net.  In some cases I would resort to guesswork and experimentation to find a solution.  &lt;/p&gt;&lt;p&gt;Android has the benefit of being open source.  Within minutes I had the full Android platform source code on my system, and had re-built the SDK from sources to ensure that the source I had matched the runtime classes in the emulator.  So not only could I see how things were implemented in the Android platform and learn by example, I could step through the platform code in the emulator and discover why my code wasn’t producing the desired results.   &lt;/p&gt;&lt;p&gt;In general I found the layout, organization, and naming conventions of Android platform classes was consistent and predictable.  This made it much easier to learn.&lt;/p&gt;&lt;h5 id="ProgrammingModel"&gt;Programming Model&lt;/h5&gt;&lt;p&gt;The iPhone platform does a great job of encouraging an &lt;a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;MVC&lt;/a&gt; design pattern.  With this design pattern built in to the platform, building the UI was simple and I didn’t have to figure out how to organize the UI component design myself.  It also means that when looking at sample code, it’s all organized in the same way.&lt;/p&gt;&lt;p&gt;Android also does a good job with design patterns, though their concepts varied significantly from the iPhone.  With Android’s support for multiple processes and component reuse, the platform itself provides support for Intents and Activities (an Intent is just a variant of a &lt;a href="http://en.wikipedia.org/wiki/Command_pattern"&gt;command&lt;/a&gt;).  The design results in a better user experience, however it does introduce some complexity for the developer:  when starting one Activity from another, an Intent is used to communicate any parameters.  These parameters cannot be passed by reference — only by value.  Where on the iPhone it’s simple to have screens sharing the same data structures, on Android this requires some forethought.  Apparently Android applications can manage the back button and have everything occur inside a single Activity, however this is not the norm. &lt;/p&gt;&lt;p&gt;Both Android and iPhone provide a way of declaring user preferences in XML.  Both platforms provide a default UI for editing those preferences, which is great.  Android’s XML format is extensible allowing custom UI components to be integrated, which makes user preferences a breeze.  iPhone developers that wish to customize preferences will have to implement a UI from scratch, which is a lot more work.&lt;/p&gt;&lt;h5 id="TestingandContinuousIntegration"&gt;Testing and Continuous Integration&lt;/h5&gt;&lt;p&gt;I’m of the opinion that every development effort should include unit tests.  Teams of size greater than one should also include &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;Continuous Integration&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Android developers will be happy to know that they can write JUnit tests.  I could even launch these from the Eclipse UI after some classpath fiddling.  Though I didn’t try it, I assume that it’s trivial to run these from Ant and your favorite CI server such as &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I did see some iPhone unit test documentation with the iPhone SDK but didn’t take the time to explore it — so I can’t comment there.  &lt;/p&gt;&lt;h4 id="Resources"&gt;Resources&lt;/h4&gt;&lt;p&gt;Apple does an excellent job of providing lots of resources for developers.  Important concepts are explained in videos, which makes grasping concepts easy — however I did find that videos progressed slowly and I was watching for what seemed like hours to find information that should have taken minutes.  Luckily Apple also provides lots of sample applications and code to demonstrate API usage.&lt;/p&gt;&lt;p&gt;Android developers also have access to loads of resources.  The guide and API reference are installed with the SDK, so everything is available when offline (which for me is important since I do a lot of my work in transit).  I found the Android development resources better organized and spent less time looking and more time finding.  In particular the ApiDemos sample app provides a great starting point.  I also downloaded many open source Android projects for ideas on architecture and API usage.  This is an area where Android has the advantage, with Apple’s previous NDA policy there isn’t much out there in terms of open source for iPhone.&lt;/p&gt;&lt;h4 id="Tooling"&gt;Tooling&lt;/h4&gt;&lt;p&gt;For me tooling was a real shocker.  These are the categories of tooling that I’ll cover: IDE, UI builder, debugger, profiler.  Almost everything else is related to provisioning, and in that area I didn’t notice much in the way of differences between Android and iPhone.&lt;/p&gt;&lt;h5 id="IDE"&gt;IDE  &lt;/h5&gt;&lt;p&gt;Android development leverages the excellent &lt;a href="http://www.eclipse.org/jdt"&gt;JDT&lt;/a&gt; tools, which are pretty much stock and standard with every Eclipse installation.  I’ve used these tools now for many years and they’re excellent.  Everything Java is indexed, the IDE has a rich model of the source code, and refactoring is so seamless that it has changed the way that I work.  &lt;/p&gt;&lt;p&gt;Perhaps the best feature of JDT is its &lt;a href="http://en.wikipedia.org/wiki/Incremental_compiler"&gt;incremental compiler&lt;/a&gt;, which provides immediate feedback with errors and warnings as you type.  This eliminates the code-compile-wait-for-feedback cycle that was so common in the '80s and '90s.  Errors and warnings are updated in the Java editor as I type, giving me instant feedback.  I didn’t realize just how valuable this feature is until I was coding Objective-C in XCode — when I became acutely aware at how waiting for compiler feedback can break the flow of programming.   &lt;/p&gt;&lt;p&gt;Other key features that make Eclipse so amazing to work with are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;content assist&lt;/li&gt;&lt;li&gt;quick-fixes&lt;/li&gt;&lt;li&gt;organize imports&lt;/li&gt;&lt;li&gt;open type (CTRL+Shift+T)&lt;/li&gt;&lt;li&gt;refactorings&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Integrated javadoc and content assist is quite possibly the best way to learn an unfamiliar API.  In Ecipse not only are all classes and methods immediately available in the context in which you’re writing code, their documentation is presented alongside.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/Sk2e-j6p2NI/AAAAAAAAAHU/KzoZ58wkpVY/s1600-h/content-assist-javadoc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 58px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/Sk2e-j6p2NI/AAAAAAAAAHU/KzoZ58wkpVY/s400/content-assist-javadoc.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5354110329643718866" /&gt;&lt;/a&gt;&lt;p&gt;&lt;b&gt;Content Assist with Integrated Javadoc&lt;/b&gt;&lt;/p&gt;&lt;p&gt;XCode is so shockingly bad that I almost don’t know where to start.  Here’s a &lt;em&gt;minimum&lt;/em&gt; list of things that I think need fixing in order for XCode to become a viable IDE:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Content assist that actually works.  Content assist provided by XCode is often wrong, and almost always suggests a small subset of what’s actually available.&lt;/li&gt;&lt;li&gt;A decent window/editor management system.  XCode and it’s associated tools (debugger) like to open lots of windows.  Want to open a file?  How about a new window for you!  Very quickly I found myself in open-window-hell.  The operating system’s window management is designed for managing multiple applications, not multiple editors within an IDE.  It’s simply not capable of providing management of editors in an environment as sophisticated as an IDE.&lt;/li&gt;&lt;li&gt;A project tree view that sorts files alphabetically.  Really!&lt;/li&gt;&lt;li&gt;Integrated API documentation.  I found that I was constantly switching out of the IDE and searching for API documentation using &lt;a href="http://homepage.mac.com/aglee/downloads/"&gt;Appkido&lt;/a&gt;.   This may seem trivial, but it really breaks the flow.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;One area of Eclipse that simply can’t be matched is &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt;.  Integrated task management and a focused interface introduce huge efficiencies into any project, small or large.  If you haven’t yet tried out Mylyn, it’s definitely worth your time to take a look.  A good place to start is Mylyn’s &lt;a href="http://www.eclipse.org/mylyn/start/"&gt;Getting Started&lt;/a&gt; page.&lt;/p&gt;&lt;h5 id="UIBuilder"&gt;UI Builder&lt;/h5&gt;&lt;p&gt;iPhone app developers are given a pretty good UI builder.  It does a great job of showing the UI as it will actually appear.  It’s flexible and can model some pretty sophisticated UIs, so I was impressed.   I found that using it was a little tricky — I had to read the documentation two or three times before I could really figure out how to use it properly.&lt;/p&gt;&lt;p&gt;The Android UI builder I found pretty useless: it can’t display UIs how they’ll actually appear, and it’s UI is way too inefficient.  I found that I coded all of the UIs directly in the XML source view of the UI builder.  There the content assist and validation were pretty good, making it the easiest way for me to build a UI.&lt;/p&gt;&lt;h5 id="Debugger"&gt;Debugger&lt;/h5&gt;&lt;p&gt;Having used to the Java debugger in Eclipse I was shocked at the state of the debugger in XCode.  With Eclipse I can see and modify variable values.  Not so in XCode.  Maybe this is simply the state of affairs when debugging native code, but it sure affects the usefulness of the debugger.  XCode often seemed confused as to the type of an object and presented me with a pointer value and no detail.  This is a sharp contrast to Eclipse, where I can drill down through an object graph with ease.&lt;/p&gt;&lt;p&gt;I found the XCode debugger UI extremely difficult to use.  Clicking on the stack to show code in an editor caused new windows to open, eventually resulting in dozens of windows open.  In addition I found that watch expressions rarely worked for me. &lt;/p&gt;&lt;h5 id="ProfilerandHeapAnalysis"&gt;Profiler and Heap Analysis&lt;/h5&gt;&lt;p&gt;An area where Apple development tools excel is in profiling and heap analysis.  These tools seemed mature and easy to use.  With no prior experience with these specific tools I was able to gain a better understanding of my app within minutes, find and fix several memory leaks and improve performance.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/Sk2fRWSUpAI/AAAAAAAAAHc/CEptOHGaX48/s1600-h/memory-analysis.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 225px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/Sk2fRWSUpAI/AAAAAAAAAHc/CEptOHGaX48/s400/memory-analysis.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5354110652402410498" /&gt;&lt;/a&gt;&lt;p&gt;&lt;b&gt;XCode Memory Leak Detection&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Android developers must use Android’s traceview application, which I found worked well but required significantly more effort to configure and operate.  I was surprised to find that the source code must be changed in order to get the trace files required for analysis.&lt;/p&gt;&lt;p&gt;I’m not sure if Android can provide heap dumps in hprof format.  If it can then the awesome &lt;a href="http://www.eclipse.org/mat/"&gt;MAT&lt;/a&gt; tool could be used to analyze heap usage.  According to &lt;a href="http://discuz-android.blogspot.com/2009/06/android-memory-usage-with-hprof.html"&gt;this article&lt;/a&gt; Android &lt;em&gt;can&lt;/em&gt; produce hprof heap data, though I haven’t tried it.&lt;/p&gt;&lt;h4 id="AppStore"&gt;App Store&lt;/h4&gt;&lt;p&gt;It goes without saying that the iPhone app store is excellent in that you can sell into many countries worldwide with a single setup.  I was able to provide my Canadian bank account number, sign a few legal agreements and I was up and running.  &lt;/p&gt;&lt;p&gt;Getting an app into the store however is frustrating to say the least.  Apple must approve every app before it is accepted into the store.  Mine got rejected multiple times.  Each time it was rejected I was given &lt;em&gt;almost&lt;/em&gt; no information about why.  When I emailed them to clarify the problem, I received what looked like a canned response indicating that I should refer to previous correspondence.  If it weren’t so frustrating I would have found it funny.  I highly recommend reading Brian Stormont’s &lt;a href="http://www.mobileorchard.com/avoiding-iphone-app-rejection-from-apple/"&gt;Avoiding iPhone App Rejection from Apple&lt;/a&gt; and Dan Grigsby’s &lt;a href="http://www.mobileorchard.com/avoiding-iphone-app-rejection-part-2/"&gt;Part 2&lt;/a&gt; follow-up. &lt;/p&gt;&lt;p&gt;Of course once I started selling Hudson Helper I realized that Apple won’t send me any money unless the payout is greater than $250.  This is true not only of the first payout, but every payout.  Google market on the other hand requires a minimum of $1 for each payout.  Both the iPhone app store and Google market take about %30 of your app selling price.  $0.99 applications have to have high volume, or they’re simply not worth your time. &lt;/p&gt;&lt;p&gt;The Google market by comparison to the Apple app store is terrible in that you can only sell into a &lt;a href="http://market.android.com/support/bin/answer.py?hl=en&amp;amp;answer=138294"&gt;handful of countries&lt;/a&gt;.  You also can’t see or install apps that cost money on a developer phone.  Actually you can, but not if the app has copy protection — which is almost every non-free app.  On the other hand when you upload your app to the app store it’s available within minutes, so you don’t have to worry about an approval process.&lt;/p&gt;&lt;p&gt;To set up a merchant account with Google market, I had to provide a US address and bank account number, since Google doesn’t support Canada.  For me this was a pain, but not too bad since I live within a few kilometers of the US border.  I rode my bike down to the US and opened an account with Horizon bank.  The bank required a passport and driver’s license, so no problem there.  Why Google doesn’t support more countries I don’t know.  At the very least Google market should accept alternate payment methods for countries that are not supported by Google checkout.&lt;/p&gt;&lt;h4 id="Summary"&gt;Summary&lt;/h4&gt;&lt;p&gt;Android’s platform and developer tools are excellent.  Leveraging Java and the Eclipse IDE are major winning factors for Android.  Apple’s developer tools are shockingly bad by comparison.  The Objective-C language and platform APIs are cumbersome and poorly organized.  Overall when developing for the iPhone I felt like I was back in 1993.  These factors combined in my estimation make application development about three times more expensive when developing for iPhone.  The only area where Apple’s developer tools excelled was in profiling and heap analysis.&lt;/p&gt;&lt;p&gt;Apple’s app store from a user’s standpoint and from a worldwide coverage standpoint are excellent.  In this area Google market for Android is weak.&lt;/p&gt;&lt;p&gt;Development for iPhone may improve as tools such as &lt;a href="http://code.google.com/p/iphonical/"&gt;iphonical&lt;/a&gt; (MDD for iPhone) and &lt;a href="http://code.google.com/p/objectiveclipse/"&gt;objectiveclipse&lt;/a&gt; (Eclipse plug-in for Objective-C) emerge.&lt;/p&gt;&lt;p&gt;We may see a shake-up in the mobile market, with at least 18 new Android handsets being released this year.  Until that happens, iPhone will remain a market leader and developers will have to put up with XCode and Objective-C.&lt;/p&gt;&lt;p&gt;For me, my love is with Android.  Sure, the iPhone is great — but can you install a new Linux kernel?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5669151997714898459?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5669151997714898459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5669151997714898459' title='95 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5669151997714898459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5669151997714898459'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/07/android-versus-iphone-development.html' title='Android versus iPhone Development: A Comparison'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/Sk2e-j6p2NI/AAAAAAAAAHU/KzoZ58wkpVY/s72-c/content-assist-javadoc.png' height='72' width='72'/><thr:total>95</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5545038659639005688</id><published>2009-06-29T08:00:00.000-07:00</published><updated>2009-06-29T09:13:43.699-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper for Android</title><content type='html'>&lt;p&gt;Recently I &lt;a href="http://greensopinion.blogspot.com/2009/05/hudson-helper-hudson-on-your-iphone.html"&gt;launched Hudson Helper&lt;/a&gt; for iPhone and iPod Touch, enabling &lt;a href="http://en.wikipedia.org/wiki/Continuous_Integration"&gt;Continuous Integration&lt;/a&gt; fans to stay in touch with their projects.  Android users can get in on the game now too, with Hudson Helper for Android.&lt;/p&gt;&lt;table width="100%" border="0"&gt;&lt;tr&gt;&lt;td align="center"&gt;&lt;img  src="http://lh4.ggpht.com/_vw9l2nnub6c/SkRLRmlN2FI/AAAAAAAAAHQ/Dtv1IMkFURc/s800/hh-blog1.png" border="0" alt="Hudson Helper for Android"  /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Hudson Helper for Android provides all of the same features as the iPhone version including support for multiple servers and authentication.  New for this version are build controls: start and stop builds right from your phone.  CI can be even more fun with shake-to-build and sound effects.&lt;/p&gt;&lt;p&gt;To get Hudson Helper for Android, search for 'Hudson Helper' in the Google Market on your Android device.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5545038659639005688?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5545038659639005688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5545038659639005688' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5545038659639005688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5545038659639005688'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/06/hudson-helper-for-android.html' title='Hudson Helper for Android'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vw9l2nnub6c/SkRLRmlN2FI/AAAAAAAAAHQ/Dtv1IMkFURc/s72-c/hh-blog1.png' height='72' width='72'/><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5411327153330086719</id><published>2009-06-22T09:23:00.000-07:00</published><updated>2009-06-22T09:31:44.089-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter'/><title type='text'>Tip: Twittering About Eclipse Bugs</title><content type='html'>&lt;p&gt;A quick tip for including an Eclipse bug URL in your 140-character tweets: Bugzilla for Eclipse supports &lt;a href="http://wiki.eclipse.org/Bug_Reporting_FAQ#How_do_I_find_a_particular_bug_number.3F"&gt;short links&lt;/a&gt;.   Instead of using the long-form URL or impossible-to-decipher &lt;a href="http://en.wikipedia.org/wiki/URL_shortening"&gt;URL shorteners&lt;/a&gt;, take out the '&lt;tt&gt;bugs/show_bug.cgi?id=&lt;/tt&gt;' part of the Bugzilla URL as follows: &lt;b&gt;&lt;tt&gt;https://bugs.eclipse.org/277974&lt;/tt&gt;&lt;/b&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5411327153330086719?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5411327153330086719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5411327153330086719' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5411327153330086719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5411327153330086719'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/06/tip-twittering-about-eclipse-bugs.html' title='Tip: Twittering About Eclipse Bugs'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2696676696519371596</id><published>2009-06-21T14:47:00.000-07:00</published><updated>2009-06-21T14:57:34.706-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><category scheme='http://www.blogger.com/atom/ns#' term='TLS'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper and Untrusted Certificates on the iPhone</title><content type='html'>&lt;p&gt;If you're using a self-signed certificate or a certificate signed by your own certificate authority with the &lt;a href="https://hudson.dev.java.net"&gt;Hudson Continuous Integration Server&lt;/a&gt; then you may be wondering how to get your iPhone to trust your certificate.  Well, you're in luck because Benjamin Boksa has written an excellent tutorial explaining how to do just that: &lt;a href="http://blog.boksa.de/2009/06/18/iphone-os-30-root-certificate-authority-installation/"&gt;iPhone OS 3.0 – Root Certificate Authority installation&lt;/a&gt;.  After following the tutorial you should be able to use &lt;a href="http://greensopinion.blogspot.com/2009/05/hudson-helper-hudson-on-your-iphone.html"&gt;Hudson Helper&lt;/a&gt; with untrusted certs and your secure Hudson server.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2696676696519371596?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2696676696519371596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2696676696519371596' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2696676696519371596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2696676696519371596'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/06/hudson-helper-and-untrusted.html' title='Hudson Helper and Untrusted Certificates on the iPhone'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1535102088184325459</id><published>2009-06-21T14:34:00.000-07:00</published><updated>2009-06-21T14:47:17.774-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>WikiText in Eclipse Galileo</title><content type='html'>&lt;p&gt;Ian Bull of EclipseSource has provided an &lt;a href="http://eclipsesource.com/blogs/2009/06/21/eclipse-galileo-feature-top-10-list-number-4/"&gt;excellent summary of Mylyn WikiText&lt;/a&gt; and what it means for Mylyn users and Galileo.  WikiText is number 4 on Ian's &lt;a href="http://eclipsesource.com/blogs/"&gt;Eclipse Galileo Feature Top 10 List&lt;/a&gt;.  &lt;a href="http://www.eclipse.org/mylyn/"&gt;Mylyn WikiText&lt;/a&gt; is part of the highly anticipated Eclipse Galileo release due out this Wednesday.  You can find out more about WikiText from &lt;a href="http://www.eclipse.org/mylyn"&gt;the Mylyn homepage&lt;/a&gt;, the &lt;a href="http://wiki.eclipse.org/index.php/Mylyn/FAQ#WikiText"&gt;FAQ&lt;/a&gt;, and the &lt;a href="http://www.eclipse.org/mylyn/downloads/"&gt;downloads page&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1535102088184325459?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1535102088184325459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1535102088184325459' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1535102088184325459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1535102088184325459'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/06/wikitext-in-eclipse-galileo.html' title='WikiText in Eclipse Galileo'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1355542954604200507</id><published>2009-06-17T08:28:00.001-07:00</published><updated>2009-06-17T13:22:17.113-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Galileo Builds On Your iPhone</title><content type='html'>&lt;p&gt;With the Eclipse &lt;a href="http://wiki.eclipse.org/Galileo"&gt;Galileo release&lt;/a&gt; exactly a week away, I'm presenting a quick tutorial on how you can monitor the &lt;a href="https://build.eclipse.org/hudson/view/Galileo/"&gt;Galileo hudson build&lt;/a&gt; on your iPhone with Hudson Helper.  I'm also giving Hudson Helper away for free to the first 20 Eclipse contributors&lt;sup&gt;*&lt;/sup&gt; to &lt;a href="mailto:dgreen99@gmail.com"&gt;send me an email&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SjkNfsjmpEI/AAAAAAAAAGI/7cQ--rfu1UM/s1600-h/IMG_0007.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SjkNfsjmpEI/AAAAAAAAAGI/7cQ--rfu1UM/s400/IMG_0007.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5348320870666773570" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Here are the steps to take to get Hudson Helper set up with the Galileo build:&lt;/p&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;If you haven't done so already, install Hudson Helper.  You can get it by searching for 'Hudson Helper' in the app store, or &lt;a href="itms://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=311514567&amp;amp;mt=8&amp;amp;s=143441"&gt;via iTunes here&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Next, go to the Settings application on your iPhone or iPod touch.  Under Hudson Helper, Server 1 URL enter the following: &lt;tt&gt;https://build.eclipse.org/hudson/view/Galileo&lt;/tt&gt;&lt;br/&gt;Notice that it's https, not http.&lt;br/&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SjkSfHsgoBI/AAAAAAAAAGg/yUvdLuEh8JI/s1600-h/IMG_0010.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 400px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SjkSfHsgoBI/AAAAAAAAAGg/yUvdLuEh8JI/s400/IMG_0010.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5348326358330155026" /&gt;&lt;/a&gt;&lt;br/&gt;&lt;br /&gt;You may have noticed from the screenshot that a new version of Hudson Helper is now available with support for HTTP authentication.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;That's it!  Close the Settings app, launch Hudson Helper and you should see the following:&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SjkQ0_5i0tI/AAAAAAAAAGY/T83cdZM9kNw/s1600-h/IMG_0008.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SjkQ0_5i0tI/AAAAAAAAAGY/T83cdZM9kNw/s400/IMG_0008.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5348324535171207890" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Even though Hudson Helper has &lt;a href="http://divby0.blogspot.com/2009/05/no-more-blue-balls.html"&gt;blue balls&lt;/a&gt;, it can still help to keep you up to date with your builds!&lt;/p&gt;&lt;p&gt;&lt;sup&gt;*&lt;/sup&gt;If you're an Eclipse committer you qualify.  If you're not an Eclipse committer but have contributed at least 2 patches to Eclipse, you qualify.  If you were a committer you qualify.  If you aren't a committer, but are a &lt;a href="http://www.eclipse.org/donate/"&gt;Friend of Eclipse&lt;/a&gt; you qualify.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1355542954604200507?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1355542954604200507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1355542954604200507' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1355542954604200507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1355542954604200507'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/06/galileo-builds-on-your-iphone.html' title='Galileo Builds On Your iPhone'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SjkNfsjmpEI/AAAAAAAAAGI/7cQ--rfu1UM/s72-c/IMG_0007.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5785201672961229116</id><published>2009-05-28T13:00:00.000-07:00</published><updated>2009-05-28T13:45:38.997-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='Continuous Integration'/><title type='text'>Hudson Helper: Hudson On Your iPhone</title><content type='html'>&lt;p&gt;I'm a big fan of &lt;a href="http://en.wikipedia.org/wiki/Continuous_Integration"&gt;Continuous Integration&lt;/a&gt; and automated builds.  So much so that today I released &lt;a href="itms://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=311514567&amp;amp;mt=8&amp;amp;s=143441"&gt;Hudson Helper&lt;/a&gt;, a native iPhone and iPod Touch application for monitoring your &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt; continuous integration engine.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/Sdp2DarOo5I/AAAAAAAAAFA/EYhfDdcMwPo/s1600-h/screenshot-main.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 278px; height: 400px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/Sdp2DarOo5I/AAAAAAAAAFA/EYhfDdcMwPo/s400/screenshot-main.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5321695710763983762" /&gt;&lt;/a&gt;&lt;p&gt;Hudson Helper has a simple interface that gives you what you need to know first.  Offline data caching helps to minimize network bandwidth and makes it work when the network is unavailable.  Includes job status, health and build trends.&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/Sdp77QH9ceI/AAAAAAAAAFI/OXlKMOIASwI/s1600-h/hh-multiple.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 281px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/Sdp77QH9ceI/AAAAAAAAAFI/OXlKMOIASwI/s400/hh-multiple.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5321702167562514914" /&gt;&lt;/a&gt;&lt;p&gt;Build status display adapts to your project to report on build health including JUnit test results, Clover coverage, findbugs and any other Hudson build report plug-ins your project is running.&lt;/p&gt;&lt;p&gt;To get started all you need to do is point Hudson Helper at the URL of your build server.  You can get Hudson Helper by searching for 'Hudson Helper' in the app store, or &lt;a href="itms://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=311514567&amp;amp;mt=8&amp;amp;s=143441"&gt;click here to get it with iTunes&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5785201672961229116?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5785201672961229116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5785201672961229116' title='48 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5785201672961229116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5785201672961229116'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/05/hudson-helper-hudson-on-your-iphone.html' title='Hudson Helper: Hudson On Your iPhone'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/Sdp2DarOo5I/AAAAAAAAAFA/EYhfDdcMwPo/s72-c/screenshot-main.png' height='72' width='72'/><thr:total>48</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-242174787200674003</id><published>2009-05-08T15:49:00.000-07:00</published><updated>2010-01-19T08:54:17.116-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SoyLatte'/><category scheme='http://www.blogger.com/atom/ns#' term='JDK7'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 7'/><title type='text'>Eclipse 3.5 (Galileo) on Java 7</title><content type='html'>&lt;p&gt;Some time ago I blogged about running &lt;a href="http://greensopinion.blogspot.com/2008/08/eclipse-ganymede-on-soylatte.html"&gt;Eclipse (Ganymede) on SoyLatte&lt;/a&gt;.  Since then there has been little improvement in the state of Java on the Mac.  Apple has released a Java 6 for Mac, but it's a 64-bit only VM which &lt;a href="https://bugs.eclipse.org/214092"&gt;crashes regularly&lt;/a&gt; when running Eclipse.  Having heard about recent improvements in performance for 64-bit VMs using &lt;a href="http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.devel/875"&gt;pointer compression&lt;/a&gt; (&lt;a href="http://users.elis.ugent.be/%7Eleeckhou/papers/ecoop07.pdf"&gt;PDF paper&lt;/a&gt;), I decided to have another look at launching Eclipse with an OpenJDK-based VM such as JDK7.  After a few false starts I managed to get it working.  Here's what I did:&lt;/p&gt;&lt;p&gt;First, to get JDK7 on my machine.  I followed &lt;a href="http://infernus.org/2009/02/building-java-7-on-mac-os-x/"&gt;these instructions&lt;/a&gt; to build JDK 7 from source.  The instructions didn't work perfectly for me -- but they were pretty good.  Basically followed everything verbatim with the exception that I had to install MacPorts and use it to get wget.&lt;/p&gt;&lt;p&gt;With a 32-bit JDK7 on my machine, now to use it to start Eclipse.  I downloaded the latest I-build of 32-bit Eclipse 3.5 &lt;a href="http://download.eclipse.org/eclipse/downloads/"&gt;from here&lt;/a&gt;  (Look for '3.5 Stream Integration Builds').&lt;/p&gt;&lt;p&gt;Using a combination of jconsole and guesswork I came up with this shell script to start Eclipse:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;JDKPATH=/Users/dgreen/Documents/packages/jdk7-32bit-2009-05-08&lt;br /&gt;DEBUG_OPTS=&lt;br /&gt;# DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y&lt;br /&gt;MEM_OPTS=-Xms1024m\ -Xmx1536m\ -XX:MaxPermSize=256m&lt;br /&gt;ECLIPSE_LOC=/Users/dgreen/Downloads/eclipse&lt;br /&gt;WORKSPACE=/Users/dgreen/Documents/workspace&lt;br /&gt;&lt;br /&gt;$JDKPATH/bin/java $DEBUG_OPTS \&lt;br /&gt;-cp $ECLIPSE_LOC/plugins/org.eclipse.equinox.launcher_1.0.200.v20090429-1630.jar \&lt;br /&gt;-XstartOnFirstThread $MEM_OPTS \&lt;br /&gt;-Dorg.eclipse.swt.internal.carbon.smallFonts \&lt;br /&gt;-Djava.library.path=$ECLIPSE_LOC/Eclipse.app/Contents/MacOS/lib \&lt;br /&gt;-Dswt.library.path=$ECLIPSE_LOC/Eclipse.app/Contents/MacOS/lib \&lt;br /&gt;org.eclipse.equinox.launcher.Main \&lt;br /&gt;-os macosx -ws cocoa -arch x86 \&lt;br /&gt;-showsplash -launcher $ECLIPSE_LOC/Eclipse.app/Contents/MacOS/eclipse \&lt;br /&gt;-name Eclipse \&lt;br /&gt;--launcher.library $ECLIPSE_LOC/plugins/org.eclipse.equinox.launcher.cocoa.macosx_1.0.0.v20090429-1630  \&lt;br /&gt;-startup $ECLIPSE_LOC/plugins/org.eclipse.equinox.launcher_1.0.200.v20090429-1630.jar \&lt;br /&gt;-data $WORKSPACE \&lt;br /&gt;-keyring /Users/dgreen/.eclipse_keyring -consoleLog -showlocation \&lt;br /&gt;-vm $JDKPATH&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that all of the above should appear on one line: your browser may split it into multiple lines.&lt;/p&gt;&lt;p&gt;After starting Eclipse as above I saw the following stack trace on startup:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;java.lang.UnsatisfiedLinkError: no swt-pi-cocoa-3547 or swt-pi-cocoa in swt.library.path, java.library.path or the jar file&lt;br /&gt; at org.eclipse.swt.internal.Library.loadLibrary(Library.java:248)&lt;br /&gt; at org.eclipse.swt.internal.Library.loadLibrary(Library.java:159)&lt;br /&gt;       ... snip ...&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;To solve the problem I extracted all of the &lt;tt&gt;*.jnilib&lt;/tt&gt; files from &lt;tt&gt;org.eclipse.equinox.launcher_*.jar&lt;/tt&gt; and put them in a &lt;tt&gt;lib&lt;/tt&gt; folder.  I then renamed all of those &lt;tt&gt;*.jnilib&lt;/tt&gt; files in the &lt;tt&gt;lib&lt;/tt&gt; folder so that they had &lt;tt&gt;*.dylib&lt;/tt&gt; file extensions.  I ended up with a file structure as follows:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;david-greens-macbook-pro:MacOS dgreen$ pwd&lt;br /&gt;/Users/dgreen/Downloads/eclipse/Eclipse.app/Contents/MacOS    &lt;--- where I put the files&lt;br /&gt;david-greens-macbook-pro:MacOS dgreen$ ls -l *.sh lib&lt;br /&gt;-rwxr-xr-x@ 1 dgreen  staff  1100  8 May 15:47 eclipse.sh    &lt;--- the startup script&lt;br /&gt;&lt;br /&gt;lib:        &lt;---- the lib folder for shared libraries&lt;br /&gt;total 2296&lt;br /&gt;-rw-r--r--  1 dgreen  staff   37104  8 May 14:20 libswt-awt-cocoa-3547.dylib&lt;br /&gt;-rw-r--r--  1 dgreen  staff  287116  8 May 14:20 libswt-cocoa-3547.dylib&lt;br /&gt;-rw-r--r--  1 dgreen  staff  539240  8 May 14:20 libswt-pi-cocoa-3547.dylib&lt;br /&gt;-rw-r--r--  1 dgreen  staff  300428  8 May 14:20 libswt-xulrunner-cocoa-3547.dylib&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Trying the shell script again, Eclipse started up and ran beautifully!  Once again I'm running Eclipse with a newer-than-Apple-will-give-me VM, this time JDK 7!!  My next step is to compile a 64-bit JDK7 and see if it works too...  I have high hopes.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;em style="color:red;"&gt;Updated 2009-05-15&lt;/em&gt; see &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=276564"&gt;this issue (bug 276564)&lt;/a&gt; for the underlying cause of failure&lt;/li&gt;&lt;li&gt;&lt;em style="color:red;"&gt;Updated 2010-01-19&lt;/em&gt; see &lt;a href="http://greensopinion.blogspot.com/2010/01/eclipse-36-on-openjdk-on-mac-os-x.html"&gt;Eclipse 3.6 on OpenJDK on Mac OS X&lt;/a&gt; for an update&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-242174787200674003?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/242174787200674003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=242174787200674003' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/242174787200674003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/242174787200674003'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/05/eclipse-35-galileo-on-jdk-7.html' title='Eclipse 3.5 (Galileo) on Java 7'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6649441147265442296</id><published>2009-05-05T22:30:00.000-07:00</published><updated>2009-05-05T22:47:04.283-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>How Babel Saved My Bacon</title><content type='html'>&lt;p&gt;Recently I blogged about &lt;a href="http://greensopinion.blogspot.com/2009/04/galileo-icu4j-requirement-is-painful.html"&gt;the pain of moving to ICU4J&lt;/a&gt;, lamenting the fact that using NLS instead of ResourceBundle would require me to change the keys in my resource bundles.  After much consideration and &lt;a href="https://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5052361975001061519"&gt;the advice of knowledgeable people&lt;/a&gt;, I decided that moving to NLS was the way to go.  After changing resource bundle keys across the board, I expected that translations to other languages would need their corresponding keys changed as well.  Not so.  Apparently &lt;a href="http://babel.eclipse.org/babel/"&gt;Babel&lt;/a&gt; can magically handle such refactorings, with the help of a mystery user named 'Babel Syncup'.  I wonder what else it can do.&lt;/p&gt;&lt;p&gt;The &lt;a href="http://babel.eclipse.org/babel/"&gt;Eclipse Babel&lt;/a&gt; project is truly impressive.  Kudos to the &lt;a href="http://www.eclipse.org/babel/development/"&gt;Babel team&lt;/a&gt; for thinking ahead.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6649441147265442296?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6649441147265442296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6649441147265442296' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6649441147265442296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6649441147265442296'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/05/how-babel-saved-my-bacon.html' title='How Babel Saved My Bacon'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4061902831485536856</id><published>2009-05-05T22:12:00.000-07:00</published><updated>2009-05-05T22:20:20.791-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='DocBook'/><title type='text'>Peter Friese on Advanced WikiText</title><content type='html'>&lt;p&gt;Peter Friese has upped the stakes in his latest article on &lt;a href="http://www.peterfriese.de/advanced-wikitext/"&gt;Advanced WikiText&lt;/a&gt;.  Friese tackles tough topics, such as how to optimize team collaboration with multiple source files and integrating with a DocBook toolchain.  Great article, a must-read for those who are authoring documentation.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4061902831485536856?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4061902831485536856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4061902831485536856' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4061902831485536856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4061902831485536856'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/05/peter-friese-on-advanced-wikitext.html' title='Peter Friese on Advanced WikiText'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-675847401360079227</id><published>2009-04-23T20:22:00.000-07:00</published><updated>2009-04-23T20:29:06.661-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Textile'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='markup languges'/><category scheme='http://www.blogger.com/atom/ns#' term='DocBook'/><title type='text'>Peter Friese on Writing Documentation with WikiText</title><content type='html'>&lt;p&gt;Peter Friese has published a great article titled &lt;a href="http://www.peterfriese.de/getting-started-with-wikitext/"&gt;Getting Started With WikiText&lt;/a&gt;.  He starts off with some background on various documentation formats, then provides a step-by-step guide to getting started with &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt;.  Peter also shows how images can be scaled using some simple wiki markup using a new WikiText feature that Peter Friese &lt;a href="https://bugs.eclipse.org/273355"&gt;recently contributed&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-675847401360079227?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/675847401360079227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=675847401360079227' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/675847401360079227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/675847401360079227'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/04/peter-friese-on-writing-documentation.html' title='Peter Friese on Writing Documentation with WikiText'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5052361975001061519</id><published>2009-04-14T08:33:00.000-07:00</published><updated>2009-04-14T08:40:06.205-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>Galileo ICU4J Requirement Is Painful</title><content type='html'>&lt;p&gt;Projects &lt;a href="http://wiki.eclipse.org/Galileo_Simultaneous_Release#Requirements_For_Participation"&gt;must use ICU4J&lt;/a&gt; if they wish to participate in the &lt;a href="http://wiki.eclipse.org/Galileo"&gt;Galileo&lt;/a&gt; release of Eclipse, due this summer.  Though such a requirement may make good sense in theory, in practice adopting &lt;a href="http://wiki.eclipse.org/ICU4J"&gt;ICU4J&lt;/a&gt; is painful.  Not only does this policy stifle innovation for questionable gain, such a burden makes participating in the Eclipse ecosystem less attractive.   &lt;/p&gt;&lt;p&gt;ICU4J is designed to be a drop-in replacement for Java classes that perform poorly or behave incorrectly.  The idea is that you change your import statements, recompile and everything works.  In applying this technique to Mylyn WikiText, I’ve discovered that it’s not that easy.&lt;/p&gt;&lt;p&gt;Mylyn WikiText makes use of two classes that are to be replaced by ICU4J equivalents: MessageFormat and ResourceBundle.&lt;/p&gt;&lt;h4 id="ResourceBundle"&gt;ResourceBundle&lt;/h4&gt;&lt;p&gt;ResourceBundle conversion should be easy, right? ICU4J provides UResourceBundle, which extends ResourceBundle.... wow, that’s great.  All I should have to do is replace &lt;code&gt;ResourceBundle.getBundle()&lt;/code&gt; with &lt;code&gt;UResourceBundle.getBundleInstance()&lt;/code&gt;... easy peasy.  Unfortunately, not so easy.&lt;/p&gt;&lt;p&gt;Previously I had used the JDT ‘Externalize Strings’ wizard to externalize strings in my plug-in.  The JDT nicely created a utility class &lt;code&gt;Messages.java&lt;/code&gt; alongside the resource bundle &lt;code&gt;messages.properties&lt;/code&gt;.  This worked really well and has been in use for some time.  Unfortunately after converting to ICU4J UResourceBundle throws a NoClassDefFoundError because of the presence of the Messages class.  It seems that UResourceBundle cannot handle a resource bundle alongside a class by a similar name.  While I’ve logged &lt;a href="https://bugs.eclipse.org/272166"&gt;bug 272166&lt;/a&gt; to track this issue and have it fixed, it doesn’t help me.  Even if the bug gets fixed in time for Galileo, Mylyn WikiText must work on Eclipse 3.3 and 3.4 as well.&lt;/p&gt;&lt;p&gt;It gets worse.  Mylyn WikiText is already translated to German and Japanese.  Resource bundle message keys must remain relatively stable in order to avoid losing those translations.  JDT creates message keys containing dots ‘.’, and the Eclipse externalized string pattern cannot have dots in message keys... so I cannot convert over to the Eclipse externalized string pattern without losing these translations.&lt;/p&gt;&lt;h4 id="MessageFormat"&gt;MessageFormat  &lt;/h4&gt;&lt;p&gt;MessageFormat evolved for Java 5: it’s &lt;code&gt;format()&lt;/code&gt; method became a var-args method, meaning that you can pass a variable number of arguments to it.  WikiText makes extensive use of this method, so you’ll see code such as this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;MessageFormat.format(messageTemplate,arg1,arg2)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ICU4J does not have a var-args method equivalent — it seems that ICU4J has not evolved for Java 5.  To adopt ICU4J’s MessageFormat, all calls had to be converted as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;MessageFormat.format(messageTemplate, new Object[] { arg1, arg2 })&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While the conversion can be achieved using regular expressions, the resulting code is less readable and more prone to bugs.  Localization is hard enough as it is without having to jump through age-old languageisms.&lt;/p&gt;&lt;h4 id="Summary"&gt;Summary&lt;/h4&gt;&lt;p&gt;While converting all plug-ins to ICU4J may make sense for some, making it a requirement for Galileo participation has caused me and presumably others a lot of work.  From what I can see ICU4J brings little or no value to the users of my project.  This is one case where good intentions with policy have a substantial negative impact.  In my case, the time I’m spending converting to ICU4J could have been used to innovate or improve the quality of WikiText.  Instead I’m jumping through hoops with no apparent benefit for me, my project or my users.   &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5052361975001061519?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5052361975001061519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5052361975001061519' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5052361975001061519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5052361975001061519'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/04/galileo-icu4j-requirement-is-painful.html' title='Galileo ICU4J Requirement Is Painful'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2335721230258962248</id><published>2009-04-13T14:30:00.000-07:00</published><updated>2009-04-13T14:47:37.917-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='RichFaces'/><title type='text'>A Tough Lesson in AJAX, JSF and Client-Side State</title><content type='html'>&lt;p&gt;Last week I learned a tough lesson in AJAX, JSF and client-side component state. This one had me fooled for the better part of a day, in part due to the complexity of debugging multiple related AJAX requests, intermittent symptoms, and the opaque nature of JSF component state. Here's what happened:&lt;/p&gt;&lt;p&gt;The page that was problematic has multiple tabs (rich:tab), some combo boxes (h:selectOneMenu), and at least one suggest control (rich:suggestionbox). The tabs are server-side AJAX tabs. The suggest control also uses AJAX.&lt;/p&gt;&lt;p&gt;To reproduce the problem, the user takes the following steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Use a suggest control to select a value on the first tab of a&lt;br /&gt;multi-tab form &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/SeOS5k1DI1I/AAAAAAAAAFQ/UzzgRhlQMAE/s1600-h/Picture+2.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; cursor: hand; width: 212px; height: 235px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/SeOS5k1DI1I/AAAAAAAAAFQ/UzzgRhlQMAE/s400/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5324260702318437202" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Switch tabs to a different part of the form &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/SeOTdexL78I/AAAAAAAAAFY/r4kEqtbAc_Y/s1600-h/Picture+4.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; cursor: hand; width: 400px; height: 34px;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/SeOTdexL78I/AAAAAAAAAFY/r4kEqtbAc_Y/s400/Picture+4.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5324261319166914498" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Press a button on the form in the 2nd tab&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;At this point the application complains with validation errors on the first tab. The experience is very confusing to the user: after switching tabs successfully with no errors, pressing a button on the second tab causes validation errors on the first tab. This should never happen in the application: validation is designed to occur only for components that are submitted. When the button is pressed on the second tab, validation should not occur for components in the first tab. So what's going on?&lt;/p&gt;&lt;p&gt;Here's what I expected to happen:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The AJAX suggest control sends requests and renders itself&lt;/li&gt;&lt;li&gt;The tab sends requests and renders its contents&lt;/li&gt;&lt;li&gt;The button submits the form to make a request&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;Here's what really happened, depending on timing of the requests:&lt;/p&gt;&lt;p&gt;&lt;a href="http://picasaweb.google.ca/lh/photo/66hrk6qNPbvKUL2Yx9se3Q?authkey=Gv1sRgCJqn9bqIucagtQE&amp;amp;feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_vw9l2nnub6c/SeOkgU-2MdI/AAAAAAAAAFg/deZWgnKAim8/ajax-sequence.png" width="600" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The AJAX suggest control sends a request&lt;/li&gt;&lt;li&gt;The tab sends a request&lt;/li&gt;&lt;li&gt;The browser receives the response from the tab click and re-renders the tab and its form controls&lt;/li&gt;&lt;li&gt;The browser receives the response from the suggest control&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;After receiving every response the DOM is updated to contain the new JSF component state (remember: we're using client-side component state, so all JSF component state is stored in a hidden form parameter in the browser).&lt;/p&gt;&lt;p&gt;Due to out-of-order responses, the component state was being set to that of the first tab by the suggest control -- thus the application thinks that the current tab is the first, not the second as we would expect. Pressing the button on the second tab results in the application interpreting the submitted form as submitting values for components on the first tab. This is problematic for HTML &amp;lt;select&amp;gt; controls, since HTTP parameters are not submitted for &amp;lt;select&amp;gt; controls where no value is selected.&lt;/p&gt;&lt;p&gt;So how do we fix this problem? Have all AJAX controls use the same request queue. This is achieved by setting the attribute &lt;code&gt;eventsQueue="myApplicationEventQueue"&lt;/code&gt; on all of the richfaces AJAX controls. By doing so we end up with the following sequence:&lt;/p&gt;&lt;p&gt;&lt;a href="http://picasaweb.google.ca/lh/photo/as_qhL59U5EmorC-r6doWw?authkey=Gv1sRgCJqn9bqIucagtQE&amp;amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_vw9l2nnub6c/SeOl2G8KyTI/AAAAAAAAAGA/kDnaGUB5ZSU/ajax-sequence2.png"  width="600" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Lesson learned: AJAX requests update JSF component state on the client, and thus must all complete in sequence.  Otherwise component state on the client can get out of sync with what we expect it to be.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2335721230258962248?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2335721230258962248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2335721230258962248' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2335721230258962248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2335721230258962248'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/04/tough-lesson-in-ajax-jsf-and-client.html' title='A Tough Lesson in AJAX, JSF and Client-Side State'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/SeOS5k1DI1I/AAAAAAAAAFQ/UzzgRhlQMAE/s72-c/Picture+2.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3789216568958021664</id><published>2009-04-03T22:42:00.000-07:00</published><updated>2009-04-03T23:01:44.471-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Textile'/><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>Mylyn WikiText produces PDF</title><content type='html'>&lt;p&gt;&lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt; becomes increasingly flexible with its latest addition of &lt;a href="http://en.wikipedia.org/wiki/XSL_Formatting_Objects"&gt;XSL-FO&lt;/a&gt; to the list of output formats supported by its wiki markup conversion capability.  Combined with the excellent &lt;a href="http://xmlgraphics.apache.org/fop/"&gt;Apache FOP&lt;/a&gt; project, it becomes easy to produce PDF from your wiki markup.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt; &amp;lt;-- create XSL-FO --&amp;gt;&lt;br /&gt; &amp;lt;wikitext-to-xslfo markupLanguage="Textile" file="My File.mediawiki"/&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;-- create PDF --&amp;gt;&lt;br /&gt; &amp;lt;exec command="${fop.home}/fop"&amp;gt;&lt;br /&gt;  &amp;lt;arg value="${basedir}/help/My File.fo"/&amp;gt;&lt;br /&gt;  &amp;lt;arg value="${basedir}/help/My File.pdf"/&amp;gt;&lt;br /&gt; &amp;lt;/exec&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From a single source you can now produce HTML, Eclipse Help, DocBook, DITA and now XSL-FO and PDF.  Wiki markup never looked so good!&lt;/p&gt;&lt;p&gt;If you're interested in trying out this new functionality you'll have to wait until the next weekly release is posted on the &lt;a href="http://www.eclipse.org/mylyn/downloads"&gt;Mylyn downloads page&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3789216568958021664?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3789216568958021664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3789216568958021664' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3789216568958021664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3789216568958021664'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/04/mylyn-wikitext-produces-pdf.html' title='Mylyn WikiText produces PDF'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3851532234334477446</id><published>2009-03-30T11:33:00.000-07:00</published><updated>2009-03-30T12:14:34.482-07:00</updated><title type='text'>Mylyn WikiText Experiences from EclipseCon 2009</title><content type='html'>&lt;p&gt;I presented my session titled "&lt;a href="http://www.eclipsecon.org/2009/sessions?id=608"&gt;WikiText: Generate Eclipse Help from Eclipsepedia&lt;/a&gt;" (about &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt;) at EclipseCon 2009.  I had a lot of fun and lots of positive feedback.  If you're interested in the slides from my presentation, you can &lt;a href="https://textile-j.dev.java.net/EclipseCon2009-WikiText.pdf"&gt;find them here&lt;/a&gt;.  A couple of interesting experiences from EclipseCon:&lt;/p&gt;&lt;h4&gt;Trying Something New&lt;/h4&gt;&lt;p&gt;Before the presentation I saw the &lt;a href="http://www.appleiphoneapps.com/2009/01/review-keynote-remote/"&gt;Keynote Remote&lt;/a&gt; application for iPhone.  Thinking it would be fun, I tried it for the first time while presenting my session.  It turns out that to control slides you must swipe, which means that you need to know the orientation of your iPhone.  Every time you want to change slides you have to look at your phone.  Very distracting for both the presenter and the audience. &lt;/p&gt;&lt;p&gt;I learned the hard way something that I already knew: don't try anything new in a presentation!&lt;/p&gt;&lt;h4&gt;Twitter&lt;/h4&gt;&lt;p&gt;Twitter helped to create a &lt;a href="http://search.twitter.com/search?q=%23eclipsecon"&gt;buzz&lt;/a&gt; both before, during and after my session.  The best thing about it for me was that I got immediate feedback on my presentation even before it was over.&lt;/p&gt;&lt;h4&gt;iPhone Alarm&lt;/h4&gt;&lt;p&gt;Beware that your iPhone alarm will sound even if the quiet switch has been activated.  I discovered this design feature as Mike Milinkovich, the executive director of the Eclipse foundation was presenting.  I think he was as surprised as me when the get-to-a-bomb-shelter-now siren sounded from my phone's alarm.  Sorry Mike!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;All in all I had a great time.  I highly recommend EclipseCon for those who wish to meet interesting people with a common focus and lots of ideas.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3851532234334477446?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3851532234334477446/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3851532234334477446' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3851532234334477446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3851532234334477446'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/03/mylyn-wikitext-experiences-from.html' title='Mylyn WikiText Experiences from EclipseCon 2009'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6392815625391569771</id><published>2009-03-17T06:41:00.000-07:00</published><updated>2009-03-17T11:04:06.288-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DITA'/><category scheme='http://www.blogger.com/atom/ns#' term='WikiText'/><category scheme='http://www.blogger.com/atom/ns#' term='Textile'/><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='EclipseCon'/><category scheme='http://www.blogger.com/atom/ns#' term='markup languges'/><title type='text'>Mylyn WikiText 1.0 Released</title><content type='html'>&lt;p&gt;I'm pleased to announce that &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText 1.0&lt;/a&gt; has been released as part of &lt;a href="http://tasktop.com/blog/eclipse/tasktop-14-and-eclipse-mylyn-31-released"&gt;Mylyn 3.1&lt;/a&gt;.  Mylyn WikiText provides lightweight markup (wiki) parsing and editing capabilities to the Eclipse platform, Mylyn, Ant and stand-alone applications.  Mylyn WikiText is the result of more than a year of development and over 170 bug fixes and enhancements.   This marks the first release of WikiText after its move from the &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt; project.  Many thanks to the Mylyn team and the Eclipse community for making this possible.  Here are some of the highlights:&lt;/p&gt;&lt;h4 id="MylynTaskEditorIntegration"&gt;Mylyn Task Editor Integration&lt;/h4&gt;&lt;p&gt;With WikiText the Mylyn task editor is now markup-aware, providing source formatting, integrated preview, content assist and markup help.  The following shows how markup can be authored and previewed in the task editor:  &lt;/p&gt;&lt;p&gt;&lt;img style="padding:0px;border:0px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/Sb8mS9i-lLI/AAAAAAAAAD4/zY3F5xKxun8/s1600/task-editor-integration.png" id="BLOGGER_PHOTO_ID_5314008192521245874" alt="Task Editor Integration" /&gt;&lt;/p&gt;&lt;p&gt;WikiText selects the markup language that best suits your task repository.  This is great for users of JIRA, Trac and other repositories that provide support for wiki markup in task descriptions and comments.  Users can also benefit from using wiki markup with task repositories such as Bugzilla that don't provide support for wiki markup.&lt;/p&gt;&lt;h4 id="EclipseIntegration"&gt;Eclipse Integration&lt;/h4&gt;&lt;p&gt;WikiText provides an editor for files containing wiki markup:&lt;/p&gt;&lt;p&gt;&lt;img style="padding:0px;border:0px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/Sb8oapBlQ7I/AAAAAAAAAEA/unv3QAYg7_s/s1600/wikitext-editor.png" border="0" alt="WikiText Editor" id="BLOGGER_PHOTO_ID_5314010523474674610" /&gt;&lt;/p&gt;&lt;p&gt;WikiText provides support for several markup languages, including MediaWiki, Textile,  Confluence, TracWiki and TWiki.  WikiText also provides an extensible parser framework for adding support for other markup languages.&lt;/p&gt;&lt;h4 id="AntIntegration"&gt;Ant Integration&lt;/h4&gt;&lt;p&gt;Users of Apache Ant now have access to Ant tasks for transforming wiki markup to alternative formats such as HTML, Eclipse Help, &lt;a href="http://www.docbook.org/"&gt;DocBook&lt;/a&gt; and &lt;a href="http://dita.xml.org/"&gt;OASIS DITA&lt;/a&gt;.  &lt;/p&gt;&lt;h4 id="RCPSWTandServerApplications"&gt;RCP, SWT and Server Applications&lt;/h4&gt;&lt;p&gt;WikiText provides public APIs for integrating wiki markup capabilities into RCP applications and stand-alone SWT applications.  These APIs make it easy to display markup and inspect its structure.&lt;/p&gt;&lt;p&gt;WikiText can also be used server-side to transform markup in web applications.&lt;/p&gt;&lt;h4 id="WikiTextInTheField"&gt;WikiText In The Field&lt;/h4&gt;&lt;p&gt;WikiText is being used in many interesting ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The Mylyn project uses WikiText to convert MediaWiki content from Eclipsepedia to Eclipse Help content.   &lt;/li&gt;&lt;li&gt;Mylyn WikiText authors help content in Textile and converts it at build time to Ecilpse Help content.  &lt;/li&gt;&lt;li&gt;A &lt;a href="http://www.eclipse.org/examples/example.php?id=slideshow"&gt;recent addition&lt;/a&gt; to the Eclipse Examples project uses WikiText to create slide presentations based on files authored in MediaWiki markup.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Mylyn WikiText will also be presented at EclipseCon 2009 in &lt;a href="http://www.eclipsecon.org/2009/sessions?id=608"&gt;WikiText: Generate Eclipse Help from Eclipsepedia&lt;/a&gt;.&lt;/p&gt;&lt;h4 id="MoreInformation"&gt;More Information&lt;/h4&gt;&lt;p&gt;Mylyn WikiText can be installed using an Eclipse update site or by downloading the standalone jars.   See &lt;a href="http://www.eclipse.org/mylyn/downloads"&gt;Mylyn downloads&lt;/a&gt; for details.&lt;/p&gt;&lt;p&gt;For more information about WikiText, see the &lt;a href="http://www.eclipse.org/mylyn/new/"&gt;Mylyn 3.1 New and Noteworthy&lt;/a&gt;, &lt;a href="http://wiki.eclipse.org/Mylyn/FAQ#WikiText"&gt;Mylyn FAQ&lt;/a&gt; or the &lt;a href="http://www.eclipse.org/"&gt;Mylyn homepage&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6392815625391569771?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6392815625391569771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6392815625391569771' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6392815625391569771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6392815625391569771'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/03/mylyn-wikitext-10-released.html' title='Mylyn WikiText 1.0 Released'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/Sb8mS9i-lLI/AAAAAAAAAD4/zY3F5xKxun8/s72-c/task-editor-integration.png' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6244911075519954953</id><published>2009-03-09T14:07:00.000-07:00</published><updated>2009-03-09T14:08:45.757-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Open Type Dialog - Small Things Make A Big Difference</title><content type='html'>I'm a big fan of attention to detail when creating tooling for developers.  Our brains are tuned to notice the smallest perceptible visual clues.  We can absorb a lot of information with the right visual cues, however presenting that information without adding clutter to an IDE is a real challenge.&lt;br /&gt;&lt;br /&gt;The JDT team has mangaged to improve the Open Type dialog in Eclipse 3.5 without falling into the trap of a cluttered interface:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SbV9A8aZHaI/AAAAAAAAADI/uSxBSPXvcg4/s1600-h/Picture+1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 340px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SbV9A8aZHaI/AAAAAAAAADI/uSxBSPXvcg4/s400/Picture+1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5311288790723665314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Notice how a bold font is being used to identify regions of text that match the filter.  The JDT team is using &lt;a href="http://download.eclipse.org/eclipse/downloads/drops/S-3.5M4-200812111908/eclipse-news-M4.html"&gt;a new platform feature&lt;/a&gt; where multiple fonts can be used in one cell.  Here's what it used to look like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SbV9RQxjJmI/AAAAAAAAADQ/hM5Dc2csl9M/s1600-h/Picture+2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 340px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SbV9RQxjJmI/AAAAAAAAADQ/hM5Dc2csl9M/s400/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5311289071067407970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The best thing about this new feature is that my brain doesn't have to work harder to know more -- it's immediately evident to me which matches are better and why without having to think about it.&lt;br /&gt;&lt;br /&gt;The importance of eliminating and minimizing visual noise is evidenced by products such as &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; that are designed to do just that: filter out what you don't need.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Kudos to the JDT team for raising the bar.  I can't wait for Eclipse 3.5 Galileo to be released this summer.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6244911075519954953?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6244911075519954953/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6244911075519954953' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6244911075519954953'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6244911075519954953'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/03/open-type-dialog-small-things-make-big_09.html' title='Open Type Dialog - Small Things Make A Big Difference'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SbV9A8aZHaI/AAAAAAAAADI/uSxBSPXvcg4/s72-c/Picture+1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-643200896318826905</id><published>2009-02-24T09:10:00.000-08:00</published><updated>2009-02-24T09:54:56.202-08:00</updated><title type='text'>Eclipse Foundation Committer Member</title><content type='html'>Today I became an Eclipse Foundation &lt;a href="http://www.eclipse.org/membership/become_a_member/committer.php"&gt;Committer Member&lt;/a&gt;.  This will enable me to vote in the &lt;a href="http://www.eclipse.org/org/elections/"&gt;2009 Eclipse Foundation Elections&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Being committed (no pun intended) to the &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt; project and being interested in projects such as &lt;a href="http://www.eclipse.org/swtbot/"&gt;SWTBot&lt;/a&gt; and &lt;a href="http://www.eclipse.org/swt/"&gt;SWT&lt;/a&gt; (for Cocoa) I believe that small and innovative projects should be well-represented on the Eclipse Foundation board. &lt;br /&gt;&lt;br /&gt;Having known and worked with &lt;a href="http://www.eclipse.org/org/elections/candidate.php?year=2009&amp;amp;id=kersten"&gt;Mik Kersten&lt;/a&gt; in various ways over the past few years, I am constantly surprised by his breadth of knowledge, leadership, and grass-roots understanding of the needs of developers and committers.  I've seen Mik uphold a high level of integrity and demonstrate consistent follow-through.  I know that he has a solid understanding of what is needed by such projects and is capable of using his board experience and influence to make a difference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-643200896318826905?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/643200896318826905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=643200896318826905' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/643200896318826905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/643200896318826905'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/02/eclipse-foundation-committer-member.html' title='Eclipse Foundation Committer Member'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4197434354621866581</id><published>2009-02-20T10:53:00.000-08:00</published><updated>2009-02-21T13:47:36.829-08:00</updated><title type='text'>Unsung Heroes Of Eclipse</title><content type='html'>&lt;p&gt;I'm glad to see a Top Contributor category in the &lt;a href="http://www.eclipse.org/org/foundation/eclipseawards/index.php"&gt;Eclipse Community Awards&lt;/a&gt; that takes into account more than just code contributions.  It's all too easy to lavish praise and recognition on those who contribute code -- but what about the others?  The &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt; project has been lucky to garner the attention of individuals who spend their own time installing, testing, performing quality assurance, and collaborating on Bugzilla.&lt;/p&gt;&lt;p&gt;In particular I'd like to thank Jörg Thönnes of Aachen, Germany for the many hours that he &lt;a href="https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced&amp;amp;short_desc_type=allwordssubstr&amp;amp;short_desc=&amp;amp;classification=Tools&amp;amp;product=Mylyn&amp;amp;component=WikiText&amp;amp;long_desc_type=allwordssubstr&amp;amp;long_desc=&amp;amp;bug_file_loc_type=allwordssubstr&amp;amp;bug_file_loc=&amp;amp;status_whiteboard_type=allwordssubstr&amp;amp;status_whiteboard=&amp;amp;keywords_type=allwords&amp;amp;keywords=&amp;amp;emailassigned_to1=1&amp;amp;emailreporter1=1&amp;amp;emailcc1=1&amp;amp;emaillongdesc1=1&amp;amp;emailtype1=substring&amp;amp;email1=Joerg.Thoennes%40macd.com&amp;amp;emailtype2=substring&amp;amp;email2=&amp;amp;bugidtype=include&amp;amp;bug_id=&amp;amp;votes=&amp;amp;chfieldfrom=&amp;amp;chfieldto=Now&amp;amp;chfieldvalue=&amp;amp;cmdtype=doit&amp;amp;order=Reuse+same+sort+as+last+time&amp;amp;field0-0-0=noop&amp;amp;type0-0-0=noop&amp;amp;value0-0-0="&gt;has contributed&lt;/a&gt; in making Mylyn WikiText a better product.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4197434354621866581?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4197434354621866581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4197434354621866581' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4197434354621866581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4197434354621866581'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/02/unsung-heroes-of-eclipse.html' title='Unsung Heroes Of Eclipse'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1486784436654202648</id><published>2009-02-13T09:21:00.000-08:00</published><updated>2009-02-13T09:52:13.743-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='WebKit'/><category scheme='http://www.blogger.com/atom/ns#' term='Safari'/><category scheme='http://www.blogger.com/atom/ns#' term='Profiler'/><category scheme='http://www.blogger.com/atom/ns#' term='NetBeans'/><title type='text'>TPTP is Not For Me</title><content type='html'>&lt;p&gt;I've been using the &lt;a href="http://www.netbeans.org/profiler"&gt;NetBeans profiler&lt;/a&gt; to help me develop Eclipse plug-ins.  A couple of years ago I tried TPTP (Eclipse Test and Performance Tools Platform), but found it extremely difficult to configure and it couldn't compete with Sun's &lt;a href="http://research.sun.com/projects/dashboard.php?id=90"&gt;dynamic bytecode instrumentation technology&lt;/a&gt;.  Today I thought I'd give TPTP another go.  The first link that I clicked on the &lt;a href="http://www.eclipse.org/tptp/index.php"&gt;TPTP homepage&lt;/a&gt; was the Getting Started link, and this is what I got:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; border: 0px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SZWtdZzzmbI/AAAAAAAAACo/4eqBjV8SrN8/s1600/Picture+1.png" border="0" alt="Popup dialog with message: Sorry but this page is not currently viewable in Safari" /&gt;&lt;/p&gt;&lt;p&gt;Given that Safari and &lt;a href="http://webkit.org/"&gt;WebKit&lt;/a&gt; are largely acclaimed as being some of the most advanced web technology available, it seems that the TPTP team really doesn't want me to use their profiler.  I'll check back later -- in the meantime I'll continue using NetBeans to profile Eclipse.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1486784436654202648?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1486784436654202648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1486784436654202648' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1486784436654202648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1486784436654202648'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/02/tptp-is-not-for-me.html' title='TPTP is Not For Me'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SZWtdZzzmbI/AAAAAAAAACo/4eqBjV8SrN8/s72-c/Picture+1.png' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5562267641130051509</id><published>2009-02-10T12:07:00.000-08:00</published><updated>2009-02-10T12:27:17.633-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>JPA 2.0: Why AccessType is Relevant</title><content type='html'>It has long been debated whether field access is superior to property access for JPA entities.  Most arguments that I've seen hinge on performance versus encapsulation.  While these arguments may be interesting from a theoretical perspective, control over field access is in fact very useful in solving real-world problems.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Field access allows code to distinguish between the JPA provider setting properties while loading an entity versus application code setting a property.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This was very useful for a project that I'm involved with that has de-normalized data or other fields that are computed and persisted.  Here's how it works:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;In the setter for various properties that affect the computation of a de-normalized property, the value of the de-normalized property is set to null.  @AccessType(FIELD) is used for these properties.&lt;/li&gt;&lt;li&gt;In the getter for the de-normalized property, if the value is null the de-normalized value is recomputed.&lt;/li&gt;&lt;li&gt;In a @PrePersist method, the getter is called to ensure that the de-normalized value is properly persisted.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Using this technique maintaining de-normalized data becomes simple and robust.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While this feature has been available within Hibernate since 3.0, I'm glad to see that JPA 2.0 (&lt;a href="http://jcp.org/en/jsr/detail?id=317"&gt;JSR 317&lt;/a&gt;) introduces a standard annotation to control this behaviour.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5562267641130051509?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5562267641130051509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5562267641130051509' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5562267641130051509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5562267641130051509'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/02/jpa-20-why-accesstype-is-relevant.html' title='JPA 2.0: Why AccessType is Relevant'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-987577170150007708</id><published>2009-02-02T09:31:00.000-08:00</published><updated>2009-02-02T09:37:32.055-08:00</updated><title type='text'>WikiText as a Slideshow Presentation Technology</title><content type='html'>I'm constantly surprised by the versatility of wiki markup.  Wayne Beaton has recently blogged about &lt;a href="http://dev.eclipse.org/blogs/wayne/2009/01/20/eclipse-slideshow/"&gt;using MediaWiki markup to author presentation slides&lt;/a&gt;.  He does this using &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt; to parse wiki markup.  Neat stuff!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-987577170150007708?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/987577170150007708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=987577170150007708' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/987577170150007708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/987577170150007708'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/02/wikitext-as-slideshow-presentation.html' title='WikiText as a Slideshow Presentation Technology'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-236556181710520747</id><published>2009-01-30T15:02:00.000-08:00</published><updated>2009-01-30T15:09:46.839-08:00</updated><title type='text'>Eclipse: Hitting a Breakpoint After The Fact</title><content type='html'>Ever wonder how to hit a breakpoint after you've passed it?  You can do this with the Eclipse Java debugger.  Here's how I do it:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Stop at a breakpoint&lt;/li&gt;&lt;li&gt;Realize that you want to step through a method called a few lines back&lt;/li&gt;&lt;li&gt;Put a breakpoint in that method&lt;/li&gt;&lt;li&gt;Highlight an expression that uses the method in the java editor and from the context menu select &lt;b&gt;Inspect&lt;/b&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SYOITeTyHzI/AAAAAAAAACg/uU_0Jn3386k/s1600-h/Picture+2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 164px;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SYOITeTyHzI/AAAAAAAAACg/uU_0Jn3386k/s400/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5297227454852767538" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;That should do it!  Before you see the value in the inspection pop-up, your debugger should hit the breakpoint that you just created.  Very cool.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-236556181710520747?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/236556181710520747/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=236556181710520747' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/236556181710520747'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/236556181710520747'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/01/eclipse-hitting-breakpoint-after-fact.html' title='Eclipse: Hitting a Breakpoint After The Fact'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vw9l2nnub6c/SYOITeTyHzI/AAAAAAAAACg/uU_0Jn3386k/s72-c/Picture+2.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-7768312146570545421</id><published>2009-01-28T09:20:00.001-08:00</published><updated>2009-01-28T09:47:59.924-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CVS'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Working With Patches and CVS</title><content type='html'>Many contributions to Eclipse projects start as a patch.  Patches are great because they can encompass changes to multiple files and make it easy to collaborate with others.  Anyone can make a patch, so contributors don't need commit privileges to participate.  Patches can be attached to tickets in the Eclipse Bugzilla so that committers can be confident of their IP cleanliness.  The problem with patches is that they're static.  That is, they don't evolve as the source code in CVS changes.  Thus patches become stale, often resulting in committers asking contributors to re-cut a patch.  So how do we overcome this problem?&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I've had the pleasure of working from both sides of the problem.  Starting as a contributor with the &lt;a href="https://textile-j.dev.java.net"&gt;Textile-J&lt;/a&gt; contribution to &lt;a href="http://wiki.eclipse.org/Mylyn/FAQ#WikiText"&gt;Mylyn WikiText&lt;/a&gt; I created and maintained multiple patches, some small and some large.  Eventually after earning Mylyn commit privileges I saw patches coming from contributors.  The problem of the stale patch comes up again and again.  Often contributions must wait a few days, weeks or even months before they're evaluated.  During this time the files affected by the patch often change in CVS, thus rendering the patch unusable until it is massaged to fit the new code.  Unfortunately this process of massaging is usually manual since the line-based merge algorithms used  by CVS cannot account for non-trivial changes to source files.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So how do we evaluate and apply these valuable contributions?  It's tempting to apply a stale patch and store conflicting changes in a temporary .rej file, and then merge those conflicting changes by hand afterwards.  Though in some cases this approach makes sense, there is an easier way for those other cases.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The following formula can ease the pain for committers and contributors alike as they try to use a patch that has become stale:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Start by attempting to apply the patch using the Eclipse 'Apply patch' functionality, either from the Team menu or from within the Mylyn task editor.&lt;/li&gt;&lt;li&gt;Take note of which files have problems in the apply patch wizard.&lt;/li&gt;&lt;li&gt;Exit the wizard without applying the patch.&lt;/li&gt;&lt;li&gt;Take note of the time-stamp on the patch.&lt;/li&gt;&lt;li&gt;For each file in the patch that was problematic, use the CVS 'Team-&gt;Replace With-&gt;History...' wizard and select the most recent revision that predates the patch.  Take note of which revision was used.&lt;/li&gt;&lt;li&gt;Now apply the patch.  It should merge without issues.  If not, you've missed something so go back to step 1.&lt;/li&gt;&lt;li&gt;For each file that was moved back in time in step 5, use the CVS History to compare the revision that was used with the patch to the latest revision in CVS.  Apply the changes that are shown in the compare editor to the file in the workspace.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;The formula turns the problem around.  Instead of trying to apply the patch to the latest revision, we apply the changes made since the patch to the patched files.  Sometimes this can be much easier.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How do you choose which approach to start with?  I recommend starting with an up-to-date workspace and try applying the patch.  If proceeding down that path is starting to feel too hard, then revert your changes and try the formula described here.  In some cases, the hard problem becomes easy!  If not.... well, patches aren't perfect.  Sometimes hard is just hard.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-7768312146570545421?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/7768312146570545421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=7768312146570545421' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/7768312146570545421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/7768312146570545421'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/01/working-with-patches-and-cvs.html' title='Working With Patches and CVS'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4749796456597517040</id><published>2009-01-02T08:55:00.000-08:00</published><updated>2009-01-02T09:06:56.522-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='EclipseCon'/><title type='text'>Mylyn WikiText at EclipseCon 2009</title><content type='html'>&lt;a href="http://www.eclipsecon.org/2009/sessions?id=608"&gt;WikiText will be at EcilpseCon 2009&lt;/a&gt; in Santa Clara, California.  The presentation time was expanded to allow for more coverage of WikiText -- so if there's something specific that you'd like to hear about please let me know.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The WikiText presentation is included in a session titled "&lt;a href="http://www.eclipsecon.org/2009/sessions?id=708"&gt;50 minutes towards a better you&lt;/a&gt;".  Don't ask me about the title -- I don't know if WikiText will make you a better person or not.  I'm very excited to be there presenting this new technology alongside Steffen Pingel, who will be providing a Mylyn connector crash course.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4749796456597517040?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4749796456597517040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4749796456597517040' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4749796456597517040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4749796456597517040'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2009/01/mylyn-wikitext-at-eclipsecon-2009.html' title='Mylyn WikiText at EclipseCon 2009'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2494289565577216492</id><published>2008-12-09T12:51:00.000-08:00</published><updated>2008-12-09T13:53:41.873-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDE'/><category scheme='http://www.blogger.com/atom/ns#' term='MDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='GEF'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='Tasktop'/><title type='text'>Mylyn Context-Driven Domain Diagram</title><content type='html'>Lately I've been working on some large legacy modernization projects with &lt;a href="http://www.maketechnologies.com/"&gt;MAKE&lt;/a&gt;'s MDD tooling.  These projects have upwards of 400 entities in their domain (read: minimum 400 database tables).  With such a large application domain it's common to have large, complex domain diagrams which are often at the wrong level of granularity for programming tasks.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I found myself creating small, purpose-specific diagrams showing only those things that were relevant to the task at hand.  These diagrams would get tweaked and maintained as my understanding of the task and domain evolved.  While maintaining diagrams is not really all that hard, it is time-consuming and distracting.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;After some thinking I realized that the answer to developer productivity was sitting right in front of me: &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; task context-driven diagrams.  Why can't the MDD tooling create and maintain a domain diagram based on the interesting domain elements in the active Mylyn context?  There would be no need to create or maintain such a diagram manually: it would always exist, and it would always show only those items that are relevant to the currently active task.  Suddenly, purpose-specific diagrams become implied, easy, expected.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vw9l2nnub6c/ST7nN2BfnmI/AAAAAAAAACM/T9BOr4t7PnM/s1600-h/mylyn-driven-diagram.png" title="Complex diagrams become simple"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 155px;" src="http://3.bp.blogspot.com/_vw9l2nnub6c/ST7nN2BfnmI/AAAAAAAAACM/T9BOr4t7PnM/s400/mylyn-driven-diagram.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5277910038350896738" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;I realized that a few simple rules could make such a diagram really work:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;domain elements in the Mylyn context always appear in the diagram&lt;/li&gt;&lt;li&gt;associations between elements in the diagram are always shown&lt;/li&gt;&lt;li&gt;removing an element from the Mylyn context causes it to be removed from the diagram&lt;/li&gt;&lt;li&gt;adding an element to the diagram causes it to be added to the Mylyn context&lt;/li&gt;&lt;li&gt;diagram elements are positioned according to a default layout algorithm, but remember their positioning if moved manually&lt;/li&gt;&lt;li&gt;diagram state is saved and associated with the Mylyn task id, such that it is restored when the task is re-activated&lt;/li&gt;&lt;li&gt;switching active tasks automatically resets the diagram to correspond to the new active task&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;As my Mylyn task context evolves with its decaying interest model the diagram becomes ever more pertinent.  Mylyn landmarks prevent those really essential things from disappearing, other things come and go as needed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now that I've been working with automatic domain diagramming I wonder how I ever got by without.  Context-driven diagrams are now an essential part of my toolkit that let me focus on the task at hand without distraction.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2494289565577216492?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2494289565577216492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2494289565577216492' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2494289565577216492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2494289565577216492'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/12/mylyn-context-driven-domain-diagram.html' title='Mylyn Context-Driven Domain Diagram'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vw9l2nnub6c/ST7nN2BfnmI/AAAAAAAAACM/T9BOr4t7PnM/s72-c/mylyn-driven-diagram.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3079991525381630407</id><published>2008-11-26T08:58:00.000-08:00</published><updated>2008-11-26T09:33:24.419-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='DemoCamp'/><title type='text'>Eclipse DemoCamp Vancouver</title><content type='html'>I had a great time at &lt;a href="http://wiki.eclipse.org/Eclipse_DemoCamps_November_2008/Vancouver"&gt;Eclipse DemoCamp&lt;/a&gt; last night.  I was really impressed by two presentations in particular, both of them about testing tools.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One tool, called &lt;a href="http://www.webfoot.com/blog/2008/09/10/tripoli-differential-code-coverage-tool/"&gt;Tripoli&lt;/a&gt; is a differential code coverage tool.  Tripoli shows you the difference in coverage between two execution runs of a program.  It looked like a great tool to have for finding code relevant to a specific behaviour or event.  Kaitlin Sherwood both created Tripoli and presented at DemoCamp.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The other tool presentation was about an &lt;a href="http://pleiad.dcc.uchile.cl/tod/"&gt;Omniscient Debugger&lt;/a&gt;.  Going into the DemoCamp, I was thinking "Omniscient, yeah right!".  In reality, the debugger can show you exactly what happened on a timeline.  You can &lt;i&gt;go back in time&lt;/i&gt; and see when variables were assigned specific values.  Yes, &lt;i&gt;after&lt;/i&gt; it has already happened!!  This is the first real time machine that I've seen actually working.  The tool is called TOD and was presented by its creator Guillaume Pothier, who is a PHD student at the University of Chile.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Mik Kersten did a presentation about &lt;a href="http://tasktop.com"&gt;Tasktop&lt;/a&gt; and Mylyn.  Being an avid Mylyn user, I can see how Tasktop adds a lot of value beyond the basics of Mylyn.  As an early beta user of Tasktop ages ago, I'm impressed by how far they've come.  At the time I was disappointed by their lack of support for the Mac.  Though the Mac is still not officially supported by Tasktop, I've heard that it is much improved.  Maybe it's time for another test drive.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My presentation on WikiText went well.  If you're interested in the slides, &lt;a href="https://textile-j.dev.java.net/Eclipse%20DemoCamp%20Vancouver%202008.pdf"&gt;here they are&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All in all, a great night.  We ended up at the Lenox pub where I got to talk to more great people.  I came home wishing that I'd had the opportunity to talk to more people.  It's great to see such a lively Eclipse community in Vancouver.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3079991525381630407?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3079991525381630407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3079991525381630407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3079991525381630407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3079991525381630407'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/11/eclipse-democamp-vancouver.html' title='Eclipse DemoCamp Vancouver'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-5798462602804238024</id><published>2008-11-18T12:39:00.000-08:00</published><updated>2008-11-18T13:24:35.387-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>Mylyn Converts Wiki-Based User Guide To Eclipse Help</title><content type='html'>The &lt;a href="http://wiki.eclipse.org/Mylyn/User_Guide"&gt;Mylyn user guide&lt;/a&gt; is maintained online in the Eclipse wiki.  While this has the many benefits of using a collaborative wiki for help content, this has meant that the Mylyn user guide is &lt;i&gt;only&lt;/i&gt; available online on the wiki, not from within the Eclipse help system.  Until now.  Using &lt;a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText"&gt;WikiText Ant tasks&lt;/a&gt; Mylyn is able to convert the online wiki content into Eclipse help using an automated process.  Users of Mylyn now have access to the user guide by using the standard Eclipse help system, including search, table of contents, etc. even when offline.&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Going forward this means that the Mylyn project will be able to continue maintaining the user guide in the Eclipse wiki while also providing the same content from within the Eclipse.  Furthermore, other Eclipse projects will be able to do the same by adopting the same approach.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Users wishing to see the user guide in Eclipse can do so by installing the Mylyn weekly build &lt;a href="http://www.eclipse.org/mylyn/downloads/"&gt;as described here&lt;/a&gt;.  Eclipse committers that would like to have their wiki-based content available within Eclipse can do so by looking at the Mylyn project (&lt;tt&gt;org.eclipse.mylyn.help.ui/build-helper.xml&lt;/tt&gt;) and by making use of the &lt;a href="http://www.eclipse.org/mylyn/downloads/"&gt;WikiText stand-alone package&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-5798462602804238024?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/5798462602804238024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=5798462602804238024' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5798462602804238024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/5798462602804238024'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/11/mylyn-converts-wiki-based-user-guide-to.html' title='Mylyn Converts Wiki-Based User Guide To Eclipse Help'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-9144854657660747395</id><published>2008-11-10T16:08:00.000-08:00</published><updated>2008-11-11T07:54:34.330-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><title type='text'>Mylyn WikiText Stand-Alone Package Available</title><content type='html'>I'm pleased to announce that the Mylyn WikiText stand-alone package &lt;a href="http://www.eclipse.org/mylyn/downloads/"&gt;is available for download&lt;/a&gt;.  The download contains jar files and documentation for including WikiText Ant tasks in your build or for using WikiText APIs outside of an Eclipse runtime.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The WikiText project is still in the incubation phase, thus users should be aware that the download has not yet completed a release review.   The availability of WikiText stand-alone download enables users to try out WikiText prior to its release, including the &lt;a href="http://www.dita-op.org/2008/11/05/mylyn-wikitext-targets-oasis-dita/"&gt;much-anticipated&lt;/a&gt; wiki-to-DITA functionality.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is the first time that WikiText stand-alone jars have been made available since the move of &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt; to the WikiText project.  WikiText includes many improvements and fixes since the &lt;a href="https://textile-j.dev.java.net/builds/net.java.textilej/index.html"&gt;Textile-J 2.2 release&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Many thanks to Mik Kersten, Steffen Pingel and others at &lt;a href="http://tasktop.com/"&gt;Tasktop&lt;/a&gt; who have facilitated the WikiText project.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-9144854657660747395?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/9144854657660747395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=9144854657660747395' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9144854657660747395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9144854657660747395'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/11/mylyn-wikitext-stand-alone-package.html' title='Mylyn WikiText Stand-Alone Package Available'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3397904460076392862</id><published>2008-11-03T09:13:00.000-08:00</published><updated>2008-11-03T09:22:58.590-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DITA'/><category scheme='http://www.blogger.com/atom/ns#' term='Textile'/><category scheme='http://www.blogger.com/atom/ns#' term='Ant'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>Mylyn WikiText targets OASIS DITA</title><content type='html'>&lt;div&gt;Wiki content has become increasingly popular within the &lt;a href="http://dita.xml.org/"&gt;DITA community&lt;/a&gt;, as evidenced by the &lt;a href="http://development.lombardi.com/?p=68"&gt;recent announcement&lt;/a&gt; of the &lt;a href="http://sourceforge.net/projects/dita2wiki/"&gt;DITA2Wiki&lt;/a&gt; open source project.  While most of these projects enable DITA-to-wiki conversions, community interest has been expressed for full-cycle wiki-DITA-wiki transformation capability.  &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn WikiText&lt;/a&gt; hopes to close this loop with the addition of its support for DITA output.  Mylyn WikiText can now create &lt;a href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=dita"&gt;OASIS DITA&lt;/a&gt; as output from wiki markup.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Mylyn WikiText provides a flexible architecture supporting multiple wiki markup languages.  This provides organizations with many options when considering a DITA toolchain, whether it be a one-time conversion of existing wiki assets to DITA, or as an integrated part of a publishing process.  Currently supported are MediaWiki, Textile, Confluence, TWiki and TracWiki.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I am also very excited about the recent  &lt;a href="http://www.dita-op.org/"&gt;DITA Open Platform project&lt;/a&gt;, which aims to enable Eclipse as a full-fledged DITA authoring and publishing platform.  With the capabilities provided by Mylyn WikiText and DITA-op, Eclipse as a platform becomes a compelling cross-platform DITA authoring toolset.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Mylyn WikiText originated from &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt;, where parsing capabilities evolved to support more than 5 wiki markup languages, conversion of wikitext to DocBook, integrated support for the Ant build system and first-class integration with the Eclipse platform.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Mylyn WikiText is looking for community feedback on wikitext to DITA conversion.  Please feel free to &lt;a href="mailto:dgreen99@gmail.com"&gt;contact me directly via email&lt;/a&gt; or &lt;a href="https://bugs.eclipse.org/bugs/enter_bug.cgi?classification=Tools"&gt;post a bug or enhancement request&lt;/a&gt; to the WikiText component of the Mylyn project.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3397904460076392862?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3397904460076392862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3397904460076392862' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3397904460076392862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3397904460076392862'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/11/mylyn-wikitext-targets-oasis-dita.html' title='Mylyn WikiText targets OASIS DITA'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-4270813416718277945</id><published>2008-09-23T20:49:00.000-07:00</published><updated>2008-09-23T21:08:29.758-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='SWTBot'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='GEF'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><title type='text'>Eclipse GUI Testing Is Viable With SWTBot</title><content type='html'>&lt;div&gt;For several years I've struggled with the viability of automated Eclipse GUI test frameworks.  Now for the first time I've found an approach that works reliably.  This article discusses the approach and details some specific techniques that greatly increase productivity in creating useful tests.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;In the past automated GUI test frameworks have failed to meet my needs because they have focused too much on recording and playback.  The complexity of the Eclipse environment makes it near impossible to get this approach to work reliably.  Simple things such as background jobs and processing become major issues.  Testing of GEF-based editors is not possible since all record/playback frameworks rely on identifying controls and widgets -- which GEF does not use.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Recently I've come across &lt;a href="http://www.swtbot.org/"&gt;SWTBot&lt;/a&gt;, which uses a novel approach.  SWTBot tests are written in Java, and run inside the Eclipse process as an Eclipse test application.  This gives SWTBot full access to the SWT and Eclipse APIs.  SWTBot tests are written as JUnits, which makes integration with common technologies such as Ant, Continuous Integration and code coverage tools easy.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;But all is not rosy when writing tests with SWTBot.  There are some Eclipse-specific idiosyncrasies that come back to bite you such as context menus that are reconstructed when shown and the GEF framework that does not use controls or widgets.  That said, SWTBot provides and excellent starting point.  Here's how I built on SWTBot to create a powerful test environment that is easy to use.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Initially my tests contained code that looks like this:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public void resetPerspective() {&lt;br /&gt;bot.menu("Window").menu("Reset Perspective...").click();&lt;br /&gt;bot.shell("Reset Perspective").activate();&lt;br /&gt;bot.button("OK").click();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Not bad!  It's easy to see what's going on.  In other cases the code looked more like this:&lt;/div&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;SWTBotTree tree = bot.tree();&lt;br /&gt;String[] path = name.split("/");&lt;br /&gt;SWTBotTreeItem[] items = tree.getAllItems();&lt;br /&gt;SWTBotTreeItem selectedItem = null;&lt;br /&gt;for (SWTBotTreeItem item: items) {&lt;br /&gt;if (path[0].equals(item.getText())) {&lt;br /&gt;item.expand();&lt;br /&gt;sleep();&lt;br /&gt;selectedItem = item;&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;for (int x = 1;selectedItem != null &amp;amp;&amp;amp; x&lt;path.length;++x) string="" pathitem="path[x];" selecteditem="selectedItem.getNode(pathItem);" if="" throw="" new="" cannot="" select="" tree="" segment=""&gt;&lt;/path.length;++x)&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;As you can see, it's hard to see the forest for the trees.  Way too much code is required to do simple things.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here's what I did to make things easier:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;1. Create classes that directly model the UI parts that you're working with in your tests.&lt;/b&gt;  For example, instead of directly using SWTBotView to manipulate the Package Explorer view, create a class called PackageExplorer that delegates to SWTBotView and provides richer functionality.  For example, in my PackageExplorer class I have the following method:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/**&lt;br /&gt;* select the first element that adapts to the given resource&lt;br /&gt;*/&lt;br /&gt;public void select(final IResource resource)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;2. Create a method to find and click context menu items in one go.&lt;/b&gt;  In SWTBot the finding and clicking occur in two seperate UI runnables.  In Eclipse this can cause problems for some context menus as the menu item gets disposed before it is clicked due to a loss of focus.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;3. Instead of using sleep() to wait for something to be done, wait for the real thing to be done.&lt;/b&gt;  A heavily loaded machine can cause processing times to vary.  Instead of having a fragile sleep(500L), use a reliable technique to determine when the job is really done.&lt;/div&gt;&lt;div&gt;For example, if you know that your processing is holding a resource lock, post a no-op empty workspace job and wait on it inside your test.  It will only be invoked once all other resource locks are released, so when it's complete you're guaranteed that your other job is done:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;// ensure that all queued workspace operations and locks are released&lt;br /&gt;ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {&lt;br /&gt;public void run(IProgressMonitor monitor) throws CoreException {&lt;br /&gt;// nothing to do!&lt;br /&gt;}&lt;br /&gt;}, new NullProgressMonitor());&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;4. Leverage and extend the SWTBot framework with Eclipse-specific behaviour.&lt;/b&gt;  For example, make use of SWTBot's conditional waiting APIs by creating Eclipse-specific conditions like this one that is used to wait until an editor is opened on a resource:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/**&lt;br /&gt;* a condition that is used to wait for an editor to open on a specific file.&lt;br /&gt;*&lt;br /&gt;* @author dgreen&lt;br /&gt;*/&lt;br /&gt;public class EditorOpenCondition extends DefaultCondition {&lt;br /&gt;private final IFile file;&lt;br /&gt;&lt;br /&gt;public EditorOpenCondition(IFile file) {&lt;br /&gt;this.file = file;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public String getFailureMessage() {&lt;br /&gt;return String.format("Timed out waiting for editor on %s to open",file.getFullPath());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public boolean test() throws Exception {&lt;br /&gt;if (!file.exists()) {&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;return UIThreadRunnable.syncExec(new UIThreadRunnable.BoolResult() {&lt;br /&gt;public boolean run() {&lt;br /&gt;IEditorReference[] editorReferences = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences();&lt;br /&gt;for (IEditorReference reference: editorReferences) {&lt;br /&gt; try {&lt;br /&gt;  IEditorInput input = reference.getEditorInput();&lt;br /&gt;  if (input instanceof IFileEditorInput) {&lt;br /&gt;   IFileEditorInput editorInput = (IFileEditorInput) input;&lt;br /&gt;   if (editorInput.getFile().equals(file)) {&lt;br /&gt;    return true;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (input instanceof IStorageEditorInput) {&lt;br /&gt;   IStorageEditorInput editorInput = (IStorageEditorInput) input;&lt;br /&gt;   IPath fullPath = editorInput.getStorage().getFullPath();&lt;br /&gt;   if (fullPath.equals(file.getFullPath())) {&lt;br /&gt;    return true;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; } catch (PartInitException e) {&lt;br /&gt;  e.printStackTrace();&lt;br /&gt; } catch (CoreException e) {&lt;br /&gt;  e.printStackTrace();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;});&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;5. Create a Java-based DSL for often-repeated use of editors or views.&lt;/b&gt;  For example, for a static class diagram editor you might end up with a DSL that could be used as follows:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;DomainModelDsl domainModelDsl = new DomainModelDsl();&lt;br /&gt;domainModelDsl.create(project, "domain.dm");&lt;br /&gt;&lt;br /&gt;domainModelDsl.&lt;br /&gt;createEntity("A").&lt;br /&gt;createEntity("B");&lt;br /&gt;&lt;br /&gt;domainModelDsl.entity("A").extension("B");&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Inside the DSL implementation the dirty work of manipulating the editor occurs.  This makes it really fast to create complex tests that cover lots of ground.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;6. Create GEF EditPart wappers.&lt;/b&gt;  For GEF-based editors SWTBot doesn't give a lot of lift.  For these you'll need to create classes similar to those provided by SWTBot, but instead of being widget-focused, they'll need to be EditPart-focused.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;All in all I'm very impressed with SWTBot, which has finally delivered a viable automated GUI test framework for Eclipse and Eclipse RCP applications.  I'm pleased to see that SWTBot has made an &lt;a href="http://www.eclipse.org/proposals/swtbot/"&gt;Eclipse project proposal&lt;/a&gt;, which if approved will hopefully lead to continued improvements and community adoption.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-4270813416718277945?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/4270813416718277945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=4270813416718277945' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4270813416718277945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/4270813416718277945'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/09/eclipse-gui-testing-is-viable-with.html' title='Eclipse GUI Testing Is Viable With SWTBot'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3122533636292453770</id><published>2008-09-08T19:56:00.000-07:00</published><updated>2008-09-08T20:41:11.977-07:00</updated><title type='text'>Beyond Rich Text (Part 2): Displaying Images in an SWT StyledText</title><content type='html'>Recently &lt;a href="http://greensopinion.blogspot.com/2008/09/beyond-rich-text-tricks-using.html"&gt;I wrote about a few simple tricks&lt;/a&gt; that make it possible to display more than just text in a StyledText widget.  In this article I take it a step further: displaying images in an SWT StyledText widget.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Normally a StyledText widget cannot display images.  Why would you want to display images in a text control, you might ask?  Well, there are cases where a rich text control is used to render text which may have embedded images.  Take for example the &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; task editor.  It displays bug descriptions and comments.  In some repositories (such as Trac or JIRA) it's possible to use markup to display rich text and images.  It's common to see screenshots or other visual data included in bug reports.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To display images in a StyledText we use a small variation on the tricks from &lt;a href="http://greensopinion.blogspot.com/2008/09/beyond-rich-text-tricks-using.html"&gt;my previous post&lt;/a&gt;.  Previously I used an annotation to mark the location of the thing I wanted to draw, and a painter to paint it.  This approach works well when we know the size of the thing we're about to draw (such as a character or horizontal rule).  In the case of images, it gets more complicated.  We need to know the size of the image so that we can create enough space for it in the text.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since loading image data can be slow (over a network or from a busy disk) we must load the image asynchronously and render it when it becomes available.  We can get away with it since users are used to this experience -- that's how web browsers work.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So here's a run-down of the technique I used:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Mark the location of the image with an annotation&lt;/li&gt;&lt;li&gt;Asynchronously download the image&lt;/li&gt;&lt;li&gt;When the image becomes available, determine its height (in pixels)&lt;/li&gt;&lt;li&gt;Determine the font in use at the annotated location&lt;/li&gt;&lt;li&gt;Using font metrics compute the number of blank lines required to create enough vertical space (in pixels) to display the image without obscuring text&lt;/li&gt;&lt;li&gt;Insert the required number of newlines into the text widget&lt;/li&gt;&lt;li&gt;Adjust text presentation offsets (some styled regions may need to be translated by the same number of characters as we inserted in the previous step)&lt;/li&gt;&lt;li&gt;Associate the image data with the annotation so that the painter will have access to it&lt;/li&gt;&lt;li&gt;Cause the text widget to repaint&lt;/li&gt;&lt;li&gt;Use the custom painter to paint the image in the empty space that we created&lt;/li&gt;&lt;/ol&gt;Here's a screenshot demonstrating this technique in action:&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SMXsxvUqowI/AAAAAAAAAB4/cDv0Q6j3VXc/s1600-h/Picture+1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SMXsxvUqowI/AAAAAAAAAB4/cDv0Q6j3VXc/s400/Picture+1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5243857680403702530" /&gt;&lt;/a&gt;It works!  The SWT StyledText can now display images!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Though the result is pleasing, there remain a few issues with this approach:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Images don't flow with the text, that is we can't wrap text around the image on the left or right side, only above and below.&lt;/li&gt;&lt;li&gt;Images aren't included in clipboard operations such as copy and paste.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;If you're interested in the code involved in this technique &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=246034"&gt;you can find it here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The fact that we can do this using standard JFace and SWT APIs is a testament to the quality and completeness of the platform.  Thanks Eclipse!&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3122533636292453770?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3122533636292453770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3122533636292453770' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3122533636292453770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3122533636292453770'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/09/beyond-rich-text-displaying-images-in.html' title='Beyond Rich Text (Part 2): Displaying Images in an SWT StyledText'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SMXsxvUqowI/AAAAAAAAAB4/cDv0Q6j3VXc/s72-c/Picture+1.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1391405770367614885</id><published>2008-09-02T08:36:00.000-07:00</published><updated>2008-09-02T09:10:44.131-07:00</updated><title type='text'>Beyond Rich Text:  Tricks Using SourceViewer, Annotations and AnnotationPainter</title><content type='html'>&lt;div&gt;&lt;br /&gt;An SWT StyledText can display text attributes such as bold, italic and strikethrough, alter colors and fonts.  What are we to do when we need to go beyond rich text formatting?  In this article we present three simple tricks using standard SWT and JFace APIs to create a polished presentation where text attributes alone won't do the job.&lt;/div&gt;&lt;br /&gt;&lt;h2 style="color:black"&gt;Marking The Spot&lt;/h2&gt;&lt;br /&gt;&lt;div&gt;In order to display something interesting, we need to mark the spot.  Normally this is done with a TextPresentation, which specifies character offsets and style ranges.  Fortunately the Eclipse APIs give us another mechanism to mark regions of text in an extensible manner: annotations.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The key APIs at our disposal are as follows:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;code&gt;org.eclipse.jface.text.source.Annotation&lt;/code&gt;&lt;br /&gt;&lt;code&gt;org.eclipse.jface.text.source.AnnotationModel&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Using these APIs we can create annotations and specify their location.  An easy place to do this is in your document partitioner.  It will be called at the appropriate times to partition your document.  By using a &lt;code&gt;RuleBasedPartitionScanner&lt;/code&gt; you can modify your rules to create the appropriate annotations.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Now that our document is annotated, we know where we need to draw.  To hook up the drawing strategy, we add the following code to the initialization of the SourceViewer:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;IAnnotationAccess annotationAccess = new IAnnotationAccess() {&lt;br /&gt;public Object getType(Annotation annotation) {&lt;br /&gt;    return annotation.getType();&lt;br /&gt;}&lt;br /&gt;public boolean isMultiLine(Annotation annotation) {&lt;br /&gt;    return true;&lt;br /&gt;}&lt;br /&gt;public boolean isTemporary(Annotation annotation) {&lt;br /&gt;    return true;&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;AnnotationPainter painter = new AnnotationPainter(sourceViewer, annotationAccess);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Now all we have to do is add a drawing strategy to the painter for every kind of annotation that we're interested in drawing.  Read on to find out how we do that.&lt;div&gt;&lt;br /&gt;&lt;h2 style="color:black"&gt;Repainting Characters&lt;/h2&gt;&lt;br /&gt;&lt;div&gt;Not all fonts can display all characters.  This is problematic in an application that is internationalized or one where the user can change the font.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Take for example bullets.  Unicode &lt;code&gt;\u2022&lt;/code&gt; can be used to display a solid round bullet with most fonts, but what about an empty one, or a square one?  These characters cannot be reliably found in commonly used fonts.  The trick we use is to always use the &lt;code&gt;\u2022&lt;/code&gt; character, but repaint it where we want to display something more interesting.  By doing that we get the display just right and the text works nicely with copy/paste operations.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;To make it work, we create annotations where our bullet characters are in the document.  We then hook up a bullet drawing strategy to our painter as follows:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;painter.addDrawingStrategy(BulletAnnotation.TYPE, new BulletDrawingStrategy());&lt;br /&gt;painter.addAnnotationType(BulletAnnotation.TYPE, BulletAnnotation.TYPE);&lt;br /&gt;painter.setAnnotationTypeColor(BulletAnnotation.TYPE, getTextWidget().getForeground());&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;The painter won't invoke our drawing strategy unless the type and type color are also added.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;What does our bullet annotation look like?  It needs to have enough information for the drawing strategy to know what to draw.  In this case the shape of the bullet is dependent on the 'level' of indentation.  Here's what I used:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;public class BulletAnnotation extends Annotation {&lt;br /&gt;&lt;br /&gt;public static final String TYPE = "org.eclipse.mylyn.internal.wikitext.ui.viewer.annotation.bullet";&lt;br /&gt;&lt;br /&gt;private final int indentLevel;&lt;br /&gt;&lt;br /&gt;public BulletAnnotation(int indentLevel) {&lt;br /&gt;super(TYPE, false, Integer.toString(indentLevel));&lt;br /&gt;this.indentLevel = indentLevel;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public int getIndentLevel() {&lt;br /&gt;return indentLevel;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Now we need to implement our drawing strategy.  The drawing strategy must 'erase' the existing bullet character and then draw the new bullet shape where the old bullet was.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;We erase the previous character by drawing a rectangle the size of the character in the background color:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;// erase whatever character was there&lt;br /&gt;gc.fillRectangle(left.x, left.y, right.x - left.x, lineHeight);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;then we draw the new shape:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;// now paint the bullet&lt;br /&gt;switch (bullet.getIndentLevel()) {&lt;br /&gt;case 1: // round solid bullet&lt;br /&gt; gc.setBackground(color);&lt;br /&gt; gc.fillOval(hcenter - 3, vcenter - 2, 5, 5);&lt;br /&gt; break;&lt;br /&gt;case 2: // round empty bullet&lt;br /&gt; gc.setForeground(color);&lt;br /&gt; gc.drawOval(hcenter - 3, vcenter - 3, 5, 5);&lt;br /&gt; break;&lt;br /&gt;default: // square bullet&lt;br /&gt; gc.setBackground(color);&lt;br /&gt; gc.fillRectangle(hcenter - 3, vcenter - 2, 5, 5);&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Here's a screenshot showing an example of this technique in use:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_vw9l2nnub6c/SL1fIXv3JDI/AAAAAAAAABo/4GWMMFdPvww/s1600-h/Picture+1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SL1fIXv3JDI/AAAAAAAAABo/4GWMMFdPvww/s400/Picture+1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5241450138747479090" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2 style="color:black"&gt;Drawing Non-Characters&lt;/h2&gt;&lt;br /&gt;&lt;div&gt;Sometimes there's a need to display non-characters.  For example, browsers display a horizontal line for &amp;lt;hr /&amp;gt; (horizontal rule).  By marking the spot with annotations and registering a custom painter, we can do the same thing.  Here's the result we're looking for.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vw9l2nnub6c/SL1fz_LHdlI/AAAAAAAAABw/UqKLpUMk_ic/s1600-h/Picture+2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_vw9l2nnub6c/SL1fz_LHdlI/AAAAAAAAABw/UqKLpUMk_ic/s400/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5241450888065152594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;To create this effect we put an empty line in the text we're displaying and annotate it with a HorizontalRuleAnnotation.   Drawing the annotation is easy:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {&lt;br /&gt;if (gc != null) {&lt;br /&gt;final Color foreground = gc.getForeground();&lt;br /&gt;&lt;br /&gt;Point left = textWidget.getLocationAtOffset(offset);&lt;br /&gt;Point right = textWidget.getLocationAtOffset(offset + length);&lt;br /&gt;if (left.x &gt; right.x) {&lt;br /&gt; // hack: sometimes linewrapping text widget gives us the wrong x/y for the first character of a line that&lt;br /&gt; // has been wrapped.&lt;br /&gt; left.x = 0;&lt;br /&gt; left.y = right.y;&lt;br /&gt;}&lt;br /&gt;right.x = textWidget.getClientArea().width;&lt;br /&gt;&lt;br /&gt;int baseline = textWidget.getBaseline(offset);&lt;br /&gt;&lt;br /&gt;int vcenter = left.y + (baseline / 2) + (baseline / 4);&lt;br /&gt;&lt;br /&gt;gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance&lt;br /&gt;gc.setLineStyle(SWT.LINE_SOLID);&lt;br /&gt;&lt;br /&gt;left.x += 3;&lt;br /&gt;right.x -= 5;&lt;br /&gt;vcenter -= 2;&lt;br /&gt;&lt;br /&gt;if (right.x &gt; left.x) {&lt;br /&gt; // draw the shadow&lt;br /&gt; gc.setForeground(shadowForeground);&lt;br /&gt; gc.drawRectangle(left.x, vcenter, right.x - left.x, 2);&lt;br /&gt;&lt;br /&gt; // draw the horizontal rule&lt;br /&gt; gc.setForeground(color);&lt;br /&gt; gc.drawLine(left.x, vcenter, right.x, vcenter);&lt;br /&gt; gc.drawLine(left.x, vcenter, left.x, vcenter + 2);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;gc.setForeground(foreground);&lt;br /&gt;} else {&lt;br /&gt;textWidget.redrawRange(offset, length, true);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;As before, we hook the drawing strategy up to the painter:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  painter.addDrawingStrategy(HorizontalRuleAnnotation.TYPE, new HorizontalRuleDrawingStrategy());&lt;br /&gt;  painter.addAnnotationType(HorizontalRuleAnnotation.TYPE, HorizontalRuleAnnotation.TYPE);&lt;br /&gt;  painter.setAnnotationTypeColor(HorizontalRuleAnnotation.TYPE,  getTextWidget().getForeground());&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;h2 style="color:black"&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Eclipse provides some powerful APIs for hooking into the painting of StyledText.  Using some simple tricks we can create powerful polished visuals in an SWT user interface.  All of these techniques are applied in the &lt;a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText"&gt;Mylyn WikiText&lt;/a&gt; project, where you can find source code that works.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1391405770367614885?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1391405770367614885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1391405770367614885' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1391405770367614885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1391405770367614885'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/09/beyond-rich-text-tricks-using.html' title='Beyond Rich Text:  Tricks Using SourceViewer, Annotations and AnnotationPainter'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SL1fIXv3JDI/AAAAAAAAABo/4GWMMFdPvww/s72-c/Picture+1.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-8110109485373634337</id><published>2008-08-17T09:51:00.000-07:00</published><updated>2008-08-17T09:57:33.118-07:00</updated><title type='text'>Mik Kersten on Rich Editing for Tasks via Mylyn WikiText</title><content type='html'>Mik Kersten has writen a great article about WikiText and what it means for Mylyn users, titled &lt;a href="http://tasktop.com/blog/?p=37"&gt;Rich Editing for Tasks via Mylyn WikiText&lt;/a&gt;.  He really does a great job of capturing the essence behind Eclipse's powerful source editing features and how it was applied to the markup editing features of WikiText.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-8110109485373634337?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/8110109485373634337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=8110109485373634337' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8110109485373634337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/8110109485373634337'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/08/mik-kersten-has-writen-great-article.html' title='Mik Kersten on Rich Editing for Tasks via Mylyn WikiText'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-255464302827262974</id><published>2008-08-12T17:04:00.000-07:00</published><updated>2008-08-12T17:29:40.670-07:00</updated><title type='text'>Textile-J Is Moving to Mylyn WikiText!</title><content type='html'>The &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt; project &lt;a href="https://textile-j.dev.java.net/mylyn-wikitext.html"&gt;announced today&lt;/a&gt; that it is moving to Eclipse as the &lt;b&gt;Mylyn WikiText&lt;/b&gt; component.  Textile-J, now WikiText, is a library for parsing and displaying &lt;a href="http://en.wikipedia.org/wiki/Lightweight_markup_language"&gt;lightweight markup languages&lt;/a&gt; (&lt;a href="http://en.wikipedia.org/wiki/Wikitext"&gt;wikitext&lt;/a&gt;).  With the addition of WikiText, &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; and &lt;a href="http://www.tasktop.com/"&gt;Tasktop&lt;/a&gt; will be able to display markup as it is intended and provide a markup-aware authoring environment.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The move was primarily motivated by my desire to see Mylyn provide first-class support for lightweight markup languages in its task editor.  Even though most issue-tracking systems provide support for markup (for example &lt;a href="http://trac.edgewall.org/"&gt;Trac&lt;/a&gt; and &lt;a href="http://www.atlassian.com/software/jira/"&gt;JIRA&lt;/a&gt;), WikiText can also benefit those who use issue-tracking systems with no markup support.  For example, using &lt;a href="http://www.bugzilla.org/"&gt;Bugzilla&lt;/a&gt; with WikiText will enable users to use markup in their descriptions and comments.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Overall I'm very excited about the move to Mylyn WikiText, as it will not only improve Mylyn and Tasktop but will also benefit existing Textile-J users by ensuring a higher quality library.  In designing the move I built some protection for existing Textile-J users into the WikiText project charter so that existing users will be able to continue using WikiText independently of an Eclipse runtime.  This is important for users that have integrated Textile-J into their non-Eclipse projects and for those who use the Textile-J Ant tasks.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;This project move has been many months in the making, and with the backing and help of the Mylyn team has finally come to fruition.  Many thanks go to the team, especially Seffen Pingel, Mik Kersten and Jingwen 'Owen' Ou for their help in making this a reality.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;WikiText is available immediately for download from the &lt;a href="http://www.eclipse.org/mylyn/downloads/"&gt;Mylyn incubation weekly update site&lt;/a&gt;.  To find out more about WikiText, see the &lt;a href="https://textile-j.dev.java.net/mylyn-wikitext.html"&gt;original announcement&lt;/a&gt; and the &lt;a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText"&gt;WikiText project homepage&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-255464302827262974?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/255464302827262974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=255464302827262974' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/255464302827262974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/255464302827262974'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/08/textile-j-is-moving-to-mylyn-wikitext.html' title='Textile-J Is Moving to Mylyn WikiText!'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2779556195936190058</id><published>2008-08-06T17:07:00.000-07:00</published><updated>2009-05-11T13:05:51.984-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SoyLatte'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6'/><title type='text'>Eclipse Ganymede on SoyLatte (Mac+Java6+Eclipse)</title><content type='html'>Mac has no Java 6.  The &lt;a href="http://docs.info.apple.com/article.html?artnum=307403"&gt;Java 6 beta that Apple released is only 64-bit&lt;/a&gt;, which is no good for running Eclipse.  Not only is this a major annoyance, it is a problem for developers that need to profile and take heap dumps of their applications.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some time ago I found &lt;a href="http://mail.openjdk.java.net/pipermail/porters-dev/2007-December/000019.html"&gt;this post&lt;/a&gt; by Landon Fuller which supposedly launches Eclipse on &lt;a href="http://landonf.bikemonkey.org/static/soylatte"&gt;SoyLatte&lt;/a&gt;.  While I believe that Landon is a genius, it did not work for me.  Well lately I've needed to take some heap dumps, so I tried again.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's what worked for me:&lt;/div&gt;&lt;div&gt;&lt;code&gt;&lt;br /&gt;/Users/dgreen/Documents/java/soylatte16-i386-1.0.2/bin/java  -server -Xms128m -Xmx1536m -XX:MaxPermSize=256m -Dosgi.requiredJavaVersion=1.5 -Dorg.eclipse.swt.internal.carbon.smallFonts  -Djava.library.path=/Users/dgreen/bin/jnilib  -cp /Applications/eclipse/Eclipse.app/Contents/MacOS/../../../plugins/org.eclipse.equinox.launcher_1.0.100.v20080509-1800.jar org.eclipse.equinox.launcher.Main -os macosx -ws carbon -arch x86 -showsplash -launcher /Applications/eclipse/Eclipse.app/Contents/MacOS/eclipse -name Eclipse --launcher.library /Applications/eclipse/Eclipse.app/Contents/MacOS//../../../plugins/org.eclipse.equinox.launcher.carbon.macosx_1.0.100.v20080509-1800/eclipse_1114.so -startup /Applications/eclipse/Eclipse.app/Contents/MacOS/../../../plugins/org.eclipse.equinox.launcher_1.0.100.v20080509-1800.jar -data /Users/dgreen/Documents/workspace -launcher /Applications/eclipse/Eclipse.app/Contents/MacOS/eclipse -keyring /Users/dgreen/.eclipse_keyring -consoleLog -showlocation -vm /Users/dgreen/Documents/java/soylatte16-i386-1.0.2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I also had to extract all *.jnilib files from &lt;tt&gt;eclipse/plugins/org.eclipse.swt.carbon.macosx_3.4*.jar&lt;/tt&gt; and copy them into &lt;tt&gt;/Users/dgreen/bin/jnilib&lt;/tt&gt;&lt;/div&gt;&lt;div&gt;Finally, Eclipse running in a Java 6 VM on a Mac!  Apple, time to get your Java act in gear.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Update 2008-08-21: &lt;/i&gt;SoyLatte 1.0.2 works for me, but 1.0.3 and &lt;a href="http://landonf.bikemonkey.org/code/java/OpenJDK_7_Binaries.20080820.html"&gt;OpenJDK 7 darwin packages posted here&lt;/a&gt; don't.  If you find a solution, please let me know!&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;i&gt;Update 2009-05-11:&lt;/i&gt; See this related article &lt;a href="http://greensopinion.blogspot.com/2009/05/eclipse-35-galileo-on-jdk-7.html"&gt;Eclipse 3.5 (Galileo) on Java 7&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2779556195936190058?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2779556195936190058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2779556195936190058' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2779556195936190058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2779556195936190058'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/08/eclipse-ganymede-on-soylatte.html' title='Eclipse Ganymede on SoyLatte (Mac+Java6+Eclipse)'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1935939954997587869</id><published>2008-07-08T20:26:00.000-07:00</published><updated>2008-07-08T21:02:43.550-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='View'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='Perspective'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><title type='text'>Optimizing Screen Real-Estate in Eclipse</title><content type='html'>I recently read &lt;a href="http://www.webfoot.com/blog/category/programmer-productivity/"&gt;an article&lt;/a&gt; stating that the Eclipse Package Explorer and Outline view are wasted space.  Mylyn also &lt;a href="http://wiki.eclipse.org/index.php/Mylyn/User_Guide#Eclipse_settings"&gt;recommends&lt;/a&gt; minimizing or closing the Outline view.  Eclipse makes it really easy to make views go away with &lt;a href="http://www.cornilliac.com/blog/2007/08/how-to-use-eclipse-fast-view/"&gt;fast views&lt;/a&gt; and minimizing tab groups.  An in-place Outline can be activated with Ctrl+O.  All of these focus on providing maximum screen real-estate for editors.  While these paradigms work really well for many editors, some editors are designed to make use of the Outline view and Properties view as integral parts of the editor.  So how does a tool designer resolve this tension between editor styles?  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;Imagine that a developer is switching from a Java source editor -- where views should be minimized -- to an editor that makes use of the properties view and outline view.  When switching back and forth the developer is going to feel friction.  To try this out, try using the XSD editor in Eclipse with the properties view.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enter the editor-controls-view paradigm.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For editors that require the outline and properties views, the editor 'restores' the views to their visible state when the editor gains focus.  When such an editor loses focus, the views are minimized to their iconized state.   Eclipse could support this editor-controls-view paradigm by allowing editors to declare their affinity with specific views. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This solution provides an optimal use of screen real-estate for both editing scenarios without requiring the developer to take additional action every time she switches editors.  It also does not require the developer to switch perspectives, which is a heavier solution to the same problem.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1935939954997587869?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1935939954997587869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1935939954997587869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1935939954997587869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1935939954997587869'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/07/optimizing-screen-real-estate-in.html' title='Optimizing Screen Real-Estate in Eclipse'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6578851312433944613</id><published>2008-06-18T20:17:00.000-07:00</published><updated>2008-07-04T12:05:24.713-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CVS'/><category scheme='http://www.blogger.com/atom/ns#' term='Subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='SVN'/><title type='text'>Why Is Eclipse.org Still On CVS?</title><content type='html'>&lt;div&gt;With the &lt;a href="http://blogs.open.collab.net/svn/2008/06/subversion-150.html"&gt;recent announcement&lt;/a&gt; of their 1.5 release, &lt;a href="http://subversion.tigris.org/"&gt;Subversion (SVN)&lt;/a&gt; has clearly surpassed their original goal "to build a compelling replacement for CVS in the open source community".  So why are  all &lt;a href="http://www.eclipse.org/"&gt;Eclipse &lt;/a&gt;projects using CVS as their source repository?   I explore some of the reasons that go beyond the &lt;a href="http://www.itbusinessedge.com/item/?ci=11982"&gt;commonly&lt;/a&gt; &lt;a href="http://subversion.tigris.org/testimonials.html"&gt;extolled&lt;/a&gt; &lt;a href="http://www.linux.ie/articles/subversion/"&gt;superiority&lt;/a&gt; of SVN.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In &lt;a href="http://wiki.eclipse.org/Mylyn/Incubator/WikiText"&gt;a new collaboration&lt;/a&gt; with the Eclipse &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn project&lt;/a&gt;, I've started working with CVS again after about 4 years of active &lt;a href="http://subversion.tigris.org/"&gt;SVN&lt;/a&gt; usage.  Although I had used CVS previously (for several years) in my career, I had forgotten  how bad it really is.  After having near-instant branching, fast history retrieval, atomic commits, efficient storage, directory versioning, commit version numbers  and  web browser access, it is really, really hard to go back.  What the Eclipse foundation may not realize is that using CVS is costing them time and money.  So why CVS?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Tooling&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Eclipse committers use Eclipse.  From the early days, Eclipse has had really good support for CVS.  UI integration is tight, and CVS plug-in features such as workspace-relative patches make it easy to collaborate with others.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The &lt;a href="http://subclipse.tigris.org/"&gt;Subclipse&lt;/a&gt; plug-in for Eclipse is excellent, however it's still not as good as the CVS plug-in.  The &lt;a href="http://www.eclipse.org/subversive/"&gt;Subversive&lt;/a&gt; plug-in for Eclipse (which is now an Eclipse.org incubation project) is unusable in its current state.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One Eclipse committer claims that there remain some major usability issues with SVN inside of Eclipse.  I'm not sure if he was talking about Subclipse or Subversive -- and in part I agree.  If more Eclipse committers were using SVN, then these plug-ins would improve to the same polished level of quality seen in the CVS plug-in.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Inertia&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Projects that are already using CVS have inertia in the form of commit history, tagging and branching habits, build scripts, etc.  It's difficult to overcome this inertia.  I've done it on several large projects, and it can be painful.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Training&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Eclipse committers who use CVS daily are used to CVS and the way it works.  Minor differences in UI paradigms of the Eclipse plug-ins and some larger differences with how SVN works take effort to learn and overcome.  There is a cost associated with this, and people dislike change.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4. Lack of Organizational Support&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Many issues facing the Eclipse project teams would be greatly diminished if the Eclipse foundation provided tools, tutorials, documentation, recommendations and guidelines for making the switch.  With the right support, Eclipse could make the switch one project at a time over weeks, months or even years.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5&lt;/span&gt;&lt;span style=""&gt;&lt;b&gt;. Time Pressures&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Eclipse committers are already busy supporting Eclipse and meeting their milestones.  The last thing they want is to be spending time learning new tools and managing project infrastructure.  The perceived time-savings of remaining on CVS is a falsehood.  I can't tell you how much time I've wasted waiting for a CVS update to complete.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Eclipse and Eclipse users suffer because of continued use of an antiquated source versioning system.  The cost in time and money is substantial.  With the right vision and organizational support, Eclipse can overcome their roots in CVS and become a more agile organization.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Update July 4 2008:&lt;/i&gt; A few Eclipse projects are using SVN for revision control.  See &lt;a href="http://dev.eclipse.org/svnroot/technology/"&gt;Eclipse SVN technology projects&lt;/a&gt; for a list of technology projects in SVN.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6578851312433944613?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6578851312433944613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6578851312433944613' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6578851312433944613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6578851312433944613'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/06/why-is-eclipseorg-still-on-cvs.html' title='Why Is Eclipse.org Still On CVS?'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1339742509532258024</id><published>2008-06-16T11:36:00.000-07:00</published><updated>2008-06-16T12:40:57.349-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDE'/><category scheme='http://www.blogger.com/atom/ns#' term='MDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mylyn'/><category scheme='http://www.blogger.com/atom/ns#' term='Tasktop'/><title type='text'>Focus Your Work Week (Even More): Beyond Mylyn 3.0</title><content type='html'>&lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt; and &lt;a href="http://www.tasktop.com/"&gt;Tasktop&lt;/a&gt; provide a revolutionary approach to focusing the Eclipse UI.  They work on the concept of having a task 'context', that defines a degree of interest (DOI) for resources related to a task.  We can make this task context even richer and deliver efficiencies by providing first-class integration of our MDD tools with Mylyn.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When using Mylyn, developers build up their task context using their natural navigation though source code while performing the task.  For example, to fix a bug a developer opens the relevant Java source files, and Mylyn will automatically add these to the task context.  Based on the frequency and type of interactions with these files, Mylyn will increase or decrease the DOI.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;img align="right" style="display:block; margin:0px auto 10px;" src="http://4.bp.blogspot.com/_vw9l2nnub6c/SFa4FmY1ClI/AAAAAAAAABQ/5a-CbBE8Bq4/s400/Picture+2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5212556025071471186" /&gt;The issue here is that developers start a task with an empty context.  The context is empty because Mylyn doesn't know what the developer is doing (or what resources are related to a task), and as a result the Package Explorer and other views in Eclipse are initially empty.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Developers can get around this problem by disabling the Mylyn filter on those views, at the cost of having views filled with all kinds of information that the developer doesn't need to complete the task.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enter MDD tooling.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;MDD tools have the advantage of knowing which things are related.  For example, an MDD model is typically used to generate source code.  Thus the source code is related to the model.  Models rarely exist in isolation: they usually have associations with other models.  Thus MDD tools know which source files are related to which models, and which models are related to other models.  Given a specific model, most files in a system can be related in some way with a 'degree of separation'.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How can this information be used to our advantage with Mylyn?  With first-class Mylyn integration MDD tools can let Mylyn 'see' into the MDD models.  Furthermore, these MDD tools can tell Mylyn which files to put into a task context based on how they are related to models in the context.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/SFa5qCysK7I/AAAAAAAAABY/EzVUAz3Lu3o/s1600-h/Picture+4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/SFa5qCysK7I/AAAAAAAAABY/EzVUAz3Lu3o/s400/Picture+4.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5212557750683052978" /&gt;&lt;/a&gt;&lt;br /&gt;Developers can start their task by selecting the relevant MDD model, and tell the MDD tools to 'populate' the Mylyn task context.  The developer starts their task with a context that already has interesting things in it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The richer and more expressive the MDD models, the better the integration can be.  For example, if the MDD models can express business requirements and methodology, development tasks and other project tasks can be created in a task repository such as Bugzilla or JIRA, prepopulated with a Mylyn context before the developer even starts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1339742509532258024?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1339742509532258024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1339742509532258024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1339742509532258024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1339742509532258024'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/06/focus-your-work-week-even-more-beyond.html' title='Focus Your Work Week (Even More): Beyond Mylyn 3.0'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_vw9l2nnub6c/SFa4FmY1ClI/AAAAAAAAABQ/5a-CbBE8Bq4/s72-c/Picture+2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-3622479679227973817</id><published>2008-05-15T10:54:00.000-07:00</published><updated>2008-05-15T11:15:04.546-07:00</updated><title type='text'>Code coverage tools can be misleading</title><content type='html'>Software development best-practices encourage a thorough automated test strategy and continuous integration (for me that's &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt;, &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt; and &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt;).  Even better is to measure code coverage of your tests using a tool such as &lt;a href="http://cobertura.sourceforge.net/"&gt;Cobertura&lt;/a&gt;.  Code coverage tools will tell you which code was executed by your tests.  This kind of analysis is ideal for finding untested code, however it's easy to be mislead by code coverage reports.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Take for example the following ficticious example:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public class Calculator {&lt;br /&gt;BigDecimal currentValue = new BigDecimal(0);&lt;br /&gt;Stack&lt;operation&gt; history = new Stack&lt;operation&gt;();&lt;br /&gt;&lt;br /&gt;public void add(BigDecimal augend) {&lt;br /&gt; currentValue = currentValue.add(augend);&lt;br /&gt; commandPerformed(Type.ADD,augend);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void commandPerformed(Type type, BigDecimal augend) {&lt;br /&gt; // TODO Auto-generated method stub&lt;br /&gt; System.out.println(type+": "+augend);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void undo() {&lt;br /&gt; // TODO&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void redo() {&lt;br /&gt; // TODO&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/operation&gt;&lt;/operation&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Initially the coverage report will show that all of this class is not covered.&lt;/div&gt;&lt;div&gt;Create a test for the add() function, and the code coverage reports will show that the commandPerformed() has been covered -- even though it is clearly not doing what it is suppoed to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Be careful when reading code coverage reports -- don't assume that code that is covered is tested.  The most a coverage report can tell you is if code is not covered at all by your tests.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-3622479679227973817?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/3622479679227973817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=3622479679227973817' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3622479679227973817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/3622479679227973817'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/05/code-coverage-tools-can-be-misleading.html' title='Code coverage tools can be misleading'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-2571577870787060654</id><published>2008-04-29T07:50:00.000-07:00</published><updated>2009-03-16T21:55:16.111-07:00</updated><title type='text'>Authoring Cross-Platform Wiki Markup</title><content type='html'>&lt;p&gt;I'll never think of line delimiters the same way again.  Mac uses \r, Unix uses \n, and Windows uses \r\n.  Who cares?  Well, when it comes to writing platform-independent Wiki markup, it matters.&lt;/p&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Take for example a document that was written on a Unix platform.  Every line is separated by a single \n character.  For Wiki markup, paragraphs are separated by an empty line, which is represented in the document as two consecutive newlines \n\n.  When a Wiki markup parser converts this document to HTML, it looks for the empty newline (the second \n) and uses that to close the previous paragraph and start a new one.&lt;/p&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Now what happens if the document is opened and edited on a Mac?  Mac uses \r as a newline.  Suppose the Mac user adds a new empty line just before an existing one in this same document.  For example:&lt;/p&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Prior to editing:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;some text\nmore text&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;After editing:&lt;/p&gt; &lt;pre&gt;&lt;code&gt;some text\r\nmore text&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Prior to adding the newline, the document contains a single \n character, and afterwards the document contains \r\n.  In the users editor (on a Mac) the \r\n will appear visually as two lines, however \r\n happens to be a Windows line delimiter and thus will be parsed by Windows-accomodating Wiki markup parsers as a single line delimiter.  In many documents this may not matter, however in Wiki markup this can make a big difference.&lt;/p&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Editors that support editing Wiki markup on multiple platforms must be coded carefully to avoid this issue.  For &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt; this means that the end-of-line markers are converted to the platform default when the editor first opens a file.&lt;/p&gt; &lt;p&gt;&lt;br /&gt;&lt;/p&gt; &lt;p&gt;Who knew that line delimiters could be so important.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-2571577870787060654?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/2571577870787060654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=2571577870787060654' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2571577870787060654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/2571577870787060654'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/04/authoring-cross-platform-wiki-markup.html' title='Authoring Cross-Platform Wiki Markup'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-9209552831804846852</id><published>2008-04-24T20:39:00.000-07:00</published><updated>2008-04-24T20:50:03.861-07:00</updated><title type='text'>Roller and Textile-J</title><content type='html'>The &lt;a href="https://roller.dev.java.net/"&gt;Roller Support Project&lt;/a&gt; has &lt;a href="http://article.gmane.org/gmane.comp.java.roller.user/3051"&gt;announced&lt;/a&gt; plugins for parsing markup using &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt;.  This brings support for Textile, MediaWiki, Confluence and TracWiki markup to the &lt;a href="http://roller.apache.org/"&gt;Apache Roller&lt;/a&gt; blog server.  Great to see projects like this picking up Textile-J, good work &lt;a href="http://www.busybuddha.org"&gt;Anil&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-9209552831804846852?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/9209552831804846852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=9209552831804846852' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9209552831804846852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/9209552831804846852'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/04/roller-and-textile-j.html' title='Roller and Textile-J'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-6476212160691802188</id><published>2008-04-15T06:53:00.000-07:00</published><updated>2008-04-15T06:54:14.512-07:00</updated><title type='text'>JUnit Made It Easy!</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: Times; "&gt;&lt;div style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 3px; padding-right: 3px; padding-bottom: 3px; padding-left: 3px; width: auto; font: normal normal normal 100%/normal Georgia, serif; text-align: left; "&gt;Recently I posted about &lt;a href="http://greensopinion.blogspot.com/2008/04/evolving-wiki-markup-parser.html"&gt;evolving the architecture of a markup parser&lt;/a&gt;.  During its evolution I ended up completely rewriting the parser to a new architecture.  A solid &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; test suite made it possible to do this while maintaining confidence in the quality of the new code.  This article details the nature of JUnits that make such a switch easy.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Methodology:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While developing the &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J&lt;/a&gt; markup parser I incrementally improved the parser by adding support for markup syntax.  Paragraphs were added first, then bold text, then lists, you get the idea.  As support was added for each syntax feature, I created one or more JUnit tests to exercise the feature.  I inspected the results of the HTML output from the small snippet of Textile used, and when I was satisfied created one or more assertions in the JUnit.  This made development easy because:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;I could isolate the syntax that I wanted to support and test it independently of other syntax&lt;/li&gt;&lt;li&gt;Since the tests were small, stepping through the code in the debugger only involved the relevant code&lt;/li&gt;&lt;li&gt;It's easy to see when something breaks that worked before&lt;/li&gt;&lt;li&gt;I could be confident in the quality of the parser&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;b&gt;The Result&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The result of using this methodology was a JUnit test suite of over 100 tests, and I could easily measure progress in supporting Textile markup features.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Markup Parser Rewrite&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Rewriting the parser first involved stubbing out the new parser API, and then making the old parser API a facade to the new one.  In doing this my new parser API automatically gained over 100 JUnit tests -- which of course were now failing.  By incrementally improving the new parser until all JUnits passed, I could be certain that the new parser supports the same markup language features.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As I finished the last language feature and saw green for every JUnit test, I had a huge sense of relief.  I now knew that the new parser was working with a guaranteed level of quality.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Textile-J project now has over 250 JUnit tests.  To get an idea what feedback JUnit can give, take a look at the &lt;a href="https://textile-j.dev.java.net/builds/net.java.textilej/2.1.375/junit/junit-noframes.html"&gt;test report here&lt;/a&gt;, and the &lt;a href="https://textile-j.dev.java.net/builds/net.java.textilej/2.1.375/coverage/frame-summary.html"&gt;code coverage report here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Attributes of a Test Oriented Project&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The following are some attributes of my project that make JUnit tests so powerful:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;A JUnit test for every feature&lt;/li&gt;&lt;li&gt;Meaningful assertions&lt;/li&gt;&lt;li&gt;An environment that makes running JUnits painless and easy (like &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;A development methodology that encourages or requires tests for all new code&lt;/li&gt;&lt;li&gt;An Ant build script that runs the JUnits whenever a new build is created&lt;/li&gt;&lt;li&gt;Integrated code coverage (such as &lt;a href="http://cobertura.sourceforge.net/"&gt;cobertura&lt;/a&gt;) so that you can see which code is tested&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Once your project is set up to run tests, it's easy to add them... so jump that first hurdle and get testing with JUnit!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-6476212160691802188?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/6476212160691802188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=6476212160691802188' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6476212160691802188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/6476212160691802188'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/04/junit-made-it-easy.html' title='JUnit Made It Easy!'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-624753225023702389</id><published>2008-04-02T12:43:00.001-07:00</published><updated>2008-04-02T12:52:22.189-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textile'/><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Confluence'/><category scheme='http://www.blogger.com/atom/ns#' term='wiki'/><category scheme='http://www.blogger.com/atom/ns#' term='markup languges'/><title type='text'>Evolving a Wiki Markup Parser</title><content type='html'>Initially my &lt;a href="https://textile-j.dev.java.net/"&gt;Textile-J open-source project&lt;/a&gt; started out with modest goals: to provide a &lt;a href="http://en.wikipedia.org/wiki/Textile_%28markup_language%29"&gt;Textile markup&lt;/a&gt; parser for Java.  Since the project has started, interest in other features drove evolution of the code-base in some interesting directions.  This article discusses architectural choices and the evolution of the Textile-J parser architecture to meet changing requirements.&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The initial design of the Textile-J parser was a monolithic stateful parser class that used regular expressions and local variables to parse the markup.  The results of parsing markup were passed to the XMLStreamWriter interface as HTML elements and attributes.  This architecture worked fine initially, as it allowed me to evolve my understanding of Textile markup structure (blocks, phrases and tokens) and the XMLStreamWriter provided a solid means of outputting XHTML.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Feature: Multiple Output Formats&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first major feature request was to support multiple output formats: HTML and &lt;a href="http://www.docbook.org/"&gt;DocBook&lt;/a&gt;.  This required a major rethink of the parser.  While XMLStreamWriter was a great means of outputting XML, the parser had to know that HTML was the output format.  After some thinking and reviewing the ever-relevant &lt;a href="http://en.wikipedia.org/wiki/Design_Patterns"&gt;GOF Design Patterns book&lt;/a&gt;, I recognized that the Builder design pattern was an ideal fit.  So I created a new interface called DocumentBuilder, with an HtmlDocumentBuilder and DocbookDocumentBuilder implementations.  Now the parser need not know about the output format, meaning that a single parser could drive multiple output formats.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Feature: Markup Dialects&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next extensions to the Textile markup language were requested.  For example, &lt;a href="http://en.wikipedia.org/wiki/Atlassian_Confluence"&gt;Confluence&lt;/a&gt; markup dialect is very similar to Textile but has some additional syntax features.  With a better understanding of markup structure (blocks, phrases and tokens) I designed a new 'Dialect' concept that allowed for markup extensions to be added to the base Textile language.  The 'Dialect' design was object-oriented, making extensions modular and relatively easy to add without disturbing the base markup parser code.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Feature: Markup Languages&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While the approach of markup dialects worked well for adding extensions to Textile parsing, it did not solve the problem of fully supporting new markup languages.  Dialects at this point were only capable of extending Textile.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Community demand for supporting new markup languages was increasing, including requests for &lt;a href="http://en.wikipedia.org/wiki/MediaWiki"&gt;MediaWiki&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt;.  Supporting these languages was not possible using the existing Textile parser, as the markup rules of Textile were embedded in a single monolithic class.  After some prolonged hesitation (this would be a big job), I got down to the design of a complete rewrite of the parser architecture.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first step was to read up on various markup languages.  Most languages that I've looked at use a simple line-based parsing approach that consists of dividing the markup into blocks, phrases and tokens.  Blocks are usually multi-line constructs that have certain attributes, such as paragraphs and lists.  Phrases are modifiers that affect text on a single line, and tokens are match-and-replace elements in the text.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Requirements of the new design had to include:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;ease of adding new markup languages&lt;/li&gt;&lt;li&gt;modular object-oriented design for better maintainability&lt;/li&gt;&lt;li&gt;facilitate comprehensive JUnit tests&lt;/li&gt;&lt;li&gt;easier learning curve for community contributions&lt;/li&gt;&lt;li&gt;pluggable architecture&lt;/li&gt;&lt;li&gt;output format agnostic&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Using my experience with the previous Dialect design here's what I came up with:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_vw9l2nnub6c/R_PiEBCjcnI/AAAAAAAAABA/crP_MQNnlpo/s1600-h/parser-architecture.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_vw9l2nnub6c/R_PiEBCjcnI/AAAAAAAAABA/crP_MQNnlpo/s400/parser-architecture.png" alt="" id="BLOGGER_PHOTO_ID_5184736154660074098" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The parser delegates all language-specific parsing to the Dialect.  The Dialect defines a language with a collection of Blocks, phrase modifiers (PatternBasedElement) and tokens (PatternBasedElement).  Blocks implement rules specific to paragraphs, lists, tables, etc.  Concrete PatternBasedElementProcessor classes know how to emit portions of content affected by markup (phrase modifiers or tokens).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So far the following languages have been imlemented using this new architecture:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Textile&lt;/li&gt;&lt;li&gt;MediaWiki (a la &lt;a href="http://www.wikipedia.org/"&gt;WikiPedia&lt;/a&gt; fame)&lt;/li&gt;&lt;li&gt;Confluence&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I hope to see contributions from the community for supporting &lt;a href="http://en.wikipedia.org/wiki/Lightweight_markup_language"&gt;other languages&lt;/a&gt;, such as Markdown and Creole.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-624753225023702389?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/624753225023702389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=624753225023702389' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/624753225023702389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/624753225023702389'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/04/evolving-wiki-markup-parser.html' title='Evolving a Wiki Markup Parser'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_vw9l2nnub6c/R_PiEBCjcnI/AAAAAAAAABA/crP_MQNnlpo/s72-c/parser-architecture.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-1040862986618999275</id><published>2008-02-28T14:04:00.000-08:00</published><updated>2008-02-29T09:10:35.348-08:00</updated><title type='text'>Hacking SWT</title><content type='html'>Talking to Mik at &lt;a href="http://www.tasktop.com/"&gt;Tasktop&lt;/a&gt; about what it will take to bring his revolutionary &lt;a href="http://www.eclipse.org/mylyn"&gt;Mylyn&lt;/a&gt;-based product to the mac platform, Mik pointed me to an issue with the way the Safari-based &lt;a href="http://www.eclipse.org/swt"&gt;SWT&lt;/a&gt; browser widget accesses GMail.  Though Safari is a supported browser for GMail, the embedded Safari browser in &lt;a href="http://www.eclipse.org/"&gt;eclipse&lt;/a&gt; is not recognized by GMail.  This post highlights the ups-and-downs of my investigation into &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=220836"&gt;this issue&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Others at Tasktop had discovered that the SWT browser works if gmail is accessed via the http://mail.google.com/mail/?nocheckbrowser URL, which was promising.  This lead me to believe that the user-agent header of an embedded Safari was different than that produced by Safari launched from the desktop.  A quick test browsing to http://whatsmyuseragent.com proved my suspicions were correct.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here were the user-agent headers:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Safari Inside Eclipse: &lt;code&gt;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.12.2 (KHTML, like Gecko)&lt;/code&gt;&lt;/div&gt;&lt;div&gt;Stand-alone Safari: &lt;code&gt;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.12.2 (KHTML, like Gecko) Version/3.0.4 Safari/523.12.2&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;The next step then was to see if Safari provides an API for changing the user-agent header.  Having already installed &lt;a href="http://developer.apple.com/tools/xcode/"&gt;XCode&lt;/a&gt; on my system, it was easy to search the API documentation.  I found a section called 'Spoofing', which is specifically about an API for modifying the 'user-agent' header:&lt;ul&gt;  &lt;li&gt;setCustomUserAgent&lt;/li&gt;&lt;li&gt;setApplicationNameForUserAgent&lt;/li&gt;&lt;/ul&gt;By the looks of the difference in user-agent headers, the second one was exactly what I was looking for.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Knowing that SWT involves native calls to Cocoa, I was a little wary about the next step.  Looking at &lt;code&gt;Safari.java&lt;/code&gt; I could see in the &lt;code&gt;create()&lt;/code&gt; method other calls to the Safari web view to configure various properties.  All of them were performed using JNI native calls, one of the various &lt;code&gt;Cocoa.objc_msgSend&lt;/code&gt; variants.  Unfortunately, there wasn't one with the signature that I needed.  The one that I needed would look like this:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public static final native int objc_msgSend(int object, int selector, String string);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I added the method to Cocoa.java, and added a call to it in &lt;code&gt;Safari.create()&lt;/code&gt; as follows:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;// [webView setApplicationNameForUserAgent:];&lt;br /&gt;Cocoa.objc_msgSend(webView, Cocoa.S_setApplicationNameForUserAgent,"Safari/unknown");&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt; The next step is to provide the native implementation of the method.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Using the eclipse search command, I could see that the other objc_msgSend implementations were in cocoa.c  Never having done JNI before, it was time to do a little looking around.  The &lt;a href="http://www.eclipse.org/swt/faq.php"&gt;SWT FAQ&lt;/a&gt; had most of what I needed, indicating how to build the native libraries, etc.  The tricky parts were as follows:&lt;div&gt;&lt;ul&gt;&lt;li&gt;how to determine the mangled function name for the native method?&lt;/li&gt;&lt;li&gt;now to convert the jstring argument to an NSString?&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;For the first issue, a little guessing and a look at the&lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html"&gt; JNI spec where it talks about mangling&lt;/a&gt; and I got it mostly right.  Stubbing out the method, building it (according to the instructions in the SWT FAQ) and I got a &lt;code&gt;java.lang.UnsatisfiedLinkError&lt;/code&gt;.  Running it again in the debugger, I was able to see the method name it was looking for and fixed up the name of the method in &lt;code&gt;cocoa.c&lt;/code&gt;.  Rebuild, re-run, and so far so good!  My stubbed out method was being called whenever a new SWT Browser control is created.  This is what I had so far:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#ifndef NO_objc_1msgSend__IILjava_lang_String_2&lt;br /&gt;JNIEXPORT jint JNICALL Cocoa_NATIVE(objc_1msgSend__IILjava_lang_String_2)&lt;br /&gt;(JNIEnv *env, jclass that, jint arg0, jint arg1, jstring arg3)&lt;br /&gt;{&lt;br /&gt;jint rc = 0;&lt;br /&gt;return rc;&lt;br /&gt;}&lt;br /&gt;#endif&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So now on to the method implementation: how to call the right method?  A quick google on jstring to NSString resulted in the following:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#ifndef NO_objc_1msgSend__IILjava_lang_String_2&lt;br /&gt;JNIEXPORT jint JNICALL Cocoa_NATIVE(objc_1msgSend__IILjava_lang_String_2)&lt;br /&gt;(JNIEnv *env, jclass that, jint arg0, jint arg1, jstring arg3)&lt;br /&gt;{&lt;br /&gt;jint rc = 0;&lt;br /&gt;&lt;br /&gt;Cocoa_NATIVE_ENTER(env, that, objc_1msgSend__IILjava_lang_String_2I_FUNC);&lt;br /&gt;const char *str = (*env)-&gt;GetStringUTFChars(env, arg3, 0);&lt;br /&gt;&lt;br /&gt;/* use NSString as follows: [NSString stringWithCString: str] */&lt;br /&gt;&lt;br /&gt;(*env)-&gt;ReleaseStringUTFChars(env, arg3, str);&lt;br /&gt;Cocoa_NATIVE_EXIT(env, that, objc_1msgSend__IILjava_lang_String_2I_FUNC);&lt;br /&gt;return rc;&lt;br /&gt;}&lt;br /&gt;#endif&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Okay, so now all I had to do is actually send the Objective-C message to the object!  Copying from other examples in the same file, here's what it I ended up with:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#ifndef NO_objc_1msgSend__IILjava_lang_String_2&lt;br /&gt;JNIEXPORT jint JNICALL Cocoa_NATIVE(objc_1msgSend__IILjava_lang_String_2)&lt;br /&gt;(JNIEnv *env, jclass that, jint arg0, jint arg1, jstring arg3)&lt;br /&gt;{&lt;br /&gt;jint rc = 0;&lt;br /&gt;&lt;br /&gt;Cocoa_NATIVE_ENTER(env, that, objc_1msgSend__IILjava_lang_String_2I_FUNC);&lt;br /&gt;const char *str = (*env)-&gt;GetStringUTFChars(env, arg3, 0);&lt;br /&gt;&lt;br /&gt;rc = (jint)objc_msgSend((id)arg0, (SEL)arg1, [NSString stringWithCString: str]);&lt;br /&gt;(*env)-&gt;ReleaseStringUTFChars(env, arg3, str);&lt;br /&gt;Cocoa_NATIVE_EXIT(env, that, objc_1msgSend__IILjava_lang_String_2I_FUNC);&lt;br /&gt;return rc;&lt;br /&gt;}&lt;br /&gt;#endif&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;I rebuilt the native libraries, re-ran eclipse, and voila!! It worked!  The SWT Safari browser widget was now sending a user-agent string as follows:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/523.12.2 (KHTML, like Gecko) Safari/unknown&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I browsed back to GMail with this new patch, and GMail now recognized Safari embedded in eclipse as a supported browser.  For my first foray into hacking SWT, using JNI and Objective-C, it was a great success.... now if only the SWT team will accept my patch ;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(153, 0, 0);"&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;updated Feb 29:&lt;/span&gt; &lt;/span&gt;the fix was committed by the SWT team: see &lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=220836"&gt;https://bugs.eclipse.org/bugs/show_bug.cgi?id=220836&lt;/a&gt; for details&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-1040862986618999275?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/1040862986618999275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=1040862986618999275' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1040862986618999275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/1040862986618999275'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/02/hacking-swt.html' title='Hacking SWT'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1482979278030787271.post-991941400330798630</id><published>2008-02-25T10:52:00.000-08:00</published><updated>2008-03-04T12:28:43.376-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='MDE'/><category scheme='http://www.blogger.com/atom/ns#' term='MDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='beanshell'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='JSR-223'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='scripting'/><title type='text'>Integrating scripting languages into your DSL using JSR-223</title><content type='html'>I've been building tools for Model-Driven Engineering (sometimes known as Model-Driven Development) now for about 11 years, but this is the first time that I've integrated scripting capabilities into a DSL using Java.  The opportunity came up when creating a DSL for modernizing legacy data for my company &lt;a href="http://www.maketechnologies.com/"&gt;MAKE Technologies Inc&lt;/a&gt;.  Following is a summary of how this was done.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The idea is that the functional capabilities of the DSL can be extended by scripting the behaviour in the DSL itself.  By integrating with JSR-223 APIs we can support as many scripting languages as are JSR-223 compliant.  By the looks of &lt;a href="https://scripting.dev.java.net/"&gt;scripting.dev.java.net&lt;/a&gt;, there are many including Java itself, Groovy, JRuby, Python, BeanShell, ECMAScript (JavaScript) and PNuts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The DSL defines the concept of a 'caster', which can cast an arbitrary string value to some other value.  For example, a trivial boolean caster might consist of a function that can cast a 'Y' or 'N' value to a boolean true or false.  So we define it as follows:&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;caster language="groovy" name="YesNoToBooleanCaster"&gt;&lt;br /&gt;String cast(text) {&lt;br /&gt;   if (text == 'Yes') {&lt;br /&gt;       return true&lt;br /&gt;   } else if (text == 'No') {&lt;br /&gt;       return false&lt;br /&gt;   } else {&lt;br /&gt;       throw new Exception("Unexpected value '$text'");&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&amp;lt;/caster&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;Elsewhere in the DSL we can refer to the new caster by it's name &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;YesNoToBooleanCaster&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;The code to make the new caster work is relatively simple using the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;javax.script&lt;/span&gt; APIs:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;String languageName = "groovy"; // get the language from the DSL instance&lt;br /&gt;String script = ""; // get the script from the DSL instance&lt;br /&gt;&lt;br /&gt;ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(languageName);&lt;br /&gt;Invocable invocable = (Invocable) scriptEngine.eval(script);&lt;br /&gt;&lt;br /&gt;Object value = invocable.invokeFunction("cast", "Yes"); // test it out&lt;br /&gt;assert Boolean.TRUE.equals(value);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;With a little more work, I was able to hook up auto-suggest for the language attribute in the DSL editor (ScriptEngineManager can tell you which languages are supported) and provide validation of the scripting (ScriptEngine.eval can be used to detect script syntax errors).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All of this and the script runs very quickly at runtime, thanks to bytecode compilation by most supported scripting languages!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The only real issues that I encountered were:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;extracting useful error messages from exceptions thrown by ScriptEngine.eval can be tricky.  For example, JRuby provides the information in a &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;getException()&lt;/span&gt; method on the Exception object.  Some reflection and chained exception unraveling was used to overcome this problem in a language-independent manner.&lt;/li&gt;&lt;li&gt;Older Java 6 VMs (developer preview versions) have old preview versions of the JSR-223 APIs... stick to a non-beta version of the Java 6 VM if you can, or if you're developing for Macs then you may have to catch &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;NoSuchMethodError&lt;/span&gt; and use some reflection magic.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All in all I was very impressed with the ease of using JSR-223.  The end result when used with DSLs is that it is very easy to provide an extensible DSL API with a very low barrier to entry.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1482979278030787271-991941400330798630?l=greensopinion.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://greensopinion.blogspot.com/feeds/991941400330798630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1482979278030787271&amp;postID=991941400330798630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/991941400330798630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1482979278030787271/posts/default/991941400330798630'/><link rel='alternate' type='text/html' href='http://greensopinion.blogspot.com/2008/02/integrating-scripting-languages-into.html' title='Integrating scripting languages into your DSL using JSR-223'/><author><name>David Green</name><uri>http://www.blogger.com/profile/05853899131970655876</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/-JJ1mSjmDeU8/Tff7mVrAsGI/AAAAAAAAAkg/4jzqDDVAU3o/s220/0313T-gravatar.jpg'/></author><thr:total>0</thr:total></entry></feed>
