← Back to team overview

slub.team team mailing list archive

[Merge] lp:~ralf-claussnitzer/goobi-production/bug-1040672 into lp:goobi-production

 

Ralf Claussnitzer has proposed merging lp:~ralf-claussnitzer/goobi-production/bug-1040672 into lp:goobi-production.

Requested reviews:
  Henning Gerhardt (henning-gerhardt)
Related bugs:
  Bug #1040672 in Goobi.Production: "crash on automated script step parameter replacement"
  https://bugs.launchpad.net/goobi-production/+bug/1040672

For more details, see:
https://code.launchpad.net/~ralf-claussnitzer/goobi-production/bug-1040672/+merge/121593
-- 
https://code.launchpad.net/~ralf-claussnitzer/goobi-production/bug-1040672/+merge/121593
Your team Saxon State Library Team is subscribed to branch lp:goobi-production.
=== modified file 'src/de/sub/goobi/helper/HelperSchritte.java'
--- src/de/sub/goobi/helper/HelperSchritte.java	2012-04-24 12:00:57 +0000
+++ src/de/sub/goobi/helper/HelperSchritte.java	2012-08-28 12:09:18 +0000
@@ -30,6 +30,7 @@
 import java.util.List;
 
 import org.apache.log4j.Logger;
+import org.goobi.thread.Supervisor;
 import org.hibernate.Session;
 import org.hibernate.criterion.Order;
 import org.hibernate.criterion.Restrictions;
@@ -119,22 +120,28 @@
 		} catch (DAOException e) {
 		}
 
-		if (automatic) {
-			try {
-				session.close();
-			} catch (Exception e) {
-			}
-		}
 		/*
 		 * -------------------------------- zum Schluss alle automatischen Schritte nehmen und deren Automatik ausführen
 		 * --------------------------------
 		 */
-		// TODO: Don't use iterators, use for loops instead
-		for (Iterator<Schritt> iter = automatischeSchritte.iterator(); iter.hasNext();) {
-			Schritt myStep = iter.next();
-			ScriptThread myThread = new ScriptThread(myStep);
-			myThread.start();
+
+		Supervisor scriptThreadSupervisor = new Supervisor();
+
+		final Session sessionRef = session;
+		scriptThreadSupervisor.ifAllTerminatedRun(new Runnable() {
+			public void run() {
+				if (sessionRef.isOpen() && sessionRef.isDirty()) {
+					sessionRef.flush();
+				}
+			}
+		});
+
+		for (Schritt step: automatischeSchritte) {
+			scriptThreadSupervisor.addChild(new ScriptThread(step));
 		}
+
+		scriptThreadSupervisor.start();
+
 	}
 
 	/**

=== modified file 'src/de/sub/goobi/helper/ScriptThread.java'
--- src/de/sub/goobi/helper/ScriptThread.java	2012-02-22 07:43:02 +0000
+++ src/de/sub/goobi/helper/ScriptThread.java	2012-08-28 12:09:18 +0000
@@ -46,9 +46,6 @@
 		try {
 			boolean automatic = mySchritt.isTypAutomatisch();
 			hs.executeAllScripts(mySchritt, automatic);
-			if (automatic) {
-				Helper.getHibernateSession().close();
-			}
 		} catch (SwapException e) {
 			logger.error(e);
 		} catch (DAOException e) {

=== added directory 'src/org/goobi/thread'
=== added file 'src/org/goobi/thread/Supervisor.java'
--- src/org/goobi/thread/Supervisor.java	1970-01-01 00:00:00 +0000
+++ src/org/goobi/thread/Supervisor.java	2012-08-28 12:09:18 +0000
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ *     - http://gdz.sub.uni-goettingen.de
+ *     - http://www.goobi.org
+ *     - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.thread;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.List;
+
+/**
+ * Thread supervisor that triggers code if all child threads have terminated.
+ *
+ * The supervisor is watching a list of child threads and executes a passed
+ * Runnable object if all child threads have finished execution. It in turn
+ * starts all child threads that have not been started when the supervisor starts.
+ * 
+ * If all child threads have finished execution, the supervisor may start a given
+ * Runnable implementation and then itself ends its execution.
+ */
+public class Supervisor extends Thread {
+
+	private List<Thread> threads = new CopyOnWriteArrayList<Thread>();
+
+	private Runnable onAllTerminated = null;
+
+	private int yieldWaitTime = 1000;
+
+	/**
+	 * Add a child thread to the watch list.
+	 *
+	 * @param child Child thread to watch.
+	 */
+	public void addChild(Thread child) {
+		threads.add(child);
+	}
+
+	/**
+	 * Set time in milliseconds that delay the supervisor execution after it
+	 * has called yield() to enable other threads to run.
+	 *
+	 * The supervisor sleeps for this time span until it watches the list of
+	 * child threads again. Default yield wait time is 1000 milliseconds.
+	 * Setting the wait time to zero will let the supervisor thread spin
+	 * useless CPU cycles polling the child thread list.
+	 *
+	 * @param millis Yield wait time in milliseconds.
+	 */
+	public void setYieldWaitTime(int millis) {
+		yieldWaitTime = millis;
+	}
+
+	/**
+	 * Set up a Runnable implementation that gets executed when all child
+	 * threads have terminated.
+ 	 *
+	 * The passed Runnable is executed only once.
+	 * 
+	 * @param r java.lang.Runnable implememtation
+	 */
+	public void ifAllTerminatedRun(Runnable r) {
+		onAllTerminated = r;
+	}
+
+	/**
+	 * Start watching the list of child threads and execute a given Runnable
+	 * when all child threads have reached termination state.
+	 *
+	 * Child threads that have not been started yet get started here.
+	 */
+	public void run() {
+		while (true) {
+			for(Thread t: threads) {
+				switch (t.getState()) {
+					case NEW:
+						t.start();
+						break;
+					case TERMINATED:
+						threads.remove(t);
+						break;
+				}
+			}
+			if (threads.isEmpty()) {
+				if (onAllTerminated != null) {
+					onAllTerminated.run();
+				}
+				break;
+			}
+			yieldFor(yieldWaitTime);	
+		}
+	}
+
+	private void yieldFor(int millis) {
+		try {
+			yield();
+			sleep(millis);
+		} catch (InterruptedException ire) {
+		}
+	}
+
+}
+

