Previous Next

Technical Blog

September 2010

I've been seeing some misuse of inheritance in Java lately, and wanted to share some thoughts on when to use inheritance, and when to use composition. As a general rule, I would say that I almost always try to use composition, except where it is particularly cumbersome. I'll try to explain some of the reasons for using composition instead of inheritance, along with some concrete examples.

 

1. Building a Better Map


Let's start with the example of building a case-insensitive map. We want a map where the key is always the lower-case toString() of the key. Let's try implementing this with inheritance.

public class NoCaseHashMap extends HashMap {
 
    private String toKey(Object key) {
        if (key == null) {
            return null;
        }
        return key.toString().toLowerCase();
    }
 
    public Object get(Object key) {
        return super.get(toKey(key));
    }
 
    public Object put(Object key, Object value) {
        return super.put(toKey(key), value);
    }
 
    public boolean containsKey(Object key) {
        return super.containsKey(toKey(key));
    }
 
    public Object remove(Object key) {
        return super.remove(toKey(key));
    }
}

 

Pretty easy, right? But what if we need a case-insensitive map where they keys are stored in alphabetical order? We'd probably want a TreeMap instead of HashMap. Do we create a new class, NoCaseTreeMap that extends TreeMap? That way lies madness. We'll end up having to copy-paste all the methods from our NoCaseHashMap. Let's try reimplementing it with composition and the decorator pattern.
(If you'd like a refresher on the wonderful decorator pattern, have a look here: http://www.javaworld.com/javaworld/jw-12-2001/jw-1214-designpatterns.html)

public class NoCaseMap implements Map {
 
    private Map map;
 
    public NoCaseMap(Map map) {
        this.map = map;
    }
 
    private String toKey(Object key) {
        if (key == null) {
            return null;
        }
        return key.toString().toLowerCase();
    }
 
    public Object get(Object key) {
        return map.get(toKey(key));
    }
 
    public Object put(Object key, Object value) {
        return map.put(toKey(key), value);
    }
 
    public boolean containsKey(Object key) {
        return map.containsKey(toKey(key));
    }
 
    public Object remove(Object key) {
        return map.remove(toKey(key));
    }
 
    /* Note the need to implement putAll(map) with composition */
    public void putAll(Map t) {
        for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            put(entry.getKey(), entry.getValue());
        }
    }
 
    // --------------------------------------
    // The rest of these are just the standard cruft of composition:
    // --------------------------------------
    public void clear() {
        map.clear();
    }
 
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }
 
    public Set entrySet() {
        return map.entrySet();
    }
 
    public boolean isEmpty() {
        return map.isEmpty();
    }
 
    public Set keySet() {
        return map.keySet();
    }
 
    public int size() {
        return map.size();
    }
 
    public Collection values() {
        return map.values();
    }
}

 

Obviously that's a lot more code, but it also gives us a lot more. We can have a new NoCaseMap(new TreeMap()) as we wanted, as well as a new NoCaseMap(new LinkedHashMap()) or a new NoCaseMap(new  ConcurrentHashMap()), and so on and so forth. Eclipse helps us out with  this a bit too. Those seven last methods can be implemented in a few keystrokes with Eclipse, with Source -> Generate Delegate Methods. You might notice this pattern is similar to InputStream and its family. It's also the way the JDK does it for things like Collections.unmodifiableMap. InputStream takes it a step further by having a FilterInputStream class that already implements all the methods as boilerplate methods calling the delegate, so if you want to decorate an InputStream, you can just extend FilterInputStream, and override the methods you care about. (Those of you who are particularly on the ball might comment that InputStream is a class, not an interface, but the pattern still applies.)

 

2. Teamwork is Great, but Sometimes You Can't Depend on Others

 

Another example, taken from recent work, would be a service that makes HTTP calls to a third party and parses the response. Since this service needs to make HTTP calls, let's extend our HttpClientImpl class.

public class WidgetServiceImpl extends HttpClientImpl implements WidgetService {
    private static final String WIDGET_LIST_URL = "http://rest.widgets-inc.com/widgets";
    private final WidgetListParser parser;
    
