Re: Multithreaded construction of pages

2010-12-05 Thread NielsBo

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 AsyncLoadableDetachableModelT implements IModelT,
IAsyncLoadableT {
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 AsyncLoaderT 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 AsyncLoaderT(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 AsyncLoaderT implements CallableT {
private static final Logger log =
LoggerFactory.getLogger(AsyncLoader.class);
private static ExecutorService executor =
Executors.newCachedThreadPool();
private IAsyncLoadableT loadable;
private CoreServiceSession session;
private FutureT future;
private long started;
private static boolean TRACE = false;

public AsyncLoader(IAsyncLoadableT loadable) {
this.loadable = loadable;
//Copy the session into a thread service session that can be used by
the thread
 session = 

Multithreaded construction of pages

2010-12-04 Thread NielsBo

Hi

I would like to share my experience with implementing multithreading in my
Wicket application.
The problem was pages containing many independent panels each fething data
from external services, and the result being slow pages because each panel
is processed one at a time in Wicket. 

The solution involved creating a AsyncLoadableDetachableModel class that
does the loading in a separate thread. I use the
java.util.concurrent.ExecutorService threadpool and the
AsyncLoadableDetachableModel then contains a Future object that synchronizes
with the main thread when getObject() is called. 
The threads are started by an IComponentOnBeforeRenderListener on the
application.

Session data is attached/detached to the Threads so they execute the loading
in a context of the end user. I don't use the Wicket Session for this, but a
separate class that implements a MySession interface with properties like
userid, locale etc. that are copied from the Wicket session before starting
the threads.

I think this turned out very nice, and I can now simply replace a use of
LoadableDetachableModel with my new AsyncLoadableDetachableModel where data
is loaded from external services. And it works perfectly! Response time on
the pages are now the time of the slowest panel. Even with just one panel
use the Async model, there can still be an effect if another slow panel is
before the async one in the page/panel hierachy. 

Best regards
Niels Bo



-- 
View this message in context: 
http://apache-wicket.1842946.n4.nabble.com/Multithreaded-construction-of-pages-tp3072354p3072354.html
Sent from the Users forum mailing list archive at Nabble.com.

-
To unsubscribe, e-mail: users-unsubscr...@wicket.apache.org
For additional commands, e-mail: users-h...@wicket.apache.org