Posted by & filed under Uncategorized.

Normally a command-line Java application will have an entry method that looks something like:

public static void main(String... args) {
    /* Code that launches the application/deals with command line params here */
}

This is far too easy, there must be a way to NIH it ¬_¬. There are a few annoyances with the public static void main method.

  • Args are passed in as an Array, rather than something like a List<String>
  • We can’t choose to call the main method something other than main.
  • It’s a bit obscure how the startup process works.

There is a common way to execute code on app startup without using a static void main method. You can use a static initialization block:

static {
    /* Code here run on class load. */
}

So we could launch our application from within a static initialization block. However this presents a number of problems:

  1. We get a nasty error when the application finishes (Exception in thread “main” java.lang.NoSuchMethodError: main)
  2. We have no access to the command line arguments
  3. We can’t have multiple “Main” classes with this method (Both static initialization blocks will be run, if both classes are loaded)
  4. It doesn’t really provide any benefits over static void main.

These problems can, however, all be overcome.

Suppressing NoSuchMethodError

We can just call System.err.close(); at the end of our initialization method.

Access to the command line arguments

This one is probably the most difficult. I’ve not found any “good” way to do this, but there is a hack that works at present on Sun’s Java, though it may stop working at any point.

sun.misc.VMSupport.getAgentProperties().get("sun.java.command")

This will give the command line string used to start the application. There are alternatives involving attaching to the currently running VM with the Agent API or shelling out to external processes and using platform-specific commands to work out the command used to start the application. If anyone knows a proper way to access the command line arguments using the public API then please let me know.

Multiple Main Classes

We need to get our initialization system to ignore invocations from classes other than the one used to start our application. This can be done by checking the length of the StackTrace. e.g.

new Throwable().fillInStackTrace().getStackTrace().length == 2;

Reflection Examples

With these 3 issues solved it’s possible to do things like:

package MainlessDemo;
 
import java.util.List;
import static uk.co.benjiweber.realjava.mainless.Bootstrap.init;
 
public class ReflectionMainless {
	static { init(); }
	public void main(final List<String> args) {
		System.out.println("Hello World");
		System.err.println("From std err");
		for (String arg : args) {
			System.out.println(arg);	
		}
	}
}
/* Output:
$ javac -cp .:./RealJava.jar ./MainlessDemo/*.java && java -cp .:./RealJava.jar MainlessDemo.ReflectionMainless Foo Bar Baz
Hello World
From std err
Foo
Bar
Baz
*/

Here a statically imported “init” method instantiates our main class, invokes our non-static main method, and passes it the command line arguments. The class to instantiate can be determined by walking back up the stack trace again.

We can also inject constructor arguments if we wish:

package MainlessDemo;
 
import java.util.List;
import static uk.co.benjiweber.realjava.mainless.Bootstrap.init;
 
public class ReflectionMainlessWithArgs {
 
	// This time we pass in the constructor argument
	static { init("Hello World"); }
 
	private final String message;
 
	// Even though we load another class that is Launchable it doesn't get launched.
	static final ReflectionMainless test = new ReflectionMainless(); 
 
	public ReflectionMainlessWithArgs(final String message) {
		this.message = message;
	}
 
	public void main(final List<String> args) {
		System.out.println(message + " (Passed in through constructor)");
		for (String arg : args) {
			System.out.println(arg);	
		}
	}
}
/* Output:
$ javac -cp .:./RealJava.jar ./MainlessDemo/*.java && java -cp .:./RealJava.jar MainlessDemo.ReflectionMainlessWithArgs Foo Bar Baz
Hello World (Passed in through constructor)
Foo
Bar
Baz
 
*/

Interface Examples

Unfortunately, apart from substituting an argument Array for a List, we ‘ve not really improved anything. However, now that we’re in control of the initialization process we can do more interesting things, like use an interface:

public interface Launchable {
	public void main(List<String> args);
}
package MainlessDemo;
 
import java.util.List;
import uk.co.benjiweber.realjava.mainless.Launchable;
import static uk.co.benjiweber.realjava.mainless.Bootstrap.init;
 
public class SaferMainless implements Launchable {
	static { init(new SaferMainless()); }
 
	public void main(final List<String> args) {
		System.out.println("Hello from a Launchable");
		for (String arg : args) {
			System.out.println(arg);	
		}
	}
}
 
