My heart sank when I saw that CouchDb had to be built for each OS. No dmg? No deb? I thought this was going to be a classic painful process that works for some people and not for others. Well, not so!
Edit: After blogging this it turns out there are much easier ways to get started with CouchDb.
I installed it on both Mac OS X and on Ubuntu and both times the installation went just the way they README said it would.
In Ubuntu*:
Edit: Just do a sudo apt-get install couchdb.I just fired off a sudo apt-get install command, ran a configure and make, created the couchdb user, sudo chowned a couple of directories. I was done! I fear configure and make operations in Linux because they just *always* go wrong for me and I end up trawling around for some hint as to what dependency I'm missing. Well, this went very smoothly. Well done CouchDb guys. The one potential thorn was that for me adduser didn't create the couchdb home directory - it did tell me though: "Not creating home directory `/var/lib/couchdb'.". Creating that directory literally was the only "undocumented" step. From opening up the couchdb web site page to having couchdb installed took me seven minutes. The main reason that was so quick for me is that I looked at the README file in the download first. The web site lists many other install guides that I didn't try.
In Mac OS X:
Edit: Just follow the MacPorts installation instructions.This actually took me a lot longer than the Ubuntu install. I didn't already have the Xcode installer on my hard drive as the README suggested I might. So off I went to download the 1Gb installer from apple. This was a pain because I sunk tens of minutes into downloading it as well as the annoyance of signing up with apple's developer website, plus the time that the Xcode installer took to run. Thankfully I did already have MacPorts installed - if you don't then this will be an extra install step for you (that's really not hard, just more of your time). Again, configure and make ran smoothly - and the README pointed me at the correct Mac way to create a couchdb user. Very nice. Installation from concept to realisation took somewhere between one and two hours. Xcode took a big portion of that time as well as the MacPort package building. Again, very smooth, with none of the surprise gotcha errors that I was expecting.
* The Ubuntu install instructions where for 8.04 Hardy Heron. I installed on 8.10 Intrepid without issues.
Jan 31, 2009
Installing CouchDb
Posted
9:37 AM
2
comments
Links to this post
Jan 28, 2009
Technology ToLearn List
Two new technologies have popped up on my horizon (thanks to the pdxjs meeting this evening): Sprout Core and CouchDb. Ok, CouchDb isn't that new, but I only just started taking it seriously.
Edit: Also there's Lua's Love2D.
Posted
11:47 PM
2
comments
Links to this post
Labels: community, javascript, lua, tools
Jan 25, 2009
Google Search API Made Easier With Groovy 1.6
I think Groovy scripts are great fun. Now they're even more portable as you can use Grape to do download missing required jars for you:The json.org library leaves something to be desired. We have to break encapsulation of JSONArray (with import org.json.JSONObject
@Grab(group='org.json', module='json', version='20080701')
class GoogleSearch {
static def query(term) {
new JSONObject(new URL(
"http://ajax.googleapis.com/ajax/services/search/web"+
"?v=1.0&q=${URLEncoder.encode(term)}").text)
}
}
println GoogleSearch.query("simon's cat").
responseData.results.@myArrayList.collect { it.url }@myArrayList) in order to iterate over the results.
It's a great animation by the way:
Posted
5:51 PM
0
comments
Links to this post
Labels: groovy
Ease In & Out Demo
So here's a demonstration that builds on a number of previous posts: reading in an image, an ease in and out function, drawing a rotated image using Java2D, and double buffering (all in Groovy of course):
See this previous post for the code to setup the window for double buffering. I also used gifAnimation to encode the gif animation (code not shown).def ball = ImageIO.read(
new URL("http://www.k2lp.com/files/sprite-basketball_240.jpg"))
def ease(min, max, percentage) {
return min+(max-min)*
(1-Math.sin(Math.PI/2.0+percentage*Math.PI))/2.0
}
def drawRot(g, image, x, y, angle) {
def transform = new AffineTransform()
transform.translate(x, y)
transform.rotate(Math.toRadians(angle))
transform.translate(-image.width/2, -image.height/2)
g.drawImage(image, transform, null)
}
def mid = 340-(232/2) // x coordinate to "bounce" back from
while(true) {
for(percent = 0; percent<=100; percent += 4) {
Graphics g = bufferStrategy.getDrawGraphics()
g.setColor(Color.WHITE)
g.fillRect(0,0,width,height)
// easing in & out ignores the "bounce"
def x = ease(120, 2*mid-120, percent/100.0)
if (x>mid) {
x=2*mid-x // we "bounce" the ball
}
// note that we use our x coordinate to
// specifc the ball's rotation
drawRot(g, ball, x, 120, x)
bufferStrategy.show()
Thread.sleep(100)
g.dispose()
}
}
Posted
1:03 PM
2
comments
Links to this post
Ease In And Ease Out Functions With Grapher
Mac OS X comes with a great tool called Grapher. It makes it very easy to experiment with functions to see what is going to work for you. I'm reinventing the wheel here and looking for a suitable ease in and ease out function; one that appears to accelerate to begin with and then brake towards the end.
I've come up with (1-sin(PI/2+x*PI))/2 that given a value of x between 0 and 1 will give me a corresponding value between 0 and 1.
Here's Grapher's visualisation of the function:
(This is me regurgitating what Howard showed me.)
Posted
12:21 PM
5
comments
Links to this post
Labels: tools
Hey! Java2D Is Pretty Cool!
So, I'd been using Gosu for Ruby because it made loading and drawing rotated images very quick and easy, plus it hid all of the rendering boiler plate from you. Well, it turns out that while I've been busy ignoring what Java2D can do for you, it's matured into something pretty usable. Add Groovy into the mix and abstracting away from Java2D to provide a sweet API would be pretty easy. So we can have our cake (powerful cross-platform graphics plus ceremony-free programming) and eat it.
If like me you've been ignorant of what Java's been up to then you'll find these snippets of interest.
Read an image from disk:Draw a rotated image:def background = ImageIO.read(new File("background.jpg"))Why the three transformation steps and not a single rotation? Well, the def transform = new AffineTransform()
transform.translate(center_x, center_y)
transform.rotate(Math.toRadians(angle))
transform.translate(-image.width/2, -image.height/2)
g.drawImage(image, transform, null)Graphics2D.drawImage that takes a transform doesn't accept coordinates. So that's why we need to translate to the coordinates we want to draw at. Unless you want to rotate your image around the top left corner then you'll want to be translating to the coordinates for the center of your image. As Graphics2D.drawImage draws from the top left of the image, we then need to translate (relative to our new rotation) to where the top left corner of our image is. Sweet!
Posted
11:47 AM
0
comments
Links to this post
Slick Online Image Editor
I spent a couple of minutes using pixlr online. It has a very rich UI for a web app.
Posted
10:54 AM
1 comments
Links to this post
Labels: tools
Jan 24, 2009
Clojure API Coding
Following Howard's suggestion I've managed to pull all of my Clojure API into one chunk of code that's independent of the client code. Anything that depends on client code definitions I've made into a macro that I can define before the client code due to the nature of macro evaluation. Is that the way I'm supposed to achieve this?
Now my client code looks like this (I removed the animation logic):Looking at the code again, there's now very little need for the API code to depend on the client code, I could pass in width and height to the run function. Hmm.(def width 400)
(def height 400)
(def branch-angle 30)
(defn draw_tree [g2d length depth]
(if (> depth 0)
(do
(. g2d drawLine 0 0 length 0)
(trans g2d (int length) 0
(rot g2d (- 0 branch-angle)
(draw_tree g2d (* length 0.75) (- depth 1)))
(rot g2d branch-angle
(draw_tree g2d (* length 0.75) (- depth 1)))
)
)))
(defn render [g2d]
(draw_tree g2d 50 12))
(run render)
Posted
7:58 PM
0
comments
Links to this post
Using Processing's GifAnimator Library In Clojure
I wanted to capture a Gif animation of my Clojure code's output. It wasn't at all tricky as it turns out that the GifAnimator library is built on top of a non-Processing API. To begin with I'd tied the animation to the currentTimeMillis which didn't make any sense once I was outputting a Gif animation. Because GifEncoder wanted a BufferedImage I took the easiest route - which was to write first to a BufferedImage, and then draw that onto the Graphics of the BufferedStrategy. That's probably horribly inefficient. Anyway, here's the animation and the relevant code snippets:(def encoder (new gifAnimation.GifEncoder))
(. encoder start "export.gif")
(. encoder setRepeat 0)
;...
(let [frame (new java.awt.image.BufferedImage width height
java.awt.image.BufferedImage/TYPE_INT_ARGB)]
:...
(. encoder addFrame frame)
;...
(. encoder finish)
Posted
2:49 PM
0
comments
Links to this post
Double Buffering Render Loop In Clojure
So I took my Java/Groovy code for double buffered rendering and switched out my boiler plate "API" code from my previous Clojure program. I'm seeing six frames per second (40 FPS if I reduce the detail of the tree from 10 to 7):; -- API START --
(def canvas (new java.awt.Canvas))
(. canvas setSize width height)
(def frame (new javax.swing.JFrame
(.. java.awt.GraphicsEnvironment (getLocalGraphicsEnvironment)
(getDefaultScreenDevice) (getDefaultConfiguration))))
(. frame setDefaultCloseOperation javax.swing.WindowConstants/EXIT_ON_CLOSE)
(. (. frame getContentPane) add canvas)
(. frame pack)
(. frame show)
(. canvas createBufferStrategy 2)
(def bufferStrategy (. canvas getBufferStrategy))
(loop [] (let [g2d (. bufferStrategy getDrawGraphics)]
(. g2d setColor java.awt.Color/BLACK)
(. g2d fillRect 0 0 width height)
(. g2d setColor java.awt.Color/WHITE)
(. g2d translate (/ width 2) (/ height 2))
(. g2d rotate (radians -90))
(draw g2d)
(. bufferStrategy show)
(. g2d dispose)
(. System/out println (. (System/currentTimeMillis) toString))
(recur)
))
; -- API END --
Posted
1:37 PM
0
comments
Links to this post
Double Buffering Render Loop In Java
I'm interested in efficient rendering in Java. Here's what I have to accomplish double buffering - it's Java-like code in the form of a Groovy script. If you haven't experimented with Groovy scripts before, then try this: paste this code into a file (e.g. SomeScript.groovy), then simply run it from the command-line: groovy SomeScript.groovy.Some points of note:import java.awt.image.BufferStrategy;
import javax.swing.*;
import java.awt.*;
int width = 640;
int height = 480;
GraphicsEnvironment graphEnv =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphDevice = graphEnv.getDefaultScreenDevice();
GraphicsConfiguration graphicConf =
graphDevice.getDefaultConfiguration();
JFrame jFrame = new JFrame(graphicConf);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setTitle("double buffer demo");
jFrame.setResizable(false);
jFrame.setFocusTraversalKeysEnabled(false);
Canvas canvas = new Canvas();
canvas.setSize(width, height);
canvas.setIgnoreRepaint(true);
jFrame.getContentPane().add(canvas);
jFrame.pack();
jFrame.show();
canvas.createBufferStrategy(2); // must be after we are visible!
BufferStrategy bufferStrategy = canvas.getBufferStrategy();
while(true) {
Graphics g = bufferStrategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0,0,width,height);
g.setColor(Color.RED);
g.drawLine((int)(Math.random()*width),(int)(Math.random()*height),
(int)(Math.random()*width),(int)(Math.random()*height));
bufferStrategy.show();
g.dispose();
}
- You can either go full screen (and start messing around with preferred screen resolutions) or you can, well, not bother. I've opted for the fixed size JFrame option.
- Using a proper double buffering strategy means no more annoying
paintvoodoo. Now we can have a simple render loop. - To get a paint region of the right size we're using a Canvas inside a JFrame as opposed to just a JFrame - who's window decoration would then eat into the size of our paint region.
- You can't call
createBufferStrategyuntil after the component is visible or else you'll get a nastyjava.lang.IllegalStateException: Component must have a valid peerfailure.
Posted
12:39 PM
0
comments
Links to this post
Jan 23, 2009
Simple Clojure Graphics API
So. I have this hammer, Clojure, and this nail, having fun coding line drawing fractals. A perfect fit! But how to separate my "API" code from my "client" code? I've divided the code into three distinct parts; 1) the API code that does not depend on my client code 2) the client code which depends on that code 3) the API code that depends on the client code and bootstraps the program.
But what's Clojure style for organising my code? (I'm asking!)
Edit: I'd also like to know what the best way of eliminating all the g2d parameter passing. A binding somehow?
The API code I have provides declarative operations in the form of Clojure macros. The API is wrapping up Graphics2D and it's AffineTransform:Yes, this was me drawing trees again...; -- API START --
(defn radians [degrees] (. java.lang.Math toRadians degrees))
(defmacro rot [g2d angle & body]
`(do (. ~g2d rotate (radians ~angle))
(do ~@body)
(. ~g2d rotate (radians (- 0 ~angle)))))
(defmacro trans [g2d dx dy & body]
`(do (. ~g2d translate ~dx ~dy)
(do ~@body)
(. ~g2d translate (- 0 ~dx) (- 0 ~dy))))
; -- API END --
; -- CLIENT CODE START --
(def width 400)
(def height 400)
(defn draw_tree [g2d length depth]
(if (> depth 0)
(do
(. g2d drawLine 0 0 length 0)
(trans g2d (int length) 0
(rot g2d -30
(draw_tree g2d (* length 0.75) (- depth 1)))
(rot g2d 30
(draw_tree g2d (* length 0.75) (- depth 1)))
)
)))
(defn draw [g2d]
(draw_tree g2d 50 10))
; -- CLIENT CODE END --
; -- API START --
(def frame (new javax.swing.JFrame))
(def display (proxy [javax.swing.JPanel] []
(paintComponent [g2d]
(. g2d translate (/ width 2) (/ height 2))
(. g2d rotate (radians -90))
(draw g2d)
)
))
(.setDefaultCloseOperation frame
javax.swing.WindowConstants/EXIT_ON_CLOSE)
(.setContentPane frame display)
(.pack frame)
(.setSize frame width height)
(.show frame)
; -- API END --
Posted
9:30 PM
0
comments
Links to this post
Jan 21, 2009
Processing And Line Drawing
Processing feels very slow to me. I put together some code to morph between fractals (1900 lines per frame) and it crawled along at 800x600. I had to bring the window size down to the 300x200 range to get it to feel like it rendered reasonably fast.
So far I'm underwhelmed. I was expecting something very special from all the positive noises I'd been hearing about it. The performance seems poor. My code was very Java like. MovieMaker is based on QuickTime and so wasn't support for me in Linux. Thankfully GifAnimator did work. I disappeared down a rabbit hole trying to convert the animated gif you see here into a video using imagemagick (convert complained "sh: mpeg2encode: not found" and I didn't want to build mpeg2vidcodec_v12.tar.gz).
In Processing's defence I gave it very little time before I gave up hope of finding what I needed in the documentation, and only read the main tutorials. Perhaps someone reading this can point me at all the animation constructs that'll help with the coordinate morphing. I needed 50 lines of code for my draw method, and 150 for my setup and fractal model classes - which felt excessive for the problem I was solving. I was expecting some helpful new concepts as opposed to little more than thinly veiled Java.
Edit: Not only does Processing appear to not offer anything special for the problem I was solving, it also forced me to code in Java 1.4. Yuck.
Posted
8:38 PM
0
comments
Links to this post
Labels: processing
Jan 18, 2009
Corey Ladas @ XPDX: Lean Thinking for Agile Process Evolution
Corey Ladas of Modus Cooperandi kindly came to speak at the January XPDX meeting. We recorded the presentation and it's available for viewing on Google Video.
Here's part one (without the introduction):
Here's part two (missing a minute to switch tapes, and the ten minutes from the end that we didn't have tape for):
Thanks to Corey Ladas for his time, CubeSpace for the facilities, Agile Alliance for sponsorship, YesMail for the pizza, Calagator for the event listing, Howard for recording the video, and to everyone for participating.
I've not finished reading Corey's book "Scrumban: Essays on Kanban Systems for Lean Software Development". When I do, I'll follow up with a review.
Posted
2:11 PM
1 comments
Links to this post
Evolution Of The Build System
If you're experience has been anything like mine, then you've seen an evolution in build systems that went something like this:
- IDE managed - your IDE project file is your build system. It's a brittle solution at best that encourages you to never change your build.
- Ant - stable, repeatable build tool. It's not a build system though, so you end up coding big swaths of XML.
- Maven2 - A build system! They've solved the dependency management problem, they've standardised the project layout! Pretty soon you find out that the documentation isn't going to get any better and that any kind of customisation is painful.
- Gant + Ivy - You're back to the Ant paradigm. You've kept the great dependency management and can make use of Ant's declarative tasks when you want them. Gant does put in some sensible defaults ... but it doesn't try to be a build system. Despite Groovy's expressiveness and Gant's compact Ant task representation you still end up creating long build configuration files.
What's the next step in the evolution of build systems? I think it might be Gradle. Here's my experience:
It's quick to get started. It feels a lot like Ruby's buildr (in a good way). Very quickly I've scanned the documentation and set up my build. This is what I end up with:
group = 'curious-attempt-bunny'
version = '1.0'
usePlugin('war')
dependencies {
addMavenRepo()
excludeRules.add(org: 'javax.transaction', module: 'jta')
compile "org.apache.tapestry:tapestry-core:5.0.18"
compile "org.apache.tapestry:tapestry-spring:5.0.18"
compile "org.apache.tapestry:tapestry-hibernate:5.0.18"
compile "org.springframework:spring:2.5.6"
compile "org.hibernate:hibernate:3.2.6.ga"
compile "org.apache.geronimo.specs:geronimo-jta_1.0.1B_spec:1.1.1"
testCompile "junit:junit:4.4"
testCompile "org.mockito:mockito-all:1.6"
testCompile "org.apache.tapestry:tapestry-test:5.0.18"
testCompile "org.springframework:spring-test:2.5.6"
}
sourceCompatibility = 1.5
targetCompatibility = 1.5
test {
include '**/*Test.class'
exclude '**/Abstract*'
}
The first thing I noticed is that gradle doesn't swamp me with information. I use the target that makes sense to me and it does the right thing:$ gradle clean test
:clean
:init
:resources
:compile
:testResources
:testCompile
:test
BUILD SUCCESSFUL
Total time: 2.35 secs
Of course, this isn't the output I got the very first time. The first thing that bit me was the classic "jta jar isn't in the maven repository"::: problems summary ::
:::: WARNINGS
[NOT FOUND ] javax.transaction#jta;1.0.1B!jta.jar (0ms)
==== MavenRepo: tried
http://repo1.maven.org/maven2/javax/transaction/jta/1.0.1B/jta-1.0.1B.jar
::::::::::::::::::::::::::::::::::::::::::::::
:: FAILED DOWNLOADS ::
:: ^ see resolution messages for details ^ ::
::::::::::::::::::::::::::::::::::::::::::::::
:: javax.transaction#jta;1.0.1B!jta.jar
::::::::::::::::::::::::::::::::::::::::::::::
That's when I added my exclusion config (Gradle can do global and local exclusions, I opted for a global exclusion), and replaced Sun's jta with Apache's Geronimo implementation:
excludeRules.add(org: 'javax.transaction', module: 'jta')
compile "org.apache.geronimo.specs:geronimo-jta_1.0.1B_spec:1.1.1"
I was curious to see whether Gradle was using my ~/.m2/repository. It wasn't. I found all the dependencies (and implicit dependencies) in ~/.gradle/cache:
$ find ~/.gradle -name '*.jar'
/home/malbery/.gradle/cache/antlr/antlr/jars/antlr-2.7.6.jar
/home/malbery/.gradle/cache/asm/asm/jars/asm-1.5.3.jar
/home/malbery/.gradle/cache/asm/asm-attrs/jars/asm-attrs-1.5.3.jar
/home/malbery/.gradle/cache/c3p0/c3p0/jars/c3p0-0.9.1.jar
/home/malbery/.gradle/cache/cglib/cglib/jars/cglib-2.1_3.jar
/home/malbery/.gradle/cache/commons-codec/commons-codec/jars/commons-codec-1.3.jar
/home/malbery/.gradle/cache/commons-collections/commons-collections/jars/commons-collections-3.1.jar
/home/malbery/.gradle/cache/commons-logging/commons-logging/jars/commons-logging-1.1.1.jar
/home/malbery/.gradle/cache/dom4j/dom4j/jars/dom4j-1.6.1.jar
/home/malbery/.gradle/cache/geronimo-spec/geronimo-spec-jta/jars/geronimo-spec-jta-1.0-M1.jar
/home/malbery/.gradle/cache/javassist/javassist/jars/javassist-3.8.0.GA.jar
/home/malbery/.gradle/cache/junit/junit/jars/junit-4.4.jar
/home/malbery/.gradle/cache/log4j/log4j/jars/log4j-1.2.14.jar
/home/malbery/.gradle/cache/net.sf.ehcache/ehcache/jars/ehcache-1.2.3.jar
/home/malbery/.gradle/cache/org.apache.geronimo.specs/geronimo-jta_1.0.1B_spec/jars/geronimo-jta_1.0.1B_spec-1.1.1.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry-core/jars/tapestry-core-5.0.18.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry-hibernate/jars/tapestry-hibernate-5.0.18.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry-ioc/jars/tapestry-ioc-5.0.18.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry-spring/jars/tapestry-spring-5.0.18.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry-test/jars/tapestry-test-5.0.18.jar
/home/malbery/.gradle/cache/org.apache.tapestry/tapestry5-annotations/jars/tapestry5-annotations-5.0.18.jar
/home/malbery/.gradle/cache/org.hibernate/ejb3-persistence/jars/ejb3-persistence-1.0.2.GA.jar
/home/malbery/.gradle/cache/org.hibernate/hibernate/jars/hibernate-3.2.6.ga.jar
/home/malbery/.gradle/cache/org.hibernate/hibernate-annotations/jars/hibernate-annotations-3.4.0.GA.jar
/home/malbery/.gradle/cache/org.hibernate/hibernate-c3p0/jars/hibernate-c3p0-3.3.1.GA.jar
/home/malbery/.gradle/cache/org.hibernate/hibernate-commons-annotations/jars/hibernate-commons-annotations-3.1.0.GA.jar
/home/malbery/.gradle/cache/org.hibernate/hibernate-core/jars/hibernate-core-3.3.1.GA.jar
/home/malbery/.gradle/cache/org.mockito/mockito-all/jars/mockito-all-1.6.jar
/home/malbery/.gradle/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.5.2.jar
/home/malbery/.gradle/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.5.2.jar
/home/malbery/.gradle/cache/org.springframework/spring/jars/spring-2.5.6.jar
/home/malbery/.gradle/cache/org.springframework/spring-test/jars/spring-test-2.5.6.jar
/home/malbery/.gradle/cache/org.testng/testng/jars/testng-5.7-jdk15.jar
/home/malbery/.gradle/cache/xml-apis/xml-apis/jars/xml-apis-1.0.b2.jar
Pretty sweet. Well done Gradle.
Posted
1:00 PM
3
comments
Links to this post
Maven2 Community Discovers Irony And/Or Self Depreciating Black Humour
I've been trying to see if there's a way to have the mvn site target fail if there are broken links. Along the way I found this maven2 plugin. Seriously, it's not a joke. Time how long it takes you to work out what the Maven Documentation Checker Plugin does. When you find the punchline (and not the information you were looking for) please feel free to laugh out loud:
"The plugin documentation standard was created to address the frequent complaint of lack of documentation, specifically for the Maven plugins. The standard was based on suggestions made on the Maven dev mailing list with some refinements. It is a community consensus of what basic documentation a Maven plugin should have."
I think the dog died eating it's own dog food. Seriously, maven folks. I love your dependency management, but I run screaming from your documentation.
Posted
11:36 AM
0
comments
Links to this post
WebApp Monitoring Using GlassBox
I'm interested in non-invasive ways of monitoring a web application for SLA-type constraints. GlassBox appears to be what I'm looking for, or at least a part of the picture. 13:30 through 21:00 of the Google Tech Talk give a demonstration of it up and running.
It works by runtime-instrumenting specific elements of your web app using AspectJ. It looks out for common problems and if it finds them can capture a dump of the thread for later analysis. What's interesting to me is the "more than 1 second response time more than 5% of the time" constraint.
It aggregates data and once this SLA-like constraint is violated, distills the data for review. Problems can then be viewed in the GlassBox webapp, via JMX, or via a remote GlassBox webapp interfacing over JMX. From what I gather GlassBox doesn't persist it's data, instead focuses on maintaining the last N aggregates of problems.
Arguably, part of it's strength is that it targets specific object instances in your web app, and doesn't scatter-shot it's focus. That works because they've set up specific pointcuts that will work with particular frameworks. The downside there is that if you're using a less popular framework then the community may not yet have done the work for you. On the upside, specifying a new pointcut really doesn't look to be that hard.
One point I neglected to highlight here is that deployment is non-invasive. You're not going to have to touch your deployment WAR, it's much more a case of deploying the GlassBox WAR into the same container, and using their generated script to launch the container with.
Does anyone have a story to tell about having used GlassBox already? I'll follow up if I use it myself. (Thanks to Douglas Bullard for pointing me at GlassBox)
Posted
10:45 AM
0
comments
Links to this post
Jan 17, 2009
Genetic Search With Fitness

