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

Reply via email to