/*
Output:
$ javac -cp .:./RealJava.jar ./MainlessDemo/*.java && java -cp .:./RealJava.jar MainlessDemo.SaferMainless Foo Bar Baz
Hello from a Launchable
Foo
Bar
Baz
*/

Now it’s clearer how the initialization process works, you can use your IDE to follow execution through from the init block to the main method. This also gives us the freedom to call our main method something different, and inject dependencies before reaching the main method.

package MainlessDemo;
 
import java.util.List;
import uk.co.benjiweber.realjava.mainless.Launchable;
import static uk.co.benjiweber.realjava.mainless.Bootstrap.init;
 
public class SaferAnonymousMainless {
 
	static { init(new Launchable() {
		public void main(final List<String> args) {
			SaferAnonymousMainless mainless = new SaferAnonymousMainless();
			mainless.setMessage("I was injected manually");
			mainless.someMethodNotCalledMain(args);
		}
	}); }
 
	private String message;
 
	public void setMessage(final String message) {
		this.message = message;
	}
 
	public void someMethodNotCalledMain(final List<String> args) {
		System.out.println(message);
		for (String arg : args) {
			System.out.println(arg);	
		}
	}
}
/* 
Output:
$ javac -cp .:./RealJava.jar ./MainlessDemo/*.java && java -cp .:./RealJava.jar MainlessDemo.SaferAnonymousMainless Foo Bar Baz
I was injected manually
Foo
Bar
Baz
 
*/

Source for the BootStrap.init method is here

Thanks to Faux for some of the ideas.

Yes I know this isn’t remotely a good idea, no need to tell me 🙂

Posted by & filed under openSUSE.

Here are my notes from the software portal talk Pascal and I gave yesterday at the openSUSE conference.

The slides we used are also available.

What is the Software Portal?

Software Portal is a free-software web-application that runs as a service on on opensuse-community.org. It provides both a web-ui and an API. It is possible you are already using the software portal if you use the webpin search page or the command line webpin programme written by Pascal. For the last few months this has been backing on to the software portal.

The software portal indexes package repositories, both those discoverable with the openSUSE build service API and those users add manually through the web interface. It also allows users to contribute additional metadata such as ratings, comments, tags, and screenshots. These two sources of information are both associated against “applications”. The web user-interface is primarily a way of finding, installing, and adding information about applications.

Applications

So, what is an application? We define applications within software portal to be a higher level concept than packages. Applications are what users perceive software as – A product they are looking for and may wish to install to perform a task. Packages are the mechanism of how applications are distributed to users. Packages are platform-specific, they require different packages for different operating systems, and different architectures. The split between packages is also defined by pragmatic issues related to distribution more than conceptual boundaries. Some applications may require multiple packages installed in order to use. Conversely some packages contain more than one application. For example the application GIMP on openSUSE on a 32bit platform might consist of the main gimp package, a gimp-banding package, and some core plugins in another package. Another opposite example is the kdenetwork3 package that contains multiple chat applications.

The web-ui provides application-level search, tag-browsing, and installation.

In the software portal we strive to map packages to the appropriate applications. There are three mechanisms we currently use to do this:

  1. A set of rules stored in the database that map common package naming conventions to application names.
  2. For example: currently kde4 applications tend to have the packagename kde4-packagename. Many non-desktop specific packages start with the application name. Rules are assigned priorities so that more-specific rules can override general rules.

  3. The presence of .desktop files in packages.
  4. User-defined mappings.
  5. Through the web-ui, users can define additional applications and edit existing applications. While editing, users can define rules for which source-packages should be mapped to which applications during repository indexing.

One of the main aims of abstracting away from packages to applications is that we want to allow users to just click install at an application level, without having to know what their hardware/software platform is or choose which package vendor to use. To this end we need some way to choose a preferred vendor for each application in the case where there are multiple vendors of that application. We do this by

  1. Associating a priority with repositories based upon their support status and stable/experimental status.
  2. So the highest priority would be the update and static distribution repositories, and the lowest would be random home: repositories in the openSUSE build service.

  3. Allowing power-users to customise the preferred vendor and selection of binary packages to install by default for an application. Via the web-ui.

The operating system agnosticism is provided by one-click-install and the system package management can choose the appropriate architecture packages.

Why is it needed?

openSUSE embraces third party vendors of packages. Which means that there’s a low barrier to entry and anyone can start packaging and publishing software to users. However, this freedom leads to a confusing situation for users.

