/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2006 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.context;
import java.lang.ThreadLocal;
import javolution.lang.Reflection;
import javolution.lang.Reusable;
import javolution.util.FastMap;
/**
* <p> This class represents an object factory; it allows for object
* recycling, pre-allocation and stack allocations.
*
* <p> Object factories are recommended over class constructors (ref. "new"
* keyword) to allows for custom allocation policy (see
* {@link AllocatorContext}). For example:[code]
* static ObjectFactory<int[][]> BOARD_FACTORY = new ObjectFactory<int[][]>() {
* protected int[][] create() {
* return new int[8][8];
* }
* };
* ...
* int[][] board = BOARD_FACTORY.object();
* // The board object might have been preallocated at start-up,
* // it might also be on the thread "stack/pool" for threads
* // executing in a StackContext.
* ...
* BOARD_FACTORY.recycle(board); // Immediate recycling of the board object (optional).
* [/code]</p>
*
* <p> For arrays of variable length {@link ArrayFactory} is recommended.</p>
*
* <p> For convenience, this class provides a static {@link #getInstance} method
* to retrieve a factory implementation for any given class.
* For example:[code]
* ObjectFactory<ArrayList> listFactory = ObjectFactory.getInstance(ArrayList.class);
* ArrayList list = listFactory.object();
* ... // Do something.
* listFactory.recycle(list); // Optional.
* [/code]</p>
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 5.2, August 14, 2007
*/
public abstract class ObjectFactory <T> {
/**
* Indicates if the objects products of this factory require
* {@link #cleanup(Object) cleanup} when recycled.
*/
private boolean _doCleanup = true;
/**
* Default constructor.
*/
protected ObjectFactory() {
}
/**
* Returns a factory implementation producing instances of the specified
* class. By default this method returns a factory creating new objects
* using the class public no-arg constructor (through reflection).
* If that constructor is not accessible, the factory instance can be
* {@link #setInstance set explicitly}:[code]
* class LogContext {
* public static final Class<LogContext> NULL = Null.class;
* ...
* private static class Null extends LogContext ... // Private.
* static {
* // Allows Null instances to be factory produced (even so the class is not accessible).
* ObjectFactory.setInstance(new ObjectFactory<Null> {
* protected Null create() { return new Null() }},
* Null.class);
* }
* }[/code]
*
* @param forClass the class for which an object factory is returned.
* @return an object factory producing instances of the specified class.
*/
public static <T> ObjectFactory <T> getInstance(Class <T> forClass) {
ObjectFactory factory = (ObjectFactory) Reflection.getInstance().getField(forClass, ObjectFactory.class, false);
return factory != null ? factory : new Generic(forClass);
}
/**
* Sets explicitely the factory to be used for the specified class
* (see {@link #getInstance}).
*
* @param factory the factory to use.
* @param forClass the associated class.
* @see #getInstance(Class)
*/
public static <T> void setInstance(ObjectFactory <T> factory,
Class <T> forClass) {
Reflection.getInstance().setField(factory, forClass, ObjectFactory.class);
}
/**
* Returns a factory object possibly recycled or preallocated.
* This method is equivalent to <code>currentAllocator().next()</code>.
*
* @return a recycled, pre-allocated or new factory object.
*/
public final T object() {
Allocator <T> allocator = _allocator;
return allocator.user == Thread.currentThread() ? allocator.next() : currentAllocator().next();
}
private Allocator <T> _allocator = NULL_ALLOCATOR; // Hopefully in the cache.
private static final Allocator NULL_ALLOCATOR = new Allocator() {
protected Object allocate() {
return null;
}
protected void recycle(Object object) {
}
};
/**
* Recycles the specified object.
* This method is equivalent to <code>getAllocator().recycle(obj)</code>.
*
* @param obj the object to be recycled.
*/
public final void recycle( T obj) {
Allocator <T> allocator = _allocator;
if (allocator.user != Thread.currentThread())
allocator = currentAllocator();
allocator.recycle(obj);
}
/**
* Returns the factory allocator for the current thread (equivalent
* to <code>AllocatorContext.current().getAllocator(this)</code>).
*
* @return the current object queue for this factory.
*/
public final Allocator <T> currentAllocator() {
// Search thread-local value first.
Allocator allocator = (Allocator) _localAllocator.get();
if (allocator.user != null) // Active.
return _allocator = allocator;
// Retrieves allocator from current allocator context.
allocator = Context.getCurrentContext().getAllocatorContext().getAllocator(this);
// Sets diverse shortcuts.
_localAllocator.set(allocator);
_allocator = allocator;
// Returns the allocator.
return allocator;
}
private ThreadLocal _localAllocator = new ThreadLocal() {
protected Object initialValue() {
return NULL_ALLOCATOR;
}
};
/**
* Constructs a new object for this factory (using the <code>new</code>
* keyword).
*
* @return a new factory object.
*/
protected abstract T create();
/**
* Cleans-up this factory's objects for future reuse.
* The default implementation {@link Reusable#reset resets} reusable
* instance. For non {@link Reusable}, this method can be
* overriden to dispose of system resources or to clear references to
* external objects potentially on the heap (it allows these external
* objects to be garbage collected immediately and therefore reduces
* the memory footprint). For example:[code]
* static ObjectFactory<ArrayList> ARRAY_LIST_FACTORY = new ObjectFactory<ArrayList>() {
* protected ArrayList create() {
* return new ArrayList();
* }
* protected void cleanup(ArrayList obj) {
* obj.clear(); // Clears external references.
* }
* };[/code]
*
* @param obj the factory object being recycled.
*/
protected void cleanup( T obj) {
if (obj instanceof Reusable)
((Reusable) obj).reset();
else // No need to cleanup.
_doCleanup = false;
}
/**
* Indicates if this factory requires cleanup.
*
* @return <code>true</code> if {@link #cleanup} is overriden and
* {@link #cleanup} has been called at least once;
* <code>false</code> otherwise.
*/
protected final boolean doCleanup() {
return _doCleanup;
}
// Generic implementation using public no-arg constructor (reflection).
private static class Generic extends ObjectFactory {
private final Class _class;
private Generic(Class cls) {
_class = cls;
}
protected Object create() {
try {
return _class.newInstance();
} catch (InstantiationException e) {
throw new Error(
"Cannot instantiate no-arg constructor for " + _class.getName() + ", the factory should be set explicitly using ObjectFactory.setInstance");
} catch (IllegalAccessException e) {
throw new Error(
"Cannot access no-arg constructor for " + _class.getName() + ", the factory should be set explicitly using ObjectFactory.setInstance");
}
}
}
}