Glassfish uses per default java.util.logging (JUL) for logging. Third-party frameworks are often using different logging frameworks. For my company and private projects I’m using slf4j for all kind of logging. I found some rare resources on the web how to consolidate the different logging frameworks to just have one single logging file.

slf4j is delivery with a bridge to redirect JUL logging to slf4j. The steps to enable slf4j with log back are:

You need these 4 artifacts:

  • slf4j-api-1.5.10.jar
  • jul-to-slf4j-1.5.10.jar
  • logback-core-0.9.20.jar
  • logback-classic-0.9.20.jar

You can download either from the slf4j and log back websites, or grab them from the maven repository

Step 1:

Copy these jar’s to the $GF_INSTALL/glassfish/lib/endorsed

Step 2:

Create a logback.xml at $GF_INSTALL/glassfish/domains/domain1/config containing:

<configuration debug="true" scan="true">
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file</tmp/gf_server.log>/file>
    <append<true>/append>
    <encoder>
      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{52} - %msg%n</Pattern>
    </encoder>
  </appender>
  <root>
    <level value="INFO" />
    <appender-ref ref="FILE" />
  </root>
</configuration>

Step 3:

Modify $GF_INSTALL/glassfish/domains/domain1/config/logging.properties

handlers=org.slf4j.bridge.SLF4JBridgeHandler
com.sun.enterprise.server.logging.GFFileHandler.flushFrequency=1
com.sun.enterprise.server.logging.GFFileHandler.file=/tmp/server.log
com.sun.enterprise.server.logging.GFFileHandler.rotationTimelimitInMinutes=0
com.sun.enterprise.server.logging.GFFileHandler.logtoConsole=false
com.sun.enterprise.server.logging.GFFileHandler.rotationLimitInBytes=2000000
com.sun.enterprise.server.logging.GFFileHandler.alarms=false
com.sun.enterprise.server.logging.GFFileHandler.formatter=com.sun.enterprise.server.logging.UniformLogFormatter
com.sun.enterprise.server.logging.GFFileHandler.retainErrorsStasticsForHours=0
com.sun.enterprise.server.logging.GFFileHandler.level=ALL
.level=INFO

Step 3:

Modify $GF_INSTALL/glassfish/domains/domain1/config/domain.xml and add two JVM options at the server-config JVM section:

-Djava.util.logging.config.file=${com.sun.aas.instanceRoot}/config/logging.properties
-Dlogback.configurationFile=file:///${com.sun.aas.instanceRoot}/config/logback.xml

Now you can restart Glassfish and you should only see two entries in the server.log:

16.06.2012 17:19:40 com.sun.enterprise.admin.launcher.GFLauncherLogger info
INFO: JVM invocation command line:
-XX:+UnlockDiagnosticVMOptions
-XX:PermSize=64m
-XX:MaxPermSize=192m
-XX:NewRatio=2
-Xmx512m
-d32
-client
-javaagent:/srv/glassfish-3.1.2-logback/glassfish/lib/monitor/flashlight-agent.jar
-Dfelix.fileinstall.disableConfigSave=false
-Djavax.net.ssl.keyStore=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/keystore.jks
-Djava.awt.headless=true
-Dfelix.fileinstall.poll=5000
-Djava.endorsed.dirs=/srv/glassfish-3.1.2-logback/glassfish/modules/endorsed:/srv/glassfish-3.1.2-logback/glassfish/lib/endorsed
-Dfelix.fileinstall.bundles.startTransient=true
-Djavax.net.ssl.trustStore=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/cacerts.jks
-Djava.util.logging.config.file=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/logging.properties
-Dcom.sun.enterprise.security.httpsOutboundKeyAlias=s1as
-DANTLR_USE_DIRECT_CLASS_LOADING=true
-Djava.security.auth.login.config=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/login.conf
-Dgosh.args=--nointeractive
-Dosgi.shell.telnet.maxconn=1
-Djdbc.drivers=org.apache.derby.jdbc.ClientDriver
-Dfelix.fileinstall.dir=/srv/glassfish-3.1.2-logback/glassfish/modules/autostart/
-Dosgi.shell.telnet.port=6666
-Djava.security.policy=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/server.policy
-Dfelix.fileinstall.log.level=2
-Dcom.sun.enterprise.config.config_environment_factory_class=com.sun.enterprise.config.serverbeans.AppserverConfigEnvironmentFactory
-Dosgi.shell.telnet.ip=127.0.0.1
-Dcom.sun.aas.instanceRoot=/srv/glassfish-3.1.2-logback/glassfish/domains/domain1
-Dcom.sun.aas.installRoot=/srv/glassfish-3.1.2-logback/glassfish
-Djava.ext.dirs=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/ext:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/jre/lib/ext:/srv/glassfish-3.1.2-logback/glassfish/domains/domain1/lib/ext
-Dfelix.fileinstall.bundles.new.start=true
-Dlogback.configurationFile=file:////srv/glassfish-3.1.2-logback/glassfish/domains/domain1/config/logback.xml
16.06.2012 17:19:41 com.sun.enterprise.admin.launcher.GFLauncherLogger info
INFO: Successfully launched in 16 msec.

all the rest is logged to the gf_server.log file located at /tmp/gf_server.log. The logging bridge jul-to-slf4j is redirecting all JUL logging to slf4j ant then to the logging backend of your choice. I used log back instead of log4j. Logback has some advantages over log4j which are described here.

David Beach wrote a good article about Developing for Android. He mentioned various pitfalls when you are thinking about developing for the Android platform:

  1. No HIG. Humean Interface Guidelines
  2. Too many OS versions in the marketplace
  3. Too many different handsets in the marketplace
  4. The app market sucks
  5. Clunky consumer experience in the app market
  6. Android Apps don’t sell

Additional to that he’ s he also mentions the good parts. The article is worth reading.

It took me half a day to figure out, why redeploying a Atmosphere war bundle always fails with a stacktrace. Atmosphere packages, e.g. the jersey into the war archive. This behavior could be changed by setting a JVM environment variable in Glassfish.

In the GF admin console, going to:

Configuration->JVM Settings

switch to the JVM Options tab and add this option:

-Dcom.sun.enterprise.overrideablejavaxpackages
=javax.ws.rs,javax.ws.rs.core,javax.ws.rs.ext

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.

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.

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.

(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.

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 😉