Saturday, July 23, 2011

Q_rsqrt

I still remember encountering the following piece of code while browsing through the Quake 3 source code back in 2005:
float Q_rsqrt( float number )
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;  // evil floating point bit level hacking
  i  = 0x5f3759df - ( i >> 1 ); // what the fuck?
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
  // y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed

  #ifndef Q3_VM
  #ifdef __linux__
    assert( !isnan(y) ); // bk010122 - FPE?
  #endif
  #endif
  return y;
}
That comment on line 10 pretty much sums it up: WTF!?!?

I again ran across this piece of code while browsing an amuzing list of the best comments in source code you've encountered, over on Stackoverflow. It turns out the original author of this illustrious piece of code is still unknown (it's not John Carmack as you might have assumed). A nice write-up of the likely history of this hack can be found on Beyond3D. Interesting reading for sure!
UPDATE: The original author is actually known and it is Greg Walsh. Wikipedia also has a lot of background.

Talking about funny comments in code, my personal favorite from the Stackoverflow list must be the following:
stop(); // Hammertime!
Not so much the comment, but the german version really cracked me up!

Tuesday, July 19, 2011

Java SE 7

It was a long time in the making but all JSRs constituting Java SE 7 have crossed the finish line! The actual Java SE 7 software release is just around the corner (July 28th). Exciting stuff!

Saturday, July 2, 2011

Tech support interference

It's a well known fact that developers and tech support people typically don't get along. The developers want freedom to do as they like with their machines, while the tech support guys want to close everything down, mainly for security reasons.

Sometimes this can get quite ridiculous. I recently heard a story about a development team building a web app that required Firefox compatibility, yet they were not allowed to install Firefox on their machines! Another typical example which recently came up at TSS, is developers going to great lengths to disable virus scanning software because it makes their IDEs unbearably slow.

As always, solving this conflict requires a middle-of-the-road solution. I agree that giving developers unlimited privileges on machines that are connected to production networks is an unwanted security violation. Similarly it's unacceptable to bog down a development machine to the point where the developer can no longer properly do his job. One of the better solutions I have encountered is a separate development network. This development network is an isolated sandbox where the developers reign supreme without putting other parts of the network or organization at risk.

Friday, May 27, 2011

Slow Java platform evolution

It's fascinating to read the Java Module-System Requirements document that Mark Reinhold has drafted to see just why a platform of Java's size and complexity (both code and community wise) is hard to evolve. The requirements document puts the bar quite high for what a Java Module-System needs to be able to do. You also clearly see the influence of systems like Maven and OSGi, and how the experiences with those systems has impacted the requirements.

The end result of all of this is that a Java Module-System can't start off small and simple and grow over time. Instead, the designers are faced with a complex and extended set of requirements right from the start. Slow progress is inevitable. Nevertheless, I'm hoping Java SE 8 will include this Module-System because it would bring a lot of interesting new possibilities to the Java platform!

Friday, April 22, 2011

Properties file troubles

I stumbled over a stupid properties file gotcha the other day trying to get a Spring META-INF/spring.schemas file working. Spring uses this file to translate an XML Schema schemaLocation into the name of a classpath resource. This allows Spring to load the schema directly from the classpath instead of downloading it from a remote server which might not be reachable from your deployment environment (e.g. because of firewalls). So for instance, suppose you have the following in your XML:
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:demo="http://www.ervacon.com/schema/demo"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.ervacon.com/schema/demo http://www.ervacon.com/schema/demo/demo.xsd">
If you combine this with the following META-INF/spring.schemas file, Spring will load demo.xsd directly from the classpath without even trying to access http://www.ervacon.com/schema/demo/demo.xsd:
http\://www.ervacon.com/schema/demo/demo.xsd=demo.xsd
The key thing to notice in the properties file is the escaping backslash before the colon! This is necessary because a colon is a valid delimiter in a Java properties file:
public static void main(String[] args) throws IOException {
 StringReader propsIn = new StringReader("http://my.server.com/foo.xsd=foo.xsd");
 
 Properties props = new Properties();
 props.load(propsIn);

 for (String propName : props.stringPropertyNames()) {
  System.out.println(propName + " --> " + props.getProperty(propName));
  // prints: http --> //my.server.com/foo.xsd=foo.xsd
 }
}
Keep in mind that all colons need to be escaped! So don't forget the colon in front of the port number:
http\://my.server.com\:8080/foo.xsd=foo.xsd
Doh!

Thursday, April 7, 2011

Software and Mud

There seems to be a strange relationship between software and mud. Many will be familiar with the Big Ball of Mud architectural style and there are countless examples of code that resembles mud more than anything else.

A while ago a colleague of mine used an analogy between a software related problem he was facing and wading through mud (or as we say in Dutch: "door de moose ploegen"). I'm amazed at how poignant this analogy actually is. Think of a situation where the first thing you notice is that you have mud on the soles of your shoes. As you trot on you quickly end up knee deep in mud. At this point you can either back out and retreat to solid ground, or persevere, in which case you often end up stuck!

Of course you might also make it through, as John Carmack once noted:
Yes, you can make windows do anything you want to if you have enough time to beat on it, but you can come out of it feeling like you just walked through a sewer. [.plan]