Many repositories

(Software Portal is currently indexing around 4000 and this is not exhaustive.)

Package duplication.

Some applications such as amarok has been packaged dozens of times for openSUSE by different people for various reasons. In this case how does a user choose which vendor to choose? We also encourage duplication by not making it easy to find existing packages.

Lack of user-feedback

One packager may package dozens of packages but may not extensively use many of them. We’re not currently good at collecting feedback of package and software quality from users. It’s also not clear for users where to file bugs for the software they’ve just installed.

Confusing concepts

It should not be necessary for users to understand concepts such as packages and package repositories just to install software. It’s also unnecessary for users to know what operating system (version) they’re running or on what hardware.

Insufficient data

We currently rely heavily upon a few package maintainers to source and supply metadata for packages. Packages come with descriptions and summaries but lack more rich metadata such as screenshots. There are many more end users than packagers so it makes sense to let them contribute this information.

Implementation Platform

Software portal project is implemented using Java 6, making heavy use of Spring Framework, Apache Wicket, Jersey, Maven, and MySQL.

Future Plans

We are currently working on finalising a “1.0” release, fixing some of the major bugs, performance issues, and polishing the user interface to get to the point that it is safe for end users to start using and we can publicise it. We need help right now in testing and finding bugs.

The biggest performance issues I am currently working on are:

  • Search of all 20+ million unique file names every time someone performs a search.
  • Calculation of the best binary packages to install for applications with dozens of different vendors.

Challenges arise from the frequency of change of the package repositories (thousands of packages may change every day). Index formats are a balance between query performance and update performance.

After first release one of the biggest issues we want to resolve is that people cannot use their existing login that they use for all other openSUSE services to log into the software portal. Due to Novell control of the authentication system. We have a dependency on Novell here, but hope to implement openID support and that Novell will be able to implement an openID provider.

Further ahead we would like to work on things like:

A recommendation engine

We could do things like recommend you plugins for software you have installed.

User-specific search.

When we know information about the user, their preferences, and what operating system they have installed we can tailor search results to match them, prioritise applications that are available for their platform, and give higher priority to applications for their preferred desktop.

Translation

The web-ui is already translatable. Application descriptions are stored in a translatable manner but the source of this information (package repositories) generally does not have translations. We could potentially build an interface to allow people to translate the content itself if there were a demand for it.

Use Build-Service remoting or Hermes notification framework

Move towards a push model where the software portal is notified of new software, rather than the current pull system where we have to poll package repositories to see if they’re changed, then compute the differences from the previous version if they have.

Integration with bugzilla

Report bugs directly from software portal.

Integration with upstream

Perhaps via the DOAP (Description of a Project) format. We could both consume DOAP files provided by upstream projects, and expose DOAP for software we have indexed.

The Master Plan

Our original aim was to supplement the web-service with a rich desktop client that utilises the information indexed by the software portal and combines that with information available to it as an application running on the local system, such as the currently installed packages, and the repositories the user subscribes to.

Perhaps a YaST module? We already have a package-search yast module that does use the software-portal API to search for packages, but much more powerful things are possible. Unfortunately we do not currently have the time to work on a desktop client on top of the web app itself so if anyone is interested in working on this side of things please get in touch.

Demo

We gave a demonstration of some of the features mentioned above during our talk. You can play with the current version online. Though please be aware there are lots of known bugs and performance issues, it’s not ready for end-users yet.

That said, please do play with it, and let us know what’s broken by filing bugs under the openSUSE.org->Software Portal bug component.

If you want to try out more of the administrator features such as adding repositories, adding applications, customising package to application mapping etc then create an account and drop me an email so I can grant you permissions.

How can you help?

  • Start testing it
  • File bug reports
  • Help create and organise content
  • Developers who know Java and want to contribute code would be welcome. We would be happy to help people get started with the codebase.
  • Designers can help us improve the look and feel and interaction. Some of the processes such as mapping packages to applications are somewhat klunky. It would be good to get some suggestions on how to make this functionality easier to use.

Posted by & filed under Holiday.

I got back this week from holiday cycling and walking in Switzerland. I cycled down from Basel in the north via Bern and Interlaken to Fiescheralp. Then back via Luzern and Zürich.

Fiescheralp is next to the beautiful Aletsch Glacier

Aletsch Glacier

