Skip to content

Blogs

Tips for the Daily Scrum

Agile Game Development - Thu, 08/13/2015 - 16:54
When visiting a studio, I'll often ask to observe the daily stand-ups meetings.  It's the best way to sense where a team is and how to focus coaching time for the visit.  An effective daily stand-up meeting is an energetic swarm of committed team members that need to quickly synchronize their efforts and identify anything that is threatening their shared goal.

Some daily stand-ups (or daily scrums), aren't like this.  They are monotonous, manager-led and pointless to the attendees.  Teams here lack any sense of commitment or ownership and they eventually question the merit of the practice and perhaps even abandon it.  Unfortunately, abandoning the stand-up doesn't improve their chances of building commitment or ownership, which are principles of Scrum.  So I've compiled a list of pointers on ways to improve the daily stand-up meeting and improve commitment and ownership.


The stand-up is for the team.  This meeting is for the team to synchronize their efforts towards achieving their goal, not just as a status report.  If their are any "management artifacts" that need to be created (such as an update to a tracking tool), it should be done outside the meeting.

Anti-Tip: A sprint's commitment is on quality, not quantity.  If a studio wants a predictable amount of garbage, then taking over and running sprint meetings for the team is the best way to do it.

The rules aren't fixed.  The "answer the three questions" rule you read about in books is only the starting place.  Teams add questions, change the format, and do anything necessary to address their needs.

Anti-Tip: Make Scrum a process by fixing all the practices.  This will ensure that continuous improvements will cease.

It's about the commitment to the goal, not just the tasks.  Task tracking is critical, but work will arise on a daily basis that wasn't considered during sprint planning.  Teams need to react to this new work and balance it daily against their goal.  They need to inform each other about new developments and threats to their goal.  They should care about accomplishing the tasks, but they should care about the goal more.

Anti-Tip: Force teams to use task tracking software.  They'll measure success by completing tasks, not adding value.

The ScrumMaster must know their role.  ScrumMasters do not run the daily stand-up.  They facilitate them.  The difference is that the rules for the stand-up come from the team and the ScrumMaster ensures that those rules are followed.  If the team has decided that being late to the stand-up is not acceptable, they'll likely have a rule in place to chastise those who are late.  Paying $1 or singing a song is common.  ScrumMasters must not let them off the hook. 

Anti-Tip: When you solve all of today's problems for the team, they'll expect you to solve tomorrow's problems as well.

Respect the time-box and start time.  Some of the most effective stand-ups I've seen were five-minute "hurricanes of energy and activity".  The team shared what they needed to share and went back to work.  Stand-ups that take longer than 15 minutes are just wrong and need to be fixed.  Also, don't waste time by having a variable start time (based on who shows up late).

Anti-Tip: Ignore the time-box and see who shows up on time (& vice-versa)

Prefer a physical task board.  This is not always possible to have, but here is no electronic replacement that is equivalent.  A physical task board with cards allows everyone on the team to handle the tasks simultaneously at the same location.  It encourages team ownership.

... & Avoid projectors.  This might seem a bit redundant given the last point, but projectors used in a stand-up meeting should be renamed "conversation killers".  Projectors usually involve a software tool that provides information in a linear, one-at-a-time way with someone, unfortunately often the ScrumMaster, at the keyboard/mouse controls.  If defeats the non-linear, swarming-on-the-goal, value of the stand-up.  Software tools are often a critical part of project planning and tracking, but they have no place in a co-located daily stand-up.

Anti-Tip: Usually tools allow only one operator (mouse/keyboard).  When there is one operator in the meeting, guess what happens to the engagement levels for everyone else?

It's owned by the team.  The team picks the format and time of day of the daily stand-up.  Since they own it, it should run the same way, regardless of whether the ScrumMaster is there or not.  One simple test I recommend to ScrumMasters is to occasionally hide around the time of the daily scrum and see what happens.  If the team meets and runs the stand-up the same way as when you are their, you are doing a good job.  If they don't meet or stand around wondering what to do, they have some things to fix.

Anti-Tip: Standing in a circle answering three questions is a secret formula which leads to the magical process fairy flying overhead, sprinkling productivity dust on people.  These practices have nothing to do with team engagement through ownership.

(this post was edited from an earlier post)
Categories: Blogs

2015 State of Scrum Report published

Ben Linders - Thu, 08/13/2015 - 13:24
The Scrum Alliance has published the 2015 State of Scrum Report. You can download the full report from the Scrum Alliance website. Continue reading →
Categories: Blogs

Teams and their proximity to the final user

Learn more about our Scrum and Agile training sessions on WorldMindware.com

A great, simple post from Mike Bowler…

Time: Teams that are writing code today that will be used by their customers tomorrow are very focused on what the customers actually need. Teams that are writing code today that won’t be seen by a customer for six months are less engaged.

https://www.linkedin.com/pulse/why-teams-care-mike-bowler

Mike Caspar
Passionate About Agile

Try out our Virtual Scrum Coach with the Scrum Team Assessment tool - just $500 for a team to get targeted advice and great how-to informationPlease share!
facebooktwittergoogle_plusredditpinterestlinkedinmail

The post Teams and their proximity to the final user appeared first on Agile Advice.

Categories: Blogs

Recent Podcast Episodes (August 2015)

Derick Bailey - new ThoughtStream - Wed, 08/12/2015 - 17:08

If you haven’t heard, I’ve been making the rounds of podcasts lately. It’s been fun to talk with people about everything from parenting and work-life balance, to JavaScript and messaging patterns, to RabbitMQ and beyond. 

Podcasting

Check out these recent episodes of podcasts on which I’ve made an appearance, and be sure to check out the more complete list of episodes on my Podcasts page.

Recent Podcast Appearances

DotNetRocks! Episode 1177: JavaScript Messaging

NewImage

Messaging in browsers? Carl and Richard talk to Derick Bailey about messaging patterns in Javascript. Yes, browsers always use messages, that’s what HTTP is about – but there are messages, and there are messages. Derick talks about using the publish/subscribe pattern with RabbitMQ to build a highly scalable system. These are patterns that are popular outside of the web, but the modern web can do anything any other system can do – so it’s time to put these messaging patterns to work in your web applications!

 

JavaScript Jabber, Episode 170: RabbitMQ

NewImage

A look at all things RabbitMQ. This particular discussion has a ton of great questions, concerns and first-hand experience from the panel. There’s a lot of hard questions to be answered, here. If you’re looking for the dirt on the downsides and difficulties that you may experience with messaging architectures, check this episode out!

 

My Life For The Code, Episode 1: RabbitMQ, C64 And More

NewImage

