Environment specific settings for Log4J 1

Posted by wsargent Tue, 04 Sep 2007 05:59:00 GMT

Finally scratched a long standing itch today and wrote something to break up Log4J configuration and enable parameter substitution. You can download it here: layered-configurator.zip

LayeredConfigurator will go through a properties files with references to DOM or Property configurator references (files or URLs), and will call the appropriate configurator in the order they are defined.

This doesn’t mean much in theory, so let’s explain why this package exists, and give an example.

Log4J lacks a way to spread configuration over several files. You have one configuration file, either log4j.properties or log4j.xml, and that’s it.

In projects where you have several environments to keep track of, you may want to have some loggers set to DEBUG in the development environment, but set to WARN on the production environment. However, you usually want to keep all the appenders and logging infrastructure the same.

Because Log4J defines appenders and loggers in the same file, you either have to define several almost identical files with the appropriate changes for the environment, or you have to have an Ant script that goes through and replaces tokens for the appropriate environment.

The file syntax for the configurators is simple. Here’s an example log4j-config.properties file:

# The complicated appenders are rendered in XML.
base-appenders.xml

# The logging levels can be defined in properties, and can use system properties
# as parameters. (in this case we assume -Dmy.environment=dev is defined)
${my.environment}-loggers.properties

The class is called LayeredConfigurator because it’s meant to work in layers. The configurators are not reset, so all the settings from the previous configurator will still apply unless you override them.

When you define -Dmy.environment=dev, then your development settings will be loaded. When you define -Dmy.environment=prod, then your production settings will be loaded. Either way, your binary distribution is exactly the same, with only the environment specific properties

To enable this class, you must start the JVM with the following system properties:

 -Dlog4j.configuratorClass=com.tersesystems.log4j.LayeredConfigurator
 -Dlog4j.configuration=log4j-config.properties

And that’s it. Pretty simple solution for something that’s bugged me for at least three years now.

Getting work out of Programmers, Part 2

Posted by wsargent Mon, 20 Aug 2007 04:07:00 GMT

So here’s how I think you get the most work out of programmers. This is a follow on from part 1.

Morale. Programmers have good morale when they are treated well, and they are given a problem that they know they can solve. Thank programmers whenever they do something. Let them know you not only know how much they work, but that you care. Keep track of morale through weekly one on ones with each and every programmer. Keep track of commitments you make to your programmers (including the verbal ones) and follow through on them. Make it clear that you are looking out for their interests. (Creating a Software Engineering Culture, Chapter 3.)

Money and stock options only have a limited effect on morale, and may have a negative effect (Agile Development, p63). Most often, a gift of money or stock options is a substitute. (Rapid Development, p262). Morale events can be fun, but don’t actually raise morale – they just allow for a different kind of interaction with people. (How to avoid Lame Morale Events).

And there are all kinds of things that can hurt morale. I think the major one is the Broken Window Theory (Pragmatic Programmer, p4). Under the Broken Window Theory, any neglect or rot in a system that is not directly addressed and countered is a drag on morale. People wonder why it is that they have to write good code and do things right, when they’re not allowed to fix the crappy code. Management assertions that “we don’t have time right now” or “we’ll do it later” start to sound empty and hollow as project after project goes by, and the crappy code festers and rots as hack after hack is piled on top of it.

The Psychology of Computer Programming, Chapter 10 deals specifically with Morale and Motivation. Rapid Development, Chapter 11 goes into typical developer motivations. They are both very much worth reading.

Sleep. You can’t make people sleep, and you can’t do much about sleeping arrangements. But you can tell them that you want them to get 8 hours of sleep a night, and you can bring up lack of sleep as an issue. Anyone who looks sleep deprived needs help; either they have been trying to sneak in work late at night, or they’re suffering in other ways. Give them all the help you can. The number of work hours will be an issue there. And if someone loads up on caffeine and junk food late at night… well, I’d point out that there may be a connection (How to Sleep Better).

Exhausted employees are easy to spot. They’re the people who frighten small children and spouses. They are not fit for work. They barely even know they are at work. They should be sent home until they know what they’re doing. Just that simple act of humanity will raise morale.