I stayed in a chalet here for a few days with family and did some climbing and walking


Eggishorn to Bettmerhorn

Eggishorn to Bettmerhorn

The highlight of the trip was cycling through the mountains. Especially Grimselpass.

 


GrimselPass from Gletch

GrimselPass From Gletch


GrimselPass Towards Meiringen

GrimselPass Towards Meiringen


Brienz

Brienz


Brunigpass

Brunigpass

Posted by & filed under Java.

Here’s a sillier one from last night…

People often complain about not being able to return multiple values from a method in Java. I can’t see a good reason for wanting to do this, but some do.

The example I was given was wanting to do:

int foo, bar, baz; list(foo, bar, bar) = func(ponies);

Where func(ponies) returns 3 values which are then assigned to the local variables foo, bar, and baz.

There are plenty of sensible ways of achieving this, but what if we’re trying to get as close as possible to the above method? We can only return one thing from a method, but that could be a chain. We also can’t modify the local foo,bar,baz from inside the list method, but we could if we wrap them in references.

If we abuse the fact that currency characters are valid identifiers in Java, for the ultimate in unreadability; We can use $(x,y) to be a pair of x and y, $$ to terminate a list, and £(x) to either reference or dereference x.

Then we can do something like this ¬_¬

/** OUTPUT
Foo = 1
Bar = 2
Bingo = Badgers
Baz = 3
**/
 
package scratchpad;
 
import static scratchpad.$.$;
import static scratchpad.$$.$$;
import static scratchpad.$.expand;
import static scratchpad.£.£;
 
public class MultipleReturn
{
	public static void main(String... args)
	{
		new MultipleReturn().demo();
	}
 
	public void demo()
	{
		£<Integer> foo = £(-1), bar = £(-1), baz = £(-1);
		£<String> bingo = £("");
 
		expand(foo()).into(foo,bar,bingo,baz);
 
		System.out.println("Foo = " + £(foo));
		System.out.println("Bar = " + £(bar));
		System.out.println("Bingo = " + £(bingo));
		System.out.println("Baz = " + £(baz));
	}
 
	public $<Integer,$<Integer,$<String,$<Integer,$$>>>> foo()
	{
		return $(1,$(2,$("Badgers",$(3,$$))));
	}
}
 
interface Tuple {}
 
class $$ implements Tuple
{
	private $$() { }
	public static $$ $$ = new $$();
}
 
class $<T,U extends Tuple> implements Tuple
{
	public T _1;
	public U _2;
 
	protected $(T t,U u)
	{
		this._1 = t;
		this._2 = u;
	}
 
	public static <T> $<T,$$> $(T t)
	{
		return new $<T,$$>(t,$$);
	}
 
	public static <T,U extends Tuple> $<T,U> $(T t, U u)
	{
		return new $<T,U>(t,u);
	}
 
	public interface Expander
	{
		public void into(£... refs);
	}
 
	public static Expander expand(final $<?,? extends Tuple> vals)
	{
		return new Expander()
		{
			public void into(scratchpad.£... refs)
			{
				if (refs.length < 1)
					return;
 
				$ current = vals;
				refs[0]._1 = current._1;
				if (current._2 instanceof $$)
					return;
				int i = 1;
				while (!(current._2 instanceof $$))
				{
					if (i >= refs.length)
						return;
					current = ($)current._2;
					refs[i]._1 = current._1;
					i++;
				}
			}
 
		};
 
	}
}
 
class £<T> extends $<T,$$>
{
	protected £(T t)
	{
		super(t,$$);
	}
 
	public static <T> £<T> £(T t)
	{
		return new £<T>(t);
	}
 
	public static <T> T £(£<T> t)
	{
		return t._1;
	}
}

Posted by & filed under Java, Uncategorized.

We often discuss Java limitations on IRC and try to come up with (sometimes silly) workarounds. Unfortunately after time passes it’s often easy to forget the outcome, and lose code snippets. So I thought I’d start blogging some of them so I don’t lose them, and other people might suggest other ways of doing things that we’ve overlooked.

This particular problem occurs when you want to assign the result of a method that can throw an exception to a final variable. For example:

final Customer c;
try
{
	c = getCustomer(id);
} catch (CustomerNotFoundException e)
{
	c = createNewCustomer();
}

This will fail to compile with “variable c might already have been assigned”. Of course making c not final would solve the problem, but that’s no fun.