Many software related situations come to mind that closely follow this story:
  • Merging exercises gone wrong
  • Refactoring plans that start simple but spiral out of control
  • Anything to do with character encoding
  • ...
Feel free to add this mud analogy to your software vocabulary!

(As an aside: some people can wade through mud more elegantly than others, as Kate Moss illustrates.)

Saturday, February 5, 2011

Hibernate hurdles

I've been using Hibernate for the better half of 10 years now (since Hibernate 2), and while I really like Hibernate it still surprises me from time to time how complex things can get. This post highlights a few of these hurdles. I'm pretty sure other ORMs have the same or similar issues, but I'll use Hibernate here because that's what I know best. To demonstrate things I will use the classic Order/LineItem example:
@Entity
@Table(name = "T_ORDER")
public class Order {

 @Id
 @GeneratedValue
 private Long id;

 @ElementCollection
 @CollectionTable(name = "T_LINE_ITEM")
 private Set<LineItem> lineItems = new HashSet<LineItem>();

 public Long getId() {
  return id;
 }

 public Set<LineItem> getLineItems() {
  return lineItems;
 }
 
 public int getTotalPrice() {
  int total = 0;
  for (LineItem lineItem : lineItems) {
   total += lineItem.getPrice();
  }
  return total;
 }
}

@Embeddable
public class LineItem {

 @ManyToOne(fetch = FetchType.LAZY)
 private Product product;
 
 private int price;
 
 @SuppressWarnings("unused")
 private LineItem() {
 }

 public LineItem(Product product, int price) {
  this.product = product;
  this.price = price;
 }
 
 public Product getProduct() {
  return product;
 }

 public int getPrice() {
  return price;
 }
}

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "T_PRODUCT")
public abstract class Product {

 @Id
 @GeneratedValue
 private Long id;
 
 private String name;
 
 protected Product() {
 }
 
 protected Product(String name) {
  this.name = name;
 }
 
 public Long getId() {
  return id;
 }

 public String getName() {
  return name;
 }
}

@Entity
public class PcProduct extends Product {
 
 @SuppressWarnings("unused")
 private PcProduct() {
 }

 public PcProduct(String name) {
  super(name);
 }
}

@Entity
public class MacProduct extends Product {
 
 @SuppressWarnings("unused")
 private MacProduct() {
 }

 public MacProduct(String name) {
  super(name);
 }
}
That's pretty straightforward stuff. Assume we have an order stored in the database:
Product pc = new PcProduct("Dell XPS M1330");
session.save(pc);
Product mac = new MacProduct("Apple MacBook Pro");
session.save(mac);

Order order = new Order();
order.getLineItems().add(new LineItem(pc, 1000));
order.getLineItems().add(new LineItem(mac, 2000));
session.save(order);
Now let's look at some surprising pieces of code.

Beware of fetch joins

Suppose you have a business transaction processing orders involving Dell XPS M1330s:
List<Order> results = session.createQuery(
  "from Order as o join fetch o.lineItems as li where li.product.name = 'Dell XPS M1330'").list();
for (Order result : results) {
 System.out.println(result.getTotalPrice());
}
What do you think this prints out for our sample order containing both a Dell XPS M1330 and an Apple MacBook Pro? If you guessed 1000 you would be right... oopsie! The fetch in the query combined with criteria on the joined relation causes the lineItems collection of the resulting Order entities to be incomplete! Unfortunately, there is no way to tell that those Order objects are crippled. Worse still, they will end up in the Hibernate session so you might inadvertently end up using them later on in the same transaction, unaware that they are damaged goods. This can lead to very hard to diagnose bugs, because the cause of the problem (the join fetch) and the effects (incorrect results) might be far apart in the code. It's interesting to note that normal joins (so not using fetch) do not have this adverse side effect. So my advice is:
Be very wary of fetch joins. Only use them if you are sure the resulting objects will not be reused later on in the same session for different purposes.

Beware of inheritance mapping

Let's look at another order processing transaction:
long orderId = ...;
Order order = (Order) session.get(Order.class, orderId);
Product product = order.getLineItems().iterator().next().getProduct();

PcProduct pcProduct = (PcProduct) session.get(PcProduct.class, product.getId());

System.out.println(pcProduct.getId().equals(product.getId()));
System.out.println(pcProduct == product);
If you run this piece of code, Hibernate will spit out a warning:
1463 [main] WARN org.hibernate.engine.StatefulPersistenceContext.ProxyWarnLog
 Narrowing proxy to class com.ervacon.order.PcProduct - this operation breaks ==
And indeed, the output is true / false, confirming that the session can now no longer guarantee that primary key equality is equivalent with reference equality (==). This is a dangarous situation that could again lead to hard to diagnose bugs, especially if some parts of the code (e.g. the lineItem.getProduct() call) are only executed is certain situations. There are other problems with mapping inheritance hierarchies (e.g. the fact that instanceof in unreliable), so my advice here is:
When using Hibernate, avoid mapping inheritance hierarchies. There are often better ways to factor your code that do not require mapping an inheritance hierarchy to the database, for instance by introducing an common interface to facilitate polymorphism.

There are other non-obvious head-scratchers you run into when using an object-relational mapper like Hibernate. If you know about an interesting one, I'd love to hear about it!