Focus. The best way to ensure focus is to ensure transparency and feedback. If programmers have a public, physical way to see what needs to be done at a granular level, whether in a todo list on a whiteboard or a series of 3x5 cards on the wall, they can see at a glance what they will be working on not just today, but next week as well. (Agile Development, p97) This works very well for helping out programmers who have a large list, or determining the priorities – you can only have one task on the top of the list, so what you are supposed to do is never in doubt. This is a technique that is used in several agile methodologies, and is known as an information radiator. (Crystal Clear, p32)

Don’t confuse your programmers, or worse, try to multitask them. If you give them two jobs to do at the same time with the same priority, you’re putting them in a situation where they cannot win; no matter what they do, they’ll be working on the wrong thing. And if they try to do them in parallel, they’ll do both jobs more slowly than they would if they did them sequentially. (Joel on Software) (The Multitasking Myth) (Quality Software Management)

Keep the number of goals small in a project. Pick one objective and make it clear that it’s the most important one. (Rapid Development, p257)

But the best thing you can do for programmers is to let them work. Don’t interrupt them. Don’t spring meetings or interviews on them with no warning. Don’t change what they’re working on. Don’t spring last minute high priority projects on them. Don’t file marketing requests to change the font size as priority one critical bugs. Don’t come up and ask when a bug is going to be fixed. Don’t ask them for status updates every five minutes. I’ve seen constant change requests happen at company after company and I can tell you from experience what happens… the programmers roll their eyes, and they stop taking priority changes seriously. Because they know that in an hour’s time, it won’t be a priority any more. (Rapid Development, p259)

If you let your programmers work uninterrupted, something wonderful will happen. There’s a psychological state called flow that has been documented by Mihaly Csikszentmihalyi. In this state, programmers are able to be “in the zone” and become focused to a great extent. Programmers in flow can produce far more code than they would be able to ordinarily. There is a catch though; it takes the average person at least 15 minutes of uninterrupted work to enter flow. If they are interrupted, or even expect to be interrupted, then they’re less lightly to enter flow. (Rapid Development, p506) Some teams implement a policy called “focus time” (Crystal Clear, p33) or the “cone of silence” (Alistair Cockburn) where meetings and interruptions are banned for a portion of the day. Other teams use a red bandanna (Peopleware) or a sign to indicate that they are not to be interrupted, or other methods.

Background. The best way to have programmers with the best background in the problem domain is to cultivate them. I’ve been surprised in the past how little programmers know what the business does. I’ve often thought it would be a useful exercise to have new programmers spend some time with each member of the business team to understand their concerns and priorities. Failing that, it can be a good idea to have documentation (business process management or six sigma documentation) that can bring new programmers up to speed on the organization as a whole. Of course, this only works so far in that it doesn’t track the history of the organization. Legacy code is nettlesome to new programmers, because typically they reflect legacy business processes and legacy business decisions. But ultimately, it comes with time.

Business specific domains, by nature, share little common ground with each other. There are only a couple of books I can recommend here. Domain Driven Design is the single best book I have read about how to effectively model and discuss domains. It is a similar book to Design Patterns, as it not only talks about implementation and common patterns, but it talks about domains as a common language. And Working Effectively with Legacy Code does an excellent job of pointing out useful ways to desnarl and refactor code that is no longer up to snuff.

Experience. Experience comes with work. It doesn’t always come with time. To quote Weinburg, experience is the best teacher, but it doesn’t necessarily teach anything. Experience can be passed on by proxy, through education and mentoring: some of the best experiences I’ve learned from have been the ones other people have had. If you want books that give experience, then Code Complete 2 and Refactoring are the best bets. If you want to read about experience, then Software Craftsmanship and Software Creativity are the best books. But if you want to make gathering experience, then make it available to your programmers. Set aside an education budget. Encourage your employees to attend conferences and seminars. Join a software engineering book club and have programmers pick out reference books for an in-house library. Have regular brown bag sessions and encourage your team to pass techniques around the company. Do this, and you will not only raise the general experience level of your team, but you will raise morale as well. (Rapid Development p257) (Software Craftsmanship, Chapter 19) (Building a Software Engineering Culture, Chapter 4).