In this episode I talk with the host, Shawn Rakowski, about my “Hello World” moment on the Commodore 64, my philosophy on software development, low moments in my career, the release of my RabbitMQ e-book/screencast/interview bundle, my 47 days of fasting, and more. It’s a great overview of what’s been going on with me and where my career came from!

 

The Five-Minute Geek Show, Episode 46: Decoupling all the Things with Queues

NewImage

If you’ve ever wanted a 5 minute overview of what messaging is, how it’s beneficial and why I like RabbitMQ – this is the episode for you. Be prepared to hear all about the golden hammer of message queues! :)

 

Parent Programming, Episode 3

NewImage

This is not a typical podcast episode that I’ve done, and I really like how this one came out. I join Kevin Lamping and talk about parenting and programming – the work life balance, and how I’ve been incredibly lucky and privileged in my career. If you’ve ever wondered what it’s like to be an entrepreneur and a parent, check this episode out!

And There’s More To Come

I’m still doing more podcasts on a fairly regular basis at this point, and I would love to be on your show! Just reach out and let me know about your show. It’ll be fun!

Categories: Blogs

Pitfall of Scrum: Product Owner Doesn’t Show

Learn more about our Scrum and Agile training sessions on WorldMindware.com

The Product Owner is a full member of the Scrum Team and should be present at all Scrum meetings (Sprint Planning, Daily Scrums, Sprint Review and Sprint Retrospective). As well, the Product Owner should also be available during work time. Of course, the PO also needs to work with stakeholders and might be away during that time, but these discussions should be scheduled outside of the team’s meeting times.

In one case with a team I was coaching at Capital One, the Product Owner didn’t show up for the Sprint Review and then didn’t show up for the Sprint Planning meeting. The rest of the team decided to delay the start of the Sprint until the Product Owner did show up. The director-level manager of the team, a deeply insightful individual, insisted that all team members take paid days off until the Product Owner was ready to attend the Sprint Review… kind of like a mini-strike. It only took two days for the Product Owner to clear his schedule to attend the Sprint Planning meeting.

Product Owner as Scrum Team Member

The Scrum Guide defines the membership of the Scrum Team as follows:

The Scrum Team consists of a Product Owner, the Development Team, and a Scrum Master. Scrum Teams are self-organizing and cross-functional. Self-organizing teams choose how best to accomplish their work, rather than being directed by others outside the team…. The team model in Scrum is designed to optimize flexibility, creativity, and productivity.

As a member of the Scrum Team, the product owner should have the same level of commitment, courage, focus, openness, and respect (the five Scrum values) as any other Scrum Team member. The Product Owner needs to collaborate actively with the Scrum Team. One way to gauge the involvement of the Product Owner is in the Sprint Review. If the Product Owner is giving feedback to the rest of the Team in the Sprint Review, it’s too late! The Product Owner should never be surprised by the product increment shown in the Sprint Review. Instead, the Product Owner should be leading the discussion to get feedback from customers, users and other stakeholders during the Sprint Review.

Being Away from the Team

There are some interesting exceptions to the Product Owner being present. Here are some of the most common:

  1. The Product Owner needs to be away from the team to work directly with customers, users, sales teams, marketing, customer service people, etc. This work is essential for making sure that the Product Backlog contains the most up-to-date information every Sprint. This time away should normally be scheduled outside of the Scrum meeting times.
  2. The Product Owner may need to travel to meet with customers and be away from the Scrum Team for an extended period of time.
  3. Of course, like any other team member, the Product Owner can and should take vacation and may be ill from time-to-time. This may seem trivially obvious. What is not obvious is that it often helps the team to leave another team member with temporary responsibility to fill in for the Product Owner. This temporary fill-in should not be someone from outside the team.
Special Case: The Daily Scrum Meeting

The latest version of the Scrum Guide also puts the Daily Scrum meeting in a special category. The meeting is for the Development Team (the subset of the Scrum Team that excludes the ScrumMaster and the Product Owner). Former versions of the Scrum Guide and other official Scrum documentation have changed this rule in various ways. As a personal comment, I believe this is a serious internal contradiction in the definition of Scrum. If the Scrum Team is self-organizing, then the Daily Scrum should include the ScrumMaster and Product Owner. I have seen this work successfully. The Scrum Guide says nothing about other people observing the Daily Scrum. I strongly recommend that the ScrumMaster and Product Owner observe the meeting even if you wish to follow Scrum strictly and restrict their participation.

If you decide to allow the Product Owner to participate in the meeting, then the Product Owner should restrict their comments to changes in the Product Backlog that require the team’s help for refinement. For example, the Product Owner could report in the Daily Scrum as follows:

“Yesterday I met with Sanjay at Deal Maker Industries and he suggested that we add a feature to allow car manufacturers to ping various stakeholders about risks and options. I think that will mean adding several new user stories to the Product Backlog. I need help from the team to write and estimate the new PBIs. Today I also have a re-prioritization meeting with three key internal stakeholders. My only obstacle is that I still can’t get a meeting with Karen about the marketing of the features from our last few Sprints and I’m worried that will delay our next release.”

In this example, the team and the ScrumMaster are kept apprised of key developments at the product level and know that there will be some extra work during the day to work on Product Backlog Refinement. The Scrum Guide says:

Product Backlog refinement is the act of adding detail, estimates, and order to items in the Product Backlog. This is an ongoing process in which the Product Owner and the Development Team collaborate on the details of Product Backlog items. During Product Backlog refinement, items are reviewed and revised. The Scrum Team decides how and when refinement is done. Refinement usually consumes no more than 10% of the capacity of the Development Team. However, Product Backlog items can be updated at any time by the Product Owner or at the Product Owner’s discretion.

Balance in the Product Owner Role

Ideally, the Product Owner would spend an equal amount of time with their Scrum Team and with outside customers and users. The Product Owner is the key conduit of information from the market to the Development Team. Not being present with the Scrum Team can hinder this flow of information and cause quality problems and unnecessary rework. Again, the Product Owner should never be surprised in the Sprint Review.

This article is a follow-up article to the 24 Common Scrum Pitfalls written back in 2011.

Try out our Virtual Scrum Coach with the Scrum Team Assessment tool - just $500 for a team to get targeted advice and great how-to informationPlease share!
facebooktwittergoogle_plusredditpinterestlinkedinmail

The post Pitfall of Scrum: Product Owner Doesn’t Show appeared first on Agile Advice.

Categories: Blogs

Agile2015 Interviews with LeadingAgile

Leading Agile - Mike Cottmeyer - Wed, 08/12/2015 - 14:06

Agile2015The Agile Alliance’s annual conference, Agile2015 took place last week in Washington D.C. and LeadingAgile was out in force. In addition to the small army of attendees. LeadingAgile CEO, Mike Cottmeyer led a session on “Three Things You Must Know to Transfer Any Sized Organization Into An Agile Enterprise” and LeadingAgile Coach, Dean Stevens gave a presentation called “Product Owner Team: Leading Agile Program Management”.

In addition to this, LeadingAgile’s Certified Scrum Trainer, Dave Prior (click for a list of upcoming classes) spent the week volunteering and producing Agile2015 Video Podcast Interviews for the Agile Alliance. He interviewed 45 Agile thought leaders and presenters in 4 1/2 days including a few LeadingAgile folks:

Mike Cottmeyer on Three Things You Must Know to Transfer Any Sized Organization into an Agile Enterprise

LeadingAgile Chief Cultural Officer, Rachel Howard (with Matt Barcomb) on the skills required to work as an Agile consultant

Dean Stevens on Product Owner Teams: Leading Agile Program Management

Chris Spagnuolo (with David Bland) on developing Innovation Centers within traditional organizations.

And you can find these and all the remaining interviews on the Agile Alliance Vimeo site

The post Agile2015 Interviews with LeadingAgile appeared first on LeadingAgile.

Categories: Blogs

10 tips for doing Scrum when you're decentralized, part-time or doing other stuff you're not supposed to do

Scrum Breakfast - Wed, 08/12/2015 - 11:34
How do you use Scrum when you're
  • integrating existing packages, not developing software
  • deploying a visual design
  • working with a decentralized team
  • working with a part-time team?
The new Scrum Breakfast Club portal is based on Wordpress and who knows how many plugins. My part-time "team" and I have been working on this project for about 6 months. Calling us a team might be a bit optimistic, because we were:
  • 5 part-time people, 
  • located in two countries (6 hours apart)
  • working no more than 40% on the project
  • working for five different companies, and
  • only 2 people have been involved for the entire project.
Despite these obstacles, we did produce a cool product (IMHO) and we enjoy working together (also IMHO)!

As we have gone through process of creating Wordpress-based web presences using Scrum, I have learned a couple lessons which I will carry forward.
  1. Produce something that works every week. This is a wonderful constraint which prevents both individuals and the project as a whole from wandering off.
  2. Do functionality first. It's easier to get the design right when you know what the system is supposed to do. You may even iterate on your target audience, which would cause the design to change.  A good design requires understanding the deeper why of your system or product and its customer and users. Especially in a startup context, it may take awhile for that to emerge.
  3. You can iterate on design. An excellent template architecture is a helpful, so most of the changes are in CSS. In our case:
    • Version 1 focused on functionality only. It had just enough design, standard templates really, so that we could work with the system. 
    • Version 2 was slightly embarrassing, but more or less attractive. People could work with it, but there was some stuff that was bad or downright evil in design or usability. 
    • Version 3 - what we are about to publish - is something for the public, and even here, we are implementing it in iterations 3.1, 3.2, 3.3 etc. We designed for the overall goal (market, usability). We started implementing and deploying with the most visible pages. Then polish and minor pages.
    • Don't break stuff that already works. 
    • Postpone activating design elements that require new functionality, until the corresponding functionality is present. For example, we want to do some fancy pop-open login and registration widgets, but those are going to wait until we can implement them.
  4. Trello is your friend for communication. Trello is nice lightweight Kanban board. Our workflow is basically: Backlog->Ready->Forecast->In Progress->Ready to Review->Ready to Deploy->Done. We use tags, columns and email notifications to enable rapid and effective communication throughout the team.
  5. Focus on getting things done. We agreed to always pull from the rightmost column on the board. Spillover, is almost always sign of something bad. The story might be too big; the acceptance criteria might not be clear; the team member might be working on something else. or the team worked on it at the last minute. 
  6. Grow up, then grow out. First we added functionality to Wordpress, often in the form of a new plugin. Then we discovered what we broke. Since many plugins are poorly documented, and the interactions between them even less so, we couldn't always prevent things from breaking. So we fixed everything we could find before moving on to new functionality. Basically a stop the line mentality. In Trello, we added a column "Bugs" and tagged the items in yellow. If a bug was present, it was handled with top priority. This is getting things really done!
  7. Definition of ready is your friend for getting things done. For each column in our Trello workflow, we created a permanent card with the definition of ready to enter that column. To enter the Ready column, each story had to have a how-to-demo checklist. This makes it much easier to know how to implement the story and how to confirm that we have achieved the goal during the Review. To leave Ready and enter the Forecast column, the team (member) had to be confident they could get the story by the end of the sprint.
  8. Do the work at the beginning of the sprint. Did I say get stuff done? Get it done early. The biggest danger with part-time staff is that they work on other things until they feel they can't postpone your stuff any longer. If they do their 20% on the first day of the sprint rather than the last, their chances of getting stuff done are much higher! Getting stuff done also removes a major source of tension between the players in the project.
  9. Wordpress is the friend I love to hate. There is plugin for that. Yes, and the interactions between those plugins can be quite unpredictable. It enabled us to get started quickly, but I suspect before our product is done we will have reimplemented all of the key plugins to suit our needs. I'm not complaining, because my site is live and I can generate potentially revenue to pay for that development.
  10. Automation is your dearest friend. This includes virtualization, backup and source code control. Yesterday, a system programming error during the sprint review managed to destroy all three instances of our system. Embarrassing, but...  We recovered the system by creating a new virtual machine on linode from the daily backup, restored the Wordpress configuration (database) from the daily Updraft Plus backup, and the current file configuration from the source code control. 45 minutes later we were back online and there was no impact on our schedule. If fact, we are actually deploying a week earlier than I had hoped! We will also get better reliability, because this incident convinced us to separate test and production on separate nodes.
As a final thought, for me as Product Owner (and entrepreneur), Sprint Planning is an opportunity to ask:What is the best possible step forward for the business, given what I know today?
The answer might not be software development. I ask myself that question every week, and it really helps me to do the right thing!
Categories: Blogs

Platform generalists versus framework specialists

Jimmy Bogard - Tue, 08/11/2015 - 21:50

A trend I’ve noticed especially in Javascript/front-end jobs are emphasizing, requiring, even titling jobs after specific Javascript frameworks. “Ember.js engineer needed”. “Angular programmer”. This raises a few of thoughts for me:

  • Is the framework so complicated you need someone with extensive experience building non-trivial apps?
  • Is your timeline so short you don’t have time to teach anyone a framework?

Earlier in my career, when WPF was announced, I had a choice to invest time in my career to learning it. It was the obvious successor to WinForms, and *the* way to build thick-client apps in .NET, if not Windows going forward. I hesitated, and decided against it, after learning more about the technology and seeing how proprietary it was.

Instead, I doubled-down on the web, learning server-side frameworks to help build web apps more than ever. ASP.NET MVC embraced the web, unlike WebForms, which had its own invented abstractions. All the WebForms knowledge I had is more or less wasted for me, and I vowed to never again become so engrossed with a technology that I become specialist in a framework at the expense of understanding the platform it’s built upon.

I don’t want to be an “Ember” guy, an “Angular” dev, an “Aurelia” expert. I want to be a web expert, a messaging expert, a REST expert, a distributed systems expert. I saw this most clearly recently when helping out on a Spring MVC project. Because I understood web platforms, I could pick up the framework easily. The parts in Spring that were hard were hard because of the complexity of the framework, and those were parts I was a lot less inclined to be an expert upon.

One of the biggest reasons I’ve shied away from new, all-inclusive, heavyweight frameworks is it’s a tradeoff for me of potential productivity and administrivia knowledge. I’ve only got so much room in my head, and I’d much rather it be occupied with more important things, like vintage Seinfeld/Simpsons quotes.

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

Categories: Blogs

Reinventing Organizations Video now with Korean subtitles

Agile For All - Bob Hartman - Tue, 08/11/2015 - 19:34

Thanks to Hahn Ryu of D.CAMP, our video describing Frederic Laloux’s book Reinventing Organizations and its application for lean and agile adoption is now available with Korean subtitles. To view the subtitles, just click on the “CC” button in the playback bar and choose Korean, as illustrated in this picture by the blue arrow:

video_subtitles

 

 

D.CAMP is a startup campus in Seoul that is passionate about helping new companies develop healthy, productive cultures from the very start. This is particularly important in a culture like Korea where the notion of rank is deeply ingrained in everyday language. different verbs, etc.

The post Reinventing Organizations Video now with Korean subtitles appeared first on Agile For All.

Categories: Blogs

Agile 2015 Presentation: Nine Immutable Principes

Agile Product Owner - Tue, 08/11/2015 - 15:32

Hi Folks,

I’ve had a number of requests for a copy of last week’s Agile2015 presentation entitled Nine Immutable Principles of Lean-Agile Development. You can download a copy below.

Download Nine Immutable Principles of Lean-Agile Development 

Also, the session was recorded, so as soon as that becomes available, we’ll note that in a future blog post as well.

Cheers,
—Dean

 

Categories: Blogs

Workshop Valuable Agile Retrospectives at Agile Tour London

Ben Linders - Tue, 08/11/2015 - 14:25
On October 22 I will do a full day workshop Valuable Agile Retrospectives at Agile Tour London. For registration please see the Agile Tour London training page. Continue reading →
Categories: Blogs

Waardevolle Agile Retrospectives verkrijgbaar via Bol

Ben Linders - Tue, 08/11/2015 - 13:57
Het succesvolle boek Waardevolle Agile Retrospectives is nu ook in paperback formaat beschikbaar op Bol.com. Continue reading →
Categories: Blogs

Why dedicated Full-Time Scrum masters are hard to implement – and what’s the alternative

Ben Linders - Tue, 08/11/2015 - 11:17
More and more organizations are implementing agile with Scrum. They define teams and assign Scrum masters to the teams to start working agile and become self-organized. Although agile looks easy, to implement the Scrum master role often turns out to be problematic. Let's discuss what makes it so difficult to work with full-time Scrum masters and explore the alternative of having technical people taking the Scrum master role. Continue reading →
Categories: Blogs

Java: Jersey – java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper. getContextClassLoaderPA()Ljava/security/PrivilegedAction;

Mark Needham - Tue, 08/11/2015 - 08:59

I’ve been trying to put some tests around an Neo4j unmanaged extension I’ve been working on and ran into the following stack trace when launching the server using the Neo4j test harness:

public class ExampleResourceTest {
    @Rule
    public Neo4jRule neo4j = new Neo4jRule()
            .withFixture("CREATE (:Person {name: 'Mark'})")
            .withFixture("CREATE (:Person {name: 'Nicole'})")
            .withExtension( "/unmanaged", ExampleResource.class );
 
    @Test
    public void shouldReturnAllTheNodes() {
        // Given
        URI serverURI = neo4j.httpURI();
        // When
        HTTP.Response response = HTTP.GET(serverURI.resolve("/unmanaged/example/people").toString());
 
        // Then
        assertEquals(200, response.status());
        List content = response.content();
 
        assertEquals(2, content.size());
    }
}
07:51:32.985 [main] WARN  o.e.j.u.component.AbstractLifeCycle - FAILED o.e.j.s.ServletContextHandler@29eda4f8{/unmanaged,null,STARTING}: java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
	at com.sun.jersey.spi.scanning.AnnotationScannerListener.<init>(AnnotationScannerListener.java:94) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.scanning.PathProviderScannerListener.<init>(PathProviderScannerListener.java:59) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:79) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at javax.servlet.GenericServlet.init(GenericServlet.java:244) ~[javax.servlet-api-3.1.0.jar:3.1.0]
	at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:612) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:395) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.start(Server.java:387) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.doStart(Server.java:354) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.neo4j.server.web.Jetty9WebServer.startJetty(Jetty9WebServer.java:381) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.web.Jetty9WebServer.start(Jetty9WebServer.java:184) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.startWebServer(AbstractNeoServer.java:474) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:230) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerControls.start(InProcessServerControls.java:59) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerBuilder.newServer(InProcessServerBuilder.java:72) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.junit.Neo4jRule$1.evaluate(Neo4jRule.java:64) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.junit.rules.RunRules.evaluate(RunRules.java:20) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309) [junit-4.11.jar:na]
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160) [junit-4.11.jar:na]
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) [junit-rt.jar:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_51]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) [idea_rt.jar:na]
07:51:32.991 [main] WARN  o.e.j.u.component.AbstractLifeCycle - FAILED org.eclipse.jetty.server.handler.HandlerList@2b22a1cc[o.e.j.s.h.MovedContextHandler@5e671e20{/,null,AVAILABLE}, o.e.j.s.ServletContextHandler@29eda4f8{/unmanaged,null,STARTING}, o.e.j.s.ServletContextHandler@62573c86{/db/manage,null,null}, o.e.j.s.ServletContextHandler@2418ba04{/db/data,null,null}, o.e.j.w.WebAppContext@14229fa7{/browser,jar:file:/Users/markneedham/.m2/repository/org/neo4j/app/neo4j-browser/2.2.3/neo4j-browser-2.2.3.jar!/browser,null}, o.e.j.s.ServletContextHandler@2ab0702e{/,null,null}]: java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
	at com.sun.jersey.spi.scanning.AnnotationScannerListener.<init>(AnnotationScannerListener.java:94) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.scanning.PathProviderScannerListener.<init>(PathProviderScannerListener.java:59) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:79) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at javax.servlet.GenericServlet.init(GenericServlet.java:244) ~[javax.servlet-api-3.1.0.jar:3.1.0]
	at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:612) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:395) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.start(Server.java:387) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.doStart(Server.java:354) [jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) [jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.neo4j.server.web.Jetty9WebServer.startJetty(Jetty9WebServer.java:381) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.web.Jetty9WebServer.start(Jetty9WebServer.java:184) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.startWebServer(AbstractNeoServer.java:474) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:230) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerControls.start(InProcessServerControls.java:59) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerBuilder.newServer(InProcessServerBuilder.java:72) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.junit.Neo4jRule$1.evaluate(Neo4jRule.java:64) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.junit.rules.RunRules.evaluate(RunRules.java:20) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309) [junit-4.11.jar:na]
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160) [junit-4.11.jar:na]
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) [junit-rt.jar:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_51]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) [idea_rt.jar:na]
07:51:33.013 [main] INFO  o.e.jetty.server.ServerConnector - Started ServerConnector@19962194{HTTP/1.1}{localhost:7475}
07:51:33.014 [main] WARN  o.e.j.u.component.AbstractLifeCycle - FAILED org.eclipse.jetty.server.Server@481e91b6: java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
	at com.sun.jersey.spi.scanning.AnnotationScannerListener.<init>(AnnotationScannerListener.java:94) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.scanning.PathProviderScannerListener.<init>(PathProviderScannerListener.java:59) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:79) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at javax.servlet.GenericServlet.init(GenericServlet.java:244) ~[javax.servlet-api-3.1.0.jar:3.1.0]
	at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:612) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:395) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298) ~[jetty-servlet-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.start(Server.java:387) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.server.Server.doStart(Server.java:354) ~[jetty-server-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) ~[jetty-util-9.2.4.v20141103.jar:9.2.4.v20141103]
	at org.neo4j.server.web.Jetty9WebServer.startJetty(Jetty9WebServer.java:381) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.web.Jetty9WebServer.start(Jetty9WebServer.java:184) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.startWebServer(AbstractNeoServer.java:474) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:230) [neo4j-server-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerControls.start(InProcessServerControls.java:59) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.internal.InProcessServerBuilder.newServer(InProcessServerBuilder.java:72) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.neo4j.harness.junit.Neo4jRule$1.evaluate(Neo4jRule.java:64) [neo4j-harness-2.2.3.jar:2.2.3]
	at org.junit.rules.RunRules.evaluate(RunRules.java:20) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) [junit-4.11.jar:na]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) [junit-4.11.jar:na]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309) [junit-4.11.jar:na]
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160) [junit-4.11.jar:na]
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212) [junit-rt.jar:na]
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) [junit-rt.jar:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_51]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_51]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_51]
	at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) [idea_rt.jar:na]
 
org.neo4j.server.ServerStartupException: Starting Neo4j Server failed: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
	at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:258)
	at org.neo4j.harness.internal.InProcessServerControls.start(InProcessServerControls.java:59)
	at org.neo4j.harness.internal.InProcessServerBuilder.newServer(InProcessServerBuilder.java:72)
	at org.neo4j.harness.junit.Neo4jRule$1.evaluate(Neo4jRule.java:64)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.NoSuchMethodError: com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoaderPA()Ljava/security/PrivilegedAction;
	at com.sun.jersey.spi.scanning.AnnotationScannerListener.<init>(AnnotationScannerListener.java:94)
	at com.sun.jersey.spi.scanning.PathProviderScannerListener.<init>(PathProviderScannerListener.java:59)
	at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:79)
	at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104)
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78)
	at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89)
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696)
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674)
	at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374)
	at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557)
	at javax.servlet.GenericServlet.init(GenericServlet.java:244)
	at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:612)
	at org.eclipse.jetty.servlet.ServletHolder.initialize(ServletHolder.java:395)
	at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:871)
	at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:298)
	at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
	at org.eclipse.jetty.server.Server.start(Server.java:387)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
	at org.eclipse.jetty.server.Server.doStart(Server.java:354)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.neo4j.server.web.Jetty9WebServer.startJetty(Jetty9WebServer.java:381)
	at org.neo4j.server.web.Jetty9WebServer.start(Jetty9WebServer.java:184)
	at org.neo4j.server.AbstractNeoServer.startWebServer(AbstractNeoServer.java:474)
	at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:230)
	... 22 more

I was a bit baffled at first but if we look closely about 5 – 10 lines down the stack trace we can see the mistake I’ve made:

	at com.sun.jersey.api.core.PackagesResourceConfig.(PackagesResourceConfig.java:89) ~[jersey-server-1.19.jar:1.19]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696) ~[jersey-servlet-1.17.1.jar:1.17.1]
	at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674) ~[jersey-servlet-1.17.1.jar:1.17.1]

We have different versions of the Jersey libraries. Since we’re writing the extension for Neo4j 2.2.3 we can quickly check which version it depends on:

$ ls -alh neo4j-community-2.2.3/system/lib/ | grep jersey
-rwxr-xr-x@  1 markneedham  staff   426K 22 Jun 04:57 jersey-core-1.19.jar
-rwxr-xr-x@  1 markneedham  staff    52K 22 Jun 05:02 jersey-multipart-1.19.jar
-rwxr-xr-x@  1 markneedham  staff   686K 22 Jun 05:02 jersey-server-1.19.jar
-rwxr-xr-x@  1 markneedham  staff   126K 22 Jun 05:02 jersey-servlet-1.19.jar

So we should’t have 1.17.1 in our project and it was easy enough to find my mistake by looking in the pom file:

<properties>
...
    <jersey.version>1.17.1</jersey.version>
...
</properties>
 
<dependencies>
...
 
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-servlet</artifactId>
        <version>${jersey.version}</version>
    </dependency>
 
...
</dependencies>

Also easy enough to fix!

<properties>
...
    <jersey.version>1.19</jersey.version>
...
</properties>

You can see an example of this working on github.

Categories: Blogs

4K Programming Monitor at 30Hz

About a year ago now I acquired a Seiki SE39UY04 37″ 4K TV as a monitor. Despite the 30Hz refresh rate and once or twice a day screen reset for 5 seconds it’s the best monitor I’ve ever worked on. A 4K display is an almost infinite amount of screen real estate. I regularly have 4 files up in vim displaying over 120 lines each. At $400 dollars it was a great way to jump into 4K and wait until the really good 4K monitors show up down the line.

Pros:

  • 4 HD screens of stuff, regular have 4 1080P screens up on a single monitor.
  • If you’re not gaming the 30Hz refresh rate has much less downside.
  • It’s cheap enough that you won’t feel bad when you replace it a year or two down the road.
  • It doubles as a 4K TV.
  • After a year it hasn’t had any glitches.
  • I can remote pair and still have 60-75% of my screen available.

Cons:

  • 30 Hz is pretty noticeable at first and for gaming/fast updating.
  • To share for pairing you generally need to drop the resolution down from 4K.
  • It goes black a few times a day for a few seconds.
  • It doesn’t really sleep since it’s not a monitor.
  • Picture quality is fine for development work, but not great.
  • It’s hard to see co-workers around it.
  • If you share screens you’re constantly downsizing the resolution since almost no-one else has 4K yet.
Categories: Blogs

Neo4j 2.2.3: Unmanaged extensions – Creating gzipped streamed responses with Jetty

Mark Needham - Tue, 08/11/2015 - 01:57

Back in 2013 I wrote a couple of blog posts showing examples of an unmanaged extension which had a streamed and gzipped response but two years on I realised they were a bit out of date and deserved a refresh.

When writing unmanaged extensions in Neo4j a good rule of thumb is to try and reduce the amount of objects you keep hanging around. In this context this means that we should stream our response to the client as quickly as possible rather than building it up in memory and sending it in one go.

The documentation has a good example showing how to stream a list of colleagues but in this blog post we’ll look at how to do something simpler – we’ll create a couple of nodes representing people and then write an unmanaged extension to return them.

We’ll first create an unmanaged extension which runs a cypher query, iterates through the rows returned and sends them to the client:

@Path("/example")
public class ExampleResource {
    private final GraphDatabaseService db;
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
 
    public ExampleResource(@Context GraphDatabaseService db) {
        this.db = db;
    }
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/people")
    public Response allNodes() throws IOException {
        StreamingOutput stream = streamQueryResponse("MATCH (n:Person) RETURN n.name AS name");
        return Response.ok().entity(stream).type(MediaType.APPLICATION_JSON).build();
    }
 
    private StreamingOutput streamQueryResponse(final String query) {
        return new StreamingOutput() {
                @Override
                public void write(OutputStream os) throws IOException, WebApplicationException {
                    JsonGenerator jg = OBJECT_MAPPER.getJsonFactory().createJsonGenerator(os, JsonEncoding.UTF8);
                    jg.writeStartArray();
 
                    writeQueryResultTo(query, jg);
 
                    jg.writeEndArray();
                    jg.flush();
                    jg.close();
                }
            };
    }
 
    private void writeQueryResultTo(String query, JsonGenerator jg) throws IOException {
        try (Result result = db.execute(query)) {
            while (result.hasNext()) {
                Map<String, Object> row = result.next();
 
                jg.writeStartObject();
                for (Map.Entry<String, Object> entry : row.entrySet()) {
                    jg.writeFieldName(entry.getKey());
                    jg.writeString(entry.getValue().toString());
                }
                jg.writeEndObject();
            }
        }
    }
}

There’s nothing too complicated going on here although notice that we make much more fine grained calls to the JSON Library rather than created a JSON object in memory and calling ObjectMapper#writeValueAsString on it.

To get this to work we’d build a JAR containing this class, put that into the plugins folder and then add the following property to conf/neo4j-server.properties (or the Neo4j desktop equivalent) before restarting the server:

org.neo4j.server.thirdparty_jaxrs_classes=org.neo4j.unmanaged=/unmanaged

We can then test it out like this:

$ curl http://localhost:7474/unmanaged/example/people
[{"name":"Mark"},{"name":"Nicole"}]

I’ve put in a couple of test people nodes – full instructions are available on the github README page.

Next we want to make it possible to send that response in the gzip format. To do that we need to add a GzipFilter to the Neo4j lifecycle. This class has moved to a different namespace in Jetty 9 which Neo4j 2.2.3 depends on, but the following class does the job:

import org.eclipse.jetty.servlets.GzipFilter;
 
public class GZipInitialiser implements SPIPluginLifecycle {
    private WebServer webServer;
 
    @Override
    public Collection<Injectable<?>> start(NeoServer neoServer) {
        webServer = getWebServer(neoServer);
        GzipFilter filter = new GzipFilter();
 
        webServer.addFilter(filter, "/*");
        return Collections.emptyList();
    }
 
    private WebServer getWebServer(final NeoServer neoServer) {
        if (neoServer instanceof AbstractNeoServer) {
            return ((AbstractNeoServer) neoServer).getWebServer();
        }
        throw new IllegalArgumentException("expected AbstractNeoServer");
    }
 
    @Override
    public Collection<Injectable<?>> start(GraphDatabaseService graphDatabaseService, Configuration configuration) {
        throw new IllegalAccessError();
    }
 
    @Override
    public void stop() {
 
    }
}

I needed to include the jersey-servlets JAR in my unmanaged extension JAR in order for this to work correctly. Once we redeploy the JAR and restart Neo4j we can try making the same request as above but with a gzip header:

$ curl -v -H "Accept-Encoding:gzip,deflate" http://localhost:7474/unmanaged/example/people
��V�K�MU�R�M,�V�Ձ��2��sR�jcf(�#

We can unpack that on the fly by piping it through gunzip to check we get a sensible result:

$ curl -v -H "Accept-Encoding:gzip,deflate" http://localhost:7474/unmanaged/example/people | gunzip
[{"name":"Mark"},{"name":"Nicole"}]

And there we have it – a gzipped streamed response. All the code is on github so give it a try and give me a shout if it doesn’t work. The fastest way to get me is probably on our new shiny neo4j-users Slack group.

Categories: Blogs

Bring Sanity to Agility

TV Agile - Mon, 08/10/2015 - 19:41
This presentation discusses experiences within teams as they attempt to embrace change and find, to their dismay, that their software itself can’t keep up. Diving through technical solutions and patterns, you will learn how to avoid building software that can’t keep up with the pace of change that was always the promise of agility. Video […]
Categories: Blogs

Reasons for Continuous Planning

Johanna Rothman - Mon, 08/10/2015 - 18:58

I’m working on the program management book, specifically on the release planning chapter. One of the problems I see in programs is that the organization/senior management/product manager wants a “commitment” for an entire quarter. Since they think in quarter-long roadmaps, that’s not unreasonable—from their perspective.

AgileRoadmapOneQuarter.copyright-300x223There is a problem with commitments and the need for planning for an entire quarter. This is legacy (waterfall) thinking. Committing is not what the company actually wants. Delivery is what the company wants. The more often you deliver, the more often you can change.

That means changing how often you release and replan.

Consider these challenges for a one-quarter commitment:

  1. Even if you have small stories, you might not be able to estimate perfectly. You might finish something in less time than you had planned. Do you want to take advantage of schedule advances?
  2. In the case of too-large stories, where you can’t easily provide a good estimate, (where you need a percent confidence or some other mechanism to explain risk,) you are (in my experience) likely to under-estimate.
  3. What if something changes mid-quarter, and you want more options or a change in what the feature teams can deliver? Do you want to wait until the end of a quarter to change the program’s direction?

If you “commit” on a shorter cadence, you can manage these problems. (I prefer the term replan.)

If you consider a no-more-than-one-monthly-duration “commit,” you can see the product evolve, provide feedback across the program, and change what you do at every month milestone. That’s better.

Here’s a novel idea: Don’t commit to anything at all. Use continuous planning.

AgileRoadmapOneQuarter.copyright-1080x804

If you look at the one-quarter roadmap, you can see I show  three iterations worth of stories as MVPs. In my experience, that is at least one iteration too much look-ahead knowledge. I know very few teams who can see six weeks out. I know many teams who can see to the next iteration. I know a few teams who can see two iterations.

What does that mean for planning?

Do continuous planning with short stories. You can keep the 6-quarter roadmap. That’s fine. The roadmap is a wish list. Don’t commit to a one-quarter roadmap. If you need a commitment, commit to one iteration at a time. Or, in flow/kanban, commit to one story at a time.

That will encourage everyone to:

  1. Think small. Small stories, short iterations, asking every team to manage their WIP (work in progress) will help the entire program maintain momentum.
  2. See interdependencies. The smaller the features, the clearer the interdependencies are.
  3. Plan smaller things and plan for less time so you can reduce the planning load for the program. (I bet your planning for one iteration or two is much better and takes much less time than your one-quarter planning.)
  4. Use deliverable planning (“do these features”) in a rolling wave (continue to replan as teams deliver).

These ideas will help you see value more often in your program. When you release often and replan, you build trust as a program. Your managers might stop asking for “commits.”

If you keep the planning small, you don’t need to gather everyone in one big room once a quarter for release planning. If you do continuous planning, you might never need everyone in one room for planning. You might want everyone in one room for a kickoff or to help people see who is working on the program. That’s different than a big planning session, where people plan instead of deliver value.

If you are managing a program, what would it take for you to do continuous planning? What impediments can you see? What risks would you have planning this way?

Oh, and if you are on your way to agile and you use release trains, remember that the release train commits to a date, not scope and date.

Consider planning and replanning every week or two. What would it take for your program to do that?

Categories: Blogs

Product Owner Team: Supporting the Portfolio Team

Leading Agile - Mike Cottmeyer - Mon, 08/10/2015 - 13:55

Last week I talked about the Product Owner Team at Agile2015. Here I’m going to focus on the relationship with the Portfolio Team. A Portfolio Team looks after the portfolio of Epics and are a key stakeholder group of the Product Owner Team. In my previous post, “Product Owner Team – Why?“, I identified a key objective for the Product Owner Team: “Provide timely information to the Portfolio Management for investment decisions”. Visibility is important to support an enterprise agile program. I’ll briefly describe the key decisions and the information the Product Owner Team provides.

Strategic Alignment

The Portfolio Team ensures work is strategically aligned to Investment Themes, Strategic Objectives, or whatever the organization uses to express their strategy goals. The Product Owner Team, working closely with the Portfolio Team, describes Epics in an Epic Brief that includes a Value Statement of the problem or opportunity, Features and Benefits, and high level constraints. With this information, the Portfolio Team validates alignment to strategy and prioritizes the Epic backlog.

Demand Planning

The Portfolio Team ensures a valuable and viable roadmap is maintained. The Product Owner team is staffed to collaboratively elaborate the features and architecture and address the key constraints for the Epic. From this work, they present the Epic on the Epic Roadmap and Epic Risk Report and update the Epic Brief with an Opportunity Statement. The Portfolio Team now has visibility to explore roadmap options and ensures significant risks are addressed.

Detailed Planning

The Portfolio Team must be assured of sufficient planning to deliver Features. The Product Owner Team has provided a clear Feature Backlog and Roadmap and supports Story Writing and Story Mapping by the Delivery Teams. Now the Product Owner Team  facilitates Release Planning resulting in a number of simply stated Release Plans covering Business (Scope and launch), Technical (Test Approach, Technical approach, Environments & coordination), and Logistics (dependencies, operations, security, etc). The Portfolio Team ensures responsible planning and explores detailed options.

Execution

The Portfolio Team requires validation of measurable progress. The Product Owner Team coordinates across Delivery Teams and support teams and is the escalation point for issues to facilitate the delivery of features. The Portfolio Team has visibility to the demo of integrated features to support decisions to continue, change or stop investing in the Epic.

With a few brief reports and frequent communication, the Product Owner Team ensures the Portfolio Team has visibility and timely information they require to make good business decisions.

The post Product Owner Team: Supporting the Portfolio Team appeared first on LeadingAgile.

Categories: Blogs

Managing Workflow In Long Running JavaScript Processes

Derick Bailey - new ThoughtStream - Mon, 08/10/2015 - 13:30

In my last post, I talked about how we can dramatically improve our application architectures by making workflow explicit. When we pull the flow of the application apart and create a higher level “workflow” object (often called a ‘controller’ or ‘mediator‘), we can separate the process from the implementation details. This gives us a lot of advantages in reading and understanding the code, as well as advantages in modifying and maintaining the code. 

Fast food workflow

While that post talked about in-memory applications – stateful code like Backbone.js in browser-based JavaScript apps – the same pattern and principle applies to nearly any form of coordinated workflow, including message based systems. 

A Long Running, Distributed Workflow

In an interview I did with Jimmy Bogard (part of the RabbitMQ For Developers package), he describes a long running, distributed workflow and calls it a “saga” or “process manager“. The general idea is to have a single object manage a process that may take a significant amount of time. This of often described as a “long running transaction” – but not in the sense of a database transaction. It’s more like a fast food order as a “transaction” – paying for something and having it delivered to you – as Jimmy describes in our interview:

Imagine going somewhere like McDonald’s and ordering something there. You go and order something and that kicks off a process behind the scenes. It represents the overall transaction of you getting your order.

Getting the food is the transaction, you’re not leaving the restaurant until you get that. You don’t stand at the counter and just sit there waiting while all the things are done before the next person goes. That would be a synchronous transaction. Instead it is this more long running transaction that has a number of independent pieces that are all communicating via messaging.

The cashier will message to the cook to say order up and it has a little piece of paper that has your order on it. That’s the message, what you’re ordering. And when they are done with it it all comes back to a single … at least at McDonald’s it comes back to a tray, and individual people finish up with stuff. That tray effectively is the state of what is done and what’s not done. In fact, if you go look at how they do it, every time a cashier or one of the people who has something to do with your order does something, they actually look at your receipt on the tray to see what’s finished. 

In this example, the order ticket that goes to each station of the restaurant is the message. The food tray where your order is placed, before it gets handed to you, is the state of the order. This is all one large, long running transaction – a “saga” – where multiple messages are sent out and things may happen sequentially or in parallel to complete your order. 

But, how would you represent that in code? What would it look like to have an object that coordinates messages in order to complete an order, like this?

Modeling The Food Order Workflow

To start out simple, take the Backbone.js code from the previous post on workflow and model a workflow that does the following:

  • Receive payment for the order
  • Request a burger
  • Request french fries
  • Request a drink
  • Present the order to the customer

The code to handle this in a Backbone.js application may look something like this, at a high level:

In this code, you can see the payment being received before any of the food is made and gathered. Once all of the food is ready and on the tray, it is handed to the customer. The tray is acting as the state of the over-all workflow. It knows what items have been added to it. After each item is added, the state of the order is checked by examining the tray’s contents. Once all of the items are on the tray, the order is done and it is delivered to the customer. 

Be sure to note that the order is not processed sequentially. You don’t make the burger before making the fries, or the fries before making the drink. To do that would adversely affect the efficiency of the restaurant. Instead, each of the items for the order is allowed to be made in parallel to the other items. They are placed on the tray when they are done, and the tray is checked each time to see whether or not the order is complete. 

If you have a process that needs to run sequentially, the code can easily be changed to work this way. The previous workflow post shows code that needs to work this way, for example.

Lastly, this code has a lot of assumptions in it such as only having one burger, fries and drink. A more realistic example would require more abstraction to represent any number of line items on the order.

Moving From In-Memory To Messages

Now that the food order workflow has been built, it’s time to change this up so that messaging is involved. The good news is, this takes very little change. In fact, it could be done with zero change at all to the high level FoodOrder object! The real change that is needed, will be in the lower level components – the parts that do the work. 

For example, you could easily build a “DrinkStation” object with RabbitMQ and Node.js:

In this example, the drink station is handled by another process somewhere else. The code in question sends off a request for the drink to be made and then waits for the drink data to be returned. Once the drink comes back, it triggers the “drinkup” event so that the higher level workflow can be notified that the drink is now ready to be put on the tray. 

As simple as this example is, there are some downsides. Running a request/response scenario like this requires a fairly fast drink station on the other side. If the response takes more than a few seconds, then the Request/Response pattern may be the wrong choice. It might be better to look at two way messaging with multiple publishers and subscribers.

A Long Running Task

Using Request/Reply often seems like a good idea at first, but can be dangerous. Request/Reply does not guarantee the response will be handled by the requester, and typically requires a very fast response for it to be useful. 

In the case of a drink station, burger station or fry station, it will probably take anywhere from 30 seconds to 3 minutes (or more) to finish making the item. This is probably too long for the Request/Reply to hang around waiting. What you need instead, is bi-directional messaging with multiple publishers and subscribers.

In the case of the drink station object, there will be two child objects

  • Drink Request Sender
  • Drink Response Handler

Off-hand, this looks like request/response, but it will be implemented with a more straightforward set of objects. One that can send a message and one that can receive a message.

Now that the requester and handler are separated, the system will be more fault tolerant – there will be more room for processes crashing and starting up again, and message will have a better chance of getting to their intended destination.

But again, this new code is not without it’s own share of potential problems.

Reconstructing Workflow Objects

When you have a long running process facilitated by messaging, you may not want to keep the process object around in memory all the time. If there are hundreds or thousands of these instances running, that could eat up a lot of memory. Additionally, you have no guarantee that the server won’t go down and come back up in between messages that are sent back and forth.

In my email course / ebook on RabbitMQ Patterns, I talk about the challenge of ensuring the response message is handled by the right object.

The easiest way to do this is to again use the Correlation ID of the message. By sending an ID with the original command, and returning it with each status event message, you can apply the message to the correct job. The correlation ID in a typical request / response scenario will likely be a random GUID or UUID. In the case of the job status events, however, the correlation ID should be the job’s unique ID. This makes it trivial to find the job to which the event message applies, and update the job accordingly.

If the original object that is managing the workflow is no longer in memory, you will have to reconstruct that object when a related message comes in. This is where the correlation ID that is mentioned above comes in to play. The correlation ID should be examined when a message arrives, and the correct workflow object should be loaded in to memory again. Once that has been done, the message can be processed by object, the state can be saved and then the workflow object can be unloaded from memory once more.

To make this happen, the code will have to be adjusted significantly with the message listener and workflow object relationship inverted.

Setting Up A Workflow Manager

To facilitate reloading the workflow object as needed, the message listener can no longer be held by the workflow object directly. Instead, there will need to be a 3rd party involved – one that will listen for the messages, load the correct workflow and tell the workflow object to process the result. 

With the FoodOrderManager in place, the code is quickly becoming more robust and fault tolerant. Instead of requiring the drink station to very quickly fill a drink and send it back, it can take it’s time and do it right. If the main process crashes or needs to pause the order for a while, it can. The original FoodOrder can be unloaded from memory and minutes, hours or even days later it can be reconstructed and continue processing.

Seeing The Flow And The Aggregation Of Patterns

The workflow object is a useful pattern in itself, but it also involves a number of other patterns at various times. There’s the workflow manager object, for one, but there’s also the individual message senders and receivers. Each of these individual steps is likely comprised of one or more chunks of code that aggregate in to the step… sub-workflows. They may not be implemented as workflow like objects, but they could be if needed.

However the steps are implemented, all of these patterns come together in the end to create the higher level workflow. Even with details omitted (error handling, missing object definitions, the need for a real RabbitMQ library, etc), the benefits of having a workflow in place for messaging are there. You get the high level process definition in the FoodOrder workflow, and you can easily modify or replace the details of individual steps as needed.

Additional Resources

This is only the beginning of exploration into workflow and architecture with messaging systems like RabbitMQ. In reality, you’ll find many more situations and many more patterns that become quite useful and necessary. But the idea of having a high level workflow is an important stepping stone in moving forward and creating maintainable systems.

There are still other questions to ask and answer, as well. For one, what happens when the FoodOrder workflow completes and needs to notify the original requesting object or code that it is done? This can be handled with additional messages and communication patterns, of course… but these and other questions are going to have to wait for another time or another set of resources.

For more information on the ideas, the interviews mentioned and the details of workflow in messaging architectures, take a look at the following resources:

While this is a good place to start, there are still other great resources out there covering the concepts of workflow, long running processes and more.

Categories: Blogs