» Publishers, Monetize your RSS feeds with FeedShow: More infos (Show/Hide Ads)
At Devoxx last week, Joshua Bloch argued during his talk “The Evolution of Java: Past, Present, and Future” that varargs are only “somewhat useful”. I think he is overlooking some usages, particularly in tests. Here is my case.
A reminder on how varargs work: essentially, they allow the last parameter of a method to be made of zero to many values of the same type.
For example, a method like this:
int max(int... values) {...};
is used by code like that:
int maximum = max(1, 2, 7, 0); int maximum = max(1);
In truth, that form is only moderately useful in production code. It turns out that, on my projects at least, it is not so common for methods to be called with varying number of parameters. And in those cases, it is often acceptable to simply overload a method with more parameters.
There are however at least two cases where varargs shine.
One is in utilities classes. For example, Math.max() only takes exactly two parameters. Which comparisons between 3 or more elements very awkward. If a varargs had been used, Math.max(3, Math.max(1, 4)) would be written Math.max(3, 1, 4). Agreed, that does not happen that often. But it does occasionally.
(I must admit that I do not understand exactly why Math.max() has not been modified to take a varargs nowadays)
However, the biggest benefit, I believe, is in the writing of tests.
Tests tend to contain lots of variations in the values passed to the code under test. And it is critical to keep the clutter to a minimum.
For example, here is how a typical series of tests might look like
@Test
public void should_find_the_longest_name_for_a_single_user() {
List users = new ArrayList();
users.add(new User("eric"));
assertThat(findLongestName(users), is("eric"));
}
@Test
public void should_find_the_longest_name_when_shorted_is_first() {
List users = new ArrayList();
users.add(new User("eric"));
users.add(new User("cecile"));
assertThat(findLongestName(users), is("cecile"));
}
@Test
public void should_find_the_longest_name_when_longest_is_first() {
List users = new ArrayList();
users.add(new User("cecile"));
users.add(new User("eric"));
assertThat(findLongestName(users), is("cecile"));
}
@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
assertThat(findLongestName(new ArrayList()), is("eric"));
}
Of course, they can be made a bit nicer using the varargs in Arrays.asList():
@Test
public void should_find_the_longest_name_for_a_single_user() {
assertThat(findLongestName(asList(new User("eric"))), is("eric"));
}
@Test
public void should_find_the_longest_name_when_shorted_is_first() {
assertThat(findLongestName(asList(new User("eric"),
new User("cecile"))), is("cecile"));
}
@Test
public void should_find_the_longest_name_when_longest_is_first() {
assertThat(findLongestName(asList(new User("cecile"),
new User("eric"))), is("cecile"));
}
@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
findLongestName(Arrays.asList());
}
However, the real goodness comes when writing our own builder method:
private static List users(String... names) {
List users = new ArrayList();
for (String name : names) {
users.add(new User(name));
}
return users;
}
(side note: I like this type of methods to be private -so that I get notified when they are not used anymore- and static -mostly because they look nicer in italic, which makes it clearer that they are not part of the code being tested-)
which allow our tests to become:
@Test
public void should_find_the_longest_name_for_a_single_user() {
assertThat(findLongestName(users("eric")), is("eric"));
}
@Test
public void should_find_the_longest_name_when_shorted_is_first() {
assertThat(findLongestName(users("eric", "cecile")), is("cecile"));
}
@Test
public void should_find_the_longest_name_when_longest_is_first() {
assertThat(findLongestName(users("cecile", "eric")), is("cecile"));
}
@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
findLongestName(users());
}
Tests become a lot shorter, consistent, and easier to read. In fact, at this point, it is worth grouping the tests (at least those that do not throw an exception) into a single one:
@Test
public void should_find_the_longest_name_for_a_single_user() {
assertThat(findLongestName(users("eric")), is("eric"));
assertThat(findLongestName(users("eric", "cecile")), is("cecile"));
assertThat(findLongestName(users("cecile", "eric")), is("cecile"));
}
@Test(expected = RuntimeException.class)
public void should_fail_when_search_for_the_longest_name_with_no_users() {
findLongestName(users());
}
Final note: I prefer not to group those builder methods into a general utilities class — I tend to duplicate them in each test class. One reason is that I prefer keeping much of the test code close together. Another is that I tend to change the name of the method from test class to test class to make the code more fluent. Also, I think that DRY principles are not as important in the tests as they are in the production code.
Play Framework has a Guice module. Unfortunately, its use is fairly limited compared to what Guice can do. In this post, I describe how it is configured on my current personal project.
In general, when I test classes that have dependencies, my favorite approach is to pass those dependencies to the constructor:
public class MyService {
private final MyDependency myDependency;
public MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
Which makes creating test harnesses with Mockito reasonably easy:
public class MyServiceTest extends UnitTest {
@Test
public void shouldComputeAResult() {
MyDependency mockMyDependency = mock(MyDependency.class);
when(mockMyDependency.findSomeValue()).thenReturn("some value");
MyService service = new MyService(mockMyDependency);
assertThat(service.computeSomething(), equalTo("result"));
}
}
However, it appears that Guice, at least under version 1.1.1 of its Play module, can only be used with Play to inject into static members:
@InjectSupport
public class MyService {
@Inject
static MyDependency myDependency;
// no constructor (a default constructor will be
// generated automatically by Play Framework)
}
The @InjectSupport is what makes the Guice module detect that the class requires dependencies (a class that extends either com.google.inject.AbstractModule or play.modules.guice.GuiceSupport must also be present in the classpath).
This makes it hard to instrument classes under test, as the value for a mock instance of MyDependency is shared amongst all tests that run currently.
public class MyServiceTest extends UnitTest {
@Test
public void shouldComputeAResult() {
MyDependency mockMyDependency = mock(MyDependency.class);
when(mockMyDependency.findSomeValue()).thenReturn("some value");
MyService service = new MyService();
// static values are brittle;
// they should not be touched in unit tests
service.myDependency = mockMyDependency; // yuck!
assertThat(service.computeSomething(), equalTo("result"));
}
}
Many subtle problems may occur when injecting dependencies in this way. For example, all instances of MyServices used concurrently will point to the same dependencies instances, so it is possible that tests will behave in inconsistent ways. Also, it makes it harder to default to sensible (null) dependencies, since tests running in the same JVM will by default use dependencies set by the first test.
The solution I’m using is to add providers in your Guice module:
public class GuicyModule extends AbstractModule {
@Override
public void configure() {
// no code needed
}
@Provides
public MyService getMyService(MyDependency myDependency) {
return new MyService(myDependency);
}
}
In this way, you are making sure that the dependencies are passed as you wish. Also, it will not be necessary to add any sort of Guice-related code in my services:
public class MyService {
private final MyDependency myDependency;
public MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
The downside is that you do have a few more lines to maintain, which is never fun. Better than the alternative, though.
My thanks to David Gageot who told me about providers in Guice.
Here is a typical example of a test method
@Test
public void should_search_by_path() {
Searcher searcher = new Searcher();
Path location = new Path("somewhere");
String data = "data";
searcher.putAt(data, location);
assertThat(searcher.findAt(location), is(data));
}
It seems that many developers consider this good code. I don’t. My main gripe here is that the presence of variables does not add much useful information. Instead, they make the code too verbose.
A first rewrite would produce something like that:
@Test
public void should_search_by_path() {
Searcher searcher = new Searcher();
searcher.putAt("data", new Path("somewhere"));
assertThat(searcher.findAt(new Path("somewhere")), is("data"));
}
Shorter, which is good. I also like the fact it makes clear that it is the value of the parameters that matter, and not their pointers.
We can go further by leveraging local static methods.
@Test
public void should_search_by_path() {
Searcher searcher = new Searcher();
searcher.putAt("data", path("somewhere"));
assertThat(searcher.findAt(path("somewhere")), is("data"));
}
private static Path path(String path) {
return new Path(path);
}
(a static method is not mandatory, but I think it makes its intention of being a utility method clearer)
And, finally, if this test class is dedicated to the Searcher class, I would probably make the searcher variable a field, as it would be used in most test methods.
private Searcher searcher = new Searcher();
@Test
public void should_search_by_path() {
searcher.putAt("data", path("somewhere"));
assertThat(searcher.findAt(path("somewhere")), is("data"));
}
private static Path path(String path) {
return new Path(path);
}
This is more lines than the original example. However, the searcher variable is on top of the class and the path() method is at the bottom, far out of the way of the test methods. Indeed, I would often have several such static methods at the bottom of my classes (and, rarely, one or two more instance variables at the top). I even tend to do so when I have such a single method using them.
Beside the fact that the method has fewer lines, I also like that it makes for more fluent code. It almost reads like a story “when the searcher is being put a String of value ‘data’ at the path of name ‘somewhere’, then it should be able to assert that the String found at the path of name ‘somewhere’ has value ‘data’”.
Another example, so that my position is clear:
@Test
public void should_find_by_address() {
String aValidAddress = "10 Downing Street";
Person aPerson = new Person("David Cameron");
contacts.put(aValidAddress, aPerson);
assertThat(contacts.get(aValidAddress), is(aPerson));
}
I’d very much rewrite this as follows:
@Test
public void should_be_able_to_find__a_person_when_storing_with_a_valid_address() {
contacts.put("10 Downing Street", person("David Cameron"));
assertThat(contacts.get("10 Downing Street"), is(person("David Cameron")));
}
Any thoughts?
I’m returning from CITCON London 2010. What a great conference (and I’m not just saying that just because I helped organize it)!
In fact, I feel it has been the best CITCON so far. I was a bit afraid of the large crowd (150 people registered, a similar number to Paris last year; I’m not sure how many showed up. 120, maybe?), but it turned out easier than expected to discuss with other participants. Also, and most importantly, there was a feeling of a higher level of experience than usual. Few talks about the basics of tests or Continuous Integration (and no “what’s the best CI server” session at all, thank God). Instead, it was “Advanced TDD”, “Share Pair Programming experience”, “Mobile Testing”, etc. All good stuff and, as usual, I just couldn’t attend all the sessions I wanted.
As a side note, there were also less talk related to competing programming environment. Only a few people introduced themselves as working in Java, .NET or another programming environment. I can think of a couple of reasons:
- Java is so overwhelmingly everywhere that there is just no point bragging about it anymore
- the .NET crowd (and possibly others) have given up on trying to make their work environments better. I didn’t attend the one “CI in .NET” session (there were no Java-specific sessions), but I was told that it was a slightly discouraging share of thoughts such as “it wasn’t my choice, but I want to make the best of it”, “I like .NET but the environment is just lacking” or “seriously, what do *you* guys do to implement CI in .NET?”.
In some ways, the Java world is also calcifying around some tools such as Maven (some contenders are barking at the door, though). But the feeling is that, by and large, things get done is a fairly productive way. (I’ll admit that I can be overly optimistic about this, considering that I am in a Java shop (Algodeal) where the incredible freedom helps us being particularly efficient)
Now, I was surprized that few mentioned up-and-coming languages such as Scala and Clojure. Are they just being lumped into the Java category? Was CITCON London the wrong area to find experts in them?
Other highlights of the conference include the venue. Thank you ever so much, Wendy, for opening the doors of SkillsMatter. You went out of your way. The location was fantastic, and the venue perfect for us. I especially appreciated the powerful wifi internet access, a rarity even in fancy hotels.
I also appreciated the fact that this area London has large pubs that can host all willing participants after the conference is over (this was frustrating in Paris last year, where bars can be small and especially crowded on Friday evenings). Too bad music can be so loud there, making conversations sometimes difficult.
A few things to remember from CITCON London:
- Narrative, by the good folks at youDevise is a new test framework/helper. They present it as a BDD framework, but I rather see it as a way to enforce readability in your test classes. I like the modest, KISS, approach too. Plus, it seems easy to extend. I wrote a quick sample here.
- Continuous Deployment was a big topic, as expected. There were few mentions on specific tools or techniques, but the feeling was that the practice was getting mainstream.
- Apparently, a big framework in the JavaScript TDD space is QUnit. The session had left a lot to be desired (I left mid-way, but I’m told the end was good), but it at least drove the point that TDD was getting common in JS.
- Again, I left with the feeling that we are not doing enough to generalize Pair Programming at Algodeal.
For CITCON 2011, there were many votes for Berlin. Sounds like a good destination. Again, it will depend on whether we can get a cheap enough venue there.
Have you noticed that few Agile luminaries earn a living from writing and selling software? Many do write code as consultants. Other are respected authors of non-commercial open-source development tools. Some do work for software companies such as RallyDev or ThoughtWork Studios, though it seems that most visible presenters coming from there are consultants or at least business-facing types. But almost none actually make money directly by doing what they teach others to do.
James Shore mentioned working on his own startup with Arlo Belshee and Kim Wallmark, but that was more than a year ago and we haven’t heard much since. Ward Cunningham is CTO of a website, which come reasonably close to being a software house. In fact, Kent Beck is the only example I know of someone who actually tries to make a living out of writing and selling software (with mixed results). Tellingly, Ward and Kent are not very visible on the conference circuit anymore (though they are certainly interviewed regularly).
This lead to an interesting discussion yesterday on Twitter with Deborah Hartmann Preuss, Alexandru Bolboaca, Willem van den Ende, Brian Marick, and Jeffrey Fredrick (see transcript at the end of this post).
I think most Agile personalities have become addicted to the relatively easy money of consulting. Why would they risk get a software product out in the highly competitive software market? It is so easy to just found your own consultancy. It is probably related to age, too, as you do need to have lots of spare energy for late night coding (Paul Graham once wrote that the ideal age to start a startup was between 23 and 38).
In fact, as I argue below, I do not believe that “being agile” is viewed as a desirable trait for a startup and I’m sure it might even deter some of the most likely candidates to create one, as it is now viewed as a process for medium to large companies. What is viewed as needed is raw hacking powers, even if that means making things hang together with duct tape. Agile techniques might be preferable, but their ROI will become mostly apparent after two years, an eternity for a startup.
So, Agile luminaries are not starting software development ventures, and the founders of successful startups that use Agile techniques probably do not have the time or the will to tell the rest of the Agile world how they did it.
This is regrettable. I wish there was more cross-pollination, like 37signals has done with Getting Real. Where are the others?
Transcript of our conversation on twitter:
- elefevre Wondering why so few Agile luminaries are into the commercial software business.
- alexboly @elefevre I think Kent Beck said it best: “As a business man, I’m a very good software developer” :)
- elefevre @alexboly interestingly, he seems to be the only one actually trying to make a living selling software (seems hard)
- mostalive @elefevre because they are too busy consulting or marketing? want to publish own commercial product? work happens in odd hours -> slow
- alexboly @elefevre As a programmer I would rather live in Castalia (http://goo.gl/AzQn) As a business man, I need to live in the real world.
- elefevre @mostalive sure they do consulting. But if agile dev is really better, then products they would make ought to be better too. => Profit?
- DeborahH @elefevre I guess Mike Beedle (PatientKeeper) is an exception, then. Co-author of first Scrum book.
- mostalive @elefevre Profit yes, and, consulting & product development don’t really mix, as the business drivers point in opposite directions.
- DeborahH @elefevre ken Schwaber came from sw dev company iirc, but perhaps you can’t change the world AND refactor your codebase at once. Or?
- mostalive @elefevre agile tool vendors may be the exception, however their consultants can not recommend the best tool for the context (post-its ;))
- mostalive @elefevre and if a product is successful, it generates so much revenue, that consulting revenue is irrelevant.
- DeborahH @elefevre … Then they wouldn’t be luminaries in the sense of “visible as a teacher, writer” imo: these things are jobs in themselves.
- DeborahH @elefevre though I respect more those who take time for practice as well, it grounds one’s teaching. But usually on s/one else’ project?
- elefevre @DeborahH yes. Maybe if you’re busy developing (not consulting), then you have less time & inclination helping others.
- elefevre @mostalive also, I suspect majority of (or the best known) consultants from Agile Tool vendors are more business-facing type than developers
- DeborahH @elefevre until recently, Jeff was CTO @ PatientKeeper.http://bit.ly/ctQJRb Once yr own house is in order, you gotta get a new challenge?
- elefevre @DeborahH @mostalive @alexboly I think my point could be: are successful/rich developers/hackers really applying Agile techniques?
- elefevre @DeborahH @mostalive @alexboly or is Agile orthogonal unnecessary for a successful startup? (maybe I’ve been reading too much Paul Graham)
- elefevre @DeborahH @mostalive @alexboly I think that, in a startup, techniques might be “agile”, but do not need to be identified as such
- elefevre @DeborahH @mostalive @alexboly while, in a bigger biz, it helps that people can relate to a consensual definition of development process
- elefevre @DeborahH @mostalive @alexboly in our startup, we are 3 former Agile consultants (out of 5 devs), but we take care not to say we’re agile
- alexboly @elefevre @DeborahH @mostalive Agile doesn’t exist. Agile tools exist. Do you need agile tools for a succesful startup? I think you do.
- alexboly @DeborahH @mostalive @elefevre Do you need to call the agile tools “agile” in order do succeed? Of course not. :)
- DeborahH @elefevre “Agile” is a means to an end. You cannot afford to primarily “be agile”n you must be primarily busines owners!
- alexboly @DeborahH @mostalive @elefevre The point was never to “be agile”, it always was to build software in a way that increases the success rate.
- elefevre @DeborahH @mostalive @alexboly there might even be a stigma to calling things “agile” in a startup. Not hip (anymore)
- DeborahH @elefevre surely “hipness” is not more important than profitability? Like “agility”, “hipness” sounds like a red herring, to me.
- alexboly @elefevre @DeborahH @mostalive I’d like to see that people don’t talk about agile anymore but do the right thing. Human nature loves labels.
- elefevre @DeborahH certainly is a red herring. But I do think it’s important for startups, often packed with smug hackers. #overgeneralization
- marick @elefevre I don’t have any good product ideas. I don’t have skills to do it all & it’s a big step from 1-person company to N-person company.
- elefevre @marick my thinking is that the mobile market is more tolerant of seemingly mediocre ideas than the desktop/enterprise space
- DeborahH @elefevre @marick perhaps because in mobile market short release cycles allow products to “grow up” in public?
- elefevre @DeborahH @marick I think it’s because there are fewer high-standard apps, so users tolerate mediocre ones. eg. quizzes, website wrappers
- Jtf @elefevre I think people who are successful consultants are addicted to fast feedback from helping. Products are delayed gratification.
For my Remote Pair Programming session with Alexandru Bolboaca, I wanted to work on our actual code, not toy programs. It was hard finding a technical solution to allow this (despite the many suggestions I received on Twitter; the biggest issue is sharing the entire development environment), but I finally settled on LogMeIn. LogMeIn basically lets you create an ad hoc VPN with them serving as a middle man. The great thing with it is that all the configuration is done on the client machines. There is nothing to change on firewalls (especially important for the other people that you are working with).
LogMeIn has a download that seems very simple to use… as long as you are under Windows. It also has a Mac OS X version and a Linux version, but they hardly come with any documentation. What’s worse, it is hard to find additional information on the support site.
So, for your eyes only, here are some instructions on how to get LogMeIn to run under Linux. (this applies only to the client machine; setting up the network can be done entirely on LogMeIn’s website)
My configuration: Ubuntu 10.04 Lucid Lynx 64 bits with LogMeIn Hamachi 2.0.0.11-1. (Hamachi is a protocal that creates a VPN that goes through their servers)
- First, create a login on http://www.logmein.com/
- Install their Linux client. Just double-clicking it after download should be enough.
- Configure the client in the command line :
- hamachi login
- hamachi attach <your email on logmein>
- hamachi set-nick <a human-readable user name for you; any should do>
- hamachi do-join <id of the VPN previously created on LogMeIn>
- The password is the one specified by the domain creator. This is not the password for your login.
- Wait for domain creator to approve your machine on the virtual network (you might need to send an email to remind her of that)
Done! From that point, you can use VNC or anything else to connect to a remote computer. Use something like ifconfig on the remote computer and use the IP address under the ham0 entry (ham is for Hamachi, obviously). The IP address has an unusual value such as 5.18.76.84.
I’ve talked recently with the CTO of a small-but-successful company, trying to explain how we do software development. I realized that many things are difficult for them to copy from us, mostly because we have a different approach to implementing features (in particular, we try to limit GUI-intensive features, while they have a very rich Javascript interface).
One thing, however, that I believe they can adopt without changing their code is the source code management tool Git. However, they had already considered it (they are currently using Subversion) and figured that it does not solve problems they already have.
I’d agree that Git doesn’t fix obvious problems. However, Git is powerful enough (once the complexity is mastered) to make lots of little things easier. Here is what it does for us.
- It makes merges much less painful, even when no branched are involved, for example when two developers modify the same file. With Git, it is possible for someone to move a file to a different package, while another developer renames it simultaneously. And removes half of the content. Git (almost always) magically resolves the conflicts and maintains the history of the file, all without any special command. No need for “svn move” or “svn rename”. And no code freeze when someone renames a package, so we tend to do refactorings more often.
- When a regression is detected late, Git can help find the faulty commit. That can be done automatically if you can write a script that detect the bug (for example if there was no automated failing test in the faulty commit), but that’s not necessary. For example, it helped us find at what point “mvn eclipse:eclipse” stopped working (it was because we enforced maven 3 usage in the POMs). The command that does this is “git bisect.”
- The entire source repository is on the developer’s machine (this is configurable). This gives us free backup across all our machines, and instantaneous access to logs. In the same spirit, I frequently use git blame to find out who wrote what on a particular piece of code, so that I can ask them for clarifications.
- There are no access rights to manage (though users’ accounts on the server machine, if you choose to have one, as many do, are still necessary).
- Suppose you’re working on something, but must suspend that work temporarily to fix a bug. In SVN, either you cancel all your changes, or re-download the entire code base in a separate directory. Or count on the fact that changes won’t overlap (maybe). In Git, you can temporarily put your changes on the side, do your fix, then retrieve them back again. This is known as “git stash“.
- If by any chance it is necessary to roll a fix in production, it is possible to hand-pick changes with “git cherry-pick“. My colleagues often use this.
- Generally speaking, there is much less mangling of the code base (none of the infamous “svn cleanup“)
None of these features are absolutely necessary. But all together, they make life easier for us. It even let do serverless CI.
Sebastien Douche has said during a presentation at a recent Paris JUG evening that DVCS are the one thing that all developers should learn in 2010. I think he’s right.
Robert “Uncle Bob” Martin has just blogged on the differences in TDD styles using Clojure, as compared to more traditional languages such as Java. Though I am a Clojure-newbie, I mostly disagree with his conclusions.
His main point is that, because Clojure is a functional language, functions have no side-effects and therefore can be used directly in the tests.
For example, the production code
(defn update-all [os] (update os))
would be tested with something like
(testing "update-all"
(let [
o1 (make-object ...)
o2 (make-object ...)
os [o1 o2]
us (update-all os)
]
(is (= (nth us 0) (update o1)))
(is (= (nth us 1) (update o2)))
)
)
There is no reason to believe that the (update) function is side-effect-free
Changing internal values is only one way of creating side-effect. I admit that Clojure encourages coders to write code that does not change variables (if I got it right, it is definitely possible to do so, but with some additional work). However, that effect only stops at the boundaries of the language. At some point, it might access the file system or a database. Clearly, the state might change there.
Correct implementation of the (update-all) function depends on the correct implementation of (update)
Bob Martin says: ”this test simply checks that the appropriate three functions are getting called on each element of the list”.
Suppose that the (update) function does not do anything or maybe does something that does not return a value, such as printing out to the console. Then, calling it will have the same effect as not calling it at all. The test above will pass even if the (update-all) function does not provide any implementation at all. When, later, the bug is found, it will be harder to fix.
The test could be clearer (with a more powerful test framework)
One of my biggest concerns is that the test looks a lot like the code itself. Looks like duplication of information to the reader.
If there was a mock framework for Clojure, I would expect to see something like
(testing "update-all"
(let [
pre-conditions (
(should-return (update 1) 1.5)
(should-return (update 3) 3.0) )
o1 (make-object 1)
o2 (make-object 3)
os [o1 o2]
us (update-all os)
]
(is (= (nth us 0) (1.5)
(is (= (nth us 1) (3.0)
)
)
Bob Martin is right to conclude that “Clojure without TDD is just as much a nightmare as Java or Ruby without TDD.”
But he should also make it clearer that it is lacking a mock framework (he does point to Brian Marick’s work on this).
It should be noted that it is possible to get a similar implementation style in Java as in Clojure, though it is significant work. In fact, that’s often how we use it here at Algodeal. That means mostly relying on immutable objects and state-less methods. Immutable collections from Google Collections help a lot, too. Still, we like to use mocks in our tests (too much for some, probably).
In the end, Uncle Bob’s post is another aspect of the (almost) age-old debate described by Martin Fowler: classicists vs. mockists. If you haven’t already, read Fowler’s article, it’s worth it.
We have started to use AppArmor as a way to strengthen the security on our platform. A reasonably good tool for which you can find rather straightforward tutorials.
Portrait of a young woman dressed as Boadecia or Mother England by Powerhouse Museum
AppArmor is a tool that can explicitly allow or deny actions by some applications. Those actions are recorded in a profile. Many profiles are already available, for such tools as Firefox, but sometimes it is necessary to create your own. This was the case for us: we wanted to make sure using Mono was not too much of a risky endeavor (we run investment strategies written by our users in .NET).
Creating a profile for AppArmor can be done is a couple of ways. One is to run the application you want to lock down in record (“complain”) mode, check the logs produced by AppArmor and select the corresponding rules you want to enforce. The tool that checks the logs and produces the profile containing the rules is logprof, generally run with the aa-logprof command.
Last morning, when I merrily tried to run logprof, it prompted me at the end of the process to create a user.
Create New User?
(Y)es / [(N)o]
I didn’t know what “Creating a user” meant here. And, at this point, there is nothing useful to do. Whether you reply Yes or No, you are always prompted for a username and password, then asked whether you want to save the configuration, which inevitably ends with a Login failure, and you are back to the Create a User question (Ctrl-C to get out). Here is the whole trace:
Updating AppArmor profiles in /etc/apparmor.d.
Create New User?
(Y)es / [(N)o]
Username: noideawhatmyusernameis
Password: noideawhatthepasswordis
Save Configuration?
[(Y)es] / (N)o
Login failure
Please check username and password and try again.
RPC::XML::Client::send_request: HTTP server error: Not Found
Create New User?
It took me a while to understand what was going on, so I’m writing this post in the hope that it will help someone (possibly me, in the not-so-distant future).
The user here refers in fact to a user on the central, public repository for AppArmor profiles. You do not normally need a login for downloading profiles, but logins are required to upload them. Now, I obviously do not want to upload my profiles, so what’s the deal?
In all likelihood, I must have enabled the upload of profiles at some point, possibly when I was trying to figure out what AppArmor was doing. There is a way to undo that, but very little documentation and few discussions about it on the internet.
I finally found it on Novell’s site.
In the end, here is what you need to do:
- move to /etc/apparmor (and not /etc/apparmor.d, which is the directory where the profiles are saved)
- edit repository.conf
- in the [repository] section, replace upload = yes with upload = no
All done!
Footnote: the status of AppArmor is not clear to me. Wikipedia indicated that Novell has fired the original team that developed it and indeed Novell’s site only points to AppArmor v2.1 and earlier. A Google search returns many links to Ubuntu and indeed Karmic Koala comes with v2.3.1 (the latest, AFAIK), but Ubuntu pages do not offer very advanced documentation. Novell does have the best documentation but is strangely not well referenced on Google and the documentation only goes until v2.1, which is not impressive. The official development site is hosted by Novell, but it only mentions v2.3beta and has not seen any release seen mid-2008. Finally, a similar tool, Tomoyo, has been merged into the Linux kernel v2.6.30 since mid-2009. So I think that, once we will have move all our servers to Karmic, we’ll dump AppArmor.