    public WidgetServiceImpl(WidgetListParser parser) {
        this.parser = parser;
    }
 
    public List<Widget> getWidgets() {
        final String content = callHttp(WIDGET_LIST_URL);
        return parser.parse(content);
    }
}

 

This  works fine, until you try to unit test it. How do you run your unit test if Widgets Unlimited's server is down? What if you're on the plane? Your WidgetServiceImpl is tightly coupled with HttpClientImpl and can't be tested without actually talking to this external server. We're depending on their server in order to test our code. Let's try again, but with composition.

public class WidgetServiceImpl implements WidgetService {
    public static final String WIDGET_LIST_URL = "http://rest.widgets-inc.com/widgets";
    private final WidgetListParser parser;
    private final HttpClient httpClient;
    
    public WidgetServiceImpl(HttpClient httpClient, WidgetListParser parser) {
        this.httpClient = httpClient;
        this.parser = parser;
    }
 
    public List<Widget> getWidgets() {
        final String content = httpClient.callHttp(WIDGET_LIST_URL);
        return parser.parse(content);
    }
}

 

With an HttpClient being passed in now, we can pass in some dummy implementation that doesn't talk to a real server. It can do whatever we need in order to test our WidgetServerImpl.
(You may notice I've used HttpClient instead of HttpClientImpl. I like putting lots of interfaces on things, because it makes them more convenient to test with JMock.)

 

Note also that WidgetListParser is being passed in, not constructed within the WidgetServiceImpl. This also makes testing easier; if we mock/stub that, we can test WidgetServiceImpl without having to worry about what the content actually looks like. We'll test that separately when we test WidgetListParserImpl. Testing WidgetServiceImpl with JMock now is easy.

@RunWith(JMock.class)
public class WidgetServiceImplTest {
 
    private Mockery mockery = new Mockery();
 
    private HttpClient httpClient;
    private WidgetListParser parser;
    private WidgetService widgetService;
 
    @Before
    public void setUp() {
        httpClient = mockery.mock(HttpClient.class);
        parser = mockery.mock(WidgetListParser.class);
 
        widgetService = new WidgetService(httpClient, parser);
    }
 
    @Test
    public void shouldMakeHttpCallAndReturnWidgetList() {
        final String content = "some dummy content";
        final List<Widget> expectedWidgetList = new ArrayList<Widget>();
 
        mockery.checking(new Expectations() {{
            one(httpClient).callHttp(WidgetServiceImpl.WIDGET_LIST_URL);
            will(returnValue(content));
 
            one(parser).parse(content);
            will(returnValue(expectedWidgetList));
        }});
 
        final List<Widget> actualWidgetList = widgetService.getWidgets();
        Assert.assertSame(expectedWidgetList, actualWidgetList);
    }
}

 

3. When to Use Inheritance and When to Use Composition

 

The traditional answer to the question is to use inheritance if there is an "is a" relationship between the classes and composition when there is a "has a" relationship. I don't think that's entirely sufficient  though. I agree that if it's not an "is a" relationship, then it certainly shouldn't be done with inheritance. Is WidgetService an HttpClient? It uses an HttpClient to do it's job, but as far as consumers of the WidgetService are concerned, it could just as well talk to a database. But even if there is an "is a" relationship, there are plenty of times to use composition instead. Does the class you're extending have an interface? If you don't need to call protected methods of that class, consider composition instead. If a class ends with "Util" or "Helper", they should always be used via composition, not inheritance.

 

If any of this was new to you, you may also enjoy reading this:
http://www.tiedyedfreaks.org/eric/CompositionVsInheritance.html
Some of my examples were stolen liberally from that page.

0 Comments Permalink

The way OpenJPA deals with collections can cause some surprising defects if you don't understand what OpenJPA is doing for you. This post explains some OpenJPA behaviour in order to help prevent future defects and find current ones.

 

OpenJPA internals

OpenJPA records which fields are dirty and only updates dirty fields to the database. Much of what the byte code enhancer does is to add code to set methods to mark the fields as dirty. OpenJPA has a problem when it comes to fields which are collections because if you add something to a collection then the parent object will not know about it and therefore cannot set the field to dirty. OpenJPA deals with this situation by using something called smart proxies. When you change a collection that is a smart proxy, OpenJPA will go to the parent object and mark the field as dirty so that you do not normally need to worry about it.

 

Prevention

Always use the objects returned from OpenJPA methods for further work. Watch how you use collections and make sure you don't put references to them in places that are not updated when the object is saved to the database.

Example defect

Sometimes abstractions bleed and cause surprising failures. Here is an example I found:

customer = (Customer) beanFactory.getBean(ContextIdNames.CUSTOMER); 
customer.setAnonymous(true); 
              
customer.setEmail(billingAddress.getEmail()); 
customer.setStore(store); 
              
this.customerService.validateNewCustomer(customer); 
// Anonymous customers are currently persisted before checkout so that their addresses can be 
// assigned UIDPKs, which is necessary for manipulating addresses during checkout. 
customer = customerService.add(customer); 
 
customer.setFirstName("James"); 
customer = customerService.update(customer);

 

It looks okay, but after this code ran, the database did not have the first name set. That's because CustomerImpl had:

 

    public void setEmail(final String email) {
        this.getCustomerProfile().setStringProfileValue(ATT_KEY_CP_EMAIL, email);    
        ...
    }
 
    public CustomerProfile getCustomerProfile() {
        if (this.customerProfile == null) {
            initializeCustomerProfile();
        }
        return this.customerProfile;
    }
 
    private void initializeCustomerProfile() {
        customerProfile = (CustomerProfile) getElasticPath().getBean(ContextIdNames.CUSTOMER_PROFILE);
        customerProfile.setProfileValueBeanId(ContextIdNames.CUSTOMER_PROFILE_VALUE);
        customerProfile.setProfileValueMap(getProfileValueMap());
    }
 

So, when setEmail() is called on the newly created object, the CustomerProfile is initialized with a plain hashmap. CustomerService.add gets called and the profile value map is updated with an OpenJPA smart proxy. However, this smart proxy is never placed into the CustomerProfile object so when the setFirstName() method is called the plain hashmap gets updated and OpenJPA never gets a chance to set the field dirty -- and therefore, the database doesn't get updated.

1 Comments Permalink

Spike for Success

Posted by Simon Droscher Sep 20, 2010

We've all faced the situation where the team needs to implement a story that no-one knows enough about to be able to estimate well. Estimating the story will be based on speculation rather than concrete data, and the story has the potential to go horribly wrong and remain at "almost done" for the entire iteration (and beyond).

 

Why does it go wrong so easily? Well the short answer is that the team will be making their best effort to develop the functionality for the story and resolve issues as they arise. This often means having to go back and rewrite code written earlier in the iteration as the problem is better understood. There may be several failed attempts before everything is understood and all problems are solved.

 

There's a better way to make this kind of story successful - something we know as a Spike.

 

The idea of a Spike is this - spend a fixed amount of time (also known as timeboxing) focused on the problem and ignoring all other concerns. These "concerns" that we can ignore as part of a spike include such things as code compliance, design principles, best practices etc. What we want to do is consider some approaches and do the minimum amount of work required to demonstrate whether the approach is feasible or not. We are experimenting with ideas on how to possibly implement the story, rather than trying to complete the story as a whole. Keeping it time-boxed allows us to get back to delivering tangible value rather than taking an extended journey down the rabbit hole.

 

At the end of the spike, we should have reduced the risk involved in implementing the story - we now have concrete data rather than speculation. Now we can estimate the work required to implement the story with a lot more confidence!

 

Typically, the deliverables of a spike should include a writeup that outlines one or more approaches, along with some working code that demonstrates the approach(es). It may go as far as producing a design for peer review. Generally the code produced by a spike is considered throw away, or at best a starting point for the implementation. It demonstrates the approach without being Production-ready code. A good thing to include in the writeup is what work will be required to take the concept to production-ready code. Other developers should be able to read the writeup and understand enough about the story to be able to estimate with confidence.

 

One rule we generally stick to in order to ensure the appropriate production-code standards are followed in the actual implementation is to ban the developer who worked on the spike from developing the actual implementation story. In a self-organizing team this also ensures (via peer reviews) that the writeup is sufficiently detailed to allow anyone to implement the production story.

 

 

Some examples of successful spikes we have done at Elastic Path:

 

1. Faceted Navigation for Price List Stack

 

The aim of this spike, during 6.2.0 development, was to demonstrate ways to ensure price faceting works in the storefront now that prices are determined by a changeable stack of price lists rather than being directly on the Product object. We started by brainstorming possible approaches, and narrowed it down to 2 that we would like to try:

 

  • Fields in the indexed document for each possible combination of price lists in a stack (may limit number of price lists/catalog)
  • Field in the document per price list, and facet on a query given the price lists involved in the stack. Care needs to be taken to exclude results already in another facet.

 

Both approaches were tried and working code was produced for each. We were then able to weigh up the pros and cons of each and decide the ultimate approach. We decided on the second approach due to the fact that there was negligible difference in query time for each, but the first solution had product indexing times increasing dramatically as stores and price lists were added.

 

Using this spike, we were able to implement the story - which originally made a whole development team very nervous - with a minimum of fuss.

 

2. Upgrading to Drools 5

 

During 6.2.1 development we received feedback from customer's desiring an upgrade of our supported version of Drools Rules from 3.0.4 to 5.0.1. Our Product Manager wanted to put this upgrade on the roadmap only if it would be small enough to not impact our ability to deliver other valuable features to the product. In a time-boxed spike we were able to establish that a small amount of work would be required due to some Drools API changes but that were no major roadblocks.

 

3. (Not) Upgrading to OpenJPA 1.2.2

 

During 6.2.1 development we did a spike to determine the feasibility of upgrading from OpenJPA 1.2.1 to 1.2.2. We were able to establish that upgrading to 1.2.2 would require a significant amount of work as there were still many patches required for it to work with the EP codebase, and no bug fixes or no features that would benefit us.

0 Comments Permalink

If you're a savvy Maven user, you may already know about Maven's enforcer plugin, which lets you set rules for the environment a project is built in (e.g., JDK version, Maven version, OS family, even user-defined rules). If you're using it, you already know the key benefits, but have you really given it a good workout?

 

Last week, I found two handy options: requirePluginVersions and bannedDependencies. The first one makes sure that you've specified a version for each plugin you're using. Often, we just use the default of "LATEST". Then a few months into development, a plugin has a new release and our build starts to act... differently. With this setting enabled, Maven will fail your build if any of your plugins don't include a version!  (Make sure you check mvn site before you push your changes!)

<build>
...
<plugins>
...
 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <executions>
   <execution>
    <id>enforce-build-environment</id>
   <phase>validate</phase>
   <goals>
    <goal>enforce</goal>
   </goals>
   <configuration>
    <rules>
....
    <requirePluginVersions>
     <message>Carefully select a version for all plugin dependencies.</message>
    </requirePluginVersions>

 

 

Remember to periodically run mvn versions:display-plugin-updates to see if new versions of your plugins are available.

 

Next, if you've tried switching away from commons-logging or log4j to use slf4j, you've probably found versions of those unwanted loggers showing up from time to time.  Sure, you can run mvn dependency:tree and see how it got there, but by that point it's too late. With bannedDependencies, Maven will walk your dependency graph and a banned dependency will cause your build to fail.  I found this blog entry which showed how to easily make Maven do the heavy lifting for me to keep log4j and commons-logging at bay.

<rules>
...
     <bannedDependencies>
      <searchTransitive>true</searchTransitive>
       <excludes>
        <exclude>log4j:log4j</exclude>
        <exclude>commons-logging</exclude>
        <exclude>org.slf4j:slf4j-log4j12</exclude>
        <exclude>org.slf4j:slf4j-jcl</exclude>
        <exclude>org.slf4j:slf4j-jdk14</exclude>
       </excludes>
       <message><![CDATA[Secondary logging frameworks are banned in preference to org.slf4j:*-over-slf4j.
Use mvn dependency:tree -Dincludes=log4j:log4j to find log4j (etc for commons-logging.)]]></message>
     </bannedDependencies>

 

 

The enforcer has some other built-in rules too. You already fail your build if your tests show your code was wrong, now you can fail your build if your build is wrong. Cool beans!

0 Comments Permalink

When developing your ecommerce site using Elastic Path, using the Eclipse WTP functionality to run your web applications from within Eclipse allows easy debugging. However, there are a few common problems that can occur which can disrupt your development and have you cursing Eclipse. Here are the top 5:

 

1. Not having enough memory allocated

By default, Java allocates only 128 Mb of memory for the heap and 64 Mb for the perm size. This isn't nearly enough for large applications. If you haven't told you server to allocate sufficient memory, it may start up without error, but once you try to do anything significant it will crash with an error like the following:

Exception in thread "RMI TCP Connection(idle)" java.lang.OutOfMemoryError: PermGen space

or

java.lang.OutOfMemoryError: Java heap space

 

As per the developer documentation, ensure you have enough heap and perm space by double-clicking your server in the Servers view, clicking Open launch configuration and adding the following to the VM arguments:

-Xmx1024m -Xms256m -XX:MaxPermSize=512m

 

If you are using Java 6 you should also ensure you have the following:

-Dsun.lang.ClassLoader.allowArraySyntax=true

 

2. Classes not enhanced

If you make changes to core persistence classes in Eclipse, it may compile the class without running the OpenJPA enhancement. When you start your application in WTP, it will try to do runtime enhancement and fail with an error like the following:

java.lang.ClassNotFoundException: org.apache.renamed.openjpa.enhance.InstrumentationFactory
Exception in thread "Attach Listener"      at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
     at java.security.AccessController.doPrivileged(Native Method)
     at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:315)
     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:330)
     at java.lang.ClassLoader.loadClass(ClassLoader.java:250)
     at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:280)
     at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:348)
