Singleton.java
/*
* Copyright © 2013 Michael Lasmanis (michael@lasmanis.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lasmanis.javapatterns;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Basic singleton implementation.
* <p>
* This class provide the implementation of a thread-safe, generic singleton
* pattern.
* {@link com.lasmanis.javapatterns.Singleton#instance(java.lang.Class)} is a
* thread-safe operation for both retreval and creation.
* Derived classes are responsible
* for initializing themselves in a thread-safe manner. If initialization is
* required, the code involved is contained in a <code>synchronized</code>
* block tied to the {@link com.lasmanis.javapatterns.Singleton} class.
* <p>
* Deriving classes should declare a non-public default constructor. This
* class assumes the existing of a default constructor (ie, takes no
* parameters) and uses reflection to locate the constructor if an instance
* of a particular derived class has not yet been instantiated. For safety
* reasons, derived classes should not declare any public constructors.
* <p>
* This class provides no facilities for calling a non-default constructor
* and initialization of the created instance is left to the derived class.
* <p>
* The expect form of a derived class looks is as follows:
* <pre>
* <code>
*public class IntegerContainer
* extends Singleton
*{
* // instance local data
* public int a;
*
* // private constructor for initialization
* private IntegerContainer()
* {
* a = 1;
* }
*
* // convenience method
* public static IntegerContainer instance()
* throws InstantiationException,
* IllegalAccessException,
* NoSuchMethodException,
* IllegalArgumentException,
* InvocationTargetException,
* ExceptionInInitializerError,
* SecurityException
* {
* return IntegerContainer.instance(IntegerContainer.class);
* }
*}
* </code>
* </pre>
* The addition of the simplified <code>instance()</code> is purely for the
* convenience of the programmer. It simplifes the invocation and reduces
* accidental type mis-matches. With this additional method, getting an
* instance is as simple as :
* <pre>
* <code>
* IntegerContainer i = IntegerContainer.instance();
* </code>
* </pre>
*
* @author mpl
*/
public class Singleton {
/**
* Map of the class to local instances data.
*/
private static final Map<Class<? extends Singleton>, Singleton> INSTANCES =
new ConcurrentHashMap<Class<? extends Singleton>, Singleton>();
/**
* Constructor.
* <p>
* Scope is protected for instantiation control.
*/
protected Singleton() {
super();
}
/**
* Access the instance backing the singleton
* <p>
* This method returns an existing derived
* {@link com.lasmanis.javapatterns.Singleton} instance or
* allocates a new instance. This method uses reflection, specifically
* {@link java.lang.reflect.Constructor#newInstance(java.lang.Object[])} and
* {@link java.lang.Class#getDeclaredConstructor(java.lang.Class[])} to
* accomplish this. The method also assumes the existence of the default
* constructor taking zero parameters and will throw
* {@link java.lang.NoSuchMethodException} if
* the default constructor is not found.
* <p>
* This is a thread-safe operation.
*
* @param <U> the type parameter of the derived object object.
* @param c the {@link java.lang.Class} object corresponding to U.
* @return the derived {@link com.lasmanis.javapatterns.Singleton} object.
* @throws java.lang.InstantiationException if the class that declares the
* underlying constructor represents an abstract class.
* @throws java.lang.IllegalAccessException if this Constructor object
* enforces Java language access control and the underlying
* constructor is inaccessible.
* @throws java.lang.NoSuchMethodException if the default constructor is not
* found.
* @throws java.lang.IllegalArgumentException if the number of actual and
* formal parameters differ; if an unwrapping conversion for
* primitive arguments fails; or if, after possible unwrapping, a
* parameter value cannot be converted to the corresponding formal
* parameter type by a method invocation conversion; if this
* constructor pertains to an enum type.
* @throws java.lang.reflect.InvocationTargetException if the underlying
* constructor throws an exception.
* @throws java.lang.ExceptionInInitializerError if the initialization
* provoked by this method fails.
* @throws java.lang.SecurityException if the security manager is present
* and prevents access. See {@link
* java.lang.Class#getDeclaredConstructor(java.lang.Class[])}
* @throws java.lang.NullPointerException if c is null.
*/
public static <U extends Singleton> U instance(
final Class<U> c)
throws InstantiationException,
IllegalAccessException,
NoSuchMethodException,
IllegalArgumentException,
InvocationTargetException,
ExceptionInInitializerError,
SecurityException,
NullPointerException {
// check
if (c == null) {
throw new NullPointerException();
}
// check for an existing instance
@SuppressWarnings("unchecked")
U i = (U) INSTANCES.get(c);
if (i != null) {
// an instance already exists, so return it
return i;
} else {
// need to make a new instance
synchronized (Singleton.class) {
// check again in case someone else created it while we were
// blocked
@SuppressWarnings("unchecked")
U j = (U) INSTANCES.get(c);
if (j != null) {
// an instance already exists, so return it
return j;
}
// get the default constructor and make it accessible to us
Class<?>[] typeList = new Class<?>[0];
Constructor<U> ctor = c.getDeclaredConstructor(typeList);
ctor.setAccessible(true);
// create the new instance and save it
j = ctor.newInstance(new Object[0]);
INSTANCES.put(c, j);
return j;
}
}
}
/**
* retrieve the instances backing store.
* @return the map of instances
*/
public static Map<Class<? extends Singleton>, Singleton> getInstances() {
return INSTANCES;
}
}