Hi
Sure, the code follows below.
There are two classes and one interface and only a few references to my own
session classes that allows attaching the session data to the threads.
In Application::init() you enable this with.
AsyncLoader.enableOnApplication(this);
If not enabled, the AsyncLoadableDetachableModel will load as a normal
LoadableDetachableModel without multithreading.
To see that it actually helps, turn on the tracing with:
AsyncLoader.enableTrace(true);
Best regards
Niels Bo
/**
* Asynchronous version of the loading LoadableDetachableModel.
*
* @Author Niels Bo
*/
abstract public class AsyncLoadableDetachableModel<T> implements IModel<T>,
IAsyncLoadable<T> {
private static final long serialVersionUID = 1L;
/**
* Logger.
*/
private static final Logger log =
LoggerFactory.getLogger(AsyncLoadableDetachableModel.class);
/**
* keeps track of whether this model is attached or detached
*/
private transient boolean attached = false;
/**
* temporary, transient object.
*/
private transient T transientModelObject;
/**
* the async loader
*/
private transient AsyncLoader<T> loader;
/**
* Construct.
*/
public AsyncLoadableDetachableModel() {
}
/**
* This constructor is used if you already have the object retrieved and
want to wrap it with a
* detachable model.
*
* @param object retrieved instance of the detachable object
*/
public AsyncLoadableDetachableModel(T object) {
this.transientModelObject = object;
attached = true;
}
/**
* @see org.apache.wicket.model.IDetachable#detach()
*/
public void detach() {
if (attached) {
try {
onDetach();
}
finally {
attached = false;
transientModelObject = null;
loader = null;
log.debug("removed transient object for {}, requestCycle
{}", this,
RequestCycle.get());
}
}
}
/**
* @see org.apache.wicket.model.IModel#getObject()
*/
public T getObject() {
if (!attached) {
attached = true;
transientModelObject = loader != null ? loader.get() : load();
loader = null;
if (log.isDebugEnabled()) {
log.debug("loaded transient object " + transientModelObject
+ " for " + this +
", requestCycle " + RequestCycle.get());
}
onAttach();
}
return transientModelObject;
}
/**
* Gets the attached status of this model instance
*
* @return true if the model is attached, false otherwise
*/
public final boolean isAttached() {
return attached;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
sb.append(":attached=").append(attached).append(":tempModelObject=[").append(
this.transientModelObject).append("]");
return sb.toString();
}
/**
* Loads and returns the (temporary) model object.
*
* @return the (temporary) model object
*/
protected abstract T load();
/**
* Attaches to the current request. Implement this method with custom
behavior, such as loading
* the model object.
*/
protected void onAttach() {
}
/**
* Manually loads the model with the specified object. Subsequent calls
to {...@link #getObject()}
* will return {...@code object} until {...@link #detach()} is called.
*
* @param object The object to set into the model
*/
public void setObject(final T object) {
attached = true;
transientModelObject = object;
}
final public void startloading() {
if (!attached && loader == null) {
loader = new AsyncLoader<T>(this);
}
}
final public T asyncLoad() {
return load();
}
/**
* Detaches from the current request. Implement this method with custom
behavior, such as
* setting the model object to null.
*/
protected void onDetach() {
}
}
/**
* Async loader that perform the load in the session context in a separate
thread.
*
* @Author Niels Bo
*/
public class AsyncLoader<T> implements Callable<T> {
private static final Logger log =
LoggerFactory.getLogger(AsyncLoader.class);
private static ExecutorService executor =
Executors.newCachedThreadPool();
private IAsyncLoadable<T> loadable;
private CoreServiceSession session;
private Future<T> future;
private long started;
private static boolean TRACE = false;
public AsyncLoader(IAsyncLoadable<T> loadable) {
this.loadable = loadable;
//Copy the session into a thread service session that can be used by
the thread
session = CoreSession.createServiceSession();
started = System.currentTimeMillis();
future = executor.submit(this);
}
public T call() throws Exception {
session.set();
T t = loadable.asyncLoad();
session.remove();
return t;
}
public T get() {
try {
if(TRACE) {
long runtime = System.currentTimeMillis() - started;
log.info("Async loading "+runtime+" ms before sync
"+loadable.getClass().getSimpleName());
}
return future.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Start the loading of all async components and models
* @param app
*/
public static void enableOnApplication(Application app) {
app.addPostComponentOnBeforeRenderListener(
new IComponentOnBeforeRenderListener() {
public void onBeforeRender(Component component) {
if(component.isVisible()) {
if (component.getDefaultModel() instanceof
AsyncLoadableDetachableModel) {
((AsyncLoadableDetachableModel)
component.getDefaultModel()).startloading();
}
if (component instanceof DataTable) {
IDataProvider dataProvider = ((DataTable)
component).getDataProvider();
if(dataProvider instanceof AsyncDataProvider) {
((AsyncDataProvider)dataProvider).startloading();
}
}}
}
});
}
/**
* Shut doen the the executor service
*/
public static void shutdown() {
executor.shutdown();
}
public static void enableTrace(boolean trace) {
TRACE = trace;
}
}
interface IAsyncLoadable<T> {
/**
*
* @return
*/
T asyncLoad();
}
--
View this message in context:
http://apache-wicket.1842946.n4.nabble.com/Multithreaded-construction-of-pages-tp3072354p3073186.html
Sent from the Users forum mailing list archive at Nabble.com.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]