Eclipse is a maze of twisty build dependencies, all different. As best as I can tell there's the following:
- Build path: this affects compiling and possibly running your project. Split into separate widgets for dependent projects and for libraries/jars.
- Order and export: the same widget handles both the build order and "exporting" stuff to dependent projects, which may or may not actually achieve anything. Certainly the export option doesn't affect what appears in your .WAR.
- Deployment assembly: this doesn't affect your build path, but instead controls what ends up in your .WAR should you generate one. This is also present in non-web projects and mostly works, except not all items are propagated to the web projects (e.g. if A is a web project and B and C are normal Java projects with a dependency A -> B -> C, the dependency from B to C isn't picked up by A. So A needs to explicitly require projects B and C even though it doesn't directly use C). Oh, and if you have a web project then I lied and any direct dependencies here are actually included in your build path.
- Project references: the Java toolchain completely ignores this and makes no attempt to keep it synchronised with the other dependencies. Which is a shame, as the Eclipse interface uses this to work out which projects depend on other projects.
It all seems massively overcomplicated when what's really needed is a way to list libraries/projects and mark them as either "build-time", "run-time", or both. Then when compiling it uses just the items listed as build-time, and when running or creating .WARs it uses everything listed as run-time and includes transitive run-time dependencies as well.
Every puzzle has an answer
Nov. 11th, 2011 11:03 pmBonus extra post, to avoid talismancer mocking the lack of content today. That said, he hasn't updated yet today...
There's been a few guesses at monday's Java puzzler. talismancer is right in that the code won't work.
olego and
pteppic took this a bit further, and correctly guessed that it'd throw an exception of some sort. Specifically, you get the following output:
Adding java.lang.Object@addbf1 at index 0 Adding java.lang.Object@42e816 at index 1 Adding java.lang.Object@9304b1 at index 2 Adding java.lang.Object@190d11 at index 3 Adding java.lang.Object@a90653 at index 4 Adding java.lang.Object@de6ced at index 5 Adding java.lang.Object@c17164 at index 6 Adding java.lang.Object@1fb8ee3 at index 7 Adding java.lang.Object@61de33 at index 8 Adding java.lang.Object@14318bb at index 9 Removing java.lang.Object@addbf1 Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(Unknown Source) at java.util.AbstractList$Itr.next(Unknown Source) at Foo.emptyBarList(Foo.java:15) at Foo.main(Foo.java:24)
Line 15 is for (Object o : barList) {
At first this seems like an odd place to get an exception - there's no obvious code in this line. But it makes sense when you realise that the Java compiler rewrites that line into something like:
Iterator<Object> it = barList.iterator(); while (it.hasNext) {Object o = it.next();
Most of the standard Java collections explicitly state that structural modifications to the collection will generally cause any iterators to throw a ConcurrentModificationException. Fortuantly there is a way round this: you're permitted to modify the list via methods on the iterator object (like Iterator.remove()). The concurrent collections also won't throw an exception, as the iterators in those are designed to be fully thread-safe and cope with (but don't necessarily show the result of) structual changes.
The fix in this case is easy enough: rewrite the loop to explicitly create an Iterator object and call the remove() method on that. And then add a unit test so that the method is actually tested, rather than being left as a hidden bug until someone does a completely unrelated change and actually adds code to call it!
A true gentleman leaves no puzzle unsolved
Nov. 7th, 2011 10:32 pmOkay, so it turns out that talismancer's post yesterday was filler. The moral balance has swung back!
As tempting as it is to fulfill the entirety of NaBloPoMo with us commenting on each other's posts, I feel like I should actually write something. So in the interests of increasing audience participation, I shall set a Java puzzler.
The puzzler consists of the following class:
import java.util.ArrayList; public class Foo { private ArrayList<Object> barList = new ArrayList<Object>(); public void fillBarList() { for (int i = 0; i < 10; i++) { Object o = new Object(); System.out.println("Adding " + o + " at index " + i); barList.add(o); } } public void emptyBarList() { for (Object o : barList) { System.out.println("Removing " + o); barList.remove(o); } } public static void main(String[] args) { Foo f = new Foo(); f.fillBarList(); f.emptyBarList(); } }
Without running it (because that would be cheating and you will be mocked for it), what do you think will happen when Foo.main() is called?
On garbage collection and memory squirrels
Aug. 5th, 2011 08:33 pmAs is becoming traditional for ramblings on train journeys, here's another technology fail. First, a bit of theory.
Recently I've been doing a lot of programming in Java. Java, unlike C, is a garbage collected language. What this means is that you don't have to keep track of memory usage - instead, a garbage collector runs periodically and frees unused objects for you. Garbage collected environments tend to have less predictable performance than non-garbage-collected ones (as you have little if any control over when and how the garbage collector runs), but on the flip side memory leaks and heap corruption tend to be impossible.
Java's particular garbage collection system is a mark-sweep system. This happens in two steps. To begin with, the garbage collector starts with a set of root objects and follows all the references from those, marking everything it finds. At the end of this step it has marked every object that can be reached from somewhere in the code. It then sweeps through all the objects, and anything that's not marked it throws away. The downside to a mark/sweep system is that there's no guarantee about when an object might be freed (so you can't do anything interesting when the object is destroyed), but on the plus side it deals with circular references and there's no way for an object to become lost.
So, on to the technology fail. This particular fail was a memory leak that occurred after a long soak test. Actually, a better term would be a memory squirrel - it's impossible for memory to be leaked and forever lost, but what can happen is that objects are held on for far too long. In this case it was a linked list, which very quickly showed up in a heap dump as holding far too many objects.
What was happening was an interaction between two pieces of code. The first was, about 300 times a second, adding an item to the list. The second was removing items, but had been throttled to remove at most 100 items per second. This was actually rather tricky to find, as the leak only showed up after the soak test had been running for a few days. If you ran a test for a day, then stopped the load, then firstly the list didn't get full enough to become noticeable (the rest of the code was chewing through objects at a crazily fast rate, with the garbage collector running several times a minute), and secondly the second part of the code kept running so the memory used was constantly going down!
Java puzzle
Jan. 27th, 2011 11:23 pmSomething a colleague came up with at work the other day...
Consider the following Java classes:
public class FooBase {}; public class Foo extends FooBase {}; public class Bar { public Bar(FooBase) { System.out.println("Constructor Bar(FooBase) called"); } public Bar(Foo f) { System.out.println("Constructor Bar(Foo) called"); } } public class Test { public static void testSomething() { java.util.ArrayList<FooBase> list = new ArrayList<FooBase>(); list.add(new Foo()); FooBase item = list.get(0); Bar b = new Bar(item); } }
Without compiling and running the program (because that would be too easy a way to solve this), what do you think will happen when you call Test.testSomething()?
My guess is that it will print "Constructor Bar(Foo) called", because while item is a reference of type FooBase the object being pointed to by item is actually of type Foo. But then the guy who came up with this pointed out that the output "Constructor Bar(FooBase) called" would also make sense, and if this were C++ that would be the expected output. The difference is all C++ knows is that it has a pointer to something that must be a FooBase, while Java knows that it has a pointer to something that is actually a Foo.
(no subject)
Mar. 14th, 2006 11:00 amThis is probably related to the lack of destructors in Java, and the complete and utter uselessness of Object.finalize() - the method that's like a destructor, except it only gets called if the garbage collector runs, and then only if the garbage collector feels like it.