Agent failed to start!

 

You may also see an error like the following:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageSource' defined in ServletContext resource
[/WEB-INF/conf/spring/views/velocity/velocity.xml]: Cannot resolve reference to bean 'storeMessageSourceDelegate' while setting bean property 'storeMessageSource';
nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'storeMessageSourceDelegate' defined in ServletContext resource [/WEB-INF/conf/spring/views/velocity/velocity.xml]:
Cannot resolve reference to bean 'messageSourceCache' while setting bean property 'messageSourceCache'; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'messageSourceCache' defined in ServletContext resource [/WEB-INF/conf/spring/views/velocity/velocity.xml]: 
Invocation of init method failed; 
nested exception is <openjpa-1.2.1-r62037:76497M nonfatal user error> org.apache.renamed.openjpa.persistence.ArgumentException:
[Error while processing persistent field com.elasticpath.domain.order.impl.AbstractOrderShipmentImpl.status, declared in null. Error details:
The accessor for field getStatus in type com.elasticpath.domain.order.impl.AbstractOrderShipmentImpl is private or package-visible. 
OpenJPA requires accessors in unenhanced instances to be public or protected. 
If you do not want to add such an accessor, you must run the OpenJPA enhancer after compilation, or deploy to an environment that supports deploy-time enhancement,
such as a Java EE 5 application server.]

 