If we were not using Exceptions, Java provides a useful ternary operator “?:” that lets us do things like:

final Customer c = customerExists(id) ? getCustomer(id) : createNewCustomer();

Which is nice and clean, but means that getCustomer is going to have to return null or throw a RuntimeException in the case that there is no matching customer, which is undesirable. Also customerExists() may be expensive.

We could also possibly use something along the lines of

final Option<Customer> c = getCustomer();

Both of these alternatives, however, require changing the API you’re consuming, and avoiding Exceptions. It would be nice if there was an equivalent of “?:” for try/catch so that you could assign the result to a final variable. The best I can manage in Java is below, can anyone do better?

import java.lang.reflect.ParameterizedType;
 
abstract class TryCatch<T, U extends Exception>
{
	public T value()
	{
		try
		{
			return Try();
		} catch (Exception e)
		{
			if (getTypeOfU().isAssignableFrom(e.getClass()))
			{
				return Catch();
			} else
			{
				throw new RuntimeException(e);
			}
		}
	}
 
	@SuppressWarnings("unchecked")
	private Class<U> getTypeOfU()
	{
		return (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
	}
 
	public abstract T Try() throws U;
 
	public abstract T Catch();
}
 
//Example
public class Main
{
	private static CustomerRepo repo = new CustomerRepo();
 
	public static void main(String[] args) 
	{
		final Customer c = new TryCatch<Customer, CustomerNotFoundException>()
		{
			public Customer Try() throws CustomerNotFoundException
			{
				System.out.println("in try");
				return repo.getCustomer(1);
			}
 
			public Customer Catch()
			{
				System.out.println("in catch");
				return repo.createCustomer();
			}
		}.value();
	}
 
}
 
class CustomerRepo
{
 
	public Customer getCustomer(int id) throws CustomerNotFoundException
	{
		throw new CustomerNotFoundException();
	}
 
	public Customer createCustomer()
	{
		return new Customer();
	}
}
 
class Customer
{
}
 
class CustomerNotFoundException extends Exception
{
}

In C# we don’t run into the same problem since readonly is much less useful than Java’s final. However, if we wanted to try/catch at the same time we can do a bit better. Here’s an alternative in C#:

using System;
 
public class Example
{
	public static void Main()
	{
		Example t = new Example();
		t.Foo();
	}
 
	public void Foo()
	{
		String result1 = this.Try(() => GetBar(true)).Catch<BarException>(() => "Caught a BarException");
		String result2 = this.Try(() => GetBar(false)).Catch<BarException>(() => "Caught a BarException");
		Console.WriteLine(result1);
		Console.WriteLine(result2);
	}
 
	public String GetBar(bool succeed)
	{
		if (succeed)
			return "Success!";
		else
			throw new BarException();
	}
}
 
public class BarException : Exception {}
 
public class Tryer<TResult>
{
	private readonly Func<TResult> toTry;
	internal Tryer(Func<TResult> toTry)
	{
		this.toTry = toTry;
	}
 