Talent. You can hire talented programmers, but I think that’s as much as can be done. I think that talent is not a static quality. I believe talent is a series of mental habits, and that new habits can be learned, just as old habits can be put aside. I believe that some useful habits are solid grasp of systems theory, along with an ability to ask the “right” question. But that’s another essay. And there is another problem: talented people get bored doing things that don’t stretch their capabilities. If you have work that doesn’t require PhDs, you may be better off not hiring them.

Hours Worked. According to decades of economists and management experts: 40 hours. Contrary to popular belief, the standard work week was not invented by the government or the unions. It was pioneered by Henry Ford in 1926. More than 40 hours a week, and the factories didn’t produce as much money; the temporary increase in productivity was more than offset by industrial accidents and mistakes, and after two weeks there was less productivity. (Why Crunch Mode Doesn’t Work) (Can People Really Program 80 Hours a Week?) (When Should You Start Project Overtime?)

This is counter-intuitive, so I’ll say it again: studies prove overtime provides a temporary benefit for a maximum of two weeks, and is worse than useless thereafter. James Shore provides a recent example.

I believe this, not just from the studies, but from my own experience with extended overtime. Programmers will not only do less work, they’ll make mistakes in the work that they do. Then they’ll get irritable and suffer from low morale. Then they’ll burn out completely. I believe (but do not have the studies to prove) that after even after normal work hours are restored, there is a convalescent effect; people will produce less work following the overtime, producing the same amount of work overall. So after the project goes live, they’ll need a long convalescent period before they’re up to snuff, or even worse, they’ll look up from their monitors, take a good hard look at the results of their labor and their (usually meager) rewards, and quit, producing a huge opportunity cost for the company in terms of hiring, maintenance, and reputation. (It’s Not Just Abusive, It’s Stupid)

For every complex problem, there is a solution which is simple, obvious, and wrong. Extended overtime is that solution.

Fine; hours worked have no effect. What about directly applied pressure? What happens if we keep the hours, and if we tell the programmers to work harder and produce more work during those 8 hours?

Surprisingly, nothing. Programmers produce work using their brains; the amount of thoughts a programmer can have is fairly constant. Tom DeMarco and Tim Lister researched this and formulated Lister’s Law: People under time pressure don’t think faster. (Slack, p50) They might be more stressed, but that doesn’t help people think faster; stress impedes complex thought, and pushes the brain to a “flight or flight” response. If you’re stressing your programmers, they’ll be at their desks more. But they’re not going to write any more code.

This advice is all simple and straightforward. I don’t see anything in here that comes as a shock, and even my mother said “Well, that’s obvious, isn’t it? It’s like being a farmer and taking care of your cows. If you want your cows producing the most milk, you make sure they’re treated like cows should be treated.”

So treat your programmers well. Keep track of morale through weekly one on ones. Make it clear you care about the health and welfare of your programmers. Make sure programmers know what they should be working on at all times. Don’t change out work that the programmers are doing or abuse the bug tracking system. Keep interruptions to a minimum to allow for flow. Establish a training and education budget, and establish mentoring and brown bag sessions to transfer experience. Keep to 40 hour work weeks, and forgo direct pressure.

Do all of these things, and you’ll get more work out of your programmers. And you’ll probably have programmers beating down your door to work for you.

EDIT: Also see this LinkedIn question that provides some useful advice. Larger monitors have been mentioned in several studies, but I don’t have the references to hand.

Custom Argument Matchers in EasyMock 1

Posted by wsargent Sun, 05 Aug 2007 22:15:00 GMT

This post goes further into EasyMock, a tool for reducing dependencies in unit tests.

Say that you’re dealing with some complex object that has to be generated by the method under test, and you want to have a unit test verify that the object is created with all the configured parts necessary. You can’t simply use equality, and there may be parts of the object that you specifically do not want to test.

So you create a custom argument matcher and pass it back through the system. This is a pain the first time, but afterwards you can reuse the argument matcher in many different tests, so it works out.

So, here’s an example. We’re going to pass in a Car, and we expect to have a new object instantiated with some of those settings.

public class CarServiceImpl implements CarService
{

CarDao carDao;
public void createCar(Car pCar)
{
     Car car = new Car();
     car.setColor(pCar.getColor());
     car.setModel(pCar.getModel());
     car.setYear(pCar.getYear());
     carDao.addCar(car);
 }

}

We can mock out the carDao with EasyMock:

public class CarServiceImplTest {
    CarDao carDao = createMock(CarDao.class);
    CarServiceImpl carService;

    @Before
    public void setUp() throws Exception {

        carService = new CarServiceImpl();
        carService.setCarDao(carDao);
    }

    @Test
    public void testCreateCar() {

        Car car = new Car();
        car.setColor("pink");
        car.setModel("lamborghini");
        car.setYear(2000);

        // expect this
        carDao.addCar(car);

        replay(carDao);
        carService.createCar(car);
        verify(carDao);
    }
}

Except that… oops. The carDao isn’t going to be called with that object. The object is instantiated inside the method, so you’ll end up with this:

java.lang.AssertionError:
  Unexpected method call addCar(com.tersesystems.easymock.Car@67ac19):
    addCar(com.tersesystems.easymock.Car@53c015): expected: 1, actual: 0
<typo:code>
</p>
<p>There are two ways we can deal with this problem.  We can either expect something vaguer:</p>
<p>
<typo:code>
carDao.addCar((Car) anyObject()); // vaguest
carDao.addCar(isA(Car.class)); // a little less vague

However, you cannot say:

carDao.addCar(and(isA(Car.class), eq(car.name, "blue"), eq(car.model, "ford"), eq(car.year, 2000));

(At least not without using a custom plugin called EasyMock PropertyUtils. But let’s not get into that right now.)

If you want to make sure that the object being passed into a mock has a precise state, then you have to be explicit about it. This means creating a custom argument matcher.

A custom argument matcher is a class that implements IArgumentMatcher. It provides EasyMock with a way to know that we want an object compared using specific criteria rather than instance equality or equals().

So we want a CarMatcher. The CarMatcher class looks like this:

public class CarMatcher implements IArgumentMatcher {

    private Car expected;

    CarMatcher(Car pCar)
    {
        expected = pCar;
    }

    public static final Car eqCar(Car pCar)
    {
        EasyMock.reportMatcher(new CarMatcher(pCar));
        return null;
    }

    public void appendTo(StringBuffer b) {
        b.append("eqCar(").append(expected).append(")");
    }

    public boolean matches(Object arg0) {
        if (! (arg0 instanceof Car))
        {
            return false;
        }

        Car actual = (Car) arg0;

        String model = expected.getModel();
        if (model == null && actual.getModel() != null)
        {
            return false;
        } else if (! model.equals(actual.getModel()))
        {
            return false;
        }

        String color = expected.getColor();
        if (color == null && actual.getColor() != null)
        {
            return false;
        } else if (! color.equals(actual.getColor()))
        {
            return false;
        }

        Number year = expected.getYear();
        if (year == null && actual.getYear() != null)
        {
            return false;
        } else if (! year.equals(actual.getYear()))
        {
            return false;
        }

        return true;
    }
}

And then we can rewrite the unit test to take advantage of the CarMatcher:

carDao.addCar(CarMatcher.eqCar(car));

So the next question: is this worth it? I’d say yes, because you can reuse the CarMatcher across many different unit tests. You’ll run into code like this in many different situations, such as dealing with DOM4J or XOM elements, where you want to check that they are being created correctly inside of a method. These kinds of cases are perfectly suited for unit tests, because there are not that many dependencies, and they’re small enough and fiddly enough that they’ll be missed by larger integration tests. So it’s good to have on hand.

EasyMock Examples 7

Posted by wsargent Mon, 25 Jun 2007 05:41:00 GMT

I’ve played with EasyMock for a while now, and I think I get it. I tend to see more from examples, so this is my interpretation of the EasyMock documentation.

Say that you have a class:

public class CommentService
{
   private CommentDao commentDao;
   private CommentDao getCommentDao() { return commentDao; }
   private void setCommentDao(CommentDao commentDao) { this.commentDao = commentDao; }

/**

* Adds a comment to the database.
*/

public void addComment(String name, String message) {

    Comment comment = new Comment();
    comment.setName(name);
    comment.setMessage(message);
    CommentDao dao = getCommentDao();
    dao.save(comment);

} }

There are several things that you want to happen in the unit test for the code. You want to make sure that dao.save is called. You want to check that the comment exists, and that it has the name and message that you passed in.

With all of that, the unit test looks like this:

public class CommentServiceTest
{

private CommentService commentService;
private CommentDao mockDao;

@Before
 public void setUp()
 {
     commentService = new CommentService();
     mockDao = EasyMock.createMock(CommentDao.class);
     commentService.setCommentDao(mockDao);
 }

@Test
public void testAddComment()
{   
      // We expect this method to be called when we call commentService.addComment()
      // and we don't care about the object that gets passed in...
      mockDao.save(EasyMock.anyObject());

      // Okay, we're done setting the mock up.
      EasyMock.replay(mockDao);

      // Run the method.
      commentService.addComment(name, message);

      // Check that the mock's happy with what happened between it and the service.
      EasyMock.verify(mockDao);
}

}

So that’s the simple case. What about situations where you’re relying on a context object being passed in, and you get your services from there?

You have two layers of lookup:

@Test
public void testApplicationContextLookup()
{

ApplicationContext mockContext = EasyMock.createMock(ApplicationContext.class);
ExampleService mockService = EasyMock.createMock(ExampleService.class);

// Here we DO care about what gets passed back:
EasyMock.expect(mockContext.getBean("exampleBean")).andReturn(mockService);
mockService.doStuff();

EasyMock.replay(mockContext);
EasyMock.replay(mockService);

// etc...

}

This works for interfaces. But what if you want to mock out classes?

Well, then you need the EasyMock Class Extension library. This is basically the same interface as the other library, the difference being that you need CGLIB to fill in a mock proxy class for you.

@Test
public void testHashtable() {

Hashtable hashTable = createMock(Hashtable.class);

Object key = anyObject();
Object value = anyObject();
expect(hashTable.put(key, value)).andReturn(value);

replay(hashTable);
callTheHashTable(hashTable);

verify(hashTable);

}

Okay, so we can do classes. What about other stuff? What if we only want to mock out a particular method in a class? Well, we can do that as well.

Hashtable hashTable = createMock(Hashtable.class, new Method[] { Hashtable.class.getMethod("put", Object.class, Object.class) } );

What if we don’t know what kind of object we want to return, or need to do some logic to construct a big hairy object? Use IAnswer:

expect(service.getSystemTime()).andAnswer(new IAnswer() {

  public Object answer() throws Throwable
     long setTime = 0; // return a custom time back based on some algorithm...
     return new Long(setTime);
  }

});

What if we want to verify that mock1 is called before mock2? We have to break down using a IMocksControl object.

@Test
public void testMockOrder() {

IMocksControl control = createStrictControl();

List mockList = control.createMock(List.class);
Map mockMap = control.createMock(Map.class);

// Set up the expected calls...

replay(mockList, mockMap);

// Make sure "list stuff" is done before "map stuff"
fixture.doStuff(mockList, mockMap);
verify(mockList, mockMap);

}

This does not cover everything, of course. In particular, it doesn’t cover some of the stranger edge cases (static methods, constructors, finalizers) that code may be nestled in. Also, private and final methods / classes are not covered at all by EasyMock.

Configuring Hibernate properties in Spring 2

Posted by wsargent Sat, 17 Mar 2007 02:28:00 GMT

Okay, so here’s the deal.

I want to have Hibernate work against Oracle and HSQL. I want it to be configured through Spring, and I want it to not be the schema owner in Oracle.

Setting a different schema owner is easy in Hibernate. You specify this in a spring.properties file, using PropertyPlaceholderConfigurer:

hibernate.default_schema=db

and then you have a properly parameterized Hibernate through Spring:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

&lt;property name="configLocation"&gt;
   &lt;value&gt;classpath:hibernate.cfg.xml&lt;/value&gt;
&lt;/property&gt;
&lt;property name="dataSource"&gt;
  &lt;ref bean="dataSource" /&gt;
&lt;/property&gt;  
&lt;!-- Overrides what is defined in hibernate.cfg.xml --&gt;
&lt;property name="hibernateProperties"&gt;
  &lt;props&gt;
    &lt;!-- properties chopped for space --&gt;
    &lt;prop key="hibernate.default_schema"&gt;${hibernate.default_schema}&lt;/prop&gt;
  &lt;/props&gt;
&lt;/property&gt;

</bean>

This works fine with Oracle. When you try a default schema with HSQL, it complains, because it doesn’t like the DB.table syntax.

However, setting hibernate.default_schema to an empty string doesn’t work. Even if you somehow hacked PropertyPlaceholderConfigurer to have ${null} as a value (which it won’t do, you can only do it through XML), it wouldn’t work, because a Properties object can’t have a null value.

The way to do this is to use a PropertiesFactoryBean, and define any “conditional” properties through the locations. Then you can define a property as hibernate.config.file=hibernate-oracle and have it pick up hibernate-oracle.properties with the hibernate.default_schema=${hibernate.default_schema} defined in there.

<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">

&lt;property name="properties"&gt;
  &lt;props&gt; 
      &lt;!-- normal properties --&gt;
  &lt;/props&gt;
&lt;/property&gt;
&lt;!-- hibernate.config should be defined somewhere in the spring.properties layers --&gt;
&lt;property name="locations"&gt;
  &lt;list&gt;
      &lt;value&gt;classpath:${hibernate.config.file}.properties&lt;/value&gt;
  &lt;/list&gt;
&lt;/property&gt;

</bean>

<bean id=”sessionFactory” class=”org.springframework.orm.hibernate3.LocalSessionFactoryBean”>

&lt;!-- normal stuff --&gt;
&lt;property name="hibernateProperties"&gt;
  &lt;ref local="hibernateProperties" /&gt;
&lt;/property&gt;

</bean>

Using DBUnit Effectively on Oracle 10g 5

Posted by wsargent Wed, 14 Feb 2007 02:51:00 GMT

Integration tests are the rule when it comes to testing. It is far more common to run a script to drop and recreate the database, set up some initial data and then manually work through the test case than it is to run a unit test.

So I’ve been playing with DBUnit, trying to get it to work with both Oracle 10g and HSQL. This is what I’ve found out about DBUnit 2.2, especially working with Oracle 10g.

Create a utility class to manage DBUnit operations

Many of the examples given in the DBUnit assume that you’ll be extending the DBTestCase class. You don’t need to do this, and I think it’s a move that will limit your options. Consider that there are only so many things that you can do with DBUnit, and look at writing a utility class that will manage the low level interaction for you, such as this:

public void insertDataSet(String schema, IDataSet dataSet) throws DatabaseUnitException, SQLException, IOException
{

LOGGER.info("inserting dataset: " + dataSet);

DataSource dataSource = getDataSource();
// Using spring-mock here as well, although you can do this directly
Connection connection = DataSourceUtils.getConnection(dataSource);
IDatabaseConnection dbconn = new DatabaseConnection(connection, schema);
try
{
    DatabaseConfig config = dbconn.getConfig();

    // Define the schema using a streaming configuration.
    config.setProperty(DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY, new ForwardOnlyResultSetTableFactory());

    DatabaseOperation.INSERT.execute(dbconn, dataSet);
} finally
{
    dbconn.close();
}

}

or use the IDatabaseTester interface.

Consider using P6Spy

In as much as DBUnit works, it runs JDBC statements. If you’re having problems with DBUnit, P6Spy will show you what JDBC statements are actually being run by DBUnit. This has helped me more than a few times when I’ve forgotten a step.

Purging Recycle Bin

DBUnit will not pick up on “purged” tables in Oracle, and will give you errors like this:

org.dbunit.database.AmbiguousTableNameException: BIN$KWdKkk3jYEvgQAB/AQAa8A==$0

A bug has been filed here. There are two solutions to this problem that I’ve seen. One is to run “PURGE RECYCLEBIN” before you try any database operation in Oracle. The other is to modify and recompile the source, following the instructions in the bug.

Truncating tables

Oracle does not allow you to truncate tables that have a foreign key constraint. Ever. Even if there is no data in either of the tables. You will get this error:

"ORA-02266: unique/primary keys in table referenced by enabled foreign key"

And then you’ll wonder which constraint on which table was responsible, because Oracle doesn’t bother to tell you. This query will show you the offending constraints:
select constraint_name from user_cons_columns where table_name = 'example_table';

And then you can disable a constraint with this command:
alter table foo disable constraint constraint_name;

If you use the DELETE_ALL command, then it’ll be slower, but it’ll work out of the box. Sometimes, that’s enough. Alternately, you may want to consider disabling foreign key constraints for the duration of the tests. If you know everything is valid, and you’re setting up the database just so you can get to a known state, then that should be good enough.

Always Specify the Schema

Oracle likes to have the schema defined. If you don’t have the schema defined, then DBUnit will get very confused. You can specify explicit schema names using a property, but then you can’t share XML data sets very easily.

Be Careful with CLOBs and LONGs

DBUnit takes some shortcuts when working with Oracle CLOBs. It assumes that you’re not using a DB Proxy, and it assumes it knows what Oracle class is valid. This assumption will fail if you are using a database proxy such as C3P0. Filed bug 1637073, but I do not know of a non-intrusive workaround at this point.

DBUnit also does not interact well with Oracle LONG objects. The bug is here and there is a workaround in the bug comments.

Use the right tool for the job

As much as DBUnit is useful for importing data, it’s still written for ease of use and generality than it is raw speed. You can use ForwardOnlyResultSetTableFactory to speed it up, but ultimately using the 10g data pump facilities is going to be faster than using DBUnit by an order of magnitude. If the data pump facilities don’t do it, you can use external tables and transportable tablespaces to set up a large database even faster.

AYE 2006

Posted by wsargent Thu, 09 Nov 2006 17:48:00 GMT

I’m back from the AYE conference this year. I’ve been going for three years now. Each year, the conference has been a little different. Each year, I have been a little different.

I am not the person I was last year. I will not be the person I am next year. This much I can say from experience. There are people who say that no-one is ever the same person twice, from moment to moment, that personality is like water, always flowing by its nature. I’m aware of my calm now, my desire to speak clearly, and my inner critic in the background examining each word for its purpose. This is who I am right now. I know I won’t be the same person in a few hours. But I can see how that person feels, and be aware of that person.

I didn’t get much from the conference this year. I was – am – sick. I’ve had a nasty flu for two weeks now. Coughing, sneezing, sore throat, and a general sense of fatigue that calls into question every movement I make. The sickness made it hard to contribute to the sessions, hard to wake up and share food with several hundred active alpha geeks, and worst of all, hard to think. My mind feels like cold suet, half-used and left in the bottom of the sink for a week. Every so often I would go out and try to interact, only to have people peer closely at me, trying to recognize the Will they knew in me. I was told I looked tired. I was told I should go to bed. I was told I should rest more. I was told I was very stubborn. Still, I learned something from it. I learn something from it every year, though it confuses and frustrates me. And it tells me how I am different this year. I can see what I know this year that I didn’t know last year. I can see how I’ve learned.

What I learned from this year (and know how much I still have to learn): how to accept praise I don’t feel is warranted; how to see my own mind in the moment; how to listen; how to be in love.

Being in love is awesome. I don’t mean that in just the California surfer dude sense. I mean that I am filled with awe, for love is both beautiful and terrifying. It destroys as it builds. I find myself looking through old albums, wandering through emails of past lovers, trying to see myself as that person. Did I love X? How did I feel about Y? What is the past compared to the future? What comparison can I make to something that overshadows me? And do I trust myself to be in love, in the present moment?

Old questions, all. Greek goatherds had better insight. I think about the implications, and in the end? I just be me.

But I am sick. And being sick makes me a shadow of myself – I am not the person I was, right now. Remembering who I was and who other people see me as, I reach for ideas and goals I cannot hold, and so I suffer. This is also an opportunity for learning. But it’s a hard lesson to learn.

New Filebrowser

Posted by wsargent Thu, 02 Mar 2006 07:54:00 GMT

There’s a new ATG Filebrowser available at datafaber. This is a very useful debugging utility, because now you can edit your JSP pages through the same interface that you view JSP pages. This can be a big help when you’re working on a foreign dev machine, or when you’re just tired off bouncing between apps. So go get it.

ATG Module Template

Posted by wsargent Sun, 19 Feb 2006 04:04:00 GMT

I’ve been playing around with the ATG Eclipse plugin. I like the Repository editor, but it can’t cope with more than two layers of XML file combination, so it’s not useful for modifying large scale catalogs. But it is great for proof of concept repositories and prototypes. And I like that I don’t have to run through a build script when using an ATG project in Eclipse.

The side effect of using the plugin is that I’ve been creating a whole bunch of disposable ATG modules. I’m fairly good at cranking these out now, but they’re a hassle in bulk. So I wrote an ant script that creates ATG modules from a template. This will setup the EAR, WAR, and libs for you automatically. Click here for the file.

There’s also some stuff in there about ATG versioned modules. I remember that there was a big annoying problem with CampaignOptimizer (otherwise known as A/B Testing). The details are in build.properties, but I don’t remember precisely.

It’s worth noting that versioned modules could have some advantages over regular modules for large scale enterprise builds. Notably, versioned modules can contain scripts that can automate deployment. Jason Goth goes over this in his presentation at the ATG Open. Relevant bits are as follows:

Versioned Modules
- Each versioned module contains an install script(s).
- The AACom module is a “shell”
– Creates a dependency on the actual version of the site
– The MANIFEST.MF contains just the following:
ATG-Required: AACom-1_36

Versioned modules allow completely automated deployments.
- The deployment script:
– Copies the module files
– Stops a portion of the servers
– Runs the install script(s)
– Starts Dynamo
– Repeat process for remaining servers
- Written in Perl

The deployment script allows us to:
– Upgrade without manual intervention
– Avoid inconsistencies during the restart
– Handle failed file transfers
- Don’t start those servers
– Rollback in case of failure

This is taken from the powerpoint slides. I have no idea exactly what he meant by much of this – since ATG versioned modules are completely undocumented, much of what I already know is trial and error. Still, it’s a good starting point.

BeanshellAdminService

Posted by wsargent Sun, 12 Feb 2006 03:46:00 GMT

I wrote my blog from the ground up. It seemed like an interesting technical exercise at the time. Now I have actual content and people who comment, I’m in a much better position to appreciate the benefits and drawbacks.

Plus: I know exactly what’s going on. It does exactly what I want.
Minus: If something doesn’t work, I can’t draw on a user community to fix it for me.
Plus: I can use the blog as a proving ground for new technology.
Minus: Spammers also learn new technology.

I thought I was very clever for writing my own blog, because spammers were oriented against WordPress and Blogger and wouldn’t bother with as small a target as one custom written blog. I failed to take into account that spammer technology would advance to the point where they would figure out how to act like users and hit a comment button without regard to the back end technology.

I tried cleaning out the spam manually. This is like trying to stop the tide with a bucket. The problem is to come up with a comment submission scheme that spammers cannot hack, and that I need to think about.

In the meantime, I’ve disabled comments altogether. And in lieu of deleting the comments by hand, I’ve used this problem as an opportunity to think about how to do ad-hoc deletes from the repository.

Hence the BeanshellAdminService. This little beauty runs in the AdminServer, and allows me to run beanshell scripts on the server. It’s so convenient that I can’t believe I didn’t think of it years ago. For development, it lets you do just about anything.

admin

I’ve uploaded it to the usual place. This is useful for your normal services as well. You can override the admin servlet to set your own parameters for the interpreter, so if there’s a function that you want to pull in or some parameters that you want to set, you can set them with an anonymous subclass. Best to do that from another module though, so you don’t inadvertently deploy scriptable code to production…