I had another burst of hacking enthusiasm. Here's some far from pretty code. What it does is takes your mouse position as the desired "center of gravity" of your tree. Behind the scenes it maintains a population of trees, and keeps culling the tree who's center of gravity is furthest away from the target you've specified. As with the previous program, it then proceeds to clone an existing tree and mutate it. The program's mouse cursor is the target, and the smaller cross is the center of gravity of the best tree in the current population.
What you end up with is a tree that wriggles around trying to reach towards your mouse cursor. Give it a try, it's fun. I'll assert that it's also a good example of when you would want to use genetic search. Good luck trying to solving the problem mathematically, or coding an algorithm to train the weightings!Read the previous post for info on how to get this code running for you.require "rubygems"
require "gosu"
class CenterOfGravityCalculator
def initialize
@sumX = 0
@sumY = 0
@count = 0
end
def draw_line(x1,y1,c1, x2,y2,c2)
@sumX += x2
@sumY += y2
@count += 1
end
def result
[@sumX/@count, @sumY/@count]
end
end
class Tree
attr_reader :genes
def initialize(window, genes)
@window = window
@genes = genes
@branch_left_angle = decode(genes[0], 0, 90)
@branch_right_angle = decode(genes[1], 0, 90)
@initial_branch_length = 50
@branch_depth = decode(genes[2], 8, 12)
@branch_left_shorten_factor = decode(genes[3], 0.2, 0.9)
@branch_right_shorten_factor = decode(genes[4], 0.2, 0.9)
@branch_left_angle_factor = decode(genes[5], 0.5, 1.5)
@branch_right_angle_factor = decode(genes[6], 0.5, 1.5)
end
def decode(gene, min, max)
(max-min)*gene + min
end
def draw(x, y, c)
draw_to(@window, x, y, c)
red = 0xffff0000
@window.draw_line(x+center_of_gravity[0]-5, y+center_of_gravity[1]-5, red, x+center_of_gravity[0]+5, y+center_of_gravity[1]+5, red)
@window.draw_line(x+center_of_gravity[0]-5, y+center_of_gravity[1]+5, red, x+center_of_gravity[0]+5, y+center_of_gravity[1]-5, red)
#puts "<" + center_of_gravity().join(",") + ">"
end
def center_of_gravity
return @cog if @cog != nil
calculator = CenterOfGravityCalculator.new
draw_to(calculator, 0, 0, 0)
@cog = calculator.result
return @cog
end
def draw_to(window, x, y, c)
draw_branch(window, x,y,0,@initial_branch_length,@branch_left_angle,@branch_right_angle,1,c)
end
def draw_branch(window, x,y,a,l,al,ar,depth,colour)
return if depth > @branch_depth
return if l < 3
nx = x + Gosu::offset_x(a, l)
ny = y + Gosu::offset_y(a, l)
window.draw_line(x,y,colour, nx, ny,colour)
draw_branch(window, nx,ny,a-al,l*@branch_left_shorten_factor,al*@branch_left_angle_factor,ar,depth+1,colour)
draw_branch(window, nx,ny,a+ar,l*@branch_right_shorten_factor,al,ar*@branch_left_angle_factor,depth+1,colour)
end
end
class TreeWindow < Gosu::Window
def initialize
@width = 1024
@height = 768
super(@width,@height,false)
@trees = []
10.times {
@trees << Tree.new(self, [Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand])
}
@cursor = Gosu::Image.new(self, "block.png", false)
@treeX = 512
@treeY = 600
end
def draw
red = 0xffff0000
if @clickX != nil
# draw_line(@clickX-10, @clickY-10, red, @clickX+10, @clickY+10, red)
# draw_line(@clickX-10, @clickY+10, red, @clickX+10, @clickY-10, red)
@trees.min { |a,b| fitness(a) <=> fitness(b) }.draw(@treeX,@treeY,0xffffffff)
end
draw_line(mouse_x-10, mouse_y, red, mouse_x+10, mouse_y, red)
draw_line(mouse_x, mouse_y+10, red, mouse_x, mouse_y-10, red)
end
def update
@clickX = mouse_x
@clickY = mouse_y
@targetX = @clickX - @treeX
@targetY = @clickY - @treeY
return if @clickX == nil
treeToCull = @trees.max { |a,b| fitness(a) <=> fitness(b) }
@trees.delete treeToCull
parentTree = @trees[Kernel.rand(@trees.size)]
parentGenes = parentTree.genes
genes = []
parentGenes.each { |gene|
if Kernel.rand > 0.8
genes << Kernel.rand
else
genes << gene
end
}
genes[Kernel.rand(genes.size)] = Kernel.rand
@trees << Tree.new(self, genes)
end
def fitness(tree)
Gosu::distance(tree.center_of_gravity[0], tree.center_of_gravity[1], @targetX, @targetY)
end
def drawzz
i = 0
@trees.each { |tree|
x = @width*(i+0.5)/(@trees.size+1)
y = @height * 0.75
if (Gosu::distance(x,y,mouse_x,mouse_y) < 100)
colour = 0xffff0000
else
colour = 0xffffffff
end
tree.draw(x,y,colour)
i += 1
}
@cursor.draw(mouse_x,mouse_y,0)
end
def button_down(id)
close if id == Gosu::Button::KbEscape
end
def button_upxx(id)
if id == Gosu::Button::MsLeft #&& @clickX == nil
@clickX = mouse_x
@clickY = mouse_y
@targetX = @clickX - @modelX
@targetY = @clickY - @modelY
end
end
def xx
if true
i = 0
@trees.reject! { |tree|
x = @width*(i+0.5)/(@trees.size+1)
y = @height * 0.75
i = i + 1
Gosu::distance(x,y,mouse_x,mouse_y) <= 100
}
parentTree = @trees[Kernel.rand(@trees.size)]
puts parentTree
parentGenes = parentTree.genes
genes = []
parentGenes.each { |gene|
if Kernel.rand > 0.8
genes << Kernel.rand
else
genes << gene
end
}
genes[Kernel.rand(genes.size)] = Kernel.rand
@trees << Tree.new(self, genes)
end
end
end
TreeWindow.new.show
Posted
5:56 PM
0
comments
Links to this post
Fun Quote
"all books should be laminated: I take showers"
- anonymous
Posted
4:47 PM
0
comments
Links to this post
Labels: fun
Die IE Die
Seeing as how I'm a bigoted Linux user, I'm pleased to see that IE users are in the minority when viewing this site:
Posted
3:14 PM
0
comments
Links to this post
Simple Genetic Search
I got talking with a friend about genetic searches (aka genetic algorithms) and became inspired to hack out some code to illustrate a simple approach. I picked an old favourite: fractal trees. The idea is that the drawing of a tree (or fern-like plant) can be encoded as a few simple parameters, where each parameter falls somewhere in a range of values. For example the angle of a branch relative to the angle of the branch it's stemming from can be expressed as being between 0 and 90 degrees. Mutating that parameter would then change just that aspect of the tree's appearance.
Here's a screenshot of the code in action:
I've cheated here to make the genetic search very easy to program. Typically you'd express what you're looking for as some sort of fitness measurement. The population would then be automatically culled of the least fit, and then repopulated based on mutations of the most fit. Then you'd leave the program running for ages and see what it came out with.
Well, here I've totally side-stepped that problem by getting the user to do the opposite. Instead, the user clicks on the one tree to remove from the population. The program then treats all remaining trees as equally fit, and picks one at random to spawn a replacement member of the population.
We're using the simplest mutation operator here (arguably also the most effective in the absence of some specific knowledge as to how to treat the encoded parameters). All we're doing is mutating at least one "gene" and then giving each gene a 20% chance of mutation.
The code is using Gosu (my new favourite hobby API) and Ruby. Gosu doesn't seem to have a way to draw lines to an image so it's horribly slow as each "frame" is completely redrawn from scratch. We're clipping the drawing of each tree as some of them explode all over and dominate the screen otherwise.
Here's the code:require "rubygems"
require "gosu"
class Tree
attr_reader :genes
def initialize(window, genes)
@window = window
@genes = genes
@branch_left_angle = decode(genes[0], 0, 90)
@branch_right_angle = decode(genes[1], 0, 90)
@initial_branch_length = 50
@branch_depth = decode(genes[2], 8, 12)
@branch_left_shorten_factor = decode(genes[3], 0.2, 0.9)
@branch_right_shorten_factor = decode(genes[4], 0.2, 0.9)
@branch_left_angle_factor = decode(genes[5], 0.5, 1.5)
@branch_right_angle_factor = decode(genes[6], 0.5, 1.5)
end
def decode(gene, min, max)
(max-min)*gene + min
end
def draw(x, y, c)
@window.clip_to(x.to_i-150, y.to_i-400,300,550) do
draw_branch(x,y,0,@initial_branch_length,@branch_left_angle,@branch_right_angle,1,c)
end
end
def draw_branch(x,y,a,l,al,ar,depth,colour)
if (depth > @branch_depth)
return
end
nx = x + Gosu::offset_x(a, l)
ny = y + Gosu::offset_y(a, l)
@window.draw_line(x,y,colour, nx, ny,colour)
draw_branch(nx,ny,a-al,l*@branch_left_shorten_factor,al*@branch_left_angle_factor,ar,depth+1,colour)
draw_branch(nx,ny,a+ar,l*@branch_right_shorten_factor,al,ar*@branch_left_angle_factor,depth+1,colour)
end
end
class TreeWindow < Gosu::Window
def initialize
@width = 1024
@height = 768
super(@width,@height,false)
@trees = []
4.times {
@trees << Tree.new(self, [Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand, Kernel.rand])
}
@cursor = Gosu::Image.new(self, "block.png", false)
end
def draw
i = 0
@trees.each { |tree|
x = @width*(i+0.5)/(@trees.size+1)
y = @height * 0.75
if (Gosu::distance(x,y,mouse_x,mouse_y) < 100)
colour = 0xffff0000
else
colour = 0xffffffff
end
tree.draw(x,y,colour)
i += 1
}
@cursor.draw(mouse_x,mouse_y,0)
end
def button_down(id)
close if id == Gosu::Button::KbEscape
if id == Gosu::Button::MsLeft
i = 0
@trees.reject! { |tree|
x = @width*(i+0.5)/(@trees.size+1)
y = @height * 0.75
i = i + 1
Gosu::distance(x,y,mouse_x,mouse_y) <= 100
}
parentTree = @trees[Kernel.rand(@trees.size)]
puts parentTree
parentGenes = parentTree.genes
genes = []
parentGenes.each { |gene|
if Kernel.rand > 0.8
genes << Kernel.rand
else
genes << gene
end
}
genes[Kernel.rand(genes.size)] = Kernel.rand
@trees << Tree.new(self, genes)
end
end
end
TreeWindow.new.show
Yes, I know. The code is cropped by the CSS of my blog. Select all the code and copy and paste it for better visibility. You'll also find that it's very unhappy without the block.png. So here it is:
Installing Gosu is also very reliable. I've seen it work first time on Mac OS X as well as Ubuntu 8.10. The docs say exactly what to do: [How to set up Gosu on ...]
Posted
2:38 PM
0
comments
Links to this post
Jan 13, 2009
JUnit 4: Open For Extension?
What if the isAssignableFrom test for @Test(expected=MyException.class) isn't enough? Hahaha! Tough luck!
You got screwed here:And here:else if (fTestMethod.isUnexpected(actual)) {
String message= "Unexpected exception, expected<" +
fTestMethod.getExpectedException().getName() +
"> but was<" +
actual.getClass().getName() + ">";
addFailure(new Exception(message, actual));
}boolean isUnexpected(Throwable exception) {
return ! getExpectedException().isAssignableFrom(exception.getClass());
}
Posted
6:02 PM
0
comments
Links to this post