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: users-unsubscr...@wicket.apache.org For additional commands, e-mail: users-h...@wicket.apache.org