/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.event.tx; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.event.EventBundle; import org.eclipse.ecr.core.event.EventStats; import org.eclipse.ecr.core.event.ReconnectedEventBundle; import org.eclipse.ecr.core.event.impl.EventListenerDescriptor; import org.eclipse.ecr.core.event.impl.ReconnectedEventBundleImpl; import org.eclipse.ecr.runtime.api.Framework; /** * Runs synchronous Listeners in a separated thread in order to enable TX * management * * @author Thierry Delprat */ public class PostCommitSynchronousRunner { public static final int DEFAULT_TIME_OUT_MS = 300; private static final Log log = LogFactory.getLog(PostCommitSynchronousRunner.class); protected final List<EventListenerDescriptor> listeners; protected final ReconnectedEventBundle event; protected long timeout = 0; public PostCommitSynchronousRunner(List<EventListenerDescriptor> listeners, EventBundle event, long timeout) { this.listeners = listeners; if (event instanceof ReconnectedEventBundle) { this.event = (ReconnectedEventBundle) event; } else { this.event = new ReconnectedEventBundleImpl(event); } this.timeout = timeout; } public PostCommitSynchronousRunner(List<EventListenerDescriptor> listeners, EventBundle event) { this(listeners, event, DEFAULT_TIME_OUT_MS); } public void run() { runSync(); } protected void handleUnfinishedThread(Thread runner) { log.warn("PostCommitListeners are too slow, check debug log ..."); log.warn("Exit before the end of processing"); } protected void runSync() { log.debug("Starting sync executor from Thread " + Thread.currentThread().getId()); Thread runner = new Thread(getExecutor()); runner.start(); try { runner.join(timeout); if (runner.isAlive()) { handleUnfinishedThread(runner); } } catch (InterruptedException e) { log.error("Exit before the end of processing", e); } log.debug("Terminated sync executor from Thread " + Thread.currentThread().getId()); } protected Runnable getExecutor() { return new MonoThreadExecutor(); } protected class MonoThreadExecutor implements Runnable, Thread.UncaughtExceptionHandler { protected EventBundleTransactionHandler txh = new EventBundleTransactionHandler(); protected EventStats getEventStats() { try { return Framework.getService(EventStats.class); } catch (Exception e) { log.warn("Failed to lookup event stats service", e); } return null; } @Override public void run() { long t0 = System.currentTimeMillis(); log.debug("Start post commit sync execution in Thread " + Thread.currentThread().getId()); EventStats stats = getEventStats(); for (EventListenerDescriptor listener : listeners) { try { long t1 = System.currentTimeMillis(); txh.beginNewTransaction(); listener.asPostCommitListener().handleEvent(event); event.disconnect(); txh.commitOrRollbackTransaction(); if (stats != null) { stats.logAsyncExec(listener, System.currentTimeMillis() - t1); } log.debug("End of post commit sync execution for listener " + listener.getName() + " " + (System.currentTimeMillis() - t1) + "ms"); } catch (Throwable t) { log.error( "Exception during post commit sync execution for listener " + listener.getName(), t); event.disconnect(); txh.rollbackTransaction(); } } log.debug("End of all post commit sync executions : " + (System.currentTimeMillis() - t0) + "ms"); } @Override public void uncaughtException(Thread t, Throwable e) { event.disconnect(); txh.rollbackTransaction(); } } }