To avoid these, ensure the classes are enhanced by doing one of the following whenever you change a core persistence class:

  • Run ant enhance from the command line if you are working directly on com.elasticpath.core. If you do this be sure to refresh the project in Eclipse.
  • Launch an ant-based enhance from within Eclipse by running coreAntJpaEnhance.launch in core
  • If using binary based development, ensure you are using the Maven OpenJPA plugin for enhancement and you have Eclipse configured to call the maven build

 

 

3. Missing datasource context

The datasource used by the Elastic Path applications to connect to the database is configured through JNDI. When using WTP with Tomcat, for example, you need to make sure you have the JNDI datasource defined in the container. If it can't be found, you will end up with an error something like the below (note that this will be preceded by several pages of  various "Error creating bean..." messages:

Caused by: org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
     at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1150)
     at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:880)
     at org.apache.renamed.openjpa.lib.jdbc.DelegatingDataSource.getConnection(DelegatingDataSource.java:106)
     at org.apache.renamed.openjpa.lib.jdbc.DecoratingDataSource.getConnection(DecoratingDataSource.java:87)
     at org.apache.renamed.openjpa.jdbc.sql.DBDictionaryFactory.newDBDictionary(DBDictionaryFactory.java:91)
     ... 115 more
Caused by: java.sql.SQLException: No suitable driver
     at java.sql.DriverManager.getDriver(DriverManager.java:264)
     at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1143)
     ... 119 more

 

When using Tomcat with the Elastic Path web applications, the ant eclipse-setup task will create a META-INF/context.xml file which defines the datasource using values from your env.config. Ensure this file is present and has the database details you are expecting.

 

Alternatively (or when using binary based development), you may add a <Resource> definition to the Context for your web app directly in the server.xml file. In either case it will look as follows:

    <Resource name="jdbc/epjndi"
        auth="Container"
        scope="Shareable"
        type="javax.sql.DataSource"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        removeAbandoned="true"
        username="root"
        password="password"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/COMMERCE_DB?AutoReconnect=true&useUnicode=true&characterEncoding=utf-8"
    />

 

4. Changes not published

Another common problem is making changes in Eclipse and (mysteriously) not having these changes present when you start your web applications. One cause of is related to the fact that our ant build copies spring configuration to the web applications at build time. All the files from WEB-INF/conf/spring are copied, namely:

 

  • commons/serviceChangeset.xml
  • commons/util-config.xml
  • dataaccess/openjpa/openjpa.xml
  • dataaccess/dao.xml
  • models/domainModel.xml
  • service/service.xml
  • service/serviceRemote.xml

 

If you change any of these files in core within Eclipse, they won't get automatically copied across to your web applications; you will need to re-run ant build for those projects from the command line. Conversely, if you change the copies in your web applications directory, then next time you run ant build your changes will get overwritten.

 

You may also encounter issues where code changes you make do not seem to be present when running your web applications. If this occurs, you may want to check the following:

  • Did you already have your server running when you made changes? Don't rely on Eclipse's ability to "hot swap" code into place.
  • Do you have maven Workspace Resolution disabled? If so, WTP won't be looking at the eclipse-compiled version of your code, but rather using the last version that was installed in your local maven repository. If you do feel the need to have workspace resolution disabled, you will need to run the appropriate ant or maven target on the command line to build and install the changed code into the repository.
  • Did you make any changes outside of Eclipse (including updating from your source code repository if working in a team environment) or run a command line build? If so, make sure you refresh the project in Eclipse so it knows there are changes to publish to WTP

 

5. Unable to publish due to file locks

This one happens less frequently than the others but is one of the more frustrating problems. Sometimes when you have been stopping and starting your web applications through WTP enough, Eclipse may have held onto a file lock for a little to long, and you get a message popup during publishing/startup that it is unable to publish a particular file. It is quite tempting to treat the OK button of this popup message as a "yeah, whatever" button and just continue. Sometimes when this occurs everything does still work, but other times Eclipse will, from that point on, fail to start your web applications properly.

 

There is no specific error message, but rather one or more of the apps will fail to start up and you may see pages of meaningless tomcat DEBUG lines or blank lines. The only good solution to this is to shut down Eclipse completely and relaunch it, which forces it to release the file locks and thus allow a republish. You may also need to right-click your server in the Servers view and select the Clean options to force a full republish.

0 Comments Permalink

 

The MouseFeed update site is located at: http://update.mousefeed.com/

0 Comments Permalink