=== added directory 'test/src/org/goobi/thread'
=== added file 'test/src/org/goobi/thread/SupervisorTest.java'
--- test/src/org/goobi/thread/SupervisorTest.java	1970-01-01 00:00:00 +0000
+++ test/src/org/goobi/thread/SupervisorTest.java	2012-08-28 12:09:18 +0000
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ *     - http://gdz.sub.uni-goettingen.de
+ *     - http://www.goobi.org
+ *     - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.thread;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SupervisorTest {
+
+	@Test
+	public void canBeStarted() {
+		Supervisor sv = new Supervisor();
+		assertEquals("Supervisor thread should be in NEW state.", Thread.State.NEW, sv.getState());
+	}
+
+	@Test
+	public void terminatesWhenStartedWithoutChildThreads()
+	throws InterruptedException {
+		Supervisor sv = new Supervisor();
+		sv.setYieldWaitTime(100);
+		sv.start();
+
+		Thread.sleep(200);
+
+		assertEquals("Supervisor thread should be in TERMINATED state.", Thread.State.TERMINATED, sv.getState());
+	}
+
+	@Test
+	public void addedChildThreadShouldRemainInNewState() {
+		Supervisor sv = new Supervisor();
+		
+		Thread child = new Thread();
+		sv.addChild(child);
+		
+		assertEquals("Child thread should be in NEW state.", Thread.State.NEW, child.getState());
+	}
+
+	@Test
+	public void runsChildThreads()
+	throws InterruptedException {
+		Supervisor sv = new Supervisor();
+		
+		Thread child = new Thread();
+		sv.addChild(child);
+		sv.setYieldWaitTime(100);
+		sv.start();
+
+		Thread.sleep(200);
+
+		assertEquals("Child thread should have been run.", Thread.State.TERMINATED, child.getState());
+	}
+	 
+	@Test
+	public void runsHandlerWhenAllChildThreadsTerminated()
+	throws InterruptedException {
+		Supervisor sv = new Supervisor();
+
+		final Trigger trigger = new Trigger();
+
+		sv.addChild(new Thread());
+		sv.addChild(new Thread());
+		sv.addChild(new Thread());
+
+		sv.ifAllTerminatedRun(new Runnable() { public void run() { trigger.pull(); } } );
+		sv.setYieldWaitTime(100);
+		sv.start();
+
+		Thread.sleep(200);
+
+		assertTrue("Supervisor has not triggered Runnable on child termination.", trigger.hasBeenPulled());
+	}
+
+	private class Trigger {
+		private Boolean pulled = false;
+		public void pull() {
+			pulled = true;
+		}
+		public Boolean hasBeenPulled() {
+			return pulled;
+		}
+	}
+
+}
+


Follow ups