tag:blogger.com,1999:blog-79424852665757391572024-02-20T13:27:50.167+13:00There Can Be Only Oneopinions about the tech industryRichard Vowleshttp://www.blogger.com/profile/16809664838871828087noreply@blogger.comBlogger46125tag:blogger.com,1999:blog-7942485266575739157.post-72043584000836590902018-12-02T11:07:00.003+13:002018-12-02T11:07:52.641+13:00Working with DialogFlow and Actions on GoogleIn a phrase, do not use Javascript for this.<br />
<br />
The development cycle is appalling, the documentation is abominable, and putting functions in the cloud is incredibly slow and tedious.<br />
<br />
Given node 8, use Typescript.Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-54989348703776600652018-12-02T11:05:00.001+13:002018-12-02T11:05:07.882+13:00Mono-repos, Microservices and Continuous Delivery<h3>
Mono-repos, Microservices and Continuous Delivery</h3>
Since I have been involved with ClearPoint NZ Ltd, and its Accelerate Continuous Delivery effort (renamed from Connect as people were considering it a "product") - something that has evolved over the two or so years I have been working with them, I have come to realize that the only thing that matters is that at any time you can deliver a bug fix to the client, and one's precious releases and versions don't really matter.<br />
<br />
Under Continuous Delivery, for <i>an application</i>, we <b>never release anything</b>. The code always rolls forward, and that is a fundamental in the customer engagement model. We always roll forward, and new code is put behind a feature toggle. This feature toggle goes through various stages - initial commit into the repository (and promotion to production), code and tests being written, eventual turn on in production, removal from code base.<br />
<br />
While we are very heavily focused on the Microservices pattern, we are pragmatic, modularity is actually more important, many changes cross more than one repository (especially when you separate API from implementation, thats at least two, then you have the consumer app, thats three), and sometimes when you are refactoring - especially in the early s tages where you are getting your patterns right, changes affect many different repositories. Common functionality gets pulled out, so you don't repeat yourself over and over again, it allows you to consistently wrap Open Tracing and Metrics around your calls, queues, etc, and consistently expose them.<br />
<br />
Furthermore, especially when you are starting - what was a monolithic app old style realistically represents the application space of the problem you are solving. You might have products, stock levels, promotions, order buckets, etc - but those would have just been part of the one app in the past, but mature Microservices departments realize that old school modularity in a Monolith are just being pushed out with experimental edges as you chop up your problem. You end up with an Application space - if another group starts using your core services, then you can start versioning those, but ideally you really want stuff to propagate.<br />
<br />
Further, you want your master branch to always be green, so running your build, cross Microservice integration and e2e tests should always operate in a separate namespace and only merge once they have passed. Ideally these changes are kept small, incrementals (so they are easy to review and don't result in long running branches) and are behind the feature toggles.<br />
<br />
Given we don't actually release any Java artifacts, and we can use Git to determine what changed between the master branch and what you are trying to submit, and then tell the Maven Reactor build what to build. These then general Docker images in our case (generally) and we now have a new manifest ready for a Canary deploy into our e2e test cycle - consisting of master artifacts + the new docker images.<br />
<br />
Having moved to a Monolithic repository for an "application space" has made all of this considerably easier. All of our artifacts are in there - Terraform, Helm for k8s, Jenkins Pipeline code, how container images used for the process are build, as well as all test and source code - all in the one repository. We then have a submit-queue, and the change in confidence in the pipeline, tests and code that gets delivered is something else. UX testing is the only fly in the ointment because it can be hard and be flaky, but that isn't something one can do much about.<br />
<br />
In all, having moved our codebase towards Microservices and a Monolithic repository has been a pretty amazing experience.<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-4432514523743066632017-10-23T14:00:00.000+13:002017-10-23T14:00:18.337+13:00Maven Release and Sub-Directories<h2>
Revisiting Repository Management for Java Artifacts</h2>
<h3>
On a single repository per Java artifact</h3>
On the Illegal Argument Podcast that I formed with <a class="g-profile" href="https://plus.google.com/113913703302067206043" target="_blank">+Mark Derricutt</a> several years ago now, and before I left it as I had "run out of things to say", I argued vehemently that for Java projects to have the correct contracts and to be releasable as proper binary artifacts they had to have their own repository in Git.<br />
<br />
Git is different from Subversion - which we were all shifting from - in one pretty fundamental way - tags are not tree relative. You don't tag from where you are, you tag the <i>whole repository</i>. This meant that if you were releasing and had other artifacts in your repository, they would all come along with that tag. If you had to do a patch fix for production and you were on a release cycle, you had to swap back to that tag, branch again and do a patch fix. Multiple artifacts in one repository would mean they would all swap back to earlier versions, requiring you to then have to clone out a repository specifically for that fix, which essentially gave you one repository per artifact so you might as well start as you mean to go on and not muddy the waters.<br />
<br />
Further, traditional build systems would want to build a particular repository, and since best practice is, <i>and remains</i>, that you should only build what changed and rely on the declared dependencies - that your build system would create a cascading build for when you actually need one.<br />
<br />
The downside to this of course is an explosion in repositories.<br />
<br />
So a few things have turned up that has changed my mind on this and I'd like to detail one of them here.<br />
<h3>
Mono-repos, and Maven Release</h3>
If you are doing CD, you operate on snapshots - there is no need to do anything else. Unless you have third parties relying on your artifacts. And then you are back into this single repo vs mono repo problem. The problem came that when you release, the whole of your Git repository is checked out into your target folder from your tag, and compiled again. Up until 2.5, you couldn't actually release subfolders.<br />
<br />
I realized as part of the <a href="http://connect.cd/" target="_blank">Connect Project</a> that I was releasing from sub-folders successfully. So I went to talk to Mark about it, who told me I should be able to - which led to this blog post.<br />
<br />
Now you can, and this changes the game somewhat. In my case, I have a bunch of repositories that I'm totally find chucking in a single mono-repo and just releasing forward. I can branch and do a patch release if I want, but they are all largely unrelated, they don't depend on each other and should never be released using a Multi-Module Build.<br />
<br />
One of the real pains of managing finely grained repositories is having to manage so many. This ability to release from a single mono-repo has tipped me into that camp. I can still do patches - I'd never have merged them back anyway - but this is going to make my life considerably easier.<br />
<h3>
Why I hate Multi-Module Builds</h3>
While we are here, lets have a rant about Multi-Module Builds.<br />
<br />
A Multi-Module build is one where you have a pom that only has module references in it - and these reference artifacts that are in subdirectories. They are not in themselves evil, they work well for CD as long as you tell them what to build. They do not work well for open source projects.<br />
<br />
Typically these are used in open source projects for having all artifacts released together, they all have the same version number and in released projects, they tend to have a slow cadence. A bug fix takes forever to get released because <i>everything</i> gets released, even when it doesn't need to. 99% of artifacts in these kinds of projects experience no change, it is simply because the build process is silly.<br />
<br />
This kind of project<b> really annoys me</b>. Projects that release like Spring (although the level of stupid in Spring's build system beggars belief) and CXF (as much as I appreciate the work they do to make me not have to deal with the vagaries of WebServices) but it means they batch their bug fixes. And you can wait weeks for a fixed bug to be released, because they batch them.<br />
<br />
What should they do? They should individually release their artifacts as soon as they have been verified to be correct and have a single artifact that represents the project as a whole - a simple pom that just lists the project modules in their specific versions as dependencies. This allows bug fixes to just release day after day, with no change in all of the other artifacts and then they can batch them in the pom only release if they want. And people who need the fixes can just override that released pom with the new artifact. Simple.<br />
<br />
I distinguish Multi-Module builds from Reactor Builds. Reactor Builds can take in a whole bunch of artifacts and are never intended for release - just to make a developer's life easier to pick up the Application in their IDE or build a complete installation of an application. They are used in <i>Applications</i>, Multi-Module builds are abused in <i>Libraries</i>.<br />
<br />
I'll be shifting the <a href="https://github.com/clearpointnz" target="_blank">Connect project Java repositories</a> to a single repository soon.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-42344765661213484182016-04-25T14:33:00.001+12:002016-04-25T14:33:27.972+12:00Docker Registry and running mini-code-campsMost of my interest in the last six months has really been consumed by electronics - it happens each year around Christmas when I start having a bit of time to myself again. As I have now started back into the technology deep dive, having found something I'm actually interested in learning and pushing into the <a class="g-profile" href="https://plus.google.com/110978348289846087139" target="_blank">+Code Lounge</a> sessions, I am hitting the same Docker problem I had before - bandwidth. I simply do not have enough bandwidth on ADSL to support even two or three people downloading Docker images.<br />
<br />
The last time we tried it, it just wasted huge amounts of time and people were failing to pull images. This time however, there appears to be a <a href="https://blog.docker.com/2015/10/registry-proxy-cache-docker-open-source/" target="_blank">Docker Registry</a> project, which I am hoping will solve my problem.<br />
<br />
My next Code Lounge is on <a href="http://kubernetes.io/" target="_blank">Kubernetes</a> and we will be using Docker, CoreOS, flanneld, etc all that stuff - hopefully to create a distributed docker cluster on people's machines. As such, I am upgrading my old Mac mini to ensure I can actually run it. The last time I tried to install Docker Machine on my Mac, it didn't have the architecture to run and kept crashing until I learnt you had to install it via HomeBrew.<br />
<br />
I'll report back how successful I have been.Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-36166119558609774172014-11-30T10:41:00.002+13:002014-11-30T10:41:23.803+13:00Fast Delivery<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjF_Q_GJMBsHZqhrRdawftQx_S9Ib05sPB9MRY7Eiv5oTjJD3PPKy74b81_3G0_GQ5dPxCYctev3U7MT_VRg9KAuf24OiXz6RBu8rNZEul__TiQl62fFnXfX00JvoPHFkehyfqA5JNHr5hB/s1600/Screenshot_2014-11-29-14-55-26.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjF_Q_GJMBsHZqhrRdawftQx_S9Ib05sPB9MRY7Eiv5oTjJD3PPKy74b81_3G0_GQ5dPxCYctev3U7MT_VRg9KAuf24OiXz6RBu8rNZEul__TiQl62fFnXfX00JvoPHFkehyfqA5JNHr5hB/s1600/Screenshot_2014-11-29-14-55-26.png" height="320" width="180" /></a></div>
So I wanted to give myself an exercise in creativity in the last week, scratch an itch and see just how fast I can deliver a project. It tooks me 2.5 days.<br />
<br />
My local high school (secondary school) runs a website that the pupils and their parents can log into. It allows you to see the usual grades and so forth, but also their timetable.<br />
<br />
That can be pretty important as they run a 6 day cycle - so if you have a long weekend or a holiday, if even a particularly busy week it can be difficult for some people (particularly my son) to remember what "day" it is at school.<br />
<br />
In my case, the project was to allow students (including my son) to synchronize that school provided timetable (which is customized per pupil) with their Google Calendar - which is synchronized to their phone. Many of the kids turn off their data plan, as they are only allowed one device on the school network.<br />
<br />
So in my case I wanted to let them log in with their Google Account - give me permission to their Calendar, tell me what their school username and password is (no OAuth there, I encrypt and store) and then I go off and create a calendar and push their timetable into it. And I do that every week, automatically.<br />
<br />
Now this had a few challenges:<br />
<br />
<br />
<ul>
<li>I had never worked with OAuth before (or OAuth2 in this case). I had read the book, felt I understood it and then forgot it. <a class="g-profile" href="https://plus.google.com/113913703302067206043" target="_blank">+Mark Derricutt</a> and I had done a short stint with the Google provided libraries and found some problems, but I couldn't even find that project. </li>
<li>I hadn't worked with the Calendar API and that turns out to have some significant quirks</li>
<li>I wanted to make a mobile first application, it had to look good and work - so I wanted to use Material Design components</li>
<li>I wanted to run it against an https server</li>
<li>I needed to see what parts of the start-up framework were "quirky" and needing of cleaning rough edges</li>
</ul>
<div>
<br /></div>
<div>
Some lessons I learnt from the experience - stuff that will make it into the next <a class="g-profile" href="https://plus.google.com/110978348289846087139" target="_blank">+Code Lounge</a> I run with <a class="g-profile" href="https://plus.google.com/102477976455620241458" target="_blank">+Irina Benediktovich</a> .</div>
<div>
<br /></div>
<div>
<ul>
<li>Busy logic - that there is one or more inflight XHR requests really need to be pulled out into a separate module and just included in every project. It is simply such a useful structural pattern that it simply has to be a module</li>
<li>The OAuth2 stuff behaved in an unexpected fashion, every single time you go through the "login" process, you get a new token. And if you are starting and stopping your service, the session you get passed is different to the session your request.getSession().getId() provides you - so you have to make special concessions to try and track the user properly</li>
<li>I still haven't found a way to track a user in an opaque fashion - if they change their primary email address, their account is hosed. I was pretty sure there was a way to do this, but I haven't worried about it too much yet.</li>
<li>JAWR really hates CSS with embedded SVG images. That took me a while to figure out.</li>
<li>Make sure your server is running on the correct time zone or ensure you provide a time zone to all constructed DateTime's in your Google APIs. </li>
<li>It turns out that Chrome on some phones can only search - it ignores any URLs that you type in. It is bizarre and very frustrating!</li>
</ul>
<div>
I may add more to this post as I remember things!</div>
</div>
Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-90990538506467423162014-11-22T19:08:00.002+13:002014-11-22T19:08:28.882+13:00Polymer without Dependency Injection is dead to me<br />
<div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;">
<img class="irc_mi" height="250" src="http://www.gaj-it.com/wp-content/uploads/dead-parrot.jpg" style="margin-top: 72px;" width="250" /></div>
<br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">I have been watching quite a bit of the content from the recent Chrome Dev Summit - its the only way to watch it (after the fact) because there is so much fluff in the talks. I understand why this has to be done, but appreciate that they get off the fluff and into the useful how-tos. The Service Worker stuff I am afraid to say was cleanly and clearly covered in </span><a href="https://www.youtube.com/watch?v=4uQMl7mFB6g" style="font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;" target="_blank">Jake Archibald's DevBytes</a><span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"> with the Train Spotters example, and for a feature that still isn't readily available, it seemed fairly heavily hyped. I have yet to check whether Safari will support it on iOS, but if it does I think we are going to start to be able to claw back some territory from native phone apps.</span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">The Material Design / Web Components / Polymer focus however was more interesting - this has been going on for some time and I have been avidly following it. I like the format of web components much more than Angular Directives, I like the encapsulation of essentially everything you need. </span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">What I don't like however is the lack of dependency injection - even an interface for adding your own.</span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">Whenever I see a "new XXXX' in someone's code - particularly for a service of some kind I outwardly cringe - with Angular and DI, we have been able to significantly improve the way that we build our web applications, focusing on what the customer needs and wants to see in terms of interactivity, workflow, look and feel, and just overall user experience. We attribute this not just to the superior nature of developing in Angular, but partcularly the DI capability. We can mock out our services, and even when we eventually replace them, we are still able to use them for our unit tests with Karma.</span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">There is no such capability with +Polymer - it is listed as part of the "future roadmap" but really, it is critical. The ability to inject your actual services provides for such as well structured application.</span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">The only workaround at the moment is to reverse it, so that every object would pull them from the global context - no Hollywood principle for us!</span><br />
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;"><br /></span>
<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13.3333339691162px; line-height: 20.2222232818604px;">I would really like to use and recommend Polymer, but I simply cannot at the moment and won't be able to until I see at least some activity in this area.</span>Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-68936791283310467492014-05-27T22:12:00.000+12:002014-05-27T22:14:45.833+12:00Groovy fun with @CompileStatic<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy100SEqnJNKAtu65Y8-zt9vhcO_FDvB_tK0Ukt0OCxC4nQ3mG6zBU9T45EZ5gjDJsmdQS9SsOn-mpgFmIQEm3Kwt_8P0Qj1Kjr3L0UEKGUhSXLv6EU1LXbyP3473WShyun0OtAgS79Ild/s1600/Screen+Shot+2014-05-27+at+10.13.20+pm.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy100SEqnJNKAtu65Y8-zt9vhcO_FDvB_tK0Ukt0OCxC4nQ3mG6zBU9T45EZ5gjDJsmdQS9SsOn-mpgFmIQEm3Kwt_8P0Qj1Kjr3L0UEKGUhSXLv6EU1LXbyP3473WShyun0OtAgS79Ild/s1600/Screen+Shot+2014-05-27+at+10.13.20+pm.png" height="320" title="" width="269" /></a></div>
Groovy is one of the few languages that allow both static and dynamic compilation, and among the "big" languages on the JVM (Java, Groovy, Scala, JRuby and Clojure), the only one as far as I am aware.<br />
<br />
Unless I am doing Grails (we stopped at 2.1.13 because of the crazy buggy-ness of that platform, its dependence on Hibernate and just random magic insanity that happens). So just using Groovy in a basic Spring + AngularJS web framework has been empowering - I've only been swapping back to non type safe only really for tests. I still look at Mockito with pain.<br />
<br />
One of the things that has bugged me however is Closures. If I wanted a callback to work, I lost the type safety. Consider this method:<br />
<br />
<pre class="brush: groovy; highlight: [2, 10, 12];"> void listProfiles(User user, boolean favourite, String filter, Closure callback)
</pre>
<br />
So I now am not telling the compiler what types are being passed. I thought about this problem tonight and remember that an interface with a single method is treated as a closure - so I thought I'd try it:<br />
<br />
<pre class="brush: groovy; highlight: [2, 10, 12];"> interface ListProfileCallback {
void profile(Profile profile, boolean favourite)
}
void listProfiles(User user, boolean favourite, String filter, ListProfileCallback callback)
</pre>
and sure enough - the type system kicks in and tells me I'm missing a parameter!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEg0bjXIiz1_sxZWkogYWRe-v0KhbRdxuTcw3rLL5YgWmjLr9oQhAdJL4URelNqgdOkfpkKszXygPYN7m8ZprIlX7t8kyAM0mgks7tml_A89l7NUc28G8HfDnYyZ2-cUKXdpy462lBSwup/s1600/Screen+Shot+2014-05-27+at+10.06.24+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEg0bjXIiz1_sxZWkogYWRe-v0KhbRdxuTcw3rLL5YgWmjLr9oQhAdJL4URelNqgdOkfpkKszXygPYN7m8ZprIlX7t8kyAM0mgks7tml_A89l7NUc28G8HfDnYyZ2-cUKXdpy462lBSwup/s1600/Screen+Shot+2014-05-27+at+10.06.24+pm.png" height="91" width="640" /></a></div>
<br />
(image taken from http://www.luvyababes.co.uk/)Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-43610743215617079702014-05-23T15:48:00.001+12:002014-05-23T15:48:05.123+12:00Gerrit, +2 Looks Good To Me and self plussing<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJLA_VWEYVCy9KOAIk4Ez7i6L1My28f_bzNzZPthgwkGF7yLwZpHXGpMOVj69tlu4XwCzTzNTCYzKRl2V-VG-BJV3h3EA-hZDXtRtvouYkeTnLQrulOxIcEyY_XsonqrFS5V9fV3dl1tmI/s1600/mindblown.jpeg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJLA_VWEYVCy9KOAIk4Ez7i6L1My28f_bzNzZPthgwkGF7yLwZpHXGpMOVj69tlu4XwCzTzNTCYzKRl2V-VG-BJV3h3EA-hZDXtRtvouYkeTnLQrulOxIcEyY_XsonqrFS5V9fV3dl1tmI/s1600/mindblown.jpeg" height="320" width="244" /></a></div>
One of the weird things about <a href="https://code.google.com/p/gerrit/" target="_blank">Gerrit</a> is that it appears to operate by default in nonsense mode.<br />
<br />
What do I mean? Well, Gerrit is a code review tool. But the default setup of the tool is to allow you to accept your own reviews and submit them directly - you can push for review, +2, and then submit.<br />
<br />
I mean, this blows my mind. Why on earth would this be allowed, by default, in a code review tool? I know some projects might see a good reason for it - I found a few discussions about them on the <a href="https://code.google.com/p/gerrit/issues/detail?id=308" target="_blank">Issue 308</a> that discusses this very topic. Two people saying <i>"I do this"</i> seems to make it the default behaviour.<br />
<br />
So in my case, it was important that I could set this on the Permissions Project - it happens across all our projects. So the example from the <a href="https://gerrit-review.googlesource.com/Documentation/prolog-cookbook.html" target="_blank">Gerrit Cookbook</a> - which uses Prolog (that hurt my brain the last time I tried to learn it at University) only allows you to do it on a per project basis. So I needed a submit_filter rather than a submit_rule.<br />
<br />
So to put this somewhere so I don't forget it or lose it, here is my series of commands:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">git clone ....</span><br />
<span style="font-family: Courier New, Courier, monospace;">git fetch origin refs/meta/config</span><br />
<span style="font-family: Courier New, Courier, monospace;">git branch refs/meta/config</span><br />
<span style="font-family: Courier New, Courier, monospace;">git checkout refs/meta/config</span><br />
<span style="font-family: Courier New, Courier, monospace;">vi rules.pl</span><br />
<span style="font-family: Courier New, Courier, monospace;">git add rules.pl</span><br />
<span style="font-family: Courier New, Courier, monospace;">git commit -a -m "your message"</span><br />
<span style="font-family: Courier New, Courier, monospace;">git push origin HEAD:refs/meta/config</span><br />
<br />
in rules.pl you put:<br />
<br />
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">submit_filter(In,Out) :-</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> In =.. [submit | Ls],</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> add_non_author_approval(Ls, R),</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> Out =.. [submit | R].</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">add_non_author_approval(S1, S2) :-</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> gerrit:commit_author(A),</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> gerrit:commit_label(label('Code-Review', 2), R),</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> R \= A, !,</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> S2 = [label('Non-Author-Code-Review', ok(R)) | S1].</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<br />
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">add_non_author_approval(S1, [label('Non-Author-Code-Review', need(_)) | S1]).</span></div>
<div class="p1">
<br /></div>
<div class="p1">
Enjoy.</div>
<div class="p1">
<br /></div>
<div class="p1">
PS this is about preventing gerrit users from moderating their own commits or reviews and allowing them to submit. Just in case this sentence makes it easier for Google Search to find it.</div>
<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-7067952498774760102014-05-17T15:26:00.003+12:002014-05-17T16:25:03.726+12:00Dealing with bugs in open source libraries<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF6os41wYan6IaQcTdJH_QEt0h_0dOsuE_oz0yJEtLJfv8a7UfcEyPlSkdAo3fcAv6Qvnatp4-0Fyu7fksvEoLw1P4g4vj9MMx1E9w6x6xeaNr5PEj2nZGoytuNsQZQ6K-yMmoZzSCOON0/s1600/110859301_5cc01c7ed5_o.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF6os41wYan6IaQcTdJH_QEt0h_0dOsuE_oz0yJEtLJfv8a7UfcEyPlSkdAo3fcAv6Qvnatp4-0Fyu7fksvEoLw1P4g4vj9MMx1E9w6x6xeaNr5PEj2nZGoytuNsQZQ6K-yMmoZzSCOON0/s1600/110859301_5cc01c7ed5_o.jpg" height="289" width="320" /></a></div>
<br />
One thing you have to do when dealing with open source libraries a lot is sorting out buggy libraries. Or libraries where you hit edge cases that you need to get fixed but you can't get it done in a timely fashion. Sometimes you hit fundamental technical problems where you know your fork won't be included in the upstream version and you just have to make a decision.<br />
<br />
As <a class="g-profile" href="https://plus.google.com/101545167381334659973" target="_blank">+Robert Watkins</a> points out in the comments, at least you <i><b>can</b></i> deal with these bugs. In closed source libraries (yes, some still use them), the problem is much harder to deal with, but I don't discuss that here.<br />
<br />
This post is not about how to use Github to contribute back. There are lots of posts that do that. <i>Its about what to do when you have to fix the problem right now.</i><br />
<br />
This problem has particularly hit us in our Grails libraries - people scratch their own itch and then abandon the projects, they never wrote any tests for it and out of its basic functionality its painful or they just don't get updated. The Grails Resources plugin had a problem where it didn't detect the scheme (http vs https) of an incoming request and re-write external javascript resources properly. We had to fix that, and years later it still hadn't been resolved.<br />
<br />
But just as often, we hit problems in other libraries - and they take time to resolve. People hit problems in libraries that I make ( <a class="g-profile" href="https://plus.google.com/100659523889097701109" target="_blank">+Peter Cummuskey</a> for example) and although he makes a pull request, I may not have time to deal with it or potentially prefer to fix it another way (which also takes time). But generally the people on the ground just want it fixed. The software is usually free, so people are not under any obligation to take your fix or spend any time on it. Thats not much comfort - when you need the fix.<br />
<br />
The mechanism we have evolved for dealing with this is helped because we use Apache Maven. Most of our third party (not written in house) libraries are locked up what are called "composite poms". If they have problems we need to resolve, they immediately go into a composite and the problem is isolated at that point.<br />
<br />
The tl;dr of this is simply:<br />
<br />
<ul>
<li>put your third party dependencies in a composite-pom that you control</li>
<li>fork the dependency</li>
<li>fix the dependency, rename the groupId</li>
<li>release it to your own version to your own hosted Nexus or equivalent</li>
<li>update the composite-pom to point to your version and release it</li>
<li>version ranges will automatically update your projects, problem solved</li>
<li>put your fork in technical debt, </li>
<li>when the developer maintaining the main artifact solves the problem, try using their new version, if it works, change your composite, remove technical debt</li>
<li>profit (ok, too many steps for Gnomes perhaps)</li>
</ul>
<br />
<br />
<h3>
Composite Maven Projects</h3>
Simply put, a Composite Maven Project (or <i>composite</i> for short) is simply a Maven Artefact that is one file - a pom.xml file that contains a listing of dependencies. We don't use multi-module builds with parent dependencies, we put them in composites and use version ranges.<br />
<br />
We usually lock down versions of third party libraries [2.4] for commons-io for example - it allows us to ensure that no other version leaks in without the build breaking (which can tend to happen). And yes, we want the build to break if someone starts fiddling versions. <br />
<br />
Composites are bought into projects via version ranges, so changes in them automatically flow down. We upgrade the version of Jackson in composite-jackson from 1.9.8 to 1.9.9, everyone gets it automatically. If we change to 2.x of Jackson (a big change), we change the major version number of the pom so it doesn't automatically flow down.<br />
<br />
And no, this isn't my idea - as far as I'm aware, I'm 3rd hand for this. It works really well. If you use version ranges extensively, like we do - check out my release-pom plugin (which at release tells you exactly what you were using) and the bounds plugin (which brings up the lower bounds of the minor version ranges, Maven resolves much more quickly when you do this).<br />
<br />
<h3>
Forking</h3>
Many of the plugins or libraries we have traditionally used have been hosted in Subversion. This had made forking and keeping track of the changes relatively difficult. You have to check them out, check them in locally and then apply your set of patches on top. If a new version comes out you have to deal with that situation again. Submitting a patch is time-consuming and a painful process with Subversion.<br />
<br />
Git, Github and Bitbucket have made this situation easier - forking is encouraged, if something is a patch that should fix a bug, it can be submitted easily, you can track your changes against the other repository and rebase against it. Even working with subversion libraries can be easier - being able to create a new Github or Bitbucket repository from a Subversion one is easy - contributing the changes back are more difficult however.<br />
<br />
However, timeliness is a problem - you need to be able to get your code into a tracked, versioned environment that allows you to reproduce repeatable builds and still deliver your software. <i>And you need to do it now. Or within a reasonable period of time.</i> It creates technical debt, but that can go on the technical debt register.<br />
<br />
<h3>
Maven Repository</h3>
Having a hosted Maven repository (we use Sonatype's Nexus) solves many of these issues. It allows us to fork these libraries, we typically rename the groupId so put it into our thirdparty heirarchy, and then re-release it to our Nexus. This can be done by hand (for a one off - effectively duplicating the Release plugin) or for a longer term solution, taking our top level parent (which contains Release information) and pushing it into the pom.xml so it tags and pushes to our Nexus third party repository directly.<br />
<br />
<h3>
Tying it all together</h3>
Having forked the source, released the new artifact to our "third party" repository on our hosted Nexus, we then just have to change the composite. The composite points to the new, fixed artifact and the fixed artifact will, of course, automatically flow into all downstream projects that depend on it.<br />
<br />
<h4>
Attributions</h4>
<br />
<ul>
<li><a class="g-profile" href="https://plus.google.com/113385421126901251617" target="_blank">+Michael McCallum</a> for introducing me to composites and version range use and making Maven wonderful to use again</li>
<li>Post documenting this because I asked <a class="g-profile" href="https://plus.google.com/102477976455620241458" target="_blank">+Irina Benediktovich</a> to do it for the logstash logback logger - which uses Jackson 2.x and our Grails apps use 1.9.9. She had to get rid of all that OSGi crap.</li>
<li>Photo from <a href="https://www.flickr.com/photos/heatsink/110859301/in/photolist-fQGBnL-3ftfgM-6tnotK-e38KVA-5vmwuv-tDohG-dN4GpL-e6UW8G-6prtHp-aNbBg-bwRyni-5aB3zk-9EJw4Z-2ixBq-5iLc16-9r4hCi-fC7wGK-kkQiJa-exPiM-4oS2wY-8vkM2v-f8vEYj-7kfy56-59qeDV-7k9DFU-6bFMwJ-kN6sqR-676ib3-bwDV3G-6dHiUN-9oSHCH-kNoEUz-3nR78L-ndY9Et-9e8C3o-dXkF1B-4ee3bX-cHFbm1-RpBFL-8N4BWg-9wjiV8-8d8Q59-5Nwzi4-7ax1QU-7ax1Nh-5XCkjK-61W3TL-e7eMDe-cBoqcY-AhmtF/" rel="nofollow" target="_blank">Jon Watson @ Flickr</a> </li>
</ul>
Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-40827353472175409032014-05-15T19:35:00.001+12:002014-05-16T21:22:55.532+12:00Of conferences and things<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVQCj_fa4zz0DFhNL01IcOHnNRd3XrD2qfwG4Bab7DvDJDxer5iF3ULupXhBk70SglNQysfFHS6P0Pd-jrfeVogRQdPw10nbOXbFieDpSs1acUEeBfQLEYUo8vdgnHHArykIjNq7o7tQTG/s1600/IMG_7963.JPG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVQCj_fa4zz0DFhNL01IcOHnNRd3XrD2qfwG4Bab7DvDJDxer5iF3ULupXhBk70SglNQysfFHS6P0Pd-jrfeVogRQdPw10nbOXbFieDpSs1acUEeBfQLEYUo8vdgnHHArykIjNq7o7tQTG/s1600/IMG_7963.JPG" height="400" width="266" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Planning boards from Illegal Argument Conference</td></tr>
</tbody></table>
I am notorious about how I feel about Conferences. In fact, I have gotten into trouble with people over being outspoken particularly about face-forward conferences, and with the recent Code Mania, I thought I would take the time to explain my point of view.<br />
<br />
Now don't get me wrong - people who organise tech conferences are showing the best side of humanity in my opinion, they almost never get rewarded for the effort they put in and there are always critics, like me, who complain, so they do a a job that few people, except those who have organised tech conferences understand.<br />
<br />
What prompted me to post this is that I was listening to a podcast of In Beta on the way home - now that they have stopped talking about Twitter they actually have some interesting topics, but I asked myself - would I rather listen to a podcast on the way home, or would I rather have an person experienced in a topic I was interested in in the car with me on the way home. Would I rather have a dialog or passively listen?<br />
<br />
I asked the same question of my wife who loves Science oriented podcasts - would you prefer the to listen to the <a href="http://www.thenakedscientists.com/" target="_blank">Naked Scientists</a> or would you prefer to have them in your vehicle as you drive so you could have discussions with them?<br />
<br />
<i>Face forward conferences</i> - where the speaker is up front sharing slides and/or code and talking to them, these to me are like podcasts you can't fast forward through. Sometimes that works when someone is an expert about a topic you know nothing about, but want to (e.g. distributed logging). however, isn't a video a better use of your time? You can fast forward through the rhetoric, through the justification and get to the meat. Take a post from <a href="https://plus.google.com/+MichaelMahemoff" target="_blank">+Michael Mahemoff</a> - who shared a link to what appears to be a face-forward presentation from Open World Forum on a particular <a href="http://www.slideshare.net/roidelapluie/devops-andmonitoringowf13" target="_blank">open source </a><u>monitoring</u> stack (or combination of tools is probably a better phrase) based on Logstash and a few others. Now in itself, this to me is a five minute presentation - less the rhetoric.<br />
<br />
Sometimes however, you want the justifications - they help with convincing those in charge of purse strings that something is worth doing.<br />
<br />
Given the "watch a video" delivery vs attending "someone delivering a video to you in real life", who goes to these kind of conferences? There appear to be two types (obviously a generalization) - the corporate employee who has an allocated training budget and the socialiser who primarily goes for the hall-way conversations. We could say all sorts of people who chose to spend their training budgets on conferences at least 50% filled with content they can not apply to their jobs, but really, isn't the second type of person really better served by a conference where all you actually do is talk in the hallway? Wouldn't a mechanism to allow people who want to talk in the hallway about some particular topic work better with a little better organisation? Oh, that would be an <i>Unconference</i>.<br />
<br />
Unconferences tend to allow groups of people who want to talk about similar kinds of topics to get together and talk about those topics. They tend to work well when they are wildly generic around a certain area and well-known solutions to the problems are not well known. How vague is that? Lets go to where they don't tend to work - or they work, but not for a certain kind of person. Unconferences tend to focus on sharing of experiences and showing tidbits of work - this is good for people who haven't solved those problems or on the way to solve those problems, but it is terribly dull for those who have solved those problems. Experts in a more most fields tend not to attend a session in an unconference on a topic they are expert in because they aren't learning anything, so you get the almost solved or generally interested attending such sessions. Sometimes you have people who don't perceive themselves as experts but have a wealth of background and are passionate (e.g. when talking about testing or CI) still attend and there are many unexperienced people, and those are often gold<br />
<br />
Unconferences can work, but they don't when people are just trying to bolster numbers vs actually focus on a topic or set of topics where people haven't solved those problems yet. The best unconferences actually decide their topics up front based on who is attending (such as Citcon - thats pronounced Kitkon) so they get focus, and that lets people bail if they aren't interested in those topics rather than wasting a day. It also lets people who are interested in those topics really get value out of it. However when the topic is fairly narrow (Citcon is continuous integration and testing, a topic that largely hasn't changed in the last five years) then going to more than two of these conferences can get pretty dull.<br />
<br />
But hold on? Aren't those the only two models?<br />
<br />
No, not really, you have code camps (1+ days), micro code camps (like the Code Lounge that I run) which usually last half a day to a day, there are all sorts of tech focused events.<br />
<br />
I run the Code Lounge like I do because it allows me to suggest topics that I am interested in, list them, garner interest and then schedule them. They are focused, short and to the point. And the only people to attend are those that actually are interested in the topic.<br />
<br />
If I were to run a conference again (and I ran two, a face forward and then an unconference), what would it look like? I've talked quite a bit about that with <a class="g-profile" href="https://plus.google.com/113913703302067206043" target="_blank">+Mark Derricutt</a> and <a class="g-profile" href="https://plus.google.com/107749741713271708259" target="_blank">+Greg Amer</a> on <a class="g-profile" href="https://plus.google.com/100902757664379178394" target="_blank">+Illegal Argument</a> when I was still on that podcast. For me it would go something like this:<br />
<br />
<br />
<ul>
<li>Organiser(s) determine general topics and call for suggested papers</li>
<li>People vote on papers and others offer to present based on remuneration, etc</li>
<li>Papers once agreed on by Organiser(s) are put out as a schedule - if its free, people just vote, otherwise they have to stump up some cash - or the supporting companies have to for the people producing papers to create videos of their content</li>
<li>Rounds happen where Organisers help coach presenters on how best to present their content, material is uploaded and conference attendees are able to see.</li>
<li>People are allowed to post questions for discussion during conference, they can be voted on</li>
<li>On the day, the presenter gives a 5 minute overview of their presentation and starts going through the questions from top to bottom discussing and taking points from the attendees</li>
<li>Ample free corridor and work-together time is to be had</li>
</ul>
<br />
Organisers can always sell on (with a cut to the presenters) the video and Q&A session of the conference.<br />
<br />
Thats possibly a lot more work, but it would be really interesting to go to such a blended conference.<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-54953459159387770872013-11-02T13:53:00.000+13:002013-11-02T13:53:05.159+13:00Finding well known resources in the JVMA lot of people don't seem to know this, but it is actually extremely easy to find resources in the JVM without class path scanning.<br />
<br />
Quite a few people use a scanning library or implement one themselves (which given the number and variety of ways you can specify a classpath is always going to miss something out). But if you have a well known resource, then its pretty easy to gather up all instances of it in all of the different jars that make up your application, and this is true if you are using Groovy, Java, Scala, JPython or JRuby (or any of the others, as long as you can get access to a class loader).<br />
<br />
Since 1.2 of Java, there has been this method in the ClassLoader interface:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> <span style="font-size: x-small;"> </span></span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">public Enumeration<URL> getResources(String name) throws IOException;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: inherit;">So all you need to do is call:</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> getClass().getClassLoader().getResources("/META-INF/myfiles.properties")</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">for example and all copies of that will be returned. It may not be the most efficient implementation, but it is the most well supported and it comes straight in the JVM, no extra dependencies required.</span><br />
<span style="font-family: inherit;"><br /></span>Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-62841935060724752552013-08-25T21:05:00.001+12:002013-08-25T21:05:14.425+12:00Groovydoc and Maven<h2>
Groovydoc and Maven</h2>
<br />
One of the things we discovered this last week, when trying to release to Apache Maven Central, was that javadoc (which is required for every artifact that has code in it) was not being generated for our Groovy artifacts.<br />
<br />
Now in the past, we have used GMaven, which generated stubs (albeit poorly) and those caused the javadoc to trigger off, but since we moved to the Eclipse Maven plugin, this no longer worked - it compiling them both together. So we needed to start using actual Groovydoc - and it turned out, on a brief search, that there was no Maven plugin for Groovydoc.<br />
<br />
There was however an Ant plugin, but having an Ant script, although nice that it can be dropped back to, is usually somewhat error prone and can lead to difficult configuration problems. That seemed to be the case for the Ant plugin as well, with quite a few people not being able to get it working. One of the good things however that I discovered was that the real work was in fact done outside of the Ant Groovydoc task - it just collected the information and passed it on.<br />
<br />
The one thing I learned however, and the reason I am writing this up, is that <i>source paths must be relative</i>. If you pass a path that ends up having the full path to the file in it, Groovydoc will treat it as being in the Default package - and you will get a whole lot of classes in your DefaultPackage in the generated documentation. If you encounter this problem when using Ant or Gradle, then this will be the reason - make sure you use offsets from the directory where you run the build script from.<br />
<br />
I have sorted this problem out in the Maven build, and it will pick up all source directories that get added and include them - this means generated sources and anything you add with the build helper Maven plugin.<br />
<br />
The documentation and source is over on <a href="https://github.com/rvowles/groovydoc-maven-plugin" target="_blank">Github</a>, and the artifact is in Maven Central.Richard Vowleshttp://www.blogger.com/profile/16809664838871828087noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-45859919278573086352013-08-10T21:19:00.002+12:002013-08-10T21:19:45.888+12:00Latest plugins on Apache Maven Central<h2>
<span style="font-family: inherit;">Blue Train Software Plugins</span></h2>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">I've made a couple of plugins lately for the projects that we use at Group Applications at the University of Auckland. I decided they would be better as open source plugins, so I did them on my own time, and they address two aspects of the lifecycle of Maven projects that we build these days.</span><br />
<span style="font-family: inherit;"><br /></span>
<h3>
<span style="font-family: inherit;">The Release POM Plugin</span></h3>
<span style="font-family: inherit;">The first one, the Release POM plugin is specifically designed to generate a single pom with all transitive dependencies resolved. All dependendencies also add an exclusion clause for all dependencies of their own.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Why? Because sometimes, particularly when you are patching a production artifact, you need to make sure <i>all dependencies stay exactly the same, except for the one you are changing.</i> That is to me what a patch is, and that is very hard to do, as Maven doesn't actually store the versions of the artifacts you use. </span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">This is exacerbated because we use version ranges, which greatly aids development, bug fixing, feature enhancement and general working within the team, but it also means you need to make sure versions are locked down once you do an actual release intended for production. </span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">The<a href="https://github.com/rvowles/release-pom" target="_blank"> release pom plugin</a> and its documentation are on Github.</span><br />
<span style="font-family: inherit;"><br /></span>
<h3>
<span style="font-family: inherit;">The Karma Runner Plugin</span></h3>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">This one is for our Javascript, as we use AngularJS, however it can be used with any Javascript library, it just works very well with AngularJS. It requires the use of Node JS as the <a href="http://karma-runner.github.io/" target="_blank">Karma Runner</a> project uses that framework. </span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Much of what the Karma Runner Plugin does could be done with a considerable amount of manual setup in a Maven pom - it just assumes you are using a war or (preferably) Servlet 3 JAR setup, it scans your dependencies, unpacks the javascript, re-writes the Karma config file, brings in local developer overrides (e.g. browser setups) and runs your tests. It means with minimum fuss, and maximum compatibility, we can run our Jasmine tests for Angular JS. In our case, the definition of the plugin is in the parent of every Servlet 3 jar project, nothing else needs to be configured for an individual project.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">If you use Karma in a Maven project lifecycle, it really is a useful plugin.</span><br />
<span style="font-family: inherit;"><br /></span>
The <a href="https://github.com/rvowles/karma-runner-plugin" target="_blank">Karma Runner plugin</a> for Karma is on Github.<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-62012230474693121062012-04-30T17:55:00.004+12:002012-04-30T19:26:55.273+12:00Critical failures of Apache Maven Central<a href="https://issues.sonatype.org/browse/OSSRH-3437" target="_blank">Today's failure of the meta-data of logback</a> really hit us hard and resulted in significant downtime. What was the issue? Its an issue that appears to happen relatively frequently - someone releases a new version of their library and the release process blows away the Maven metadata - making it appear to anything checking the meta data that there is only one version available - the current one.<br />
<br />
As at time of writing, the Maven metadata for logback-classic looks like this:<br />
<br />
<br />
<div class="line" style="font-family: monospace; font-size: 13px;">
<span class="webkit-html-tag"><metadata></span></div>
<div class="collapsible-content" style="font-family: monospace; font-size: 13px; margin-left: 1em;">
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><groupId></span><span class="text">ch.qos.logback</span><span class="webkit-html-tag"></groupId></span></div>
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><artifactId></span><span class="text">logback-classic</span><span class="webkit-html-tag"></artifactId></span></div>
<span class="text"></span><br />
<div class="collapsible" id="collapsible1">
<div class="expanded">
<div class="line">
<span class="button collapse-button" style="-webkit-user-select: none; background-image: -webkit-canvas(arrowDown); background-position: 0% 0%; background-repeat: no-repeat no-repeat; cursor: pointer; display: inline-block; height: 10px; margin-left: -10px; vertical-align: bottom; width: 10px;"></span><span class="webkit-html-tag"><versioning></span></div>
<div class="collapsible-content" style="margin-left: 1em;">
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><latest></span><span class="text">1.0.2</span><span class="webkit-html-tag"></latest></span></div>
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><release></span><span class="text">1.0.2</span><span class="webkit-html-tag"></release></span></div>
<span class="text"></span><br />
<div class="collapsible" id="collapsible2">
<div class="expanded">
<div class="line">
<span class="button collapse-button" style="-webkit-user-select: none; background-image: -webkit-canvas(arrowDown); background-position: 0% 0%; background-repeat: no-repeat no-repeat; cursor: pointer; display: inline-block; height: 10px; margin-left: -10px; vertical-align: bottom; width: 10px;"></span><span class="webkit-html-tag"><versions></span></div>
<div class="collapsible-content" style="margin-left: 1em;">
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><version></span><span class="text">1.0.2</span><span class="webkit-html-tag"></version></span></div>
<span class="text"></span></div>
<div class="line">
<span class="webkit-html-tag"></versions></span></div>
</div>
</div>
<span class="text"></span><br />
<div class="line">
<span class="webkit-html-tag"><lastUpdated></span><span class="text">20120426133004</span><span class="webkit-html-tag"></lastUpdated></span></div>
<span class="text"></span></div>
<div class="line">
<span class="webkit-html-tag"></versioning></span></div>
</div>
</div>
<span class="text"></span></div>
<div class="line" style="font-family: monospace; font-size: 13px;">
<span class="webkit-html-tag"></metadata></span></div>
<div class="line" style="font-family: monospace; font-size: 13px;">
<br /></div>
We are using the version range [0.9.17] for Grails 1.3.7 projects and [1.0.1] for Grails 2.0.3 projects. This is good Maven hygiene - we want the build to fail if these <i>specific</i> versions are not available for a <b>good reason</b> (such as no repository available) - not for a <b>bad reason</b> like someone broke the repository. We don't want Maven to choose a different version, we want <i>that specific version</i>.<br />
<br />
When you use version ranges, you need the meta data - that tells Maven what versions are available. If you don't specify a range, it shouldn't use the meta data. However... With Maven 3.0.4 today, that turned out not to happen for some reason.<br />
<br />
Whats worse, is that when we wanted to work around the problem by relaxing the version ranges, it didn't work. It just got worse and weirder. Eventually, on the brainwave of Michael McCallum - installing the artifact in our 3rd party repository and moving it ahead of Central in the Nexus allowed us to get back to work.<br />
<br />
Now if this was the first time this happened, it wouldn't ring such alarm bells, but it isn't. Its at least the third (SOLR being the first time we hit it). I'm now in the unenviable position to have to discuss with my team whether direct Apache Maven Central access will be banned. If the artifact isn't taken from Central and put into our 3rd party repo, it can't be used. We just cannot afford the downtime.<br />
<br />
<b>What I don't understand is why there is no automatic process for repair for Central.</b> Its a critical resource, which we enormously appreciate but its value in accessing directly has become much lessened after today's extreme waste of productive, valuable time.<br />
<br />
Update: Another one - <a href="http://repo1.maven.org/maven2/woodstox/stax2/2.1/">http://repo1.maven.org/maven2/woodstox/stax2/2.1/</a><br />
<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-74805680377589008452012-04-27T22:56:00.000+12:002012-04-27T22:56:11.681+12:00Code Lounge, Post Mortem + Hangout LoungeWe held our first Code Lounge on Wednesday - Anzac Day in Auckland. We went to the morning service at Glendowie College and then proceeded a rapid clean up to ensure we were ready for the three people arriving. I've posted elsewhere about the results (ZeroMQ vs RabbitMQ vs Pusher), the day went pretty well in my opinion - we stopped every hour and made sure we were aware of what the other team was doing - we set ourselves goals which we overstepped periodically and when we got to the end of the day (3pm) and had only an hour left, we decided whether we continued learning (David and Irina decided to continue getting the Clustering working for Rabbit and trying to work out its fail over strategy) and Mark and I decided to push on with Pusher (which turned out to be an exercise in re-writing the library instead).<br />
<br />
All in all a good day. But I was lying on the bed after an exhausting week while listening to my wife practice Cello (which is a surprisingly meditate experience) and thinking about my backlog of books to read. They consist of programming language books (Dart 4 Hipsters for example), Tool books (a few around VIM are bubbling around, Pragprog have just released a new early access), Electronics books (MSP430 and various others), General informational/idealogical books (Information Diet, Biohacking) and fiction. And I'm not really reading them, particularly the programming language ones and I really really want to.<br />
<br />
So I'm going to get inspired by Chris Strom (Dart 4 Hipsters, The Spdy Book, Backbone.js recipes) and try starting chains (chains.cc) and see if I can just knuckle down and self educate.<br />
<br />
I spoke with Mark about it tonight and wondered aloud whether or not having a Hangout open when I was working away at a particularly topic might be a worthwhile idea. I think I will do it, I will try just having an open hangout around a particular topic and ensure I circle Programming related people in my G+ stream. If they want to come in they can otherwise I at least will continue to learn. I'll set a goal for 1 hour a day and see how I get on.<br />
<br />
(PS Manning have a great special on at the moment which includes a lot of interesting books, I'm trying desperately to not go and buy them and have them sitting there as well!)Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-28424448808818973292012-03-26T01:18:00.000+13:002012-03-26T01:21:04.311+13:00Code LoungeLast year I started a conference - I was looking for a barcamp style conference that I could really enjoy going to - that would be full of topics I wanted to cover. It eventually got named The Exceptional Conference (given Illegal Argument as the theme) - and it got a good turnout. However, the topics really didn't interest me all that much. They interested a lot of other people however and the feedback was good. We got some Pizza sponsorship from Fronde, the rest was paid for by my company Blue Train Software Ltd.<br />
<br />
We had a few people come up from Wellington - which was excellent, I enjoy John and Nigel's company and it was great to meet the other guys who seem to form a really informed and friendly group. John and Nigel decided to run one in Wellington and so it was simply polite that I went - Mark and I went down (I took my son Xavier with me). Unfortunately it was pretty much the same topics, so I didn't enjoy it a whole lot. I felt pretty guilty about that - John, Nigel, and the VUW crew had gone to a lot of trouble to organize it - I just couldn't get into it. Later I just realized that my original goal - something discussing things that <b>I</b> am interested in is hard to find at conferences - even ones of such a small size. I get it from the IllegalArgument podcast, but only in general chat. I also found there wasn't enough core-geek depth.<br />
<br />
So when Nigel asked when the next conference was going to take place I said I'd bow out of this one, but more prodding from Nigel and Mark got me thinking, and I really appreciate that they did prod.<br />
<br />
What I realized I wanted in reflection was what John, Nigel, Mark and myself did at the start of all this - a geek weekend or even just a single day where we could take a topic or idea and really explore it - a small number of people (4 in that case - perhaps up to 8?) would probably work. Idea had to be fairly tight, people shouldn't know too much about it otherwise its not a learning experience - or perhaps they should - I don't know yet. Close environment, good for pairing, good food, good drinks (coffee for those that like it, water for those that don't :-), good wifi, projector if necessary, white board, all that good stuff.<br />
<br />
And I realized I had most of these in my lounge - and so was born the concept of CodeLounge. The idea is to take a topic, advertise your lounge will be a meeting place on a specific date for a specific set of times, specify what you can provide and just go for it. At the moment, I'm not sure how it will turn out - but I am hoping that we'll learn ways of making the experience interesting.<br />
<br />
I registered a domain name for it - CodeLounge.IO (a play on Google.IO - a conference I no-longer wish to attend, the videos are good enough), there is no website yet - perhaps that is the first job (Twitter Bootstrap anyone?), but we have a <a href="http://www.meetup.com/codelounge/" target="_blank">Meetup group</a> and a logo.<br />
<br />
I call them a micro-code-camp.<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-28142723873006532382012-03-26T01:04:00.002+13:002012-03-26T01:21:26.633+13:00Bluegrails Maven PluginThere have been some considerable changes made to the Bluegrails Maven plugin - we are starting support for Grails at 2.0.1 - it is quite difficult to ensure compatibility as I have found a number of quirks to how the Grails Gant scripts behave.<br />
<br />
We are now using it internally, and it seems to be going well - we found one dependency that wasn't in central (Spring UAA) but that can be excluded and is only used by the plugin (via the scripts inclusion). I hope to have video tutorials up once we are happy that it is solid and working well.<br />
<br />
It can be downloaded on Github now under the <a href="http://www.github.com/bluegrails/grails-maven">http://www.github.com/bluegrails/grails-maven</a> repository.<br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-86892495776560916412012-03-16T22:43:00.001+13:002012-03-16T22:43:41.380+13:00Can you afford to not know Git any longer?<div><p>As much as people still debate the Subversion vs Git arguments, particularly in the JVM community I asked myself today whether or not you can get away with not knowing the basics of Git any longer. Why? Because 2/3 of your source base (or more) is likely to be open source and an ever increasing portion of that seems to reside on Github - for the better in my opinion as it has a superior mechanism for supporting patches via pull requests. One of the fundamental tenants of open source is that you can fix the bug and with Git, not being able to get a patch accepted - like with Guava or many Grails plugins) or in a timely fashion so it is released and usable (for example with Guice), is not fatal. At least you will be able to keep merging the upstream changes into your code base with the patches relatively easily (as I recently did with the Grails Maven plugin).</p>
<p>I think any organisation that chooses an open source project that isn't on Git is actually choosing one that is a higher risk for their organization and when you do, you need to have a basic working knowledge of it. If it was me, I'd get every organisation to have a Github organisation account where any forked artifacts reside so as people come and go in their organisation, they have one central place where their patched versions reside. Its not a perfect solution, ideally projects that need patching with do it quickly and update themselves, but the way most projects have their artifacts organized and how often they release is always a problem, so I think it is inevitable and Git offers such a better mechanism than Subversion.</p>
<p>So if you are a JVM software developer and you don't know Git, now is the time to learn. There are great books out there and some excellent screen casts from Orielly, so get on it now!</p>
</div>Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-85530797677336268572012-03-09T23:35:00.001+13:002012-03-09T23:37:03.597+13:00Grails 2.0.1 and Maven<br />
One of the projects I have been working on is moving from Grails 1.3.7 to Grails 2.0.1 - so I naturally went looking for the Maven artifacts for the project. We use Maven extremely successfully with composites and I have had a talk to Graeme about using them to reduce clutter in the project pom and make things clean.<br />
<br />
My first hurdle is that Grails 2.x is not in Apache Maven Central - <a href="https://twitter.com/#!/graemerocher/status/175479464451514369" target="_blank">asking Graeme on twitte</a>r gave the answer that it wasn't going to be there and was in http://repo.grails.org/grails/core. Fair enough I thought until I realized that in fact this would cause problems for me - I couldn't release bluegrails because bluegrails needs to be in central. Bluegrails as a refresher is a set of composite dependencies that allow you to be consistent with Grails dependencies in your non Grails artifacts.<br />
<br />
Greame's <a href="https://twitter.com/#!/graemerocher/status/175481713189859328" target="_blank">reasoning when @talios</a> prodded him was that Grails had external dependencies that weren't in central - which I understand - if you have that you can't use Apache Maven Central, and it is a rule that makes sense especially from a corporate perspective. Grails run their own repo, just point to it. Yet, still - it has hosed my ability to deploy these artifacts that add considerable value to anyone who uses Maven and Grails generally in their organization and want to maintain version compatibility.<br />
<br />
So tonight I decided to check -<i> if I could chase those projects down that weren't in Apache Maven Central and offer to help get them there</i>, I'd hope the Grails team would push to Central.<br />
<br />
I downloaded and build the Grails Maven Archetype and Grails Maven Plugin (which wouldn't build in Maven 3.0.4 - there are a couple of annotation problems with it). Once I had those two working, I created an archetype and found to my delight that Graeme had taken the Composite approach to Maven dependencies - the new pom is a <b>delight</b>!<br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"> <dependencies></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><dependency></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> <groupId>org.grails</groupId></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> <artifactId>grails-dependencies</artifactId></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> <version>${grails.version}</version></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <type>pom</type></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> </dependency></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> </dependencies></span><br />
<br />
Thats it. Awesome! Exactly how it should be.<br />
<br />
Now to ascertain the naughty dependencies - so a mvn dependency:tree<br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] - org.grails:grails-dependencies:pom:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-core:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- commons-io:commons-io:jar:2.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.slf4j:slf4j-api:jar:1.6.2:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.slf4j:jcl-over-slf4j:jar:1.6.2:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - commons-lang:commons-lang:jar:2.6:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-bootstrap:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-crud:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-datastore-core:jar:1.0.2.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-hibernate:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- javax.validation:validation-api:jar:1.0.0.GA:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.grails:grails-datastore-simple:jar:1.0.2.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- javax.transaction:jta:jar:1.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.hibernate:hibernate-validator:jar:4.1.0.Final:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.hibernate:hibernate-ehcache:jar:3.6.7.Final:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.grails:grails-datastore-gorm:jar:1.0.2.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- org.hibernate:hibernate-core:jar:3.6.7.Final:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | | +- antlr:antlr:jar:2.7.7:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | | +- dom4j:dom4j:jar:1.6.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | | - org.hibernate:hibernate-commons-annotations:jar:3.2.0.Final:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - javassist:javassist:jar:3.12.0.GA:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-codecs:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - commons-codec:commons-codec:jar:1.5:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-controllers:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-converters:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-datasource:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- commons-dbcp:commons-dbcp:jar:1.4:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - commons-pool:commons-pool:jar:1.5.6:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-domain-class:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-filters:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-gsp:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-i18n:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-logging:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-log4j:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- log4j:log4j:jar:1.2.16:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - org.slf4j:jul-to-slf4j:jar:1.6.2:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-mimetypes:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - commons-collections:commons-collections:jar:3.2.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-scaffolding:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-services:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-servlets:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-url-mappings:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-plugin-validation:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-resources:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-spring:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - org.springframework:spring-web:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - aopalliance:aopalliance:jar:1.0:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-web:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- commons-fileupload:commons-fileupload:jar:1.2.2:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | +- xpp3:xpp3_min:jar:1.1.4c:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - opensymphony:sitemesh:jar:2.4:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.grails:grails-webflow:jar:2.0.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.codehaus.groovy:groovy-all:jar:1.8.6:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.aspectj:aspectjweaver:jar:1.6.10:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.aspectj:aspectjrt:jar:1.6.10:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- cglib:cglib:jar:2.2:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- asm:asm:jar:3.1:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- oro:oro:jar:2.0.8:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- commons-beanutils:commons-beanutils:jar:1.8.3:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- commons-el:commons-el:jar:1.0:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- commons-validator:commons-validator:jar:1.3.1:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:jar:1.2_jdk5:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- com.h2database:h2:jar:1.2.147:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- javax.servlet:jstl:jar:1.1.2:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- net.sf.ehcache:ehcache-core:jar:2.4.6:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-aspects:jar:3.1.0.RELEASE:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-jdbc:jar:3.1.0.RELEASE:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-jms:jar:3.1.0.RELEASE:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-orm:jar:3.1.0.RELEASE:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-tx:jar:3.1.0.RELEASE:runtime</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] - org.springframework:spring-webmvc:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-asm:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-beans:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-context:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - org.springframework:spring-aop:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-context-support:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] +- org.springframework:spring-core:jar:3.1.0.RELEASE:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] | - commons-logging:commons-logging:jar:1.1.1:compile</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[INFO] - org.springframework:spring-expression:jar:3.1.0.RELEASE:compile</span><br />
<br />
(I removed the \)<br />
<br />
And a bit of code (I put that all in a def called <i>deps</i>, its ugly but it doesn't matter) - ignored the org.grails ones as these were accepted to not be in central:<br />
<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">def failed = []</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">deps.split("\n").each { line -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> if (line.startsWith("[INFO]")) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> line.split(" ").each { dep -></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> if (dep.size() > 0 && Character.isLetter(dep.charAt(0)) ) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> if (!dep.startsWith("org.grails")) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> //println dep</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> String[] splitDep = dep.split(':')</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> String url = "http://repo1.maven.org/maven2/${splitDep[0].replace('.', '/')}/${splitDep[1]}/${splitDep[3]}/${splitDep[1]}-${splitDep[3]}.pom"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> try {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> new URL(url).openStream()</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> println "ok ${url}"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> } catch ( Exception ex ) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> println "failed ${url}"</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> failed.add(url)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
And the result?<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-io/commons-io/2.1/commons-io-2.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.2/slf4j-api-1.6.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.6.2/jcl-over-slf4j-1.6.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-lang/commons-lang/2.6/commons-lang-2.6.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/javax/validation/validation-api/1.0.0.GA/validation-api-1.0.0.GA.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/hibernate/hibernate-validator/4.1.0.Final/hibernate-validator-4.1.0.Final.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/hibernate/hibernate-ehcache/3.6.7.Final/hibernate-ehcache-3.6.7.Final.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/hibernate/hibernate-core/3.6.7.Final/hibernate-core-3.6.7.Final.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/antlr/antlr/2.7.7/antlr-2.7.7.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/hibernate/hibernate-commons-annotations/3.2.0.Final/hibernate-commons-annotations-3.2.0.Final.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/javassist/javassist/3.12.0.GA/javassist-3.12.0.GA.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-codec/commons-codec/1.5/commons-codec-1.5.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-pool/commons-pool/1.5.6/commons-pool-1.5.6.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/log4j/log4j/1.2.16/log4j-1.2.16.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/slf4j/jul-to-slf4j/1.6.2/jul-to-slf4j-1.6.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-web/3.1.0.RELEASE/spring-web-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/aopalliance/aopalliance/1.0/aopalliance-1.0.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-fileupload/commons-fileupload/1.2.2/commons-fileupload-1.2.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/opensymphony/sitemesh/2.4/sitemesh-2.4.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/codehaus/groovy/groovy-all/1.8.6/groovy-all-1.8.6.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/aspectj/aspectjweaver/1.6.10/aspectjweaver-1.6.10.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/aspectj/aspectjrt/1.6.10/aspectjrt-1.6.10.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/cglib/cglib/2.2/cglib-2.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/asm/asm/3.1/asm-3.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/oro/oro/2.0.8/oro-2.0.8.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.3/commons-beanutils-1.8.3.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-el/commons-el/1.0/commons-el-1.0.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-validator/commons-validator/1.3.1/commons-validator-1.3.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/com/googlecode/concurrentlinkedhashmap/concurrentlinkedhashmap-lru/1.2_jdk5/concurrentlinkedhashmap-lru-1.2_jdk5.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/com/h2database/h2/1.2.147/h2-1.2.147.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/javax/servlet/jstl/1.1.2/jstl-1.1.2.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/net/sf/ehcache/ehcache-core/2.4.6/ehcache-core-2.4.6.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-aspects/3.1.0.RELEASE/spring-aspects-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-jdbc/3.1.0.RELEASE/spring-jdbc-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-jms/3.1.0.RELEASE/spring-jms-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-orm/3.1.0.RELEASE/spring-orm-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-tx/3.1.0.RELEASE/spring-tx-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-webmvc/3.1.0.RELEASE/spring-webmvc-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-asm/3.1.0.RELEASE/spring-asm-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-beans/3.1.0.RELEASE/spring-beans-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-context/3.1.0.RELEASE/spring-context-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-aop/3.1.0.RELEASE/spring-aop-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-context-support/3.1.0.RELEASE/spring-context-support-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-core/3.1.0.RELEASE/spring-core-3.1.0.RELEASE.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.pom</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">ok http://repo1.maven.org/maven2/org/springframework/spring-expression/3.1.0.RELEASE/spring-expression-3.1.0.RELEASE.pom</span><br />
<div>
<br /></div>
<div>
No failures.</div>
<div>
<br /></div>
<div>
Now I'm perplexed - it isn't because of dependencies in the Grails artifacts. </div>
<br />
<br />
<br />
<output> </output><br />
<br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<output></output><br />
<br />
<br />Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-35336564041034498992012-02-16T20:03:00.000+13:002012-02-16T20:03:47.242+13:00I will defeat you Grails config loader!As part of the way we do thing, we wanted to use properties files rather than the plain config.groovy - and thats pretty normal - Grails documentation even gives you a mechanism to load in extra config.<br />
<br />
Ideally for us, this could be configured outside of Grails - through some Spring mechanism rather than having to modify the Grails config for every app, but its ok - it has worked. Until today. Today I hit the bug in Groovy where the Properties -> ConfigObject via ConfigSlurper started giving weird results. I had these lines<br />
<br />
<pre>grails.mail.host = localhost
grails.mail.port = 5000
</pre>
<br />
and i was getting<br />
<br />
<pre>grails.mail.host = localhost
grails.'mail.port' = 5000
grails.mail.port = [:]
</pre>
<br />
which turns out to be a peculiarity of how Groovy loads the file - and there are quite a few bugs around this on the Groovy bug parade - back since 1.6 (and unclosed).<br />
<br />
So how to get Grails to load a properties file in my own fashion? Well thats where the fun started. Turns out that Grails hard codes in its scripts the creation of the DefaultGrailsApplication, which in turn has hard coded the ConfigurationHelper. Neither of these were overrideable or extendable without forking Grails 1.3.7. So one of the things we had been doing was using static methods on our class - and in the end, this turned out to be our saviour - that and Groovy's MOP.<br />
<br />
As there were lots of parse methods already, I added a methodMissing to detect for a request for a parse with our class and then passed it onto a method of my choosing that would turn the tree into something more resembling common sense.<br />
<br />
That seemed to fix it - so if you have the same problem with broken properties loader when using Grails, that approach worked for me.Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-40808045949328140572012-02-15T21:57:00.000+13:002012-02-15T21:57:10.743+13:00Bad request errors from Sonatype's Nexus during releaseSo we changed the way we published our artifacts to the repository - going straight to the Nexus, instead of via Apache and having Nexus index them. This meant it was easier from a permissions perspective - making sure the right people could upload artifacts into the right places and Nexus and Apache kept mixing up on-disk file permissions.<br />
<br />
That unfortunately turned up a very annoying bug in Nexus - you can't attach 0 length files. Typically for artifacts that could generate a WSDL from code, we'd attach them as a -wsdl classifier artifact into the repository. If there were none, then we would touch the file, so the build-helper plugin could be configured to attach the file, otherwise it would fail.<br />
<br />
But the move to Nexus knocked that on the head and we could no longer release web service artifacts - so if you run into a situation when you are releasing artifacts and you get a bad request for no good reason - it may be because you are trying to upload a zero length artifact.<br />
<br />
The fix was to remove the zero length file and upgrade to 1.6 of the build helper plugin which has a new capability to <a href="http://mojo.codehaus.org/build-helper-maven-plugin/attach-artifact-mojo.html" target="_blank">skip the attachment if its empty</a>. Our plugin definition now looks something like this:<br />
<br />
<br />
<br />
<pre class="brush: groovy; highlight: [4, 13];"><plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<skipAttach>true</skipAttach> <!-- if the file doesn't exist, skip attaching it, added since 1.6 -->
<artifacts>
<artifact>
<file>${project.build.directory}/generated-wsdl.jar</file>
<type>jar</type>
<classifier>wsdl</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</pre>Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-26173266468990325432012-02-15T21:49:00.000+13:002012-02-15T21:49:20.507+13:00So I don't forget thisOne of the things that that I'm always doing is posting to this blog with the SyntaxHighlighter javascript extension. So to make it easy for myself to remember a bit of code to quickly format the code, I'm posting it here:
<br />
<br />
<pre class="brush: groovy;">'''
code goes <> here
'''.each { c -> print '"><&'.contains(c)?('&#'+(int)c+';'):c }
</pre>
<br />
And it works in the <a href="http://groovyconsole.appspot.com/" target="_blank">online Groovy console</a>.Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-67502289299852035852012-02-10T17:07:00.002+13:002012-02-10T17:07:37.577+13:00Android TabletsLast year I purchased a Samsung Galaxy Tab 10.1 - I was spending a lot of time browsing the news feeds for stuff for the podcasts, being away from a computer and not being able to make use of valuable time. So I decided to grab a Tablet and see how useful it would be. I was also considering one for use by my wife - given the capabilities of Google Docs Drawing, it was pretty awesome in its ability to do something she was paying for Comic Life on her Mac. I really quite like Android 3.1 and it made moving to my Galaxy Nexus easy and familiar.<br />
<br />
It turned out way cheaper than I expected and I have very much enjoyed the experience - unfortunately lately the YouTube has been playing up, but other than that its excellent (seems to be a network issue). The cheap $130 APAD 2 I got was totally useless not because of its resistive screen but because the wifi is utterly lousy, even 1 metre away from the wifi and I can't use it.<br />
<br />
The problems I now have with it is that to really use it, I need a keyboard - so ideally I'd replace it with a Transformer Prime with a keyboard - in hindsight I should have waited, but I can always get a keyboard for the Galaxy Tab. The lack of a decent Google Docs client for Android continues to make that application wholly useless unfortunately, so if you are looking for one for that reason, don't bother.Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-65357611901595231522011-12-16T17:31:00.000+13:002012-02-10T17:07:58.355+13:00Starting in Maven?It surprises me again and again when I meet developers who are using Maven and they don't have a local repository. For anyone using Maven, once the decision has been made to actually use it, the next thing you do is get a local Maven repository. Really, its at the top of my list for things to do.<br />
<br />
If you are working on projects at home, open source or not, at work with a single developer, whatever, you should install a Repository Manager. If you have more than one developer using Maven, you are simply insane not to use a one. So why would you install a repository manager even if you are only a single developer? Surely its hard?<br />
<br />
Well, no, it is incredibly simple, Sonatype's Nexus takes 10 minutes to set up for example. Here are a few if you don't already have one:<br />
<br />
<ul>
<li><a href="http://nexus.sonatype.org/" target="_blank">Sonatype's Nexus</a> - this comes in a Pro and Free version. I use Free in three different places and it has served me very well. </li>
<li><a href="http://archiva.apache.org/" target="_blank">Apache Archiva</a> - I used this in my last job, and I didn't like it as much as I like Nexus. It seemed a little more complex to set up. </li>
<li><a href="http://www.jfrog.com/" target="_blank">JFrog's Artifactory</a> - I haven't used this, but they do a lot with the Gradle community and are going to do some work around Grails plugins. </li>
</ul>
<div>
So back to why? A few reasons</div>
<div>
<ul>
<li>Unfortunately, not everything is in Maven Central. This means you have to edit your settings.xml to add each repo in as you need it. Don't, put them in your Repository Manager and just point to it - let it sort them out. Add a new repo? Just add it in the Repository Manager. </li>
<li>If you are doing Maven releases properly, you are using version ranges. Version ranges cause Maven to scan all repos in your settings.xml for updates on a regular basis - and you don't want this traffic going far if you can help it, it slows you right down.</li>
<li>If you don't have a Repository Manager, you are either (a) not releasing you artifact (a <b><i>cardinal sin</i></b>) or (b) only releasing to central (which is feasible for some projects, e.g. <a href="https://github.com/easyb" target="_blank">easyb</a>). </li>
<li>It allows you to blow away your local repository when it gets untidy from snapshot builds which people use periodically. Note if you do everything using snapshot builds, you are using Maven in a broken fashion.</li>
<li>You can put a Repository Manager on your own machine as a proxy as well, if you want to keep as much network traffic local for example, but you have the main Repository Manager hosted elsewhere - on a slow VPN or in another country.</li>
</ul>
<div>
<b>Releasing</b></div>
<div>
I've known people working on projects who have never released - they just work with snapshots! Thats nuts! A release in maven is an individual artifact (your entire war would be one, as well as an individual api) - it is something that has been cleaned, started from scratch, compiled, verified, all tests run, tagged in the remote repository, checked out again to a different location and all that done again and then uploaded to your Repository Manager. You should be releasing very regularly. And be fine grained about your artifacts! As soon as you have done a significant lot of work, release it. </div>
<div>
<br /></div>
<div>
<b>Version Ranges</b></div>
<div>
Version ranges are important for two main areas of Maven usage. </div>
<div>
<ul>
<li>You have chosen an open source project based on its functionality, license, code quality, whatever. But you choose a specific version. For example, in a recent project we chose <a href="http://lucene.apache.org/solr/" target="_blank">SOLR</a> - 3.3.0. Our use of the 3.3.0 means we use <version>[3.3.0]</version> as our version. This means I want 3.3.0 and nothing else. Not 3.3.1, not 3.3.5, not 3.4.0. Why is that important? SOLR when it released 3.4.0 caused the meta data in (Apache) Maven Central to get corrupted - this means the only version available was 3.4.0. If I had instead specified <version>3.3.0</version> - if Maven didn't have it locally, it would go pick up 3.4.0 and happily use it. If the corruption had caused only 2.3.0 to be available, Maven would have used that - it would have been a mess! Always specify exactly what ranges you are happy to include artifacts for - if stuff breaks, <a href="https://issues.sonatype.org/browse/MVNCENTRAL-46" target="_blank">lodge JIRA tickets</a> on Sonatype's JIRA, they are pretty quick about it and will be ensured actually using what you have chosen. </li>
<li>Generally projects decide on how they issue a new version number - some just use two (major.minor), where the major changes on a breaking api change, some use three levels. You should specify a range when you are happy so new releases turn up - and typically you will do this of your own artifacts. You start at version 1.1 (because thats how ranges work, don't start at 1.0 or 0.x, thats just nonsense) and keep releasing until you break. Release early and often. If someone then who is using your artifact says [1,2) or say [1.7,2) (if 1.7 is the start of your api having what they want) then they will just get new releases as they come out, ensuring they keep current. These kinds of version ranges make Maven use really nice and easy to use when it comes to dependencies (along with composites, another post). </li>
</ul>
</div>
<div>
Composites are just pom's that contain a collection of dependencies - we have composite-cxf, composite-spring, composite-jackson, composite-logging-api, composite-logging-api, etc. Composites keep your pom clean, they take out all of the stuff that isn't really specifically to do with your App, but the kind of App you are building. They become accepted building blocks - and they rev. like normal artifacts. Changing from Spring 2.5 to Spring 3.1? rev the major version of your pom - existing artifacts won't be broken, you can fix them as necessary. Composites can be great for public facing releases as well - if Wicket 1.6 was just a pom containing fine grained artifacts that were hard version ranged, then we they released bug fixes (early and often), you could override specific artifacts and bring in the new releases to get the ones you want. </div>
<div>
<br /></div>
<div>
And another cardinal sin - it happens, but rarely, do not put dependencies in parent poms. Except for the plugins which often need them (and try and use composites for them), but your parent poms should not have modules or dependencies defined. <b>Put them in the composites.</b> </div>
</div>
<div>
(It sometimes happens when you are using projects that are so terribly broken you can't help it, but its rare)</div>
<div>
<br /></div>
<div>
Do yourself a favour, if you are using Maven, install a Repository Manager. </div>Richardhttp://www.blogger.com/profile/12610332640364071037noreply@blogger.com0tag:blogger.com,1999:blog-7942485266575739157.post-10667281225379871882011-10-11T19:40:00.000+13:002011-10-11T19:40:59.064+13:00Initializing your Grails ServicesIn one of my current Grails projects I need to do what I would normally use @PostConstruct for, but am unable to as usually GORM has not completed its additional methods by the time it comes to use them. So the general recommendation is to do it in the bootstrap - a technique I generally do not like as it means you cannot make it a best practice easily - just include a dependency and its always done.<br />
<br />
Ideally I think I'll need to turn this into a plugin, but I hate source code plugins enough to just include the Bootstrap at the moment.<br />
<br />
So this goes in your Grails conf, and its a slight modification of the one you find on Google - it checks the scope of the bean, singleton ones only are initialized.<br />
<br />
<pre class="brush: groovy; highlight: [2, 10, 12];">import org.codehaus.groovy.grails.commons.GrailsClass
import org.codehaus.groovy.grails.commons.GrailsClassUtils
class BootStrap {
def grailsApplication
def init = { servletContext ->
grailsApplication.serviceClasses.each { GrailsClass gClass ->
String scope = GrailsClassUtils.getStaticPropertyValue(gClass.clazz, "scope")
if (!scope || scope == "singleton") {
def serviceBean = grailsApplication.mainContext.getBean(gClass.propertyName)
if (serviceBean.metaClass.respondsTo(serviceBean, 'grailsInit')) {
serviceBean.grailsInit()
}
}
}
}
def destroy = {
}
}
</pre><div><br />
</div><br />
<br />Richard Vowleshttp://www.blogger.com/profile/16809664838871828087noreply@blogger.com0