ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Using the Singleton Pattern

by Budi Kurniawan
08/27/2003

The Singleton pattern is one of the simpler design patterns in the book Design Patterns: Elements of Reusable Object-Oriented Software. This pattern is effective for limiting the maximum number of instances of a class to exactly one. In this case, if more than one object needs to use an instance of the Singleton class, those objects share the same Singleton class instance. In more advanced use, this pattern can also be used to manage exactly n instances of a class. This article introduces this pattern with a simple example and provides a real-world example by presenting the StringManager class in Tomcat, the most popular servlet container. All of the examples used in this article can be downloaded here.

Beginners and seasoned object-oriented programmers know that they create an instance of a class by invoking the class' constructor preceded by the new keyword. For example, the following code constructs an instance of class MyClass by calling its no-argument constructor:

new MyClass()

You get one object each time a constructor is called. If you call a class' constructor three times, you get three instances of the class. Even if you don't write a constructor in your class, the compiler creates a no-argument constructor for you. However, if a class does have a constructor, whether it is a no-argument constructor or not, the compiler does not create a new one. Normally, these constructors have a public access modifier because they are meant to be invoked from outside of the class.

However, there are cases where you want to limit the number of instances of a class to one. For example, recall that in Microsoft Word you can press Ctrl-F to display a Find dialog. However, for the whole life of Microsoft Word, there can only be one Find dialog. If you press Ctrl-F two times, there is still only one Find dialog. Even when there are multiple documents open, there can only be one Find dialog that works with any active document. Indeed, you don't need more than one instance of the Find dialog. Having more than one will probably complicate matters. (Imagine having multiple instances of the Find dialog while editing a document in Microsoft Word.)

Related Reading

Tomcat: The Definitive Guide
By Jason Brittain, Ian F. Darwin

The Singleton pattern can be used for this purpose. This pattern is effective for limiting the maximum number of instances of a class to exactly one. In this case, if more than one object needs to use an instance of the Singleton class, those objects share the same Singleton class instance. A class that implements the Singleton pattern is called a Singleton class.

How do you write a Singleton class? Simple: create a public static method that is solely responsible for creating the single instance of the class. This also means that the client of the class should not be able to invoke the Singleton class's constructor, either. Because the absence of a constructor will make the compiler create a no-argument public constructor, a class applying the Singleton pattern has a private or protected constructor. Because the constructor is private or protected, there is no way a client can create an instance of that class by calling its constructor. The constructor is not accessible from outside of the class!

If the only constructor cannot be accessed, how do we get an instance of that class? The answer lies in a static method in the class. As mentioned above, a Singleton class will have a static method that calls the constructor to create an instance of the class and return this instance to the caller of the static method. But isn't the constructor private? That's right. However, remember that the static method is also in the same class; therefore, it has access to all members of the class, including the private members of the class.

You might ask the following question: "You can't create an instance of a class by calling its constructor, so how do you call the static method (responsible for creating the single instance of the class) without having the class instance?" Note, though, that static members of a class can be invoked without having an instance of that class. To limit the number of instances to one, the static method has to check if an instance has been created before. If it has, it simply returns a reference to the previous created instance. If it has not, it calls the constructor to create one. It's as simple as that.

A Simple Example

As an example, consider a class called SingletonFrame in Listing 1. This class is a Singleton class.

Listing 1. A Singleton class

package singleton;
import javax.swing.*;

public class SingletonFrame extends JFrame {
    private static SingletonFrame myInstance;

    // the constructor
    private SingletonFrame() {
        this.setSize(400, 100);

        this.setTitle("Singleton Frame. Timestamp:" +
            System.currentTimeMillis());

        this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    }

    public static SingletonFrame getInstance() {
        if (myInstance == null)
            myInstance = new SingletonFrame();

        return myInstance;
    }

}

First of all, notice that the class only has one constructor and its access modifier is private. Secondly, to get an instance of that class, you have the static getInstance method, and there is also a static variable called myInstance (of type SingletonFrame). The getInstance method returns the myInstance variable. The method checks if myInstance is null and, if so, calls the constructor.

if (myInstance == null)
    myInstance = new SingletonFrame();
return myInstance;

The method then returns myInstance.

To obtain a reference to the only instance of the SingletonForm class, call the getInstance method, as in the following snippet.

SingletonFrame singletonFrame =
    SingletonFrame.getInstance();

Once you have an instance, you can call its public members just as you would a normal class's object. For example, since SingletonFrame extends the Frame class, you can call its setVisible method.

The MyFrame class in Listing 2 shows how to use the SingletonFrame class. MyFrame is a JFrame with two buttons. Clicking one of the buttons creates an instance of SingletonFrame. However, clicking either button again will return the same instance.

Listing 2. Using the SingletonFrame

package singleton;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class MyFrame extends JFrame {

    JButton jButton1 = new JButton();
    JButton jButton2 = new JButton();

    public MyFrame() {
        try {
            init();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    private void init() throws Exception {
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        jButton1.setText("Show Singleton Frame");
        jButton1.setBounds(new Rectangle(12, 12, 220, 40));
        jButton1.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(ActionEvent e) {
                jButton1_actionPerformed(e);
            }
        });

        jButton2.setText("Show the same Singleton Frame");
        jButton2.setBounds(new Rectangle(12, 72, 220, 40));
        jButton2.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(ActionEvent e) {
                jButton2_actionPerformed(e);
            }
        });

        this.getContentPane().setLayout(null);
        this.getContentPane().add(jButton1, null);
        this.getContentPane().add(jButton2, null);
    }

    void jButton1_actionPerformed(ActionEvent e) {
        SingletonFrame singletonFrame = SingletonFrame.getInstance();
        singletonFrame.setVisible(true);
    }

    void jButton2_actionPerformed(ActionEvent e) {
        SingletonFrame singletonFrame = SingletonFrame.getInstance();
        singletonFrame.setVisible(true);
    }

    public static void main(String[] args) {
        MyFrame frame = new MyFrame();
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

Figure 1 shows MyFrame. If you click one of the buttons, the SingletonFrame will be displayed (Figure 2). Note the timestamp on the title of SingletonFrame.

the MyFrame frame
Figure 1. The MyFrame frame

the SingletonFrame
Figure 2. The SingletonFrame

Now close the SingletonFrame, then click a button on MyFrame and notice that you get the same timestamp, indicating that you get the same instance.

Now, let's look at the Singleton pattern used to maintain exactly n instances of a class.

Real-World Singleton Example: The StringManager Class in Tomcat

A large application such as Tomcat needs to handle error messages carefully. In Tomcat, error messages are useful for both system administrators and servlet programmers. For example, Tomcat logs error messages in order for system administrators to easily pinpoint any abnormality that has happened. For servlet programmers, Tomcat sends a particular error message inside of every javax.servlet.ServletException thrown so that the programmer knows what has gone wrong with a servlet.

Tomcat uses ResourceBundles to support localized (multilingual) error messages and store those error messages in a properties file, so editing them is easy. However, there are hundreds of classes in Tomcat. Storing all error messages used by all classes in one big properties file will easily create a maintenance nightmare. To avoid this, Tomcat allocates a properties file for each package. For example, the properties file for the org.apache.catalina.connector package contains all error messages that can be thrown from any class in that package.

Each properties file is handled by an instance of the org.apache.catalina.util.StringManager class. When Tomcat is run, there will be many instances of StringManager, each of which reads a properties file specific to a package. The base name for the ResourceBundle is LocalStrings, so properties files must be named "LocalStrings" plus the language code. For example, the default properties file will be LocalStrings.properties. The properties file containing messages in the German language is named LocalStrings_de.properties, for example.

When a class in a package needs to look up an error message in that package's properties file, it will first obtain an instance of StringManager. However, many classes in the same package may need a StringManager, and it is a waste of resources to create a StringManager instance for every object that needs error messages. The StringManager class was therefore designed so that an instance is shared by all objects inside a package. It is a Singleton class with multiple instances. The StringManager class is too long to list here, but it's easy to understand.

The only constructor in StringManager is private so that you cannot use the new keyword to instantiate it from outside the class. You get an instance by calling its public static method getManager, passing a package name. Each instance is stored in a Hashtable with package names as its keys.

private static Hashtable managers = new Hashtable();

public synchronized static StringManager getManager(String packageName) {
    StringManager mgr = (StringManager)managers.get(packageName);
    if (mgr == null) {
        mgr = new StringManager(packageName);
        managers.put(packageName, mgr);
    }
    return mgr;
}

To get an error message, use the StringManager class's getString method, passing a key. Here is the signature of one of its overloads:

public String getString(String key)

Using the StringManager Class

The following example demonstrates how to use StringManager from an application to support localized error messages in English and German. The application takes two numbers from the user and shows the result of addition of the numbers. The commands and result are displayed in either English (the default) or German. The application consists of two packages: myPackage1 and myPackage2. Listings 3 and 4 show the LocalStrings.properties and LocalStrings_de.properties files in myPackage1.

Listing 3. The LocalStrings.properties file in myPackage1

promptInput=Please enter 2 numbers which are smaller than 100.
firstNumberRequest=Enter the First Number.
secondNumberRequest=Enter the Second Number.
invalidInput=Please type in a number.
result=Result

Listing 4. The LocalStrings_de.properties file in myPackage1

promptInput=Tragen Sie bitte 2 Zahlen die sind kleiner als 100 ein.
firstNumberRequest=Tragen Sie die Erste Zahl ein.
secondNumberRequest=Tragen Sie die Zweite Zahl ein.
invalidInput=Bitte tippen eine Zahl ein.
result=Ergebnis

The LocalStrings.properties and LocalString_de.properties in myPackage2 are given in Listings 5 and 6, respectively.

Listing 5. The LocalStrings.properties file in myPackage2

smallNumbersOnlyWarning=One of the numbers is more than 100

Listing 6. The LocalStrings_de.properties file in myPackage2

smallNumbersOnlyWarning=Eine der Zahlen ist mehr als 100.

The main class in this application is myPackage1.Test1, given in Listing 7.

Listing 7. The myPackage1.Test1 class

package myPackage1;

import java.io.*;
import org.apache.catalina.util.StringManager;
import myPackage2.Adder;

public class Test1 {
    int a, b;

    protected static StringManager sm =
        StringManager.getManager("myPackage1");

    public void getInput() {
        System.out.println(sm.getString("promptInput"));
        InputReader inputReader = new InputReader();

        a = inputReader.getFirstNumber();
        b = inputReader.getSecondNumber();
    }

    private int add() {
        Adder adder = new Adder();
        return adder.addSmallNumbers(a, b);
    }

    public void printResult() {
        System.out.println(sm.getString("result") + " : " + add());
    }

    public static void main(String[] args) {
        Test1 test = new Test1();
        test.getInput();
        test.printResult();
    }
}

First note the sm variable reference that gets an instance of StringManager:

protected static StringManager sm =
    StringManager.getManager("myPackage1");

The main method instantiates the class and calls the getInput method and the printResult method. The getInput method instantiates the InputReader class in myPackage1 and calls its getFirstNumber and getSecondNumber methods. The InputReader class in listed in Listing 8.

Listing 8. The myPackage1.InputReader class

package myPackage1;

import java.io.*;
import org.apache.catalina.util.StringManager;

public class InputReader {
    protected static StringManager sm =
        StringManager.getManager("myPackage1");

    public int getFirstNumber() {
        int input = 0;
        boolean inputValid = false;

        while (!inputValid) {
            System.out.println(sm.getString("firstNumberRequest"));
            try {
                BufferedReader userInput = new BufferedReader(new
                    InputStreamReader(System.in));
                input = Integer.parseInt(userInput.readLine());
                inputValid = true;
            }
            catch (Exception e) {
                System.out.println(sm.getString("invalidInput"));
            }
        }

        return input;
    }

    public int getSecondNumber() {
        int input = 0;
        boolean inputValid = false;

        while (!inputValid) {
            System.out.println(sm.getString("secondNumberRequest"));
            try {
                BufferedReader userInput = new BufferedReader(new
                    InputStreamReader(System.in));
                input = Integer.parseInt(userInput.readLine());
                inputValid = true;
            }
            catch (Exception e) {
                System.out.println(sm.getString("invalidInput"));
            }
        }

        return input;
    }
}

The InputReader class also has a variable reference to an instance of StringManager. Both the getFirstNumber and getSecondNumber methods use the following code to obtain user input:

BufferedReader userInput = new BufferedReader(new
    InputStreamReader(System.in));
input = Integer.parseInt(userInput.readLine());

In the getFirstNumber method, the following line of code is used to prompt the user to enter the first number:

System.out.println(sm.getString("firstNumberRequest"));

If the user entered an invalid input, the following line prints an error message:

System.out.println(sm.getString("invalidInput"));

The printResult method in the Test1 class instantiates the Adder class in myPackage2 and calls its addSmallNumbers method. The Adder class is given in Listing 9.

Listing 9. The myPackage2.Adder class

package myPackage2;

import org.apache.catalina.util.StringManager;

public class Adder {
    protected static StringManager sm =
        StringManager.getManager("myPackage2");

    public int addSmallNumbers(int a, int b) {
        if (a>100 || b>100) {
            // print warning
            System.out.println(sm.getString("smallNumbersOnlyWarning"));
        }

        return a + b;
    }
}

Again, this class has the reference to an instance of StringManager.

protected static StringManager sm =
    StringManager.getManager("myPackage2");

This time however, the getManager method gets myPackage2 as the argument; therefore, a different instance of StringManager is used than the one in the myPackage1.Test1 and myPackage.InputReader classes. The addSmallNumber method will print a warning if one of the two arguments passed into it is greater than 100:

System.out.println(sm.getString("smallNumbersOnlyWarning"));

Now, run the myPackage1.Test1 class. If your computer's language setting is English or a language other than German, here is what you should see on the console after entering one number, one non-number, and another number:

Please enter 2 numbers which are smaller than 100.Enter the
First Number.120

Enter the Second Number.3r

Please type in a number.Enter the Second Number.3

One of the numbers is more than 100Result : 123

If your computer setting uses German as its language, here is the result:

Tragen Sie bitte 2 Zahlen die sind kleiner als 100 ein.Tragen
Sie die Erste Zahl ein.120

Tragen Sie die Zweite Zahl ein.3r

Bitte tippen eine Zahl ein.Tragen Sie die Zweite Zahl ein.3

Eine der Zahlen ist mehr als 100.Ergebnis : 21123

Summary

The Singleton pattern is used to limit an instance of a class to one by creating a private or protected constructor. You have learned a simple example of the Singleton pattern in the SingletonFrame class. In other uses, you can also use the Singleton pattern to restrict the number of instances of a class to n. In this case, you need a way to maintain the instances. A real-world example given is the StringManager class in Tomcat. You have also seen how to use this class from your application.

Budi Kurniawan is a senior J2EE architect and author.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.