Merge lp:~ralf-claussnitzer-deactivatedaccount/goobi-production/bug-1040672 into lp:goobi-production/1.8

Proposed by Ralf Claussnitzer
Status: Merged
Merged at revision: 100
Proposed branch: lp:~ralf-claussnitzer-deactivatedaccount/goobi-production/bug-1040672
Merge into: lp:goobi-production/1.8
Diff against target: 313 lines (+254/-14)
4 files modified
src/de/sub/goobi/helper/HelperSchritte.java (+20/-11)
src/de/sub/goobi/helper/ScriptThread.java (+0/-3)
src/org/goobi/thread/Supervisor.java (+123/-0)
test/src/org/goobi/thread/SupervisorTest.java (+111/-0)
To merge this branch: bzr merge lp:~ralf-claussnitzer-deactivatedaccount/goobi-production/bug-1040672
Reviewer Review Type Date Requested Status
Henning Gerhardt Approve
Review via email: mp+121602@code.launchpad.net

This proposal supersedes a proposal from 2012-08-28.

To post a comment you must log in.
Revision history for this message
Henning Gerhardt (henning-gerhardt) wrote : Posted in a previous version of this proposal

Needs some improvements:

- Supervisor thread is started even there are no script steps
- yield waiting time can be negative which let sleep() later throw an exception

review: Needs Fixing
Revision history for this message
Henning Gerhardt (henning-gerhardt) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== 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:49:20 +0000
@@ -30,6 +30,7 @@
30import java.util.List;30import java.util.List;
3131
32import org.apache.log4j.Logger;32import org.apache.log4j.Logger;
33import org.goobi.thread.Supervisor;
33import org.hibernate.Session;34import org.hibernate.Session;
34import org.hibernate.criterion.Order;35import org.hibernate.criterion.Order;
35import org.hibernate.criterion.Restrictions;36import org.hibernate.criterion.Restrictions;
@@ -119,22 +120,30 @@
119 } catch (DAOException e) {120 } catch (DAOException e) {
120 }121 }
121122
122 if (automatic) {
123 try {
124 session.close();
125 } catch (Exception e) {
126 }
127 }
128 /*123 /*
129 * -------------------------------- zum Schluss alle automatischen Schritte nehmen und deren Automatik ausführen124 * -------------------------------- zum Schluss alle automatischen Schritte nehmen und deren Automatik ausführen
130 * --------------------------------125 * --------------------------------
131 */126 */
132 // TODO: Don't use iterators, use for loops instead127
133 for (Iterator<Schritt> iter = automatischeSchritte.iterator(); iter.hasNext();) {128 if (!automatischeSchritte.isEmpty()) {
134 Schritt myStep = iter.next();129 Supervisor scriptThreadSupervisor = new Supervisor();
135 ScriptThread myThread = new ScriptThread(myStep);130
136 myThread.start();131 final Session sessionRef = session;
132 scriptThreadSupervisor.ifAllTerminatedRun(new Runnable() {
133 public void run() {
134 if (sessionRef.isOpen() && sessionRef.isDirty()) {
135 sessionRef.flush();
136 }
137 }
138 });
139
140 for (Schritt step: automatischeSchritte) {
141 scriptThreadSupervisor.addChild(new ScriptThread(step));
142 }
143
144 scriptThreadSupervisor.start();
137 }145 }
146
138 }147 }
139148
140 /**149 /**
141150
=== 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:49:20 +0000
@@ -46,9 +46,6 @@
46 try {46 try {
47 boolean automatic = mySchritt.isTypAutomatisch();47 boolean automatic = mySchritt.isTypAutomatisch();
48 hs.executeAllScripts(mySchritt, automatic);48 hs.executeAllScripts(mySchritt, automatic);
49 if (automatic) {
50 Helper.getHibernateSession().close();
51 }
52 } catch (SwapException e) {49 } catch (SwapException e) {
53 logger.error(e);50 logger.error(e);
54 } catch (DAOException e) {51 } catch (DAOException e) {
5552
=== 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:49:20 +0000
@@ -0,0 +1,123 @@
1/*
2 * This file is part of the Goobi Application - a Workflow tool for the support of
3 * mass digitization.
4 *
5 * Visit the websites for more information.
6 * - http://gdz.sub.uni-goettingen.de
7 * - http://www.goobi.org
8 * - http://launchpad.net/goobi-production
9 *
10 * This program is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation; either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17 * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
18 * should have received a copy of the GNU General Public License along with this
19 * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 * Suite 330, Boston, MA 02111-1307 USA
21 */
22
23package org.goobi.thread;
24
25import java.util.concurrent.CopyOnWriteArrayList;
26import java.util.List;
27
28/**
29 * Thread supervisor that triggers code if all child threads have terminated.
30 *
31 * The supervisor is watching a list of child threads and executes a passed
32 * Runnable object if all child threads have finished execution. It in turn
33 * starts all child threads that have not been started when the supervisor starts.
34 *
35 * If all child threads have finished execution, the supervisor may start a given
36 * Runnable implementation and then itself ends its execution.
37 */
38public class Supervisor extends Thread {
39
40 private List<Thread> threads = new CopyOnWriteArrayList<Thread>();
41
42 private Runnable onAllTerminated = null;
43
44 private int yieldWaitTime = 1000;
45
46 /**
47 * Add a child thread to the watch list.
48 *
49 * @param child Child thread to watch.
50 */
51 public void addChild(Thread child) {
52 threads.add(child);
53 }
54
55 /**
56 * Set time in milliseconds that delay the supervisor execution after it
57 * has called yield() to enable other threads to run.
58 *
59 * The supervisor sleeps for this time span until it watches the list of
60 * child threads again. Default yield wait time is 1000 milliseconds.
61 * Setting the wait time to zero will let the supervisor thread spin
62 * useless CPU cycles polling the child thread list.
63 *
64 * @param millis Yield wait time in milliseconds.
65 * @throws IllegalArgumentException if the value of millis is negative.
66 */
67 public void setYieldWaitTime(int millis) {
68 if (millis < 0) {
69 throw new IllegalArgumentException("Length of time to wait cannot be negative.");
70 }
71 yieldWaitTime = millis;
72 }
73
74 /**
75 * Set up a Runnable implementation that gets executed when all child
76 * threads have terminated.
77 *
78 * The passed Runnable is executed only once.
79 *
80 * @param r java.lang.Runnable implememtation
81 */
82 public void ifAllTerminatedRun(Runnable r) {
83 onAllTerminated = r;
84 }
85
86 /**
87 * Start watching the list of child threads and execute a given Runnable
88 * when all child threads have reached termination state.
89 *
90 * Child threads that have not been started yet get started here.
91 */
92 public void run() {
93 while (true) {
94 for(Thread t: threads) {
95 switch (t.getState()) {
96 case NEW:
97 t.start();
98 break;
99 case TERMINATED:
100 threads.remove(t);
101 break;
102 }
103 }
104 if (threads.isEmpty()) {
105 if (onAllTerminated != null) {
106 onAllTerminated.run();
107 }
108 break;
109 }
110 yieldFor(yieldWaitTime);
111 }
112 }
113
114 private void yieldFor(int millis) {
115 try {
116 yield();
117 sleep(millis);
118 } catch (InterruptedException ire) {
119 }
120 }
121
122}
123
0124
=== 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:49:20 +0000
@@ -0,0 +1,111 @@
1/*
2 * This file is part of the Goobi Application - a Workflow tool for the support of
3 * mass digitization.
4 *
5 * Visit the websites for more information.
6 * - http://gdz.sub.uni-goettingen.de
7 * - http://www.goobi.org
8 * - http://launchpad.net/goobi-production
9 *
10 * This program is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation; either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17 * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
18 * should have received a copy of the GNU General Public License along with this
19 * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 * Suite 330, Boston, MA 02111-1307 USA
21 */
22
23package org.goobi.thread;
24
25import org.junit.Test;
26
27import static org.junit.Assert.*;
28
29public class SupervisorTest {
30
31 @Test
32 public void canBeStarted() {
33 Supervisor sv = new Supervisor();
34 assertEquals("Supervisor thread should be in NEW state.", Thread.State.NEW, sv.getState());
35 }
36
37 @Test(expected = IllegalArgumentException.class)
38 public void throwsIllegalArgumentExceptionIfGivenNegativeYieldWaitTime() {
39 Supervisor sv = new Supervisor();
40 sv.setYieldWaitTime(-1);
41 }
42
43 @Test
44 public void terminatesWhenStartedWithoutChildThreads()
45 throws InterruptedException {
46 Supervisor sv = new Supervisor();
47 sv.setYieldWaitTime(100);
48 sv.start();
49
50 Thread.sleep(200);
51
52 assertEquals("Supervisor thread should be in TERMINATED state.", Thread.State.TERMINATED, sv.getState());
53 }
54
55 @Test
56 public void addedChildThreadShouldRemainInNewState() {
57 Supervisor sv = new Supervisor();
58
59 Thread child = new Thread();
60 sv.addChild(child);
61
62 assertEquals("Child thread should be in NEW state.", Thread.State.NEW, child.getState());
63 }
64
65 @Test
66 public void runsChildThreads()
67 throws InterruptedException {
68 Supervisor sv = new Supervisor();
69
70 Thread child = new Thread();
71 sv.addChild(child);
72 sv.setYieldWaitTime(100);
73 sv.start();
74
75 Thread.sleep(200);
76
77 assertEquals("Child thread should have been run.", Thread.State.TERMINATED, child.getState());
78 }
79
80 @Test
81 public void runsHandlerWhenAllChildThreadsTerminated()
82 throws InterruptedException {
83 Supervisor sv = new Supervisor();
84
85 final Trigger trigger = new Trigger();
86
87 sv.addChild(new Thread());
88 sv.addChild(new Thread());
89 sv.addChild(new Thread());
90
91 sv.ifAllTerminatedRun(new Runnable() { public void run() { trigger.pull(); } } );
92 sv.setYieldWaitTime(100);
93 sv.start();
94
95 Thread.sleep(200);
96
97 assertTrue("Supervisor has not triggered Runnable on child termination.", trigger.hasBeenPulled());
98 }
99
100 private class Trigger {
101 private Boolean pulled = false;
102 public void pull() {
103 pulled = true;
104 }
105 public Boolean hasBeenPulled() {
106 return pulled;
107 }
108 }
109
110}
111

Subscribers

People subscribed via source and target branches

to all changes: