[video] What is PubSubHubbub?

January 9th, 2010 devylon No comments

Google explains PubSubHubbub in a short video:

An overview of PubSubHubbub, a simple, open, web-hook-based pubsub protocol & open source reference implementation. Brett Slatkin and Brad Fitzpatrick demonstrate what PubSubHubbub is and how it works:

Categories: web 2.0 Tags:

Get My Daily Jira Worklog

December 16th, 2009 devylon 1 comment

Atlassian Jira LogoAt work I’m currently evaluating Jira as our “Scrum Tool”. Currently we work with various tools:

  • Physical Taskboard
  • TestTrack Pro
  • Excel Sheets
  • Timetracking Tool

to assist our scrum process. As a java develepors I endeavore to reuse source code and encapsulate responsibilities within components with precisely defined interfaces. In my daily work I have to manage 4 tools and keep all the information synchronized, so I gave Jira a try to replace some of the current tools.

I am currently evaluating Jira in the running sprint, and so far it seems to manage all our requirements. But there is information I couldn’t get out of Jira: My Daily Worklog. I need the daily worklog to generate productivity reports and to invoice our customer.

I haven’t found such a report in the Jira Plugin list, so i wrote a small java application to get this done by utilizing the Jira SOAP web-service:

 JiraSoapServiceService jiraSoapServiceGetter = new JiraSoapServiceServiceLocator();
 JiraSoapService soap = jiraSoapServiceGetter
     .getJirasoapserviceV2(
          new URL("http://url.to.jira/rpc/soap/jirasoapservice-v2?wsdl"));
 String token = soap.login("myUsername", "mySecret");

 RemoteIssue[] issuesFromTextSearch = soap
     .getIssuesFromJqlSearch(
         token,
         "project = MYPROJECT AND assignee = myUsername AND fixVersion = 'Sprint 46'",
         10000);

 List<WorklogEntry> entries = new ArrayList<WorklogEntry>();

 for (RemoteIssue remoteIssue : issuesFromTextSearch) {
      RemoteWorklog[] worklogs = soap.getWorklogs(token, remoteIssue.getKey());

      for (RemoteWorklog remoteWorklog : worklogs) {
           entries.add(new WorklogEntry(
                remoteWorklog.getCreated().getTime(),
                remoteWorklog.getTimeSpent(),
                remoteIssue.getKey(),
                remoteIssue.getSummary()));
     }
 }

 DateTime today = new DateTime();

 for (WorklogEntry worklogEntry : entries) {
      DateTime worklogDateTime = new DateTime(worklogEntry.getCreated());
      if (worklogDateTime.getDayOfYear() == today.getDayOfYear()
           && worklogDateTime.getYear() == today.getYear()) {
           System.out.println(
                 worklogEntry.getIssueKey() +" - "
                 + worklogEntry.getIssueDescription()
                 + " - "+ worklogEntry.getTimeSpent());
      }
 }

This is a quick hack and far away from any clean code. Maybe I’ll write a cusomt Jira Report in my spare time to get the above data out of Jira in the future.

Categories: development Tags: , , ,

Agile Bugs

December 15th, 2009 devylon 1 comment

ScrumHow should bugs in agile projects be handled? More and more teams, which are going the agile way, are asking me how to deal with bugs in the future. The first time I did not understand the question. The background of the question is the same every time: When teams are starting with an agile process (e.g. doing a scrum crash-course) the scrum coaches often state that the team will not produce bugs going the agile way and the code quality will skyscrape. But when hitting reality again most teams will realize, that the bugs are utterly disinterested in that kind of theory and will still popup regularly.

But how should we deal with bugs, when we put our complete attention to the business value? It is ambiguous and hard to evaluate what the business value of a bug fix is.

The business value fixing a bug is equivalent to the impact of this bug on the business. That also implies that a bug having no impact on the business has likely no value to be fixed. But I’ve seen bug reports all over where more than 50% of the QA findings have filed bugs which had no real impact. Only a strong business decision instance can then prevent the project and development from being trapped and jeopardized by QA (I would even consider such bug reports partly as hidden feature requests of individuals). My experience is that an agile team has to put less effort in debugging code and has to fix lesser bugs than teams developing in a traditional way. If you do pair programming some of the bugs are discovered during implementation before the code is in production. As usual a good test coverage helps to find and fix bugs early and prevent them from popping up again. But this doesn’t mean that there won’t be any bugs anymore and one has to think about how to deal with bugs in agile projects. Bugs and software tend to stick tightly together and we experience them in our day2day scrum.

When a bug appears during an iteration and the bug is related to the feature which is currently under development. Then the bug should be fixed immediately. Otherwise the feature couldn’t be moved to the one compartment at the end of the iteration. In this case the bugfix is simply part of an existing task and related to the feature under development. Proven standards like test driven development have to back agile development.

Dealing with a bug which relates to a feature that was implemented several iterations ago is a bit more difficult. It depends on:

  • the complexity of the bug
  • the life-cycle of the project/product

A really simple bug, with a well-known scope, should be fixed in the running iteration but not by the committed resources (wildcard/joker). Is the bug far more complicated and the estimation to fix the bug is uncertain then the bugfix tasks should be planned and prioritized for the next iteration (depends on the prioritization), similar to a new feature.

Bugs, which occur in a running project or product and which have been reported by the customer tend to have a very high priority most of the time. These kind of complex bugs often cannot be handled in one of the following iterations and should be fixed immediately. Now the team or rather the product owner is in a catch-22 situation. On the one side, the product owner wants progress on new features but on the other side he wants to satisfy the customer by fixing the bug.

To deal with this problem I see 2 pragmatic options:

Bigger teams: A dedicated support team could be established. This team is exclusively responsible to current engineering and operations: Fixing bugs while the development team concentrates on implementing new features. This is one solution, but most organizations are having several reasons not to split teams into two groups.

In smaller teams the maintenance for the system is timeboxed. This timebox is used to resolve bugs or to maintain the system, e.g. cleanup jobs. This is a well-defined amount of time for each team member. The rest of the time is spent to implement new features. Currently we have 2 week iterations and each team-member has 2 days for maintenance. On top of that we have established a joker role. The joker has an additional day for unforeseen adhoc issues. The joker role rotates each iteration and each team member will become the joker from time to time.

I see that the Joker role also greatly increases the overall comprehension and awareness of the product/service the team works on. In general new bugs have a high priority (defined by the product owner) and as soon as a team member has finished a task, he snaps and fixes the bug. Each team member will do this as long as his maintenance timebox has not been consumed. All other bugs are moved to the next iteration (again defined by the product owner). The balance between maintenance timebox and feature progress has to be figured out individually depending on the priority of either maintenance or progress.

The split of roles, the best way to handle the wildcard/joker and the maintenance challenge all depend very much on the team. I believe that not all teams are capable to run a successful scrum and not all team members are prepared to run.

Categories: development Tags: ,

Mozilla Raindrop: The New Inbox

October 23rd, 2009 devylon No comments

Mozilla Labs announced a new promising project: Raindrop is a new exploration by the team responsible for Thunderbird to explore new ways to use open Web technologies to create useful, compelling messaging experiences.

Raindrop UX Design and Demo from Mozilla Messaging on Vimeo.

Raindrop’s mission: make it enjoyable to participate in conversations from people you care about, whether the conversations are in email, on twitter, a friend’s blog or as part of a social networking site.

Raindrop uses a mini web server to fetch your conversations from different sources (mail, twitter, RSS feeds), intelligently pulls out the important parts, and allows you to interact with them using your favorite modern web browser (Firefox, Safari or Chrome).

Raindrop comes with a built-in experience that bubbles up what conversations are important to you. You can participate in the experience by writing extensions that use standard open Web technologies like HTMLJavaScript andCSS. Or, use the lower level APIs to make your own experience. You have control over your conversations and how you want to play with them.

As with all explorations hosted by Labs, Raindrop is an open source project and everyone is welcome to participate in its design, development and testing.

Categories: web 2.0 Tags:

GoodMorning! Twitter visualization with twitter4j and processing

September 18th, 2009 devylon No comments

(via http://blog.blprnt.com/)

GoodMorning! is a Twitter visualization tool which shows about 11,000 tweets collected over a 24 hour period between August 20th and 21st. The tweets were harvested to find people saying ‘good morning’ in English as well as several other languages.

The tweets appear as blocks and are colour-coded. Green tweets are early in the morning, orange tweets are at about 9am, and red tweets are later in the morning. Black tweets are ‘out-of-time’ messages (sent at times that aren’t in the morning at that location).

Built in Processing (processing.org) using Twitter4J, and a home-brewed client for MetaCarta’s geo-parsing APIs.

Simple Twitter Agent For The Google Wave Federation Server

August 22nd, 2009 devylon No comments

This week some code was push to the repository to implement some kind of agents for the federation server. A simple EchoAgent was added to demonstrate the code.

With this as starting point I implemented a small TwitterAgent. Just add the TwitterAgent to a wave.  The agent listens to every message  and updates your twitter status. I took the twitter4j API which is pretty easy to use.

package org.waveprotocol.wave.examples.fedone.agents.twitter;

import java.util.ArrayList;
import java.util.List;
import org.waveprotocol.wave.examples.fedone.agents.agent.AbstractAgent;
import org.waveprotocol.wave.examples.fedone.agents.agent.AgentConnection;
import org.waveprotocol.wave.examples.fedone.util.Log;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.Attributes;
import org.waveprotocol.wave.model.document.operation.AttributesUpdate;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.DocOpCursor;
import org.waveprotocol.wave.model.operation.wave.WaveletDocumentOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.WaveletData;

import twitter4j.Twitter;
import twitter4j.TwitterException;

public class TwitterAgent extends AbstractAgent {
  private static final Log LOGGER = Log.get(TwitterAgent.class);
  private Twitter twitter;

  private TwitterAgent(String username, String hostname, int port, String twitterUsername, String twitterPassword) {
    super(AgentConnection.newConnection(username, hostname, port));
    this.twitter = new Twitter(twitterUsername, twitterPassword);
  }

  private void tweetLines(List lines) {
    for (String line : lines) {
      LOGGER.info("updateStatus: " + line);
      try {
        twitter.updateStatus(line);
      } catch (TwitterException e) {
        LOGGER.warning(e.getMessage(), e);
      }
    }
  }

  @Override
  public void onDocumentChanged(WaveletData wavelet, WaveletDocumentOperation documentOperation) {
    BufferedDocOp docOp = documentOperation.getOperation();
    final List lines = new ArrayList();
    docOp.apply(new DocOpCursor() {
      @Override public void annotationBoundary(AnnotationBoundaryMap map) { }
      @Override public void characters(String chars) { lines.add(chars); }
      @Override public void deleteCharacters(String chars) { }
      @Override public void deleteElementEnd() { }
      @Override public void deleteElementStart(String type, Attributes attrs) { }
      @Override public void elementEnd() { }
      @Override public void elementStart(String type, Attributes attrs) { }
      @Override public void replaceAttributes(Attributes oldAttrs, Attributes newAttrs) { }
      @Override public void retain(int itemCount) { }
      @Override public void updateAttributes(AttributesUpdate attrUpdate) { }
    });
    tweetLines(lines);
  }

  @Override
  public void onParticipantAdded(WaveletData wavelet, ParticipantId participant) { }

  @Override
  public void onParticipantRemoved(WaveletData wavelet, ParticipantId participant) { }

  @Override public void onSelfAdded(WaveletData wavelet) { }

  @Override public void onSelfRemoved(WaveletData wavelet) { }

  public static void main(String[] args) {
    try {
      if (args.length == 5) {
        int port;
        try {
          port = Integer.parseInt(args[2]);
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Must provide valid port.");
        }

        TwitterAgent agent = new TwitterAgent(args[0], args[1], port, args[3], args[4]);
        agent.run();
      } else {
        System.out.println("usage: java TwitterAgent
  ");
      }
    } catch (Exception e) {
      System.err.println("Catastrophic failure: " + e);
      System.exit(1);
    }

    System.exit(0);
  }
}

One issue with the current implementation should be mentioned: If you’ll restart the TwitterAgent all the messages will be resubmitted to your twitter account! If someone knows how to avoid this, please leave a comment.

Within in the next days, I’ll add some more little features (e.g. grab status updates and add them to a wave, twitter direct message gateway, …). What else could you think of?

This should be considered as a small demonstration and is far away from a Twitter and Google Wave integration ;)

Categories: development Tags: , , ,

Let Yourself Feel

August 22nd, 2009 devylon No comments

Esteban Diácono released a video gifted with a sense of beauty. The video is called “Let Yourself Feel” and is mesmerizing:

From the author:

a little animation project i started this past weekend, inspired by the wonderful music of Olafur Arnalds….

This video remindes me to the electric sheep screensaver. One of my alltime favorites.

Categories: miscellaneous, video Tags:

Java Desktop Client for Google Wave Federation Server

August 14th, 2009 devylon No comments

Currently, there isn’t so much hype concerning the Wave Federation Server and with particular regards to the Gadgets API or the Robot API. But there are some people around the world starting their own client application. Unfortunately, the Wave Federation Server is a moving target and there are daily changes to the code base. The client-server RPC (based on protocoll buffers) communication is far from feature complete. Some people are complaining they won’t start projects until the Wave Federation Server is stable and almost feature complete.

As far as i know there are three projects working on a client:

As the first two projects do not have any implementation yet I took a first look into the “Java Desktop Client”:

A very simple GUI based client to be used with the reference server. This is a small experiment I investigated and thus unfortunately I cannot keep the project updated or fix bugs etc at the moment, especially as google keep updating the reference code. This may or may not work with the current distribution of the google wave reference server again I haven’t tested it.

The desktop client implements a very basic set of funtcions: new wave, open wave, add and remove participants, send a message. I took a screenshot so you can see how the desktop client looks like:

Unfortunately, there is no binary or webstart version of the Desktop Client, but you can pull the sources from github and build a binary. There is no ant or maven file, so i give you a short step-by-step installation guide how to get a running version:

  • git clone git://github.com/Thomas101/GUI-client-for-google-Wave.git
  • create a new eclipse java project in the newley created directory
  • add the “console” directory to sources
  • create a new package “org.waveprotocol.wave.examples.fedone.waveclient.console” and move all classes to this package
  • add the fedone-client.jar from the wave-protocol project to the classpath
  • run the application with the parameters: ConsoleClient <userAtDomain> <server> <port>

If everything is fine you’ll see a Java GUI:

Java Desktop Client

Categories: development Tags: ,

Run a Wave Federation Server as Linux Daemon

August 12th, 2009 devylon No comments

The Wave Federation Server is written in Java, so it is very handy to run the server as linux daemon. The JavaServiceWrapper (JWS) turns java applications into robust services that boot up automatically and restart automatically following crashes. It’s free for open source programs and has very reasonable licensing fees for commercial software.

I used JWS to start the Wave Federation Server instead of starting the server with the original run-server.sh bash script.

Download the JavaServiceWrapper:

%> wget http://wrapper.tanukisoftware.org/download/3.3.5/wrapper-linux-x86-64-3.3.5.tar.gz
%> tar xvf wrapper-linux-x86-64-3.3.5.tar.gz

I created a new directory (e.g. bin) in the wave-protocol workspace and copied the relevant wrapper files into this directory. You should have the following files in the ./bin directory:

%> bin/wrapper
%> bin/libwrapper.so
%> bin/wrapper.jar
%> bin/wrapper.conf
%> bin/wave.key
%> bin/wave.cert
%> bin/wrapper.sh

The important files are the configuration “wrapper.conf” and the startup script “wrapper.sh”. I just took the script from the JavaSericeWrapper distribution and renamed it to wrapper.sh. The changes to the default script are to adjust the application name and application long name.

My wrapper.conf:

wrapper.java.command=java
wrapper.java.command.loglevel=INFO
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
wrapper.java.classpath.1=./wrapper.jar
wrapper.java.classpath.2=../dist/fedone-0.2.jar
wrapper.java.library.path.1=.
wrapper.java.additional.auto_bits=TRUE
wrapper.java.initmemory=128
wrapper.java.maxmemory=256
wrapper.app.parameter.1=org.waveprotocol.wave.examples.fedone.ServerMain
wrapper.app.parameter.2=--client_frontend_hostname=wave.foobar.com
wrapper.app.parameter.3=--client_frontend_port=9876
wrapper.app.parameter.4=--xmpp_component_name=wave
wrapper.app.parameter.5=--xmpp_server_hostname=foobar.com
wrapper.app.parameter.6=--xmpp_server_ip=foobar.com
wrapper.app.parameter.7=--xmpp_server_port=5275
wrapper.app.parameter.8=--xmpp_server_secret=<secret>
wrapper.app.parameter.9=--xmpp_server_ping=acmewave.com
wrapper.app.parameter.10=--certificate_private_key=wave.key
wrapper.app.parameter.11=--certificate_files=wave.cert
wrapper.app.parameter.12=--certificate_domain=foobar.com
wrapper.app.parameter.13=--waveserver_disable_verification=true
wrapper.console.format=PM
wrapper.console.loglevel=INFO
wrapper.logfile=../logs/wrapper.log
wrapper.logfile.format=LPTM
wrapper.logfile.loglevel=INFO
wrapper.logfile.maxsize=0
wrapper.logfile.maxfiles=0
wrapper.syslog.loglevel=NONE

Now start the server with:

%> ./wrapper.sh console

This will start the server as a foreground process and you can kill it with CTRL-C. To start the server as daemon just type:

%> ./wrapper.sh start
Categories: development Tags: ,