Archive for September, 2007

anti-sales people at LayeredTech

Saturday, September 29th, 2007

Talk about a really interesting take on what being a sales person means. It is my general (perhaps misconceived) notion that when a customer contacts a sales person that their job is to try to evaluate the customer’s needs and maybe upsell them or find some combination of services that would met the customer’s needs and, oh may be make the company a wee bit more money. May be this is a little out-dated, after all it is a concept I learned while selling roast beef at Arby’s - guess it doesn’t apply to LayeredTech’s sales people.

I should preface this with the comment that I was planning on adding more servers but…..

From: Patrick Moore
To: sales@layeredtech.com
Subject: Re: [LTSALES #GHX-xxxxx]: Client Order: Patrick Moore [Client ID: xxxxxx] [Server ID: xxxxxx]
Date: Sep 27, 2007 5:38 PM

Hi there –

It looks like the prices for the servers have dropped in price to $49/month … So I would like to take advantage of that to move to the lower price.

I am also looking for a build/staging box build machine so this can be a slow box…or virtual machine…

-Pat

So here I am saying I want to get my account billing changed, but I am also saying that I want to add an extra server. (Wo ho) existing client, upsell opportunity (any place but layeredtech)!

Got this reply:

From: Sales Related Requests [Add to Address Book]
To: xxxxx@earthlink.net
Subject: Re: [LTSALES #GHX-xxxxx]: Client Order: Patrick Moore [Client ID: xxxxxx] [Server ID: xxxxxx]
Date: Sep 27, 2007 6:11 PM

Hello Patrick,

If you need to upgrade (or downgrade) your current server to a new processor, you will need to order an entirely new server. So, you should time it to where the server that you want to upgrade will be cancelled right after your new server will be up. We need a 2 day cancellation notice (go to support.layeredtech.com and open a ticket, chose ‘Cancellations’ and you will be guided through the process).

You can set the cancellation of the existing server to be now or at the end of your billing cycle. Please specify in the body of your ticket. There will be no refunds given for servers cancelled prior to the renewal date.

I apologize, but we will not move hard drives from one server to another. IP allotment is also not transferable to newly ordered servers.

If you have further questions or concerns, please do not hesitate to contact me for assistance.

Thank you,

Jenny Song
Layered Technologies Sales
Toll Free: 1.866.584.6784
Direct Line: 972.398.xxxx
Fax: 972.398.7055
Email: jsong {at} layeredtechnologies.com

You got to love this! Instead of trying to find out more about my needs was

  • to direct me to some series of steps on a website that I don’t want to figure out, and
  • not bother to find out if I had other needs (which I pretty darn clearly indicated)

But that’s o.k. right Jenny did say to contact her if I had “any further concerns or questions”. Well as a matter of fact I did:

From: Patrick Moore
To: jsong layeredtechnologies.com
Subject: Re: [LTSALES #GHX-xxxxx]: Client Order: Patrick Moore [Client ID: xxxxxx] [Server ID: xxxxxx]
Date: Sep 27, 2007 6:36 PM

well this looks like exactly the *same* server….so why can’t you adjust the price?

No response by the next afternoon, the offer of assistance must have been an empty one….


So I resent the email thread to sales@layeredtech.com hoping the luck of the draw would get me someone a little better. Wishful thinking. Got this reply:

From: Sales Related Requests [Add to Address Book]
To: xxxxx@earthlink.net
Subject: Re: [LTSALES #GHX-xxxxx]: Client Order: Patrick Moore [Client ID: xxxxxx] [Server ID: xxxxxx]
Date: Sep 28, 2007 3:54 PM

Hello,

We wish it was that simple however, it is not. Again, if you would like to upgrade or downgrade the processor you will need to order a new server. We will be unable to drop the current price of your server to $49.00 as per our current specials.

Thank you,

Drew Patterson
CSR/Sales/Accounts
Toll Free: 1-866-584-6784
(972)398-7000
FAX: (972)-398-7055
Email: Sales@layeredtech.com
Email: Accounts@layeredtech.com

Now at least Drew was a little more upfront and honest. He didn’t extend the (empty) offer to be of “further assistance” and he wasn’t even going to try to be helpful with a list of steps on navigating the layeredtech website. And certainly no direct number or email address for him!

I should also add that my whole humorous exchange started with me asking the same question of their pre-sales person ‘available’ via chat. I will not mention the id number of that person, because he did try to be helpful but admitted upfront that he didn’t have the ability to handle what I was asking for. He at least tried to be helpful and referred me to the (un)helpful people available at sales@layeredtech.com

Capitalism at its finest - “we won’t take you money unless you shove it in our hands”.

why do people bother with Wendell Cox?

Saturday, September 22nd, 2007

From September 2007 Railway Age editorial page:

Wendell Cox responds
“Your confusion on my position (’From the Editor,’ RA, August, p. 4) misses the difference between means and objectives. Thirty years ago, I was appointed to the Los Angeles County Transportation Commission with an objective to reduce traffic congestion by means of a rail system. Largely because of my 1980 amendment, rail transit was built in Los Angeles.

Really? In the late 80’s when I lived in LA the only thing built was the rather pathetic Blue Line. And was this built because of you or in spite of you?

While the means of rail was achieved, the objective was not, as traffic congestion continued to worsen.

Maybe you had the wrong objective?

“Around the world, rail transit has shown virtually no capability for reducing traffic congestion in urban areas.

Wendell Cox focuses exclusively on the question “does rail reduce traffic congestion in urban areas”. We have spent 50 years and billions building a nationwide interstate highway system, so we should apply the same logic to road-building. Doesn’t our 50-year experiment in the asphalt and pavement prove that “roads have virtually no capacity for reducing traffic congestion in urban areas”? Somehow with roads ‘congestion’ becomes an argument for building more infrastructure, yet with rail ‘congestion’ becomes an argument for building less!

Using this logic, I am sure Wendell’s good advice to railroads is to abandon double-tracking projects because “rail doesn’t reduce congestion”!

At the same time, with intermodal capabilities and further investment, substantial amounts of highway freight could be diverted to freight rail, which could help achieve the objective of reducing future congestion on the roads we are insufficiently expanding.

Wrong objective produces wrong results.

Furthermore, if Wendell simplistically defined the problem as ‘congestion’ it is to be expected that he can only simplistically answer “build more roads”. The reality is congestion is caused by the interaction of complex issues that has at the center poor land-use planning. Congestion is merely the symptom of the problem that we are not enabling people to live close to where they work. We also put goods distribution centers a considerable distance away from stores and existing transportation infrastructure.

One of the easiest ways to reduce congestion is to reduce the amount of miles goods and people need to move daily. If we were to cut in half the distance people needed to commute - we would easily solve the ‘congestion’ problem, without building a single road. If poor urban planning got us into this mess then better urban planning must be part of the answer. Instead we rely on rail-building as a band-aid and then blame the resulting disjointed piecemeal rail lines (which in many cases don’t even form a network) for the continued congestion.

So trillions have been spent on roads in the past 50 years and we still have a problem? May be we should try something new.

“Finally, passenger and freight rail are not the same.This is illustrated by Europe, where freight rail volumes are so small, in large measure because high passenger rail volumes make freight rail uncompetitive relative to trucks. By contrast, in the U.S., the majority of truck and rail ton-miles are on rail.
“No first world nation has both a world class freight rail and world class passenger rail system.The U.S. is better off for the choice it has made.”

Is it? In order to function in the U.S. a car is required. In the rest of the world a car (and its associated high expenses) is optional. I would rather have the option and put money into my children’s education than a car.

Email your comments to wvantuono sbpub . com.

Finally, Wendell completely ignores the ability of rail to deliver goods deep into a city, or across the country, goods and people in large volumes, speedily, and in an energy-efficient manner. In an era of increasingly high energy costs and population growth, these characteristics will become increasingly important.

If maybe for the next 50 years we should try something different than more of the same that got us into this mess, my vote is with rail.

Volunteering the kids for medical experiments

Wednesday, September 12th, 2007

Littlest participated in this study about a year ago at Stanford.

The Card Sort game is a well known task in which two through five year olds are asked to sort six cards according to one dimension (shape or color), and then sort six more cards by a second dimension, (the opposite – color or shape). The children sorted these cards into boxes with conflicting dimensions. For instance, when sorting a green flower, the child had the choice of a box with a green sailboat or a box with a yellow flower. The game is designed to assess children’s ability to switch between rules.

As expected, almost all of the children were able to correctly sort the first six cards regardless of whether they were asked to sort by color or shape. However, once the rule was switched and they were asked to sort using the opposite dimension, the younger children had trouble inhibiting their desire to use the first rule they were taught, although they clearly understood the new rule.

Past research on the Card Sort task has shown that different training methods may help children learn to successfully pass the Card Sort task when they might otherwise not be able to do so. This study was designed to assess various training methods and investigate whether children’s improved performance would carry over to a new Card Sort task with different cards and boxes.

Children played the Card Sort game with one of four training variations including having children watch a video of another child sorting correctly, telling the children whether or not they were sorting correctly, having the children label the cards by their shape or color before sorting them, and no training at all.

We found that children who labeled the cards on their own before sorting them performed significantly better in the Card Sort game, and our findings suggest that this training condition was the most effective way to teach 3-year-olds how to pass the Card Sort task. We posit that children in this condition learned a strategy for completing the task and were able to refocus their attention to the relevant sorting dimensions.

We are currently testing more children in this Card Sort task to confirm our initial findings, as well as designing another study to further investigate these training methods.

A year ago at age 3, littlest “failed” miserably.

if they think dungeons and dragons was bad….

Tuesday, September 11th, 2007

What about MMOs, I know a few people that could star in the sequel:

Dating for shallow people looking for husband #4

Tuesday, September 11th, 2007

Darwin Dating:

Attractive people are at a disadvantage on normal internet dating sites. They have to wade through a plethora of ugly people and ugly people pretending to be attractive in order to find someone who matches their own attractiveness. Our strict rules and natural selection process makes Darwin Dating the perfect medium for attractive people to find other people of their own kind.

Darwin Dating is a site for 18-35’s only. The male sex drive peaks at 18 and the female sex drive peeks at 30 making 18-35 the perfect breeding age bracket.

So if you are into sex and shallow people who’s only criteria is perky breasts, now you know where to go.

Code Review #4: Always read the documentation/code - a.k.a. java.net.URL is evil

Sunday, September 9th, 2007

The Setup
Before I plunge into my rant, lets review a little-ole documentation. Under java.lang.Object, for equals() we have this:

It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.

Over in java.util.Collection land, there is this little bit of documentation, highlighting the importance of overriding the default implementations of equals() and hashCode() if one wants to play nice in a Collection:

Many methods in Collections Framework interfaces are defined in terms of the equals method. This specification should not be construed to imply that invoking Collection.contains with a non-null argument o will cause o.equals(e) to be invoked for any element e. Implementations are free to implement optimizations whereby the equals invocation is avoided, for example, by first comparing the hash codes of the two elements.

So for instances of a class to function well as a key in a java.util.Map or be placed into a java.util.Set, the class should override the standard equals() and hashCode() provided by Object. Because of this universal need, it is a reasonable expectation that implementors of equals() and hashCode() pay attention to performance. For example, the programmers who wrote java.lang.String did a fairly simple implementation of hashCode() and then cached the result so the calculation was not repeated for a given String object.

The resulting ubiquity of overridden equals() and hashCode() results in a certain set of expectations:

  1. if the object doesn’t change the results of equals()/hashCode() shouldn’t change (otherwise Set collections will break)
  2. equals()/hashCode() should be fast and in the case of hashCode - for potentially expensive hashCode() operations the results should be cached.
  3. Immutables (such as URL) should be good candidates to be keys in a java.util.Map

The Takedown
So I was completely blindside by the brain-dead, high school, freshman implementation of java.net.URL.

The only thing I will say positive about the java.net.URL implementation equals()/hashCode() is this: buried deep, deep in the documentation, the high school students who wrote the implementation do casually tell you that they are going to screw you over.

In java.net.URL.equals() (only if you go look at the detailed documentation) do you see this:

Compares this URL for equality with another object.

If the given object is not a URL then this method immediately returns false.

Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; (emphasis mine) else if either host name can’t be resolved, the host names must be equal without regard to case; or both host names equal to null.

Since hosts comparison requires name resolution (emphasis mine), this operation is a blocking operation.

Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP. (emphasis mine)

Translation: We are going to screw you and you will enjoy it.

The Scream
How does this screw you?

  1. Two fundamental operations equals()/hashCode() are now ridiculously expensive since they involve a DNS lookup to see if they resolve to the same ip address. So now an operation that is optimize in other classes is outrageously expensive (milliseconds long) for a very fundamental internet class.
  2. URL looks to be an immutable. Immutable objects should mean that the identity: x.equals(x) is true. But this isn’t the case for URL. If you serialize a URL, the resolved hostaddress (it’s transient) is lost. So serialize/deserialize a URL. Wait a little bit. Add a little bit of the Dynamic DNS magic. Presto “http://google.com” will not equal the deserialized version of itself
  3. It is simple wrong. Take the case of a hosting service that hosts two different sites on the same server. The DNS lookup resolves the 2 domains to the same physical ip address. URL (with an “assist” from URLStreamHandler, InetAddress, and our very expensive DNS lookup) compares URLs based on the ip address not the entered hostname. As a result, http://my-porn-site.com could ‘equal’ http://jesus-has-saved-my-soul.org. I think someone might disagree with this assessment, don’t you?

The Takeaway
So after these very expensive operations URL.equals()/hashCode() is broken. The implementers of URL clearly decided that they were going to be ‘clever’ and for all their ‘cleverness’ the only thing they managed to do is screw users of this class. Sun should just admit the error of its ways and just reimplement URL.equals() (with hashCode() being the equivalent).

public boolean equals(Object o) {
    if( !(o instanceof URL)) {
        return false;
    } else if ( o == this ) {
        return true;
    } else {
        return ((URL)o).toString().equalsIgnoreCase(this.toString());
    }
}

The old functionality should be quietly pushed to a uselessEquals() method some place.

The Net
Performance problems and bugs can be introduced from the wildest and least expected locations. Always recheck your assumptions.

“When you have eliminated all which is impossible, then whatever remains, however improbable, must be the truth.”
-Sherlock Holmes.

Code Review #3: Use the debugger to check “working” code

Friday, September 7th, 2007

There is a bug (or two) in this code:

    protected void compareStreams(InputStream is, InputStream isFromGet)
            throws IOException {
        byte[] newBuf = new byte[4096];
        byte[] oldBuf = new byte[4096];

        int len = 0;
        while ((len = is.read(newBuf)) != -1) {
            boolean flag = false;
            if (isFromGet.read(oldBuf) == len) {
                flag = true;
                newBuf.equals(oldBuf);
            }
            if (!flag) {
                throw new IOException(”New file differs from old one.”);
            }
        }
        isFromGet.read(newBuf);
        if (isFromGet.read(newBuf) != -1) {
            throw new IOException(”New file differs from old one.”);
        }
    }

Did you spot any problems? Well maybe you didn’t spot any right away. But use a debugger, and the bugs would quickly become obvious.

  1. Look at :
                if (isFromGet.read(oldBuf) == len) {
                    flag = true;
                    newBuf.equals(oldBuf);
                }
    

    The results check newBuf.equals(oldBuf) is completely ignored. So what happens if 2 streams of the same length but with different contents are compared? This method says they are the same!

  2. But even if the check was flag = newBuf.equals(oldBuf), it would still be wrong! Using an IDE (or a debugger) quickly discovers that newBuf.equals():

        public boolean equals(Object obj) {
    	return (this == obj);
        }
    

    This is not the same as comparing the contents of the arrays. The correct call is Arrays.equals(newBuf, oldBuf). And of course after each read the arrays in question should be 0-filled to avoid issues with partially buffers.

  3. Next, what happens if the streams are read at different rates? newBuf and oldBuf will miscompared simply because one stream didn’t have data immediately available to it.
  4. Finally,

            isFromGet.read(newBuf);
            if (isFromGet.read(newBuf) != -1) {
                throw new IOException("New file differs from old one.");
            }
    

    What is that first line doing in there!? This allows ‘isFromGet‘ stream to be up to 4096 bytes larger than the ‘is‘ stream!

Yecks! So few lines so many major bugs.

Do a line-by-line code review with a debugger, and discover hidden bugs such as these in “perfect” code.

But finally, haven gotten this far… use the (debugged) work of others…

org.apache.commons.io.IOUtils.contentEquals(InputStream, InputStream) is good and correct. Use that.

Code Review #2: “It ain’t stored until the database says it is”

Monday, September 3rd, 2007

This was a recent comment on a bug report:

EventImpl stores start and end dates as two strings (milliseconds presentation of date). Not clearly understand how information can be lost.

What is wrong with this statement?

Well nothing is “stored” until there is a persistent representation of the data that will outlast the program being stopped. Period.

Putting some value in an instance’s field does not qualify. Unless and until some storage mechanism (database, serialization to a file, etc.) puts it some place permanent - it is not “stored”.

The developer can only say the value is being stored if:

  1. there is an active transaction;
  2. the object being modified is mapped to the database tables.
  3. the field being modified is mapped to some column within that database
  4. the specific object instance being modified is actually participating in the active transaction. There could be an active transaction that this object is not part of!
  5. the transaction commits without errors

For the particular bug report, neither the field nor the object was persisted by the database. Therefore nothing was stored.

Code Review #1: Masking of Throwables

Monday, September 3rd, 2007

When it comes to finally blocks, extra care is required. The code within a finally block can make very few assumptions about the state of variables that it is dealing with. Usually where developers slip-up is with null handling. This error is compounded by finally block lacking the try block exception management protection.

An otherwise well-written method with regards to correct try/catch blocks can still fail badly when it comes to the finally block code. A method that handles all exceptions can still throw an exception from the finally block itself. Usually the finally block throws an exception when try or catch block code threw an exception.

This code has this problem:

   private void copyFile(String fileName) throws IOException {
        File destFile = new File(archiveDirectory, fileName);
        FileChannel srcChannel = null;
        FileChannel dstChannel = null;
        try {
            srcChannel = new FileInputStream(new File(workingDirectory,
                    fileName)).getChannel();
            dstChannel = new FileOutputStream(destFile.getAbsolutePath())
                    .getChannel();
            dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
            destFile = new File(archiveDirectory, fileName+".sha");
            srcChannel = new FileInputStream(new File(workingDirectory,
                    fileName+".sha")).getChannel();
            dstChannel = new FileOutputStream(destFile.getAbsolutePath())
                    .getChannel();
            dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
        } finally {
            srcChannel.close();
            dstChannel.close();
        }
    }

Did you spot the problem?

What happens if either of the first two lines throws an exception?

  • srcChannel or dstChannel will be null.
  • A NullPointerException(NPE) will be thrown from the finally block.
  • The NPE will *mask* the original exception.

This last point bears repeating. The original cause of the problem will be lost! Now you as a developer may assume that the lost exception will be an IOException and 99% of the time especially during development this will be correct.

But many developers forget that there are a few externally generated events that can cause throwables to be thrown. Thats right! The root for “things that can be thrown” is java.lang.Throwable - not java.lang.Exception!

Some non-Exception examples include: StackOverflowError and OutOfMemoryError.

So the masking NPE could easily hide an OutOfMemoryError, which results in a lot of hair pulling when:

  • the exception is only occurring occasionally…
  • only on production boxes…
  • at a customer site…
  • they will not let you update the code until you have a fix…
  • the customer is your company’s largest account…
  • and their CEO is calling your CEO…

It may not play out like this in all its glory. But take the words to heart and make sure you never mask Throwables!

Code Reviews

Monday, September 3rd, 2007

Sometimes a code review reveals a code problem that just keeps repeating itself again and again. So rather than creating new email messages I have decided to start posting some code reviews. This will allow me to avoid repeating myself. I can just point to code-review #1 and say read this post.