slub.team team mailing list archive
-
slub.team team
-
Mailing list archive
-
Message #00370
[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