	public TResult Catch<TException>(Func<TResult> whenCaught)
		where TException : Exception
	{
		try
		{
			return toTry();
		} catch (TException)
		{
			return whenCaught();
		}
	}
}
 
namespace System
{
	public static class ProvidesTry
	{
		public static Tryer<TResult> Try<T,TResult>(this T other, Func<TResult> toTry)
		{
			return new Tryer<TResult>(toTry);
		} 
	}
}

Posted by & filed under Uncategorized.

In the last couple of weeks I’ve had some time to work on the software portal project for the first time for a while.

There is now a test snapshot that you can play with if you wish, and help find bugs.

Please do have a play and leave comments here or file a bug report under the Software Portal component.

You’ll need to register to add comments/screenshots/tags etc. Unfortunately since the account you use to login to other openSUSE.org services is controlled by Novell and they provide no openid/saml facility you cannot use the same user account. If you want permissions to edit application details just send me an email (benji at opensuse dot org).

Most of the recent work has been under the hood. Significant speed improvements to the repository indexer and a Webpin compatible webservice that will enable the various clients to continue to work against software portal as they did with webpin until they can be ported to the new api.

There are also some visible changes. The homepage now displays recent comments & top rated applications, and includes the tag cloud.

Software Portal Home Page

Users can now choose a specific vendor for an application for their distribution, as well as installing from the preferred vendor.

Editors can now customise which source packages map to which application.

and which binary packages should be installed by default when a user clicks install.

Posted by & filed under Uncategorized.

This morning we had an unusual amount of snow here, more than we’ve had in my lifetime. Most public transport was suspended and the people attempting to drive were quickly getting stuck and sliding all over the place.

Motorists were making the classic motorist error. They’ve got too much power. I’ve got half a horsepower on this – you don’t want any more than that on snow.

Even Boris Johnson mayor of London apparently cycled to work today.

Also see this comic.

Posted by & filed under openSUSE.

There is a new mailing list for operators on the openSUSE IRC channels – ircops at opensuse dot org.

If you have any queries, suggestions, or complaints regarding an openSUSE IRC channel, please email the list, supplying relevant log excerpts.

For reference, the IRC rules are located on the opensuse-community wiki..

If you are an operator on any openSUSE IRC channel, and have not yet done so – please join the list. The method of subscribing is the same as for any other openSUSE mailing list.

In other IRC related news, the SUSEhelp bot sitting in many openSUSE channels has for some time now supported localisable factoids. Some channels are already using this to provide localised answers to queries such as “SUSEhelp: mp3” and “SUSEhelp: kde4”. If you help in a non-English IRC channel and would find this useful feel free to ask me (benJIman on freenode) how to use it.

Posted by & filed under openSUSE.

Most of the time that I was able to spend actually hacking during hackweek I spent working on the software portal. It was very helpful to be able to discuss issues with Pascal in person.

The software portal project aims to expose the software available for openSUSE and other linux distributions to users as “Applications” rather than “Packages”. The differences between applications and packages are:

  • Applications can be made up of more than one package. e.g. Amarok is split into several packages on openSUSE.
  • There can be several applications in one package. e.g. kdenetwork3 contains both kget and feedbrowser on openSUSE.
  • Applications are not tied to a particular distribution.

The idea is that the user can locate available software by searching or browsing then add screenshots, comments, ratings, tags, translations, etc. The user should also be able to install an application he/she has found without having to know what operating system he/she is running.

Software Portal automates the import of package information from package repositories (rpm-md,deb) and in the future other sources. The import process also maps packages to applications automatically based on the available package metadata a process that can be refined and guided with rules.

Package information is imported from package repositories and mapped to applications. Users can also contribute to applications.

I spent quite some time working on providing a mechanism for users to guide how the repository indexer maps packages to applications, which is quite crucial to allow users to add their own applications and specify which packages make up the application.

I was also very pleased to achieve the goal I set myself before hackweek – to get an instance of the software portal running on a server where people could access it and test it. This has already led to many defects being found and some fixed. I will not publish the URL publicly yet as there are still major problems to fix, we are breaking it fairly regularly, and having lots of people playing with it at this stage would cause problems. Hopefully we can make a test version publicly available before too long.

Some current screenshots:

Software Portal Front Page
Listing applications
Viewing an application

More screenshots are available.

At present there are only two people working on the software portal project. If you are interested in helping either with java development, or with web design, or in any other area, please do join our mailing list, or drop into #opensuse-project on freenode with any questions.

Posted by & filed under openSUSE.

Having now left the University of Warwick, I have lost access to my Warwick hosted blog. So I will now be hosting it myself at http://benjiweber.co.uk/blog.

Last week was hackweek at Novell – during which Novell developers get to work full time on their own personal projects. Novell were kind enough to sponsor several openSUSE community members (including myself) to travel to Nürnberg to meet openSUSE community members (some of whom are employed by Novell) in person and participate in hackweek. It was great that so many people from outside Novell were able to take part this time.

My week was a mixture of some useful discussion about openSUSE (I’m not sure who came up with the name “chatweek”, but Andrew seems to have blogged it first at least.) and work on the software portal project (which I will blog about separately). I think it was productive to discuss some of the weaknesses of the openSUSE project, and what can be done to address them. I hope someone else will publish the outcomes of these discussions as I did not take very good notes.

It was also good to talk to people in person about the projects I am involved in (Software Portal and One Click Install) and give and be given suggestions and requests. I was able to talk to Klass about what we would need from the buildservice hermes messaging framework to allow the buildservice to push information about new packages directly to the software portal. It was also good to talk to Garret who has designed a vastly improved (if challenging to realise) user interface for the One Click Install handler.