tag:blogger.com,1999:blog-81440544334032159102024-03-13T03:03:21.615-07:00Java and then someExploring mostly Java coding with bits of OSGi, Struts 2, Maven, server-side JavaScript, and then some.Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.comBlogger9125tag:blogger.com,1999:blog-8144054433403215910.post-38008770197350482992013-01-13T20:12:00.003-08:002013-01-13T20:12:34.677-08:00Builds that use git info on HerokuIf your build expects there to be a local git repo to do things like generate <a href="http://mojo.codehaus.org/buildnumber-maven-plugin/">build numbers</a>, <a href="https://github.com/danielflower/maven-gitlog-plugin">change logs</a>, etc, you'll find it won't work on Heroku. The reason is because the slug compiler kindly removes the .git directory before the build runs. Here is how I worked around it for my Maven 3-based Java build.<br />
<br />
The key is to restore the .git directory before the rest of the Maven 3 build executes. I do this by adding this profile to my build:<br />
<br />
<script src="https://gist.github.com/4527683.js"></script>
<p>Of course, replace "https://bitbucket.org/mrdon/weapon-m.git" with your repository.</p>
What this does is check to see if the ".git" directory is missing, which it will be only for Heroku builds as they remove the directory before Maven execution, clone the repo, then move its ".git" directory back where it belongs. <br />
<br />
This approach has several notable gotchas:<br />
<ol>
<li>Your repository url must contain any authentication information. If your main repo is anonymous accessible, great, otherwise, you'll have to find someway to encode authentication credentials in the URL. </li>
<li>You'll need to push to your main repo before you push to heroku. This is a shame as I like to run "git push heroku master && git push origin master" to let me rewrite the commit in case the build fails on Heroku, which it occasionally does. However, with this technique, if you want that git information in your build, you'll have to push to your main repo first.</li>
<li>Only operations that view the history will work correctly. If you tried to run "git status" or manipulated your local copy, git is all confused.</li>
</ol>
I hope that this saves someone the hours it took me to find this workaround :) Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com0tag:blogger.com,1999:blog-8144054433403215910.post-61710771703611389032010-12-11T04:23:00.000-08:002010-12-11T05:27:20.114-08:00It's about the relationship, stupidEvery year or so, I am overwhelmed by nostalgia for the <a href="http://en.wikipedia.org/wiki/Bulletin_board_system">B</a><a href="http://en.wikipedia.org/wiki/Bulletin_board_system">BS days</a> and start playing a <a href="http://tradewars.com/">TradeWars 2002</a> game (yes, they still exist). I don't stop to think why those ANSI text days were better than the current webiness I enjoy today, but while reading this <a href="http://www.un-marketing.com/blog/">Un-Marketing</a> book, it hit me - it's about the relationship, stupid.<br /><br /><a href="http://1.bp.blogspot.com/_YveEdh2jinQ/TQN7M7p0-OI/AAAAAAAAABw/Kv0iYk-Bc2s/s1600/atd_lr9t.gif"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 138px;" src="http://1.bp.blogspot.com/_YveEdh2jinQ/TQN7M7p0-OI/AAAAAAAAABw/Kv0iYk-Bc2s/s320/atd_lr9t.gif" alt="" id="BLOGGER_PHOTO_ID_5549414627952359650" border="0" /></a>Back in the "good old days", you dialed into some local guy's computer, tying up your your line and his. There was no anonymous access, so you had to sign up. Usually, this sign up process asked more than your name and phone, but forced you to fill out a quick survey about yourself, or in some cases, write an essay on why you should be allowed access. In several cases, I had ensure my phone number was correct, as the sysop would try to call it, usually within minutes, to verify I existed and have a little chat. The relationship had begun.<br /><br />From then on, connecting to that BBS was as much a social event as anything. Sure, there were single player games and files to leach or even fidonet-backed "global" discussion groups, but the real fun I found was in the multiplayer games and local discussion groups. Some boards would have real life meetups, where you get to meet that guy who kept podding your CT (TradeWars thing).<br /><br />The icing on the cake was the sysop chat. At any time, the<a href="http://3.bp.blogspot.com/_YveEdh2jinQ/TQN5h_mGZsI/AAAAAAAAABY/OZXgNFH8CBA/s1600/bbs.png"><img style="float: right; margin: 0pt 0pt 10px 10px; cursor: pointer; width: 200px; height: 59px;" src="http://3.bp.blogspot.com/_YveEdh2jinQ/TQN5h_mGZsI/AAAAAAAAABY/OZXgNFH8CBA/s200/bbs.png" alt="" id="BLOGGER_PHOTO_ID_5549412790764463810" border="0" /></a> sysop could decide to be friendly and interrupt whatever you were doing to start chatting. Of course, you could call him as well, resulting in a physical ringing on his end. Not only was it usually the only sort of live chat boards offered, but doing it with the guy who ran the board on which you were dependent gave it extra value. Some times, the chats were simply to tell you he needed to shut down the board to reboot the computer or that he needed the line for something else. Other times, he just wanted to be friendly and find out more information about you. There is nothing like that surge of adrenaline when your screen blanked and then informed you the sysop wanted to chat.<br /><br />His powers could go even further. Once, after uploading a rather naughty Trojan horse containing code to give our TradeWars characters billions of dollars (ended up crashing the game right after we logged in, actually), the sysop was so pissed that when we tried to log in next, he kept hitting the backspace key when we tried to type in our login name. It goes to show, BBSing was about the relationship between a user and other users, but primarily for me, between the user and the sysop. The sysop was g*d. If pissed that guy off, he'd likely tell the other 5 or so boards in your area, and you'd immediately have no access to anything.<br /><br />With the Web, it feels like that extra relational dimension just isn't there, or at least it isn't forced on you as it was in the BBS days. I suppose there is social sites like Facebook, Twitter, and Reddit, but it just isn't the same. That said, it is likely better and I'm just being a curmudgeon for not putting myself out there and giving it a proper go. Maybe I should stop muttering about how Twitter has nothing on IRC, and start tweeting every day. Or not. :)Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com4tag:blogger.com,1999:blog-8144054433403215910.post-35210491774764857512009-09-27T02:09:00.000-07:002009-09-29T07:07:45.579-07:00Introducing Maven Trap - the honey pot for Maven malcontentsFor far too many years, Maven 2 has dominated my builds, and I've sat idly by, taking what they've given, meekly replying, "Thank you sir, can I have some more?" I've watched my builds break apart on unmodified code or just hang, I've nearly drown fighting the torrent of jumbled output trying to find the lone test failure, and I've lost my sanity wading through huge XML build files trying to understand why my build is downloading the internet. <br /><br />Sure, there have been moments of resistance, like when<a href="http://www.jroller.com/mrdon/entry/making_maven_2_not_suck"> I created a fork of Maven</a> to add features like parallel downloading of artifacts, but the effort in maintaining a fork is too high when, honestly, I'd rather be doing just about anything than hacking build systems. Still, the revolutionary fire never died, despite not having a sustainable outlet.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_YveEdh2jinQ/Sr9KQ3_ZDsI/AAAAAAAAAAk/dMm2YUCI4AI/s1600-h/Circus_Lion_Tamer.jpg"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 152px;" src="http://2.bp.blogspot.com/_YveEdh2jinQ/Sr9KQ3_ZDsI/AAAAAAAAAAk/dMm2YUCI4AI/s200/Circus_Lion_Tamer.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5386105333126598338" /></a><br />Like the scrappy fighter that keeps bouncing back after a huge left hook, I'm back, but this time, looking for victory by treating Maven like the unpredictable wild animal it can be. Instead of forking Maven, a fellow Atlassian Sam Le Berrigaud and I have put it in its cage by wrapping the executable with an interceptor framework, allowing modules to control how Maven gets executed and what happens after it gives up the ghost.<br /><br /><a href="http://github.com/mrdon/maven-trap">Maven Trap</a> is this executable interception framework and three modules:<ul><li><b>Console Colorization</b> - Pages of white or green text is hard to read and makes it easy for critical errors to slip by. This module transforms output into ANSI colorized text to make it easier to see errors and other key events.</li><br /><li><b>Always Offline</b> - A build system should not connect the network unless you explicitly give it permission. This module inverts the meaning of the "-o" flag to make the default offline, only to be enabled via the "-o" flag.</li><br /><li><b>YAML POM</b> - While XML is great for programs, it sucks for humans. This module automatically converts your XML pom.xml file into a YAML-formatted pom.yml for either read-only or editable usage.</li></ul>Since Maven Trap treats Maven as an opaque black box, what it can do is limited, however, it provides low-risk integration points that can be extended fairly easily and should work with most any version of Maven. Each module can be enabled by setting a unique environment variable, giving you full control over which features you want without modifying your Maven installation.<br /><br />No announcement is complete without a screenshot, so here is all three modules enabled on simple project:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_YveEdh2jinQ/Sr8ztM4sPlI/AAAAAAAAAAc/mAQF0S2BtUE/s1600-h/maven-trap.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 314px; height: 320px;" src="http://2.bp.blogspot.com/_YveEdh2jinQ/Sr8ztM4sPlI/AAAAAAAAAAc/mAQF0S2BtUE/s320/maven-trap.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5386080531004538450" /></a><br /><br />We use this in the <a href="http://confluence.atlassian.com/display/DEVNET/How+to+Build+an+Atlassian+Plugin+using+the+Atlassian+Plugin+SDK">Atlassian Plugin SDK</a>, as we include our own customized installation of Maven to try to reduce the Maven hassles that our development community continually faces. If you too are fed up with how Maven is always accessing the internet, produces heaps of unmanageable output, or uses obtuse XML configuration, give Maven Trap a go, or better yet, join GitHub and fork it for full control.Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com19tag:blogger.com,1999:blog-8144054433403215910.post-46080887768910230772009-09-24T08:44:00.001-07:002009-09-24T09:36:24.617-07:00Better server-side JavaScript unit testingAs part of a project aiming at a server and client-side tag library based Rhino, I thought it might be nice to write my unit tests in JavaScript. A quick Google search later and I find the non-uniquely named <a href="http://jsunit.berlios.de/">JsUnit</a> project, which not only implements *unit in JavaScript, but has Ant and Maven 2 support. However, quite soon, I hit some problems, mainly around very little error reporting and feedback. Few things are as annoying as knowing there is a problem, but having no idea where.<br /><br />A quick <a href="http://github.com/mrdon/jsunit">fork</a> later and I now have JsUnit 1.3-db. So far, this fork adds:<br />* Callstack reporting for errors and failures<br />* Text test result output, not just XML<br />* File and line numbers in all error messages<br />* Upgraded Rhino<br /><br />Previously, if you hit an error, you'd get this:<pre>[INFO] [jsunit2:jsunit-test {execution: test}]<br />[INFO] ------------------------------------------------------------------------<br />[ERROR] BUILD FAILURE</pre>Then, you'd have to dig into the <code>target/surefire-reports/TEST-MyTestCase.xml</code> to find this:<br /><pre><testcase name="TestFieldTest.testRender" time="0.006"><br /> <error message="ReferenceError: "bar" is not defined." type=""/><br /> </testcase></pre>Not helpful. Now, you get this:<pre>[INFO] [jsunit2:jsunit-test {execution: test}]<br />TestRunner (1 test cases available)<br />> Starting test suite "TextFieldTest"<br />- Running test 1: "TextFieldTest.testRender"<br />ERROR in TextFieldTest.testRender: ReferenceError: "bar" is not defined. (TextField.js:7)<br /><br />Stack trace: <br />1: TextField.js:7<br />2: TextFieldTest.js:6 (TextFieldTest_testRender)<br />3: JsUnit.js:827 (TestCase_runTest)<br />4: JsUnit.js:808 (TestCase_runBare)<br />5: JsUnit.js:386<br />6: JsUnit.js:409 (TestResult_runProtected)<br />7: JsUnit.js:390 (TestResult_run)<br />8: JsUnit.js:797 (TestCase_run)<br />9: JsUnit.js:1033 (TestSuite_runTest)<br />10: JsUnit.js:1014 (TestSuite_run)<br />11: JsUnit.js:2614 (EmbeddedTextTestRunner_run)<br />12: Components:4<br />< Completed test suite "TextFieldTest"<br />1 error, 0 failures.<br />[INFO] ------------------------------------------------------------------------<br />[ERROR] BUILD FAILURE</pre>For fellow Maven folks, you can find the releases at my repo: <br /><a href=" http://twdata-m2-repository.googlecode.com/svn/de/berlios/jsunit/jsunit-maven2-plugin"><br />http://twdata-m2-repository.googlecode.com/svn/de/berlios/jsunit/jsunit-maven2-plugin</a>Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com5tag:blogger.com,1999:blog-8144054433403215910.post-86309448386052779482009-09-05T21:19:00.000-07:002009-09-05T22:37:33.626-07:00GWT for JavaScript JunkiesThe most appealing thing about GWT for me isn't the fact that I, as a Java developer, don't have to write JavaScript anymore (hey, I like JavaScript!), but the single development experience. Since everything is in Java, you can have common libraries, coding patterns, frameworks, and editor behavior, drastically simplifying the development experience. My second favorite aspect of GWT is its component library. For far too long, we Java developers have treated HTML as just text to output, which is like treating programming as a series of ones and zeros - surely we can work at a higher level than that.<br /><br />Therefore, I set out to explore what it would be like to have a GWT-like development experience for JavaScript developers, only instead of Java everywhere, you write JavaScript. Also, I wanted the bulk of the UI to be built on the server-side to minimize XHR calls and improve accessibility. To implement the server-side component library, I ripped of as much of Apache Wicket as I could, while keeping it 100% stateless (no session usage or complicated state sharing schemes). I'm not sure how to package this yet (new framework, Struts 2 plugin, require OSGi, or whatever), but before I go too far down the road of making it a proper framework, I thought I'd get some feedback on the basic design.<br /><br />This is what the code of a simple application looks like:<br /><pre>/controllers/comment.js<br />/templates/comment/comment.html<br /></pre><h4>comment.html</h4>The comment.html template is a simple HTML page with "jsid" attribute markers to determine where the components are rendered. Absolutely no logic is allowed in a template:<br /><pre name="code" class="xml"><html><br /> <head><br /> // import jquery and json<br /> </head><br /> <body><br /> <form jsid="commentForm" action="#"><br /> <input jsid="title" type="text"><br /> <input jsid="body" type="text"><br /> <input jsid="submit" value="Submit" type="submit"><br /> </form><br /> <script type="text/javascript" jsid="proxies"></script><br /> <body><br /></html><br /></pre><h4>comment.js</h4>Where it gets interesting is in comments.js. This follows the basic design of Ruby on Rails controllers with some interesting twists:<br /><pre name="code" class="js">page({<br />comment : function(params) {<br /> this.commentForm = new Form();<br /> this.commentForm.add(new TextField("title"));<br /><br /> var body = new TextField("body", {<br /> value : "Great feature",<br /> label : "Body",<br /> description : "The body of the comment"<br /> });<br /> this.commentForm.add(body);<br /> this.commentForm.add(new Submit("submit"));<br /><br /> this.commentForm.onsubmit = function() {<br /> alert('calling comment');<br /> saveComment({<br /> title : jQuery('#title').val(),<br /> body : jQuery('#body').val()<br /> }, function() {<br /> alert("Comment saved");<br /> });<br /> return false;<br /> }.toString();<br /><br /> this.proxies = new Proxies(['saveComment']);<br />},<br />saveComment : function(comment) {<br /> print("comment title: " + comment.title + " body: " + comment.body);<br /> this.result = "success";<br />}<br />});<br /></pre>Ok, starting from the top. You pass into the "page" function a map of methods to be used as controller methods, although there is a mix of HTML and JSON-producing methods: "comment" returns a comment page, while "saveComment" is a JSON-invokable method.<br /><br /><span style="font-weight: bold;">comment()</span><br /><br />Since this is the method that will render HTML, we will build up our component tree. Instance variables like 'commentForm' are the first to be matched against comment.html, with subcomponents matching accordingly as the template processor works its way through the HTML. There are four components used in this page:<ol><li>"Form" - represents an HTML form</li><li>"TextField" - represents an HTML text input field</li><li>"Submit" - represents a submit button</li><li>"Proxies" - generates client-side proxy methods that call the corresponding server-side ones</li></ol>The main advantage of building your components in server-side code and not in a template is the much greater options you have for presentation logic. In this example, I just use hard-coded strings, but you can see how these could just as easily be initialized using service calls.<br /><br />Nothing eye-opening so far...until you get down to the onsubmit() method. What we are doing is creating a method that will be executed on the client-side in our server-side code, using the nifty toString() feature on functions to get the source. We could have just as easily wrote it in a big string, but this way we get better editor support and fail-fast parser failure reporting. This also serves to help "blur" the line between server and client-side code ala GWT.<br /><br />Finally, we create the "Proxies" component, passing the server-side function names that we want to be able call from the client. My implementation uses JQuery and JSON to stringify objects, but those details are irrelevant here. The point is we are able to call "saveComment()" on the client-side as if it was on the server-side, with all the json parsing and construction happening behind the scenes. Since both sides are JavaScript, you don't have the immedience mismatch you do in other languages.<br /><br /><span style="font-weight: bold;">saveComment</span><br /><br />The "saveComment()" function is a remotely executable function, meant to receive and return JSON, although you aren't limited to it. JSON in the request body is generally a good idea since it limits your vulnerability to cross-domain request forgery. Of course, this technique also means your form only works with JavaScript turned on, which may not be an option. The "comment" argument is the serialized object passed from the client, and we return a serialized instance of our controller, which is why we set the result by setting the instance variable.<br /><br />This basically completes the tutorial. One other thing to note is the components are also written in JavaScript, and since that means Rhino, you get all the E4X niceities in constructing HTML. This means even the HTML is syntaxally processed when the script is loaded.<h4>Next steps</h4>My next step is to build a real application using this little framework and see how it scales as the feature demands pile on. Thanks to Rhino, I can keep developing the backend of this application in Java with all the libraries and frameworks I depend on. The consistent use of JavaScript for the presentation layer hopefully means less code, more reuse, and more productivity.Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com8tag:blogger.com,1999:blog-8144054433403215910.post-60479771123542851722009-08-18T17:27:00.001-07:002009-08-18T17:53:22.452-07:00Playing second fiddle - Java backend for BespinSo I'm the poor sap who decided to take on <a href="http://groups.google.com/group/bespin/browse_thread/thread/44ead8a1d438496">building a Java backend</a> for <a href="https://bespin.mozilla.com/">Bespin</a>. Michael Mahemoff <a href="http://softwareas.com/will-bespin-really-end-up-with-multiple-backends">wonders if Bespin will ever have multiple backends</a>, and I obviously believe the answer is yes, so I thought I'd go into why I think it is a good idea.<br /><br />Bespin represents an opportunity to fundamentally change how we develop software. It dramatically lowers the barrier of entry to development, ideal for software modules that are smaller, more focused, and testable. At this point, it isn't suited to all software development, but for extension systems like a good plugin system that allows small bits of code to be plugged into a system at runtime, I think it is a perfect fit. <br /><br />While Java development, for example, can be quite fast once you get all your frameworks, build systems, and IDE's in place, it also be a house of cards that the slightest network or external server-related wind can bring to a tumbling crash. I'd rather have an application focus on providing a solid platform with accessible API's, then provide a development environment in the app itself to write little plugins. Then, any developer can log in and get to writing real code without all the setup and build issues.<br /><br />Therefore, building an embeddable Java backend is critical since all the web applications that I work on are Java-based. There are many times I want to write a little plugin, but dealing with Maven 2 and IDEA seems overkill. Being able to log into a dev server and hack away would dramatically reduce the time from idea to code and make it more likely I'll actually do it and not put it on the "I should do that someday" shelf.<br /><br />They say in order to take advantage of your creative potential, you should carry around a notepad to jot down ideas as you have them. Think of Bespin as a code-able notepad.Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com2tag:blogger.com,1999:blog-8144054433403215910.post-36996148972210149822009-07-25T20:07:00.000-07:002009-07-25T20:31:45.231-07:00Installing OSGi bundles with dependenciesOne of my favorite features of OSGi is you can write jars/bundles that provide code and/or services to other jar/bundles. However, one of my least favorite features of OSGi is how you can't just install a single jar, as most likely, that bundle will require packages and services from other bundles in order to work. Passing a list of bundles with instructions to install them in a certain order sucks.<br /><br />To help solve this deficiency, there is the OSGi Bundle Repository (OBR), which is a bit like Maven 2's dependency management in that when you install a bundle using it, OBR can look at external repositories to find any required dependencies and install them as well. Unfortunately, a) there is barely any documentation or code samples out there on integrating OBR into your OSGI application and b) having an OSGi app go out to the internet to download other bundles at runtime just plain scares me; take Maven's penchant for downloading the internet and put that "feature" into your production application... *shudder*.<br /><br />The solution I came up with uses the dependency resolution aspect of OBR but without needing any network access. Instead of installing a bundle directly, you install an ".obr" file, which is a zip file with the following layout:<br /><pre>main-bundle1.jar<br />obr.xml<br />dependencies/dep-bundle.jar<br />dependencies/dep-bundle2.jar</pre><br />When installed, the installer looks at the obr.xml and determines if the main bundle can be installed as is. If there are any missing dependencies, it takes them from the "dependencies" directory and installs as necessary. Therefore, only the necessary bundles are installed yet OBR doesn't go out to the network, and, bottom line, you can again distribute and install one file.<br /><br />The obr.xml file is the standard OBR descriptor file that describes the main bundle and all its dependencies. The dependency bundles are packaged in the .obr zip only to be used if needed during installation. For example, if user A installs the .obr file on their osgi system that already contains dep-bundle.jar, then the installation process will only install dep-bundle2.jar. If user B has neither, then the installation process installs both dependency bundles.<br /><br />There are other options I found (PAR, DeploymentAdmin) for grouping bundles, however, they tend to exist as a way to define multiple bundles as a single, possibly indivisible, unit, but in our case, dependencies are meant to be shared between bundles of different origins. Did I miss anything or is this the best option for achieving my goals?Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com8tag:blogger.com,1999:blog-8144054433403215910.post-79044907682798091382009-07-17T07:24:00.000-07:002009-07-17T08:03:31.140-07:00Greasemonkey for Maven 2Few things are more tedious than crawling through hundreds of lines of Maven 2 output to discover which test failed, mojo didn't execute correctly, or error was thrown during integration tests. After using git for my primary version control tool, I'm spoiled with its lovely full-color output, so why can't I have that for Maven 2?<br /><br />Turns out, there is a <a href="http://jira.codehaus.org/browse/MNG-3507">ticket open</a> in the Maven JIRA project for this, but so far, there doesn't seem much traction, and fixing it for me now would involve forking Maven again, which I'd rather not. Therefore, I decided to take the Greasemonkey approach and write a wrapper around the Maven output so that I can format it exactly how I want.<br /><br />Introducing the <a href="http://github.com/mrdon/console-colorizer/tree/master">Console Colorizer</a>, a command line tool for wrapping Maven execution, then feeding its output through a Lexer that adds VT100 color codes where appropriate. This approach is suprior to the usual colorizing the logging output, because I can match any text and highlight it any way I want. Going a bit farther, I could even detect certain bits of output and execute arbitrary code, like sending a <a href="http://growl.info/">Growl</a> or IM notification. All this without modifying any Maven Java code.<br /><br />Compare this raw Maven 2 output:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_YveEdh2jinQ/SmCMr8jTJ4I/AAAAAAAAAAM/JilXzZIeeAY/s1600-h/output-ugly.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 228px;" src="http://1.bp.blogspot.com/_YveEdh2jinQ/SmCMr8jTJ4I/AAAAAAAAAAM/JilXzZIeeAY/s320/output-ugly.png" alt="" id="BLOGGER_PHOTO_ID_5359438243187337090" border="0" /></a><br />With this:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_YveEdh2jinQ/SmCM12u93AI/AAAAAAAAAAU/QJlqnCowQuw/s1600-h/output-colorized.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 234px;" src="http://3.bp.blogspot.com/_YveEdh2jinQ/SmCM12u93AI/AAAAAAAAAAU/QJlqnCowQuw/s320/output-colorized.png" alt="" id="BLOGGER_PHOTO_ID_5359438413424352258" border="0" /></a><br />It is so much easier to see that the test failed, and what warnings your Maven plugins are complaining about. This tool works by wrapping the Maven main execution and running the System.out through a <a href="http://jflex.de/">JFlex </a>lexer. The lexer is nice because it allows me to define tokens via regular expressions, yet remain very fast and compact. A few VT100 codes allow me to add the coloring.<br /><br />Of course, this same technique could be applied to any Java program, or any program at all if you really wanted to, but for now, I think it'll do nicely to clean up Maven 2 a bit.Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com9tag:blogger.com,1999:blog-8144054433403215910.post-33676254987035742022009-07-17T07:15:00.000-07:002009-07-17T07:18:07.318-07:00Goodbye JRollerAfter some four or five years, it is time to move off of <a href="http://jroller.com/mrdon">JRoller</a>. That site goes down more often than a...well, you get the point. Let's see how blogger treats me...Don Brownhttp://www.blogger.com/profile/01122561628983983736noreply@blogger.com5