Merge lp:~slub.team/goobi-production/mq-interface-to-create-and-finalise-processes into lp:goobi-production/1.8

Proposed by Ralf Claussnitzer
Status: Merged
Merged at revision: 82
Proposed branch: lp:~slub.team/goobi-production/mq-interface-to-create-and-finalise-processes
Merge into: lp:goobi-production/1.8
Diff against target: 2000 lines (+1629/-93)
20 files modified
WEB-INF/web.xml (+5/-0)
config/GoobiConfig.properties (+35/-0)
doc/implementing_active_mq_web_services_for_goobi.txt (+80/-0)
doc/web_service_to_create_new_processes.txt (+104/-0)
src-dubious/dubious/sub/goobi/helper/Page.java (+1/-1)
src/de/sub/goobi/config/ConfigMain.java (+14/-19)
src/de/sub/goobi/config/DigitalCollections.java (+77/-0)
src/de/sub/goobi/forms/AktuelleSchritteForm.java (+1/-1)
src/de/sub/goobi/forms/ProzesskopieForm.java (+10/-32)
src/de/sub/goobi/helper/Helper.java (+54/-34)
src/de/sub/goobi/helper/UghHelper.java (+15/-4)
src/de/sub/goobi/helper/WebDav.java (+2/-1)
src/de/sub/goobi/helper/enums/ReportLevel.java (+36/-0)
src/org/goobi/mq/ActiveMQDirector.java (+231/-0)
src/org/goobi/mq/ActiveMQProcessor.java (+161/-0)
src/org/goobi/mq/MapMessageObjectReader.java (+242/-0)
src/org/goobi/mq/WebServiceResult.java (+88/-0)
src/org/goobi/mq/processors/CreateNewProcessProcessor.java (+375/-0)
src/org/goobi/mq/processors/FinaliseStepProcessor.java (+97/-0)
src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java (+1/-1)
To merge this branch: bzr merge lp:~slub.team/goobi-production/mq-interface-to-create-and-finalise-processes
Reviewer Review Type Date Requested Status
Ralf Claussnitzer (community) Approve
Review via email: mp+117031@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Ralf Claussnitzer (ralf-claussnitzer-deactivatedaccount) :
review: Approve
98. By Ralf Claussnitzer

add missing license header

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'WEB-INF/web.xml'
2--- WEB-INF/web.xml 2012-04-20 06:57:46 +0000
3+++ WEB-INF/web.xml 2012-07-27 10:58:19 +0000
4@@ -215,6 +215,11 @@
5 <listener-class>org.goobi.production.ImageIOInitializer</listener-class>
6 </listener>
7
8+ <!-- Listener to run ActiveMQ services -->
9+ <listener>
10+ <listener-class>org.goobi.mq.ActiveMQDirector</listener-class>
11+ </listener>
12+
13 <!--
14 xml-Rpc-Server starten <listener> <listener-class>
15 de.sub.goobi.XmlRpc.Listener </listener-class> </listener>
16
17=== modified file 'config/GoobiConfig.properties'
18--- config/GoobiConfig.properties 2012-07-05 13:00:48 +0000
19+++ config/GoobiConfig.properties 2012-07-27 10:58:19 +0000
20@@ -184,6 +184,41 @@
21 # Password encryption SHA or MD5
22 ldap_encryption=SHA
23
24+# -----------------------------------
25+# ActiveMQ web services
26+# -----------------------------------
27+
28+# If you want to use Goobi's ActiveMQ web servic interface, set the host here
29+#activeMQ.hostURL=tcp://localhost:61616
30+
31+# You can provide a topic that Goobi reports results and status messages to
32+#activeMQ.results.topic=GoobiProduction.ResultMessages.Topic
33+
34+# By default, Goobi instructs the server to keep status messages for a
35+# equivalent of 7 days. You can change this value in (milliseconds to) meet
36+# your needs. 0 will disable the deletion of messages completely. (However,
37+# the messages will only available on the Active MQ server if your
38+# TopicSubscriber is online with the Active MQ server before the message is
39+# sent. You migth therefore consider to configure the timeToLive for offline
40+# usage within the Active MQ server’s activemq.xml file by adding a
41+#
42+# <policyEntry topic="GoobiProduction.ResultMessages.Topic">
43+# <subscriptionRecoveryPolicy>
44+# <timedSubscriptionRecoveryPolicy recoverDuration="604800000" />
45+# </subscriptionRecoveryPolicy>
46+# </policyEntry>
47+#
48+# block inside the <policyEntries>-Element. “recoverDuration” has to be given
49+# in milliseconds here, too.)
50+#activeMQ.results.timeToLive=604800000
51+
52+# You can provide a queue from which messages are read to create new processes
53+#activeMQ.createNewProcess.queue=GoobiProduction.CreateNewProcesses.Queue
54+
55+# You can provide a queue from which messages are read to finalise steps
56+#activeMQ.finaliseStep.queue=GoobiProduction.FinaliseStep.Queue
57+
58+
59 ##################################
60 # DO NOT CHANGE THE OPTIONS BELOW!
61 ##################################
62
63=== added directory 'doc'
64=== added file 'doc/implementing_active_mq_web_services_for_goobi.txt'
65--- doc/implementing_active_mq_web_services_for_goobi.txt 1970-01-01 00:00:00 +0000
66+++ doc/implementing_active_mq_web_services_for_goobi.txt 2012-07-27 10:58:19 +0000
67@@ -0,0 +1,80 @@
68+ Implementing Active MQ web services for Goobi
69+
70+Active Message Queue is an open source Java Messaging (JMS) implementation
71+provided by the Apache Software Foundation. It is intended to be used to
72+to connect software components in a flexible way. The core is the Active MQ
73+server which can be pictured like a post office. The mail boxes are named
74+“queue” or “topic”. Queues work as expected: A producer sends a message where
75+a consumer can pick it up. Topics can be pictured as black boards: The main
76+difference is: A message read from a queue is removed from the queue. A message
77+read from a topic is still available to others. Consumer clients can actively
78+check the server or may register listeners with the server to be notified of
79+new messages.
80+
81+This behaviour has already been implemented to Goobi: The org.goobi.mq.
82+ActiveMQDirector is a ServletContextListener which is registered in web.xml.
83+On application startup, it registers all consumers from its “services” variable
84+to the server configured in “activeMQ.hostURL”.
85+
86+The elements of this variable are classes extending the abstract class
87+ActiveMQProcessor. This class implements the MessageListener and provides
88+facilities to handle exceptions and to store the consumer which is required on
89+shutdown to disconnect.
90+
91+To implement another web service processor, you have to implement a class which
92+extends ActiveMQProcessor and implements its abstract void process(MapMessage).
93+Here is the right place to do whatever your processor is intended to do. There
94+is a class MapMessageObjectReader which shall be used to type safe retrieve
95+complex objects from MapMessages. You must add your new class to the “services”
96+variable of ActiveMQDirector then.
97+
98+The Goobi server administrator shall be in control which processors are being
99+started, and which queue names they listen on. Implementation of this
100+configurability is designed this way: The implementing class must pass its
101+queue name to the constructor of the parent class. This is done by implementing
102+the constructor like in the following skeleton. If the queue name is not
103+configured, it will return null which will prevent the ActiveMQDirector from
104+registering it to the server. Inside the class, the queue name is available in
105+the global variable “queueName” which is set by the parent class. The
106+implementation may use arbitrary “activeMQ.myService.*” entries in
107+GoobiConfig.properties for configuration.
108+
109+---------------------[ Service processor skeleton sample ]---------------------
110+package org.goobi.mq.processores;
111+
112+import org.goobi.mq.*;
113+import de.sub.goobi.config.ConfigMain;
114+import de.sub.goobi.helper.enums.ReportLevel;
115+
116+public class MyServiceProcessor extends ActiveMQProcessor {
117+
118+ public MyServiceProcessor() {
119+ super(ConfigMain.getParameter("activeMQ.myService.queue", null));
120+ }
121+
122+ @Override
123+ protected void process(MapMessageObjectReader args) throws Exception {
124+ // TODO Auto-generated method stub
125+ }
126+}
127+-------------------------------------------------------------------------------
128+
129+Responses from processors are designed to be handled as WebServiceResult
130+objects. Those objects are MapMessages which send themselves to a topic
131+configured in “activeMQ.results.topic”. They consist of the Strings “queue”
132+(the name of the queue the job ticket was sent to), “id” (a String “id” in
133+the MapMessage which is mandatory), “level” and an optional “message”. When
134+designing the MapMessage layout to parameterise your web service processor,
135+please keep in mind that a String element “id” is mandatory.
136+
137+If process() terminates without error, it is meant to have done its job
138+successfully and a WebServiceResult with level “success” will be sent. If
139+process() returns an exception, a WebServiceResult with level “fatal” will be
140+sent. The exception will be returned as the “message” String. You may also use
141+the WebServiceResult class to send messages with the levels “error”, “warn”,
142+“info”, “debug”, “verbose” and “ludicrous” which are meant to be informative
143+only:
144+ new WebServiceResult(queueName, args.getMandatoryString("id"),
145+ ReportLevel.INFO, "Remote host is down, trying again later.")
146+ .send();
147+
148
149=== added file 'doc/web_service_to_create_new_processes.txt'
150--- doc/web_service_to_create_new_processes.txt 1970-01-01 00:00:00 +0000
151+++ doc/web_service_to_create_new_processes.txt 2012-07-27 10:58:19 +0000
152@@ -0,0 +1,104 @@
153+ Web service to create new processes
154+
155+Goobi.Production is equiped with a web service interface to automatically
156+create new processes based on a given template. This allows the digitization
157+process to be initiated from outside the application, for example by assigning
158+a new digital ID to a record in a library catalogue (or—at choice of the
159+library—by duplicating a record and assigning a new digital ID to the
160+duplicate) and then running a script.
161+
162+The web service infrastructure is providet by an Active MQ server (see
163+http://activemq.apache.org/ for details) which needs to be downloaded and
164+started. Without further configuration, it provides everything necessary on
165+port 61616 of the machine in question.
166+
167+The “activeMQ.hostURL” must be set in GoobiConfig.properties to point to this
168+server. The “activeMQ.createNewProcess.queue” must be set to point to a queue
169+of your choice where Goobi.Production shall pick up orders to create new
170+processes.
171+
172+Orders must be javax.jms.MapMessage objects with the following key-value-pairs
173+provided:
174+
175+ String template
176+ name of the process template to use
177+ String opac
178+ Cataloge to use for lookup
179+ String field
180+ Field to look into, usually 12 (PPN)
181+ String value
182+ Value to look for, id of physical medium
183+ String id
184+ Ticket ID (used in log responses)
185+ List<String> collections
186+ Collections to be selected
187+ Map<String, String> userFields (optional)
188+ May be used to populates AdditionalField entries
189+
190+Here is a sample java client to do the job. It expects to be passed from the
191+command line the Active MQ host (e.g. tcp://localhost:61616), the queue name
192+and the parameters as listed above.
193+
194+To run this application, the following JARs from the ActiveMQ server’s /lib
195+folder are required on the classpath:
196+ * activemq-core
197+ * geronimo-j2ee-management_1.1_spec
198+ * genonimo-jms_1.1_spec
199+ * log4j
200+ * slf4j-api
201+ * slf4j-log4j12
202+
203+--------------------------------[ Main.java ]----------------------------------
204+import java.util.*;
205+import javax.jms.*;
206+import org.apache.activemq.ActiveMQConnectionFactory;
207+
208+public class Main {
209+ public static int main(String[] args) { try {
210+
211+ // Check arguments
212+ if (args.length < 8 || (args.length % 2) != 0) {
213+ System.out.println("Parameters: Active MQ host, queue name, "
214+ + "template name, opac name,");
215+ System.out.println(" no. of search field, search "
216+ + "string, digital id, collection name,");
217+ System.out.println(" [additional details field, "
218+ + "value, [add. details field, value, [...");
219+ return 1;
220+ }
221+
222+ // Connect to server
223+ Connection connection = new ActiveMQConnectionFactory(args[0])
224+ .createConnection();
225+ connection.start();
226+ Session session = connection.createSession(false,
227+ Session.AUTO_ACKNOWLEDGE);
228+ Destination destination = session.createQueue(args[1]);
229+ MessageProducer producer = session.createProducer(destination);
230+ producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
231+
232+ // Create job ticket
233+ MapMessage message = session.createMapMessage();
234+ message.setString("template", args[2]);
235+ message.setString("opac", args[3]);
236+ message.setString("field", args[4]);
237+ message.setString("value", args[5]);
238+ message.setString("id", args[6]);
239+ List<String> collections = new ArrayList<String>();
240+ collections.add(args[7]);
241+ message.setObject("collections", collections);
242+ Map<String, String> userFields = new HashMap<String, String>();
243+ for (int i = 8; i < args.length; i += 2)
244+ userFields.put(args[i], args[i + 1]);
245+ if (userFields.size() != 0)
246+ message.setObject("userFields", userFields);
247+
248+ // Send job ticket
249+ producer.send(message);
250+
251+ // Shutdown
252+ session.close();
253+ connection.close();
254+ } catch (Exception e) { e.printStackTrace(); return 2; }
255+ return 0;
256+} }
257
258=== added file 'lib/activemq-core-5.5.1.jar'
259Binary files lib/activemq-core-5.5.1.jar 1970-01-01 00:00:00 +0000 and lib/activemq-core-5.5.1.jar 2012-07-27 10:58:19 +0000 differ
260=== added file 'lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar'
261Binary files lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar 1970-01-01 00:00:00 +0000 and lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar 2012-07-27 10:58:19 +0000 differ
262=== added file 'lib/geronimo-jms_1.1_spec-1.1.1.jar'
263Binary files lib/geronimo-jms_1.1_spec-1.1.1.jar 1970-01-01 00:00:00 +0000 and lib/geronimo-jms_1.1_spec-1.1.1.jar 2012-07-27 10:58:19 +0000 differ
264=== added file 'lib/json-simple-1.1.1.jar'
265Binary files lib/json-simple-1.1.1.jar 1970-01-01 00:00:00 +0000 and lib/json-simple-1.1.1.jar 2012-07-27 10:58:19 +0000 differ
266=== modified file 'src-dubious/dubious/sub/goobi/helper/Page.java'
267--- src-dubious/dubious/sub/goobi/helper/Page.java 2012-07-03 13:32:53 +0000
268+++ src-dubious/dubious/sub/goobi/helper/Page.java 2012-07-27 10:58:19 +0000
269@@ -47,7 +47,7 @@
270 public Page(Criteria criteria, int page) {
271 this.page = page;
272 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
273- if (login.getMyBenutzer() == null)
274+ if (login == null || login.getMyBenutzer() == null)
275 this.pageSize = 10;
276 else
277 this.pageSize = login.getMyBenutzer().getTabellengroesse().intValue();
278
279=== modified file 'src/de/sub/goobi/config/ConfigMain.java'
280--- src/de/sub/goobi/config/ConfigMain.java 2012-05-30 08:24:19 +0000
281+++ src/de/sub/goobi/config/ConfigMain.java 2012-07-27 10:58:19 +0000
282@@ -119,9 +119,9 @@
283
284
285 /**
286- * Ermitteln eines bestimmten Paramters der Konfiguration
287+ * Ermitteln eines bestimmten Parameters der Konfiguration
288 *
289- * @return Paramter als String
290+ * @return Parameter als String
291 */
292 public static String getParameter(String inParameter) {
293 try {
294@@ -135,10 +135,10 @@
295
296 //TODO: Remove this methods, they are provided by Commons Configuration
297 /**
298- * Ermitteln eines bestimmten Paramters der Konfiguration mit Angabe eines
299+ * Ermitteln eines bestimmten Parameters der Konfiguration mit Angabe eines
300 * Default-Wertes
301 *
302- * @return Paramter als String
303+ * @return Parameter als String
304 */
305 public static String getParameter(String inParameter, String inDefaultIfNull) {
306 try {
307@@ -152,18 +152,18 @@
308
309
310 /**
311- * Ermitteln eines boolean-Paramters der Konfiguration, default if missing: false
312+ * Ermitteln eines boolean-Parameters der Konfiguration, default if missing: false
313 *
314- * @return Paramter als String
315+ * @return Parameter als String
316 */
317 public static boolean getBooleanParameter(String inParameter) {
318 return getBooleanParameter(inParameter, false);
319 }
320
321 /**
322- * Ermitteln eines boolean-Paramters der Konfiguration
323+ * Ermitteln eines boolean-Parameters der Konfiguration
324 *
325- * @return Paramter als String
326+ * @return Parameter als String
327 */
328 public static boolean getBooleanParameter(String inParameter, boolean inDefault) {
329 return config.getBoolean(inParameter, inDefault);
330@@ -172,17 +172,12 @@
331
332
333 /**
334- * Ermitteln eines long-Paramters der Konfiguration
335+ * Ermitteln eines long-Parameters der Konfiguration
336 *
337- * @return Paramter als Long
338+ * @return Parameter als Long
339 */
340- public static long getLongParameter(String inParameter, int inDefault) {
341- try {
342- return config.getLong(inParameter, inDefault);
343- } catch (RuntimeException e) {
344- myLogger.error(e);
345- return 0;
346- }
347+ public static long getLongParameter(String inParameter, long inDefault) {
348+ return config.getLong(inParameter, inDefault);
349 }
350
351
352@@ -190,7 +185,7 @@
353 /**
354 * Request int-parameter from Configuration
355 *
356- * @return Paramter as Int
357+ * @return Parameter as Int
358 */
359 public static int getIntParameter(String inParameter) {
360 return getIntParameter(inParameter, 0);
361@@ -199,7 +194,7 @@
362 /**
363 * Request int-parameter from Configuration with default-value
364 *
365- * @return Paramter as Int
366+ * @return Parameter as Int
367 */
368 public static int getIntParameter(String inParameter, int inDefault) {
369 try {
370
371=== added file 'src/de/sub/goobi/config/DigitalCollections.java'
372--- src/de/sub/goobi/config/DigitalCollections.java 1970-01-01 00:00:00 +0000
373+++ src/de/sub/goobi/config/DigitalCollections.java 2012-07-27 10:58:19 +0000
374@@ -0,0 +1,77 @@
375+/*
376+ * This file is part of the Goobi Application - a Workflow tool for the support of
377+ * mass digitization.
378+ *
379+ * Visit the websites for more information.
380+ * - http://gdz.sub.uni-goettingen.de
381+ * - http://www.goobi.org
382+ * - http://launchpad.net/goobi-production
383+ *
384+ * This program is free software; you can redistribute it and/or modify it under
385+ * the terms of the GNU General Public License as published by the Free Software
386+ * Foundation; either version 2 of the License, or (at your option) any later
387+ * version.
388+ *
389+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
390+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
391+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
392+ * should have received a copy of the GNU General Public License along with this
393+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
394+ * Suite 330, Boston, MA 02111-1307 USA
395+ */
396+
397+package de.sub.goobi.config;
398+
399+import java.io.File;
400+import java.io.FileNotFoundException;
401+import java.io.IOException;
402+import java.util.ArrayList;
403+import java.util.Iterator;
404+import java.util.List;
405+
406+import org.jdom.Document;
407+import org.jdom.Element;
408+import org.jdom.JDOMException;
409+import org.jdom.input.SAXBuilder;
410+
411+import de.sub.goobi.beans.Prozess;
412+
413+public class DigitalCollections {
414+
415+ @SuppressWarnings("unchecked")
416+ public static List<String> possibleDigitalCollectionsForProcess(
417+ Prozess process) throws JDOMException, IOException {
418+
419+ List<String> result = new ArrayList<String>();
420+ String filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "digitalCollections.xml";
421+ if (!(new File(filename).exists())) {
422+ throw new FileNotFoundException("File not found: " + filename);
423+ }
424+
425+ /* Datei einlesen und Root ermitteln */
426+ SAXBuilder builder = new SAXBuilder();
427+ Document doc = builder.build(new File(filename));
428+ Element root = doc.getRootElement();
429+ /* alle Projekte durchlaufen */
430+ List<Element> projekte = root.getChildren();
431+ for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
432+ Element projekt = (Element) iter.next();
433+ List<Element> projektnamen = projekt.getChildren("name");
434+ for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
435+ Element projektname = (Element) iterator.next();
436+
437+ /*
438+ * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
439+ */
440+ if (projektname.getText().equalsIgnoreCase(process.getProjekt().getTitel())) {
441+ List<Element> myCols = projekt.getChildren("DigitalCollection");
442+ for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
443+ Element col = (Element) it2.next();
444+ result.add(col.getText());
445+ }
446+ }
447+ }
448+ }
449+ return result;
450+ }
451+}
452
453=== modified file 'src/de/sub/goobi/forms/AktuelleSchritteForm.java'
454--- src/de/sub/goobi/forms/AktuelleSchritteForm.java 2012-04-24 13:22:01 +0000
455+++ src/de/sub/goobi/forms/AktuelleSchritteForm.java 2012-07-27 10:58:19 +0000
456@@ -109,7 +109,7 @@
457 * --------------------- Vorgangsdatum generell anzeigen? -------------------
458 */
459 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
460- if (login.getMyBenutzer() != null)
461+ if (login != null && login.getMyBenutzer() != null)
462 anzeigeAnpassen.put("processDate", login.getMyBenutzer().isConfVorgangsdatumAnzeigen());
463 else
464 anzeigeAnpassen.put("processDate", false);
465
466=== modified file 'src/de/sub/goobi/forms/ProzesskopieForm.java'
467--- src/de/sub/goobi/forms/ProzesskopieForm.java 2012-05-09 15:19:19 +0000
468+++ src/de/sub/goobi/forms/ProzesskopieForm.java 2012-07-27 10:58:19 +0000
469@@ -23,6 +23,7 @@
470 package de.sub.goobi.forms;
471
472 import java.io.File;
473+import java.io.FileNotFoundException;
474 import java.io.IOException;
475 import java.sql.SQLException;
476 import java.util.ArrayList;
477@@ -84,6 +85,7 @@
478 import de.sub.goobi.config.ConfigOpac;
479 import de.sub.goobi.config.ConfigOpacDoctype;
480 import de.sub.goobi.config.ConfigProjects;
481+import de.sub.goobi.config.DigitalCollections;
482 import de.sub.goobi.helper.BeanHelper;
483 import de.sub.goobi.helper.Helper;
484 import de.sub.goobi.helper.Messages;
485@@ -932,46 +934,22 @@
486 return possibleDigitalCollection;
487 }
488
489- @SuppressWarnings("unchecked")
490 private void initializePossibleDigitalCollections() {
491 possibleDigitalCollection = new ArrayList<String>();
492- String filename = help.getGoobiConfigDirectory() + "digitalCollections.xml";
493- if (!(new File(filename).exists())) {
494- Helper.setFehlerMeldung("File not found: ", filename);
495- return;
496- }
497-
498- try {
499- /* Datei einlesen und Root ermitteln */
500- SAXBuilder builder = new SAXBuilder();
501- Document doc = builder.build(new File(filename));
502- Element root = doc.getRootElement();
503- /* alle Projekte durchlaufen */
504- List<Element> projekte = root.getChildren();
505- for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
506- Element projekt = (Element) iter.next();
507- List<Element> projektnamen = projekt.getChildren("name");
508- for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
509- Element projektname = (Element) iterator.next();
510-
511- /*
512- * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
513- */
514- if (projektname.getText().equalsIgnoreCase(prozessKopie.getProjekt().getTitel())) {
515- List<Element> myCols = projekt.getChildren("DigitalCollection");
516- for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
517- Element col = (Element) it2.next();
518- possibleDigitalCollection.add(col.getText());
519- }
520- }
521- }
522- }
523+ try{
524+ possibleDigitalCollection = DigitalCollections.possibleDigitalCollectionsForProcess(prozessKopie);
525+ } catch (FileNotFoundException e1) {
526+ myLogger.error("File not found: ", e1);
527+ Helper.setFehlerMeldung("File not found: ", e1);
528 } catch (JDOMException e1) {
529 myLogger.error("error while parsing digital collections", e1);
530 Helper.setFehlerMeldung("Error while parsing digital collections", e1);
531 } catch (IOException e1) {
532 myLogger.error("error while parsing digital collections", e1);
533 Helper.setFehlerMeldung("Error while parsing digital collections", e1);
534+ } finally {
535+ if(possibleDigitalCollection == null)
536+ possibleDigitalCollection = new ArrayList<String>();
537 }
538
539 // if only one collection is possible take it directly
540
541=== modified file 'src/de/sub/goobi/helper/Helper.java'
542--- src/de/sub/goobi/helper/Helper.java 2012-07-03 13:32:53 +0000
543+++ src/de/sub/goobi/helper/Helper.java 2012-07-27 10:58:19 +0000
544@@ -42,13 +42,16 @@
545 import javax.faces.context.FacesContext;
546 import javax.servlet.http.HttpServletRequest;
547
548+import org.apache.log4j.Level;
549 import org.apache.log4j.Logger;
550+import org.goobi.mq.WebServiceResult;
551 import org.hibernate.Session;
552 import org.jdom.Element;
553
554 import de.sub.goobi.beans.Benutzer;
555 import de.sub.goobi.config.ConfigMain;
556 import de.sub.goobi.forms.LoginForm;
557+import de.sub.goobi.helper.enums.ReportLevel;
558 import de.sub.goobi.persistence.HibernateUtilOld;
559
560 //TODO: Check if more method can be made static
561@@ -59,6 +62,7 @@
562
563 private String myMetadatenVerzeichnis;
564 private String myConfigVerzeichnis;
565+ public static Map<String, String> activeMQReporting = null;
566
567 /**
568 * Ermitteln eines bestimmten Paramters des Requests
569@@ -145,43 +149,59 @@
570 }
571
572 /**
573- * Dem aktuellen Formular eine Fehlermeldung für ein bestimmtes Control übergeben
574+ * The method setMeldung() adds an error message for a given control to the
575+ * current form.
576+ *
577+ * @param control
578+ * Name of control that caused the error or “null” if the error
579+ * was not caused by a control
580+ * @param messageKey
581+ * The key of the error message. The method will try to resolve
582+ * the key against its messages file.
583+ * @param descriptionKey
584+ * The description key of the error. The method will try to
585+ * resolve the key against its messages file.
586+ * @param infoOnly
587+ * Set to false for error messages. Set to true for info
588+ * messages.
589 */
590- private static void setMeldung(String control, String meldung, String beschreibung, boolean nurInfo) {
591+ private static void setMeldung(String control, String messageKey,
592+ String descriptionKey, boolean infoOnly) {
593 FacesContext context = FacesContext.getCurrentInstance();
594
595- // Never forget: Strings are immutable
596- meldung = meldung.replaceAll("<", "&lt;");
597- meldung = meldung.replaceAll(">", "&gt;");
598- beschreibung = beschreibung.replaceAll("<", "&lt;");
599- beschreibung = beschreibung.replaceAll(">", "&gt;");
600- /* wenn kein Kontext da ist, dann die Meldungen in Log */
601- if (context == null) {
602- if (nurInfo) {
603- myLogger.info(meldung + " " + beschreibung);
604- } else {
605- myLogger.error(meldung + " " + beschreibung);
606- }
607- return;
608- }
609-
610- String msg = "";
611- String beschr = "";
612- try {
613- msg = Messages.getString(meldung);
614- } catch (RuntimeException e) {
615- msg = meldung;
616- }
617- try {
618- beschr = Messages.getString(beschreibung);
619- } catch (RuntimeException e) {
620- beschr = beschreibung;
621- }
622-
623- if (nurInfo) {
624- context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, beschr));
625- } else {
626- context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, beschr));
627+ String message;
628+ String description;
629+ try {
630+ message = Messages.getString(messageKey);
631+ } catch (RuntimeException e) {
632+ message = messageKey;
633+ }
634+ try {
635+ description = Messages.getString(descriptionKey);
636+ } catch (RuntimeException e) {
637+ description = descriptionKey;
638+ }
639+
640+ message = message.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
641+ description = description.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
642+
643+ String compoundMessage = message.replaceFirst(":\\s*$", "") + ": "
644+ + description;
645+
646+ /* If the Active MQ service is at work, report errors there, too. */
647+ if (activeMQReporting != null) {
648+ new WebServiceResult(activeMQReporting.get("queueName"),
649+ activeMQReporting.get("id"), infoOnly ? ReportLevel.INFO
650+ : ReportLevel.ERROR, compoundMessage).send();
651+ }
652+
653+ if (context != null) {
654+ context.addMessage(
655+ control,
656+ new FacesMessage(infoOnly ? FacesMessage.SEVERITY_INFO
657+ : FacesMessage.SEVERITY_ERROR, message, description));
658+ } else { // wenn kein Kontext da ist, dann die Meldungen in Log
659+ myLogger.log(infoOnly ? Level.INFO : Level.ERROR, compoundMessage);
660 }
661 }
662
663
664=== modified file 'src/de/sub/goobi/helper/UghHelper.java'
665--- src/de/sub/goobi/helper/UghHelper.java 2012-02-22 07:43:02 +0000
666+++ src/de/sub/goobi/helper/UghHelper.java 2012-07-27 10:58:19 +0000
667@@ -42,6 +42,7 @@
668 import ugh.exceptions.DocStructHasNoTypeException;
669 import ugh.exceptions.MetadataTypeNotAllowedException;
670 import de.sub.goobi.beans.Prozess;
671+import de.sub.goobi.config.ConfigMain;
672 import de.sub.goobi.helper.exceptions.UghHelperException;
673
674 //TODO: Try to move this methods to UGH (ugh.util.UGHUtils would be a better place)
675@@ -206,8 +207,13 @@
676 public String convertLanguage(String inLanguage) {
677 /* Pfad zur Datei ermitteln */
678 FacesContext context = FacesContext.getCurrentInstance();
679- HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
680- String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";
681+ String filename;
682+ if (context != null) {
683+ HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
684+ filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";
685+ } else {
686+ filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opaclanguages.txt";
687+ }
688 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */
689 try {
690 FileInputStream fis = new FileInputStream(filename);
691@@ -233,8 +239,13 @@
692 String temp = inString;
693 /* Pfad zur Datei ermitteln */
694 FacesContext context = FacesContext.getCurrentInstance();
695- HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
696- String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";
697+ String filename;
698+ if (context != null) {
699+ HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
700+ filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";
701+ } else {
702+ filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opacumlaut.txt";
703+ }
704
705 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */
706 try {
707
708=== modified file 'src/de/sub/goobi/helper/WebDav.java'
709--- src/de/sub/goobi/helper/WebDav.java 2012-07-03 13:49:14 +0000
710+++ src/de/sub/goobi/helper/WebDav.java 2012-07-27 10:58:19 +0000
711@@ -111,7 +111,8 @@
712
713 public void UploadFromHome(Prozess myProzess) {
714 Benutzer aktuellerBenutzer = (Benutzer) Helper.getManagedBeanValue("#{LoginForm.myBenutzer}");
715- UploadFromHome(aktuellerBenutzer, myProzess);
716+ if (aktuellerBenutzer != null)
717+ UploadFromHome(aktuellerBenutzer, myProzess);
718 }
719
720 public void UploadFromHome(Benutzer inBenutzer, Prozess myProzess) {
721
722=== added file 'src/de/sub/goobi/helper/enums/ReportLevel.java'
723--- src/de/sub/goobi/helper/enums/ReportLevel.java 1970-01-01 00:00:00 +0000
724+++ src/de/sub/goobi/helper/enums/ReportLevel.java 2012-07-27 10:58:19 +0000
725@@ -0,0 +1,36 @@
726+/*
727+ * This file is part of the Goobi Application - a Workflow tool for the support of
728+ * mass digitization.
729+ *
730+ * Visit the websites for more information.
731+ * - http://gdz.sub.uni-goettingen.de
732+ * - http://www.goobi.org
733+ * - http://launchpad.net/goobi-production
734+ *
735+ * This program is free software; you can redistribute it and/or modify it under
736+ * the terms of the GNU General Public License as published by the Free Software
737+ * Foundation; either version 2 of the License, or (at your option) any later
738+ * version.
739+ *
740+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
741+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
742+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
743+ * should have received a copy of the GNU General Public License along with this
744+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
745+ * Suite 330, Boston, MA 02111-1307 USA
746+ */
747+
748+package de.sub.goobi.helper.enums;
749+
750+/**
751+ * These are the possible states for output to “activeMQ.results.topic”.
752+ *
753+ * @author Matthias Ronge <matthias.ronge@zeutschel.de>
754+ */
755+public enum ReportLevel {
756+ FATAL, ERROR, WARN, INFO, SUCCESS, DEBUG, VERBOSE, LUDICROUS;
757+
758+ public String toLowerCase() {
759+ return name().toLowerCase();
760+ }
761+}
762
763=== added directory 'src/org/goobi/mq'
764=== added file 'src/org/goobi/mq/ActiveMQDirector.java'
765--- src/org/goobi/mq/ActiveMQDirector.java 1970-01-01 00:00:00 +0000
766+++ src/org/goobi/mq/ActiveMQDirector.java 2012-07-27 10:58:19 +0000
767@@ -0,0 +1,231 @@
768+/*
769+ * This file is part of the Goobi Application - a Workflow tool for the support of
770+ * mass digitization.
771+ *
772+ * Visit the websites for more information.
773+ * - http://gdz.sub.uni-goettingen.de
774+ * - http://www.goobi.org
775+ * - http://launchpad.net/goobi-production
776+ *
777+ * This program is free software; you can redistribute it and/or modify it under
778+ * the terms of the GNU General Public License as published by the Free Software
779+ * Foundation; either version 2 of the License, or (at your option) any later
780+ * version.
781+ *
782+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
783+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
784+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
785+ * should have received a copy of the GNU General Public License along with this
786+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
787+ * Suite 330, Boston, MA 02111-1307 USA
788+ */
789+
790+package org.goobi.mq;
791+
792+import javax.jms.Connection;
793+import javax.jms.DeliveryMode;
794+import javax.jms.Destination;
795+import javax.jms.ExceptionListener;
796+import javax.jms.JMSException;
797+import javax.jms.MessageConsumer;
798+import javax.jms.MessageProducer;
799+import javax.jms.Session;
800+import javax.servlet.ServletContextEvent;
801+import javax.servlet.ServletContextListener;
802+
803+import org.apache.activemq.ActiveMQConnectionFactory;
804+import org.apache.log4j.Logger;
805+import org.goobi.mq.processors.CreateNewProcessProcessor;
806+import org.goobi.mq.processors.FinaliseStepProcessor;
807+
808+import de.sub.goobi.config.ConfigMain;
809+
810+/**
811+ * The class ActiveMQDirector is the head of all Active MQ processors. It
812+ * implements the ServletContextListener interface and is − if configured in
813+ * web.xml − called automatically upon server starup. Its job is to connect to
814+ * the Active MQ server and register the listeners configured.
815+ *
816+ * The ActiveMQDirector should ALWAYS be declared in web.xml. The Active MQ
817+ * services are intended to be run in case that “activeMQ.hostURL” is configured
818+ * in the GoobiConfig.properties file. To disable the service, the entry there
819+ * should be emptied or commented out. Otherwise, a valid configuration would
820+ * not start working and an administrator will hardly have a chance to find out
821+ * why without inspecting the source code.
822+ *
823+ * The class ActiveMQDirector also provides a basic ExceptionListener
824+ * implementation as required for the connection.
825+ *
826+ * @author Matthias Ronge <matthias.ronge@zeutschel.de>
827+ */
828+public class ActiveMQDirector implements ServletContextListener,
829+ ExceptionListener {
830+ private static final Logger logger = Logger
831+ .getLogger(ActiveMQDirector.class);
832+
833+ // *** CONFIGURATION ***
834+ // When implementing new Services, add them to this list:
835+
836+ protected static ActiveMQProcessor[] services;
837+ static{
838+ services = new ActiveMQProcessor[] {
839+ new CreateNewProcessProcessor(),
840+ new FinaliseStepProcessor()
841+ };
842+ }
843+
844+ protected static Connection connection = null;
845+ protected static Session session = null;
846+ protected static MessageProducer resultsTopic;
847+
848+ /**
849+ * The method contextInitialized() is called by the web container on startup
850+ * and is used to start up the active MQ connection. All processors from
851+ * services[] are registered.
852+ *
853+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet
854+ * .ServletContextEvent)
855+ */
856+ @Override
857+ public void contextInitialized(ServletContextEvent initialisation) {
858+ String activeMQHost = ConfigMain.getParameter("activeMQ.hostURL", null);
859+ if (activeMQHost != null) {
860+ session = connectToServer(activeMQHost);
861+ if (session != null) {
862+ registerListeners(services);
863+ if (ConfigMain.getParameter("activeMQ.results.topic", null) != null) {
864+ resultsTopic = setUpReportChannel(ConfigMain.getParameter("activeMQ.results.topic"));
865+ } } } }
866+
867+ /**
868+ * Sets up a connection to an active MQ server. The connection object is
869+ * global because it is needed later to shut down the connection.
870+ *
871+ * @param server
872+ * should be “tcp://{host}:{port}” or “vm://localhost” in case
873+ * that the server is run inside the same virtual machine
874+ * @return the session object or “null” upon error
875+ */
876+ protected Session connectToServer(String server) {
877+ try {
878+ connection = new ActiveMQConnectionFactory(server).createConnection();
879+ connection.start();
880+ connection.setExceptionListener(this); // → ActiveMQDirector.onException()
881+ return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
882+ } catch (Exception e) {
883+ logger.fatal("Error connecting to ActiveMQ server, giving up.", e);
884+ }
885+ return null;
886+ }
887+
888+ /**
889+ * This method registers the listeners with the active MQ server.
890+ *
891+ * If a queue name was configured for a service, a MessageConsumer is set up
892+ * to listen on that queue and, in case of incoming messages, make the
893+ * service process the message. The message checker is saved inside the
894+ * service to be able to shut it down later.
895+ */
896+ protected void registerListeners(ActiveMQProcessor[] processors) {
897+ for (ActiveMQProcessor processor : processors) {
898+ if (processor.getQueueName() != null) {
899+ MessageConsumer messageChecker = null;
900+ try {
901+ Destination queue = session.createQueue(processor.getQueueName());
902+ messageChecker = session.createConsumer(queue);
903+ messageChecker.setMessageListener(processor);
904+ processor.saveChecker(messageChecker);
905+ } catch (Exception e) {
906+ logger.fatal("Error setting up monitoring for \"" + processor.getQueueName() + "\": Giving up.", e);
907+ } } } }
908+
909+ /**
910+ * This sets up a connection to the topic the results shall be written to.
911+ * The delivery mode is set so “persistent” to allow consumers not online
912+ * with the server in the moment of message submission to read the messages
913+ * later. The log messages are set to be kept on the server for 7 days. This
914+ * value can be overridden from the GoobiConfig.properties parameter
915+ * “activeMQ.results.timeToLive”. The time to live must be specified in
916+ * milliseconds; 0 disables the oblivion. (See also:
917+ * http://docs.oracle.com/javaee/6/api/javax/jms/MessageProducer.html#setTimeToLive%28long%29 )
918+ *
919+ * @param topic
920+ * name of the active MQ topic
921+ * @return a MessageProducer object ready for writing or “null” on error
922+ */
923+ protected MessageProducer setUpReportChannel(String topic) {
924+ MessageProducer result;
925+ try {
926+ Destination channel = session.createTopic(topic);
927+ result = session.createProducer(channel);
928+ result.setDeliveryMode(DeliveryMode.PERSISTENT);
929+ result.setTimeToLive(ConfigMain.getLongParameter("activeMQ.results.timeToLive", 604800000));
930+ return result;
931+ } catch (Exception e) {
932+ logger.fatal("Error setting up report channel \"" + topic + "\": Giving up.", e);
933+ }
934+ return null;
935+ }
936+
937+ /**
938+ * This method is referenced from this.connectToServer() − see there.
939+ *
940+ * @see javax.jms.ExceptionListener#onException(javax.jms.JMSException)
941+ */
942+ @Override
943+ public void onException(JMSException exce) {
944+ logger.error(exce);
945+ }
946+
947+ /**
948+ * Any class that wants to create new Active MQ Messages needs read access
949+ * to the session, since Active MQ messages don’t have a constructor.
950+ *
951+ * @return the session object
952+ */
953+ public static Session getSession() {
954+ return session;
955+ }
956+
957+ /**
958+ * Instances of WebServiceResult can be sent by calling their send() method.
959+ * Therefore, they need read access on their topic.
960+ *
961+ * @return the resultsTopic object
962+ */
963+ public static MessageProducer getResultsTopic() {
964+ return resultsTopic;
965+ }
966+
967+ /**
968+ * The method contextDestroyed is called by the web container on shutdown.
969+ * It shuts down all listeners, the session and last, the connection.
970+ *
971+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.
972+ * ServletContextEvent)
973+ */
974+ @Override
975+ public void contextDestroyed(ServletContextEvent destruction) {
976+ // Shut down all watchers on any queues
977+ for (ActiveMQProcessor service : services) {
978+ MessageConsumer watcher = service.getChecker();
979+ if (watcher != null) {
980+ try {
981+ watcher.close();
982+ } catch (JMSException e) {
983+ logger.error(e);
984+ } } }
985+
986+ // quit session
987+ try {
988+ session.close();
989+ } catch (JMSException e) {
990+ logger.error(e);
991+ }
992+
993+ // shut down connection
994+ try {
995+ connection.close();
996+ } catch (JMSException e) {
997+ logger.error(e);
998+} } }
999
1000=== added file 'src/org/goobi/mq/ActiveMQProcessor.java'
1001--- src/org/goobi/mq/ActiveMQProcessor.java 1970-01-01 00:00:00 +0000
1002+++ src/org/goobi/mq/ActiveMQProcessor.java 2012-07-27 10:58:19 +0000
1003@@ -0,0 +1,161 @@
1004+/*
1005+ * This file is part of the Goobi Application - a Workflow tool for the support of
1006+ * mass digitization.
1007+ *
1008+ * Visit the websites for more information.
1009+ * - http://gdz.sub.uni-goettingen.de
1010+ * - http://www.goobi.org
1011+ * - http://launchpad.net/goobi-production
1012+ *
1013+ * This program is free software; you can redistribute it and/or modify it under
1014+ * the terms of the GNU General Public License as published by the Free Software
1015+ * Foundation; either version 2 of the License, or (at your option) any later
1016+ * version.
1017+ *
1018+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
1019+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1020+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1021+ * should have received a copy of the GNU General Public License along with this
1022+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
1023+ * Suite 330, Boston, MA 02111-1307 USA
1024+ */
1025+
1026+package org.goobi.mq;
1027+
1028+import java.util.HashMap;
1029+import java.util.Map;
1030+
1031+import javax.jms.MapMessage;
1032+import javax.jms.Message;
1033+import javax.jms.MessageConsumer;
1034+import javax.jms.MessageListener;
1035+
1036+import de.sub.goobi.helper.Helper;
1037+import de.sub.goobi.helper.enums.ReportLevel;
1038+
1039+/**
1040+ * The class ActiveMQProcessor offers general services, such as making the
1041+ * incoming messages available as MapMessages and publishing the results. When I
1042+ * came clear that this code would be necessary for every processor, I thought
1043+ * an abstract class would be the right place for it. ActiveMQProcessor also
1044+ * provides a place to save the checker for the ActiveMQDirector, to be able to
1045+ * shut it down later.
1046+ *
1047+ * @author Matthias Ronge <matthias.ronge@zeutschel.de>
1048+ */
1049+public abstract class ActiveMQProcessor implements MessageListener {
1050+
1051+ protected String queueName; // the queue name will be available here
1052+ private MessageConsumer checker;
1053+
1054+ /**
1055+ * Implement the method process() to let your service actually do what you
1056+ * want him to do.
1057+ *
1058+ * @param ticket
1059+ * A MapMessage which can be processor-specific except that it
1060+ * requires to have a field “id”.
1061+ */
1062+ protected abstract void process(MapMessageObjectReader ticket) throws Exception;
1063+
1064+ /**
1065+ * Instantiating the class ActiveMQProcessor always requires to pass the
1066+ * name of the queue it should be attached to. That means, your constructor
1067+ * should look like this:
1068+ *
1069+ * <pre>
1070+ * public MyServiceProcessor(){
1071+ * super(ConfigMain.getParameter("activeMQ.myService.queue", null));
1072+ * }
1073+ * </pre>
1074+ *
1075+ * If the parameter is not set in GoobiConfig.properties, it will return
1076+ * “null” and so prevents it from being set up in ActiveMQDirector.
1077+ *
1078+ * @param queueName
1079+ * the queue name, if configured, or “null” to prevent the
1080+ * processor from being connected.
1081+ */
1082+ public ActiveMQProcessor(String queueName) {
1083+ this.queueName = queueName;
1084+ }
1085+
1086+ /**
1087+ * The method onMessage() provides a corset which checks the message being a
1088+ * MapMessage, performs a type safe type cast, and extracts the job’s ID to
1089+ * be able to send useful results to the results topic, which it does after
1090+ * the abstract method process() has finished.
1091+ *
1092+ * Since this will be the same for all processors which use MapMessages, I
1093+ * extracted the portion into the abstract class.
1094+ *
1095+ * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
1096+ */
1097+ @Override
1098+ public void onMessage(Message arg) {
1099+ MapMessageObjectReader ticket = null;
1100+ String ticketID = null;
1101+
1102+ try {
1103+ // Basic check ticket
1104+ if (arg instanceof MapMessage)
1105+ ticket = new MapMessageObjectReader((MapMessage) arg);
1106+ else
1107+ throw new IllegalArgumentException("Incompatible types.");
1108+ ticketID = ticket.getMandatoryString("id");
1109+
1110+ // turn on logging
1111+ Map<String,String> loggingConfig = new HashMap<String,String>();
1112+ loggingConfig.put("queueName", queueName);
1113+ loggingConfig.put("id", ticketID);
1114+ Helper.activeMQReporting = loggingConfig;
1115+
1116+ // process ticket
1117+ process(ticket);
1118+
1119+ // turn off logging again
1120+ Helper.activeMQReporting = null;
1121+
1122+ // if everything ‘s fine, report success
1123+ new WebServiceResult(queueName, ticketID, ReportLevel.SUCCESS)
1124+ .send();
1125+
1126+ } catch (Exception exce) {
1127+ // report any errors
1128+ new WebServiceResult(queueName, ticketID, ReportLevel.FATAL,
1129+ exce.getMessage()).send();
1130+ }
1131+ }
1132+
1133+ /**
1134+ * This method is used to get the queue name upon initialisation.
1135+ *
1136+ * @return the queue name
1137+ */
1138+ public String getQueueName() {
1139+ return queueName;
1140+ }
1141+
1142+ /**
1143+ * The parent object which is there to check for new messages and to trigger
1144+ * the method onMessage() is saved inside the class, to have it lately for
1145+ * shutting down the service again.
1146+ *
1147+ * @param checker
1148+ * the MessageConsumer object responsible for checking messages
1149+ */
1150+
1151+ public void saveChecker(MessageConsumer checker) {
1152+ this.checker = checker;
1153+ }
1154+
1155+ /**
1156+ * This method is used to get back the message checking object upon
1157+ * shutdown.
1158+ *
1159+ * @return the MessageConsumer object responsible for checking messages
1160+ */
1161+ public MessageConsumer getChecker() {
1162+ return checker;
1163+ }
1164+}
1165
1166=== added file 'src/org/goobi/mq/MapMessageObjectReader.java'
1167--- src/org/goobi/mq/MapMessageObjectReader.java 1970-01-01 00:00:00 +0000
1168+++ src/org/goobi/mq/MapMessageObjectReader.java 2012-07-27 10:58:19 +0000
1169@@ -0,0 +1,242 @@
1170+/*
1171+ * This file is part of the Goobi Application - a Workflow tool for the support of
1172+ * mass digitization.
1173+ *
1174+ * Visit the websites for more information.
1175+ * - http://gdz.sub.uni-goettingen.de
1176+ * - http://www.goobi.org
1177+ * - http://launchpad.net/goobi-production
1178+ *
1179+ * This program is free software; you can redistribute it and/or modify it under
1180+ * the terms of the GNU General Public License as published by the Free Software
1181+ * Foundation; either version 2 of the License, or (at your option) any later
1182+ * version.
1183+ *
1184+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
1185+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1186+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1187+ * should have received a copy of the GNU General Public License along with this
1188+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
1189+ * Suite 330, Boston, MA 02111-1307 USA
1190+ */
1191+
1192+package org.goobi.mq;
1193+
1194+import java.util.Collection;
1195+import java.util.HashMap;
1196+import java.util.HashSet;
1197+import java.util.Map;
1198+import java.util.Set;
1199+
1200+import javax.jms.JMSException;
1201+import javax.jms.MapMessage;
1202+
1203+public class MapMessageObjectReader {
1204+
1205+ private MapMessage ticket;
1206+
1207+ /**
1208+ * This instantiates a new MapMessageObjectReader which is attached to a
1209+ * given MapMessage.
1210+ *
1211+ * @param message
1212+ */
1213+ public MapMessageObjectReader(MapMessage message) {
1214+ if (message == null)
1215+ throw new IllegalArgumentException(
1216+ "MapMessageObjectReader: null argument in constructor.");
1217+ this.ticket = message;
1218+ }
1219+
1220+ /**
1221+ * The function getMandatorySetOfString() fetches a Set<String> from a MapMessage.
1222+ * This is a strict implementation that requires the collection not to be
1223+ * empty and not to contain the null element.
1224+ *
1225+ * Please note that the Set is allowed to contain the empty String (“”).
1226+ *
1227+ * @param key
1228+ * the name of the set to return
1229+ * @return the set requested
1230+ * @throws IllegalArgumentException
1231+ * in case that getObject returns null, the returned object is
1232+ * not of type Collection, any of the content elements are not
1233+ * of type String or the collection is empty at all.
1234+ * @throws JMSException
1235+ * can be thrown by MapMessage.getObject(String)
1236+ */
1237+ public Set<String> getMandatorySetOfString(String key)
1238+ throws IllegalArgumentException, JMSException {
1239+ Set<String> result = new HashSet<String>();
1240+ Boolean emptiness = Boolean.TRUE;
1241+
1242+ Object collectionObject = ticket.getObject(key);
1243+ if (collectionObject == null)
1244+ throw new IllegalArgumentException("Missing mandatory argument: \""
1245+ + key + "\"");
1246+ if (!(collectionObject instanceof Collection<?>))
1247+ throw new IllegalArgumentException("Incompatible types: \"" + key
1248+ + "\" was not found to be of type Collection<?>.");
1249+ for (Object contentObject : (Collection<?>) collectionObject) {
1250+ if (contentObject == null || !(contentObject instanceof String))
1251+ throw new IllegalArgumentException(
1252+ "Incompatible types: An element of \"" + key
1253+ + "\" was not found to be of type String.");
1254+ result.add((String) contentObject);
1255+ emptiness = false;
1256+ }
1257+ if (emptiness)
1258+ throw new IllegalArgumentException("Missing mandatory argument: \""
1259+ + key + "\" must not be empty.");
1260+ return result;
1261+ }
1262+
1263+ /**
1264+ * The function getMandatoryString() fetches a String from a MapMessage. This is
1265+ * a strict implementation that requires the string not to be null and not
1266+ * to be empty.
1267+ *
1268+ * @param key
1269+ * the name of the string to return
1270+ * @return the string requested
1271+ * @throws IllegalArgumentException
1272+ * in case that getObject returns null or the returned string is
1273+ * of length “0”.
1274+ * @throws JMSException
1275+ * can be thrown by MapMessage.getString(String)
1276+ */
1277+ public String getMandatoryString(String key) throws IllegalArgumentException,
1278+ JMSException {
1279+ String result = ticket.getString(key);
1280+ if (result == null || result.length() == 0)
1281+ throw new IllegalArgumentException("Missing mandatory argument: \""
1282+ + key + "\"");
1283+ return result;
1284+ }
1285+
1286+ /**
1287+ * The function getString() fetches a String from a MapMessage. This is an
1288+ * access forward to the native function of the MapMessage. You may
1289+ * consider to use getMandatoryString() instead.
1290+ *
1291+ * @param key
1292+ * the name of the string to return
1293+ * @return the string requested (may be null or empty)
1294+ * @throws JMSException
1295+ * can be thrown by MapMessage.getString(String)
1296+ */
1297+
1298+ public String getString(String key) throws JMSException {
1299+ return ticket.getString(key);
1300+ }
1301+
1302+ /**
1303+ * The function getMandatoryInteger() fetches an Integer object from a MapMessage. This is
1304+ * a strict implementation that requires the Integer not to be null.
1305+ *
1306+ * @param key
1307+ * the name of the string to return
1308+ * @return the string requested
1309+ * @throws IllegalArgumentException
1310+ * in case that getObject returns null
1311+ * @throws JMSException
1312+ * can be thrown by MapMessage.getString(String)
1313+ */
1314+ public Integer getMandatoryInteger(String key) throws IllegalArgumentException,
1315+ JMSException {
1316+ Integer result = ticket.getInt(key);
1317+ if (result == null)
1318+ throw new IllegalArgumentException("Missing mandatory argument: \""
1319+ + key + "\"");
1320+ return result;
1321+ }
1322+
1323+ /**
1324+ * The function getMapOfStringToString() fetches a Map<String,String> from a
1325+ * MapMessage. This is a partly strict implementation that allows no null
1326+ * element neither as key, nor as value. However, if no object was found for
1327+ * the given key, or the key returned a null object, the function returns
1328+ * null. Also, the Map is allowed to contain the empty String (“”) as key
1329+ * and as values.
1330+ *
1331+ * @param key
1332+ * the name of the map to return
1333+ * @return the map requested
1334+ * @throws IllegalArgumentException
1335+ * in case that the object returned by getObject returned object
1336+ * is not of type Map or any of the content elements are not of
1337+ * type String.
1338+ */
1339+ public Map<String, String> getMapOfStringToString(String key) {
1340+ Map<String, String> result = new HashMap<String, String>();
1341+
1342+ Object mapObject = null;
1343+ try {
1344+ mapObject = ticket.getObject(key);
1345+ } catch (Exception irrelevant) {
1346+ }
1347+ if (mapObject == null)
1348+ return null;
1349+
1350+ if (!(mapObject instanceof Map<?, ?>))
1351+ throw new IllegalArgumentException("Incompatible types: \"" + key
1352+ + "\" was not found to be of type Map<?, ?>.");
1353+ for (Object keyObject : ((Map<?, ?>) mapObject).keySet()) {
1354+ Object valueObject = ((Map<?, ?>) mapObject).get(keyObject);
1355+ if (keyObject == null || !(keyObject instanceof String))
1356+ throw new IllegalArgumentException(
1357+ "Incompatible types: A key element of \"" + key
1358+ + "\" was not found to be of type String.");
1359+ if (valueObject == null || !(valueObject instanceof String))
1360+ throw new IllegalArgumentException(
1361+ "Incompatible types: A value element of \"" + key
1362+ + "\" was not found to be of type String.");
1363+ result.put((String) keyObject, (String) valueObject);
1364+ }
1365+
1366+ return result;
1367+ }
1368+
1369+ /**
1370+ * The function hasField() tests whether a field can be obtained from a
1371+ * MapMessage.
1372+ *
1373+ * @param string
1374+ * name of the field
1375+ * @return true or false
1376+ * @throws IllegalArgumentException
1377+ * can be thrown by MapMessage
1378+ * @throws JMSException
1379+ * can be thrown by MapMessage
1380+ */
1381+ public boolean hasField(String string) throws IllegalArgumentException,
1382+ JMSException {
1383+ String result = ticket.getString(string);
1384+ return (result != null && result.length() > 0);
1385+ }
1386+}
1387+
1388+
1389+
1390+
1391+
1392+
1393+
1394+
1395+
1396+
1397+
1398+
1399+
1400+
1401+
1402+
1403+
1404+
1405+
1406+
1407+
1408+
1409+
1410+
1411+
1412
1413=== added file 'src/org/goobi/mq/WebServiceResult.java'
1414--- src/org/goobi/mq/WebServiceResult.java 1970-01-01 00:00:00 +0000
1415+++ src/org/goobi/mq/WebServiceResult.java 2012-07-27 10:58:19 +0000
1416@@ -0,0 +1,88 @@
1417+/*
1418+ * This file is part of the Goobi Application - a Workflow tool for the support of
1419+ * mass digitization.
1420+ *
1421+ * Visit the websites for more information.
1422+ * - http://gdz.sub.uni-goettingen.de
1423+ * - http://www.goobi.org
1424+ * - http://launchpad.net/goobi-production
1425+ *
1426+ * This program is free software; you can redistribute it and/or modify it under
1427+ * the terms of the GNU General Public License as published by the Free Software
1428+ * Foundation; either version 2 of the License, or (at your option) any later
1429+ * version.
1430+ *
1431+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
1432+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1433+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1434+ * should have received a copy of the GNU General Public License along with this
1435+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
1436+ * Suite 330, Boston, MA 02111-1307 USA
1437+ */
1438+
1439+package org.goobi.mq;
1440+
1441+import javax.jms.MapMessage;
1442+
1443+import org.apache.log4j.Level;
1444+import org.apache.log4j.Logger;
1445+import org.joda.time.DateTime;
1446+import org.joda.time.format.DateTimeFormatter;
1447+import org.joda.time.format.ISODateTimeFormat;
1448+
1449+import de.sub.goobi.helper.enums.ReportLevel;
1450+
1451+public class WebServiceResult {
1452+ private static final Logger logger = Logger.getLogger(ActiveMQDirector.class);
1453+
1454+ private String queueName;
1455+ private String id;
1456+ private ReportLevel level;
1457+ private String message = null;
1458+
1459+ public WebServiceResult(String queueName, String id, ReportLevel level,
1460+ String message){
1461+ this.queueName = queueName;
1462+ this.id = id;
1463+ this.level = level;
1464+ this.message = message;
1465+ }
1466+
1467+ public WebServiceResult(String queueName, String id, ReportLevel level){
1468+ this.queueName = queueName;
1469+ this.id = id;
1470+ this.level = level;
1471+ }
1472+
1473+ public void send() {
1474+ if (ActiveMQDirector.getResultsTopic() == null) {
1475+
1476+ // If reporting to ActiveMQ is disabled, write log message
1477+ logger.log(level == ReportLevel.SUCCESS ? Level.INFO : Level.WARN,
1478+ "Processing message \"" + id + '@' + queueName
1479+ + "\" reports " + level.toLowerCase() + "."
1480+ + (message != null ? " (" + message + ")" : ""));
1481+ } else {
1482+ try {
1483+ MapMessage report = ActiveMQDirector.getSession().createMapMessage();
1484+
1485+ DateTime now = new DateTime();
1486+ DateTimeFormatter iso8601formatter = ISODateTimeFormat.dateTime();
1487+ report.setString("timestamp", iso8601formatter.print(now));
1488+ report.setString("queue", queueName);
1489+ report.setString("id", id);
1490+ report.setString("level", level.toLowerCase());
1491+ if (message != null)
1492+ report.setString("message", message);
1493+
1494+ ActiveMQDirector.getResultsTopic().send(report);
1495+
1496+ } catch (Exception exce) {
1497+ logger.fatal("Error sending report for \"" + id + '@'
1498+ + queueName + "\" (" + level.toLowerCase()
1499+ + (message != null ? ": " + message : "")
1500+ + "): Giving up.", exce);
1501+ }
1502+ }
1503+ }
1504+}
1505
1506=== added directory 'src/org/goobi/mq/processors'
1507=== added file 'src/org/goobi/mq/processors/CreateNewProcessProcessor.java'
1508--- src/org/goobi/mq/processors/CreateNewProcessProcessor.java 1970-01-01 00:00:00 +0000
1509+++ src/org/goobi/mq/processors/CreateNewProcessProcessor.java 2012-07-27 10:58:19 +0000
1510@@ -0,0 +1,375 @@
1511+/*
1512+ * This file is part of the Goobi Application - a Workflow tool for the support of
1513+ * mass digitization.
1514+ *
1515+ * Visit the websites for more information.
1516+ * - http://gdz.sub.uni-goettingen.de
1517+ * - http://www.goobi.org
1518+ * - http://launchpad.net/goobi-production
1519+ *
1520+ * This program is free software; you can redistribute it and/or modify it under
1521+ * the terms of the GNU General Public License as published by the Free Software
1522+ * Foundation; either version 2 of the License, or (at your option) any later
1523+ * version.
1524+ *
1525+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
1526+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1527+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1528+ * should have received a copy of the GNU General Public License along with this
1529+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
1530+ * Suite 330, Boston, MA 02111-1307 USA
1531+ */
1532+
1533+package org.goobi.mq.processors;
1534+
1535+import java.util.ArrayList;
1536+import java.util.HashSet;
1537+import java.util.Iterator;
1538+import java.util.List;
1539+import java.util.Map;
1540+import java.util.Set;
1541+
1542+import org.apache.log4j.Logger;
1543+import org.goobi.mq.ActiveMQProcessor;
1544+import org.goobi.mq.MapMessageObjectReader;
1545+import org.hibernate.Criteria;
1546+import org.hibernate.Session;
1547+
1548+import de.sub.goobi.beans.Prozess;
1549+import de.sub.goobi.config.ConfigMain;
1550+import de.sub.goobi.config.ConfigOpacDoctype;
1551+import de.sub.goobi.forms.AdditionalField;
1552+import de.sub.goobi.forms.ProzesskopieForm;
1553+import de.sub.goobi.helper.Helper;
1554+
1555+/**
1556+ * CreateNewProcessProcessor is an Apache Active MQ consumer which registers to
1557+ * a queue configured by "activeMQ.createNewProcess.queue" on application
1558+ * startup. It was designed to create new processes from outside Goobi. There
1559+ * are two ways providing to create new processes. If the MapMessage on that
1560+ * queue contains of all the fields listed, the bibliographic data is retrieved
1561+ * using a catalogue configured within Goobi. If “opac” is missing, it will try
1562+ * to create a process just upon the data passed in the “userFields” − “field”
1563+ * and “value” will be ignored in that case, and the “docType” can be set
1564+ * manually.
1565+ *
1566+ * Field summary:
1567+ *
1568+ * <dl>
1569+ * <dt>String template</dt>
1570+ * <dd>name of the process template to use.</dd>
1571+ * <dt>String opac</dt>
1572+ * <dd>Cataloge to use for lookup.</dd>
1573+ * <dt>String field</dt>
1574+ * <dd>Field to look into, usually 12 (PPN).</dd>
1575+ * <dt>String value</dt>
1576+ * <dd>Value to look for, id of physical medium</dd>
1577+ * <dt>String docType</dt>
1578+ * <dd>DocType value to use if no catalogue request is performed.</dd>
1579+ * <dt>Set&lt;String&gt; collections</dt>
1580+ * <dd>Collections to be selected.</dd>
1581+ * <dt>Map&lt;String, String&gt; userFields collections</dt>
1582+ * <dd>Fields to be populated manually.
1583+ * </dd>
1584+ * </dl>
1585+ *
1586+ * @author Matthias Ronge <matthias.ronge@zeutschel.de>
1587+ */
1588+public class CreateNewProcessProcessor extends ActiveMQProcessor {
1589+ private static final Logger logger = Logger.getLogger(CreateNewProcessProcessor.class);
1590+
1591+ public CreateNewProcessProcessor() {
1592+ super(ConfigMain.getParameter("activeMQ.createNewProcess.queue", null));
1593+ }
1594+
1595+ @Override
1596+ protected void process(MapMessageObjectReader args) throws Exception {
1597+
1598+ Set<String> collections = args.getMandatorySetOfString("collections");
1599+ String id = args.getMandatoryString("id");
1600+ String template = args.getMandatoryString("template");
1601+ Map<String, String> userFields = args.getMapOfStringToString("userFields");
1602+ if (args.hasField("opac")) {
1603+ String opac = args.getMandatoryString("opac");
1604+ String field = args.getMandatoryString("field");
1605+ String value = args.getMandatoryString("value");
1606+ createNewProcessMain(template, opac, field, value, id, null, collections, userFields);
1607+ } else {
1608+ String docType = args.getString("docType");
1609+ createNewProcessMain(template, null, null, null, id, docType, collections, userFields);
1610+ }
1611+
1612+ }
1613+
1614+ /**
1615+ * This is the main routine used to create new processes.
1616+ *
1617+ * @param template
1618+ * titel of the process template the new process shall be derived
1619+ * from
1620+ * @param opac
1621+ * name of the connection to a library catalogue to load the
1622+ * bibliographic data from (may be null)
1623+ * @param field
1624+ * number of the catalogue search field (ignored if “opac” is
1625+ * null)
1626+ * @param value
1627+ * search string (ignored if “opac” is null)
1628+ * @param id
1629+ * identifier to be used for the digitisation
1630+ * @param docType
1631+ * docType to set (may be null)
1632+ * @param collections
1633+ * collections to add the digitisation to
1634+ * @param userFields
1635+ * Values for additional fields can be set here (may be null)
1636+ * @throws Exception
1637+ * in various cases, such as bad parameters or errors in the
1638+ * underlying layers
1639+ */
1640+ protected void createNewProcessMain(String template, String opac, String field, String value, String id, String docType,
1641+ Set<String> collections, Map<String, String> userFields) throws Exception {
1642+
1643+ try {
1644+ ProzesskopieForm newProcess = newProcessFromTemplate(template);
1645+ newProcess.setDigitalCollections(validCollectionsForProcess(collections, newProcess));
1646+ if (opac != null)
1647+ getBibliorgaphicData(newProcess, opac, field, value);
1648+ if (docType != null && docTypeIsPossible(newProcess, docType))
1649+ newProcess.setDocType(docType);
1650+ if (userFields != null)
1651+ setUserFields(newProcess, userFields);
1652+ newProcess.CalcProzesstitel();
1653+ String state = newProcess.NeuenProzessAnlegen();
1654+ if (!state.equals("ProzessverwaltungKopie3"))
1655+ throw new RuntimeException();
1656+ logger.info("Created new process: " + id);
1657+ } catch (Exception exited) {
1658+ logger.error("Failed to create new process: " + id, exited);
1659+ throw exited;
1660+ }
1661+ }
1662+
1663+ /**
1664+ * The function newProcessFromTemplate() derives a ProzesskopieForm object
1665+ * from a given template.
1666+ *
1667+ * @param templateTitle
1668+ * titel value of the template to look for
1669+ * @return a ProzesskopieForm object, prepared from a given template
1670+ * @throws IllegalArgumentException
1671+ * if no suitable template is found
1672+ */
1673+ protected ProzesskopieForm newProcessFromTemplate(String templateTitle) throws IllegalArgumentException {
1674+ ProzesskopieForm result = new ProzesskopieForm();
1675+
1676+ List<Prozess> allTemplates = allTemplatesFromDatabase();
1677+ Prozess selectedTemplate = selectTemplateByTitle(allTemplates, templateTitle);
1678+ result.setProzessVorlage(selectedTemplate);
1679+ result.Prepare();
1680+ return result;
1681+ }
1682+
1683+ /**
1684+ * This method reads all Prozess objects from the hibernate.
1685+ *
1686+ * @return a List<Prozess> holding all templates
1687+ */
1688+ protected List<Prozess> allTemplatesFromDatabase() {
1689+ Session hibernateSession = Helper.getHibernateSession();
1690+ Criteria request = hibernateSession.createCriteria(Prozess.class);
1691+
1692+ @SuppressWarnings("unchecked")
1693+ List<Prozess> result = (List<Prozess>) request.list();
1694+
1695+ return result;
1696+ }
1697+
1698+ /**
1699+ * The function selectTemplateByTitle() iterates over a List of Prozess and
1700+ * returns the first element whose titel equals the given templateTitle.
1701+ *
1702+ * @param allTemplates
1703+ * a List<Prozess> which shall be examined
1704+ * @param templateTitle
1705+ * the title of the template to be picked up
1706+ * @return the template, if found
1707+ * @throws IllegalArgumentException
1708+ * is thrown, if there is no template matching the given
1709+ * templateTitle
1710+ */
1711+ protected Prozess selectTemplateByTitle(List<Prozess> allTemplates, String templateTitle) throws IllegalArgumentException {
1712+
1713+ Prozess result = null;
1714+ for (Prozess aTemplate : allTemplates) {
1715+ if (aTemplate.getTitel().equals(templateTitle)) {
1716+ result = aTemplate;
1717+ break;
1718+ }
1719+ }
1720+ if (result == null)
1721+ throw new IllegalArgumentException("Bad argument: No template \"" + templateTitle + "\" available.");
1722+ return result;
1723+ }
1724+
1725+ /**
1726+ * The function validCollectionsForProcess() tests whether a given set of
1727+ * collections can be assigned to new process. If so, the set of collections
1728+ * is returned as a list ready for assignment.
1729+ *
1730+ * @param collections
1731+ * a set of collection names to be tested
1732+ * @param process
1733+ * a ProzesskopieForm object whose prozessVorlage has been set
1734+ * @return an ArrayList which can be used to set the digitalCollections of a
1735+ * ProzesskopieForm
1736+ * @throws IllegalArgumentException
1737+ * in case that the given collection isn’t a valid subset of the
1738+ * digitalCollections possible here
1739+ */
1740+ protected List<String> validCollectionsForProcess(Set<String> collections, ProzesskopieForm process) throws IllegalArgumentException {
1741+
1742+ HashSet<String> possibleCollections = new HashSet<String>(process.getPossibleDigitalCollections());
1743+ if (!possibleCollections.containsAll(collections))
1744+ throw new IllegalArgumentException("Bad argument: One or more elements of \"collections\" is not available for template \""
1745+ + process.getProzessVorlage().getTitel() + "\".");
1746+ return new ArrayList<String>(collections);
1747+ }
1748+
1749+ /**
1750+ * The function docTypeIsPossible() tests whether a given docType String can
1751+ * be applied to a given process template. If so, it will return “true”,
1752+ * otherwise, it will throw an informative IllegalArgumentException.
1753+ *
1754+ * @param dialog
1755+ * the ProzesskopieForm object to test against
1756+ * @param docType
1757+ * the desired docType ID string
1758+ * @return true on success
1759+ * @throws IllegalArgumentException
1760+ * if a docType is not applicable to the template or the docType
1761+ * isn’t valid
1762+ */
1763+ protected boolean docTypeIsPossible(ProzesskopieForm dialog, String docType) throws IllegalArgumentException {
1764+ Boolean fieldIsUsed = dialog.getStandardFields().get("doctype");
1765+ if (fieldIsUsed == null || fieldIsUsed.equals(Boolean.FALSE))
1766+ throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide the standard field “doctype”.");
1767+
1768+ boolean valueIsValid = false;
1769+ Iterator<ConfigOpacDoctype> configOpacDoctypeIterator = dialog.getAllDoctypes().iterator();
1770+ do {
1771+ ConfigOpacDoctype option = configOpacDoctypeIterator.next();
1772+ valueIsValid = docType.equals(option.getTitle());
1773+ } while (!valueIsValid && configOpacDoctypeIterator.hasNext());
1774+ if (valueIsValid)
1775+ return true;
1776+ throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide a docType “{0}”.".replace("{0}",
1777+ docType));
1778+ }
1779+
1780+ /**
1781+ * The method setUserFields() allows to set any AdditionalField to a user
1782+ * specific value.
1783+ *
1784+ * @param form
1785+ * a ProzesskopieForm object whose AdditionalField objects are
1786+ * subject to the change
1787+ * @param userFields
1788+ * the data to pass to the form
1789+ * @throws RuntimeException
1790+ * in case that no field with a matching title was found in the
1791+ * ProzesskopieForm object
1792+ */
1793+ protected void setUserFields(ProzesskopieForm form, Map<String, String> userFields) throws RuntimeException {
1794+
1795+ for (String key : userFields.keySet()) {
1796+ setAdditionalField(form, key, userFields.get(key));
1797+ }
1798+
1799+ }
1800+
1801+ /**
1802+ * Sets the bibliographic data for a new process from a library catalogue.
1803+ * This is equal to manually choosing a catalogue and a search field,
1804+ * entering the search string and clicking “Apply”.
1805+ *
1806+ * Since the underlying OpacAuswerten() method doesn’t raise exceptions, we
1807+ * count the populated “additional details” fields before and after running
1808+ * the request and assume the method to have failed if not even one more
1809+ * field was populated by the method call.
1810+ *
1811+ * @param inputForm
1812+ * the ProzesskopieForm to be set
1813+ * @param id
1814+ * the ticket’s id
1815+ * @param opac
1816+ * the value for “Search in Opac”
1817+ * @param field
1818+ * the number of the search field, e.g. “12” for PPN.
1819+ * @param value
1820+ * the search string
1821+ * @throws RuntimeException
1822+ * is thrown if the search didn’t bring any results
1823+ */
1824+ protected void getBibliorgaphicData(ProzesskopieForm inputForm, String opac, String field, String value) throws RuntimeException {
1825+
1826+ inputForm.setOpacKatalog(opac);
1827+ inputForm.setOpacSuchfeld(field);
1828+ inputForm.setOpacSuchbegriff(value);
1829+
1830+ int before = countPopulatedAdditionalFields(inputForm);
1831+ inputForm.OpacAuswerten();
1832+ int afterwards = countPopulatedAdditionalFields(inputForm);
1833+
1834+ if (!(afterwards > before))
1835+ throw new RuntimeException("Searching the OPAC didn’t yield any results.");
1836+ }
1837+
1838+ /**
1839+ * The function countPopulatedAdditionalFields() returns the number of
1840+ * AdditionalFields in the given ProzesskopieForm that have meaningful
1841+ * content.
1842+ *
1843+ * @param form
1844+ * a ProzesskopieForm object to examine
1845+ * @return the number of AdditionalFields populated
1846+ */
1847+ protected int countPopulatedAdditionalFields(ProzesskopieForm form) {
1848+ int result = 0;
1849+
1850+ for (AdditionalField field : form.getAdditionalFields()) {
1851+ String value = field.getWert();
1852+ if (value != null && value.length() > 0)
1853+ result++;
1854+ }
1855+
1856+ return result;
1857+ }
1858+
1859+ /**
1860+ * The method setAdditionalField() sets the value of an AdditionalField held
1861+ * by a ProzesskopieForm object.
1862+ *
1863+ * @param inputForm
1864+ * a ProzesskopieForm object
1865+ * @param key
1866+ * the title of the AdditionalField whose value shall be modified
1867+ * @param value
1868+ * the new value for the AdditionalField
1869+ * @throws RuntimeException
1870+ * in case that no field with a matching title was found in the
1871+ * ProzesskopieForm object
1872+ */
1873+ protected void setAdditionalField(ProzesskopieForm inputForm, String key, String value) throws RuntimeException {
1874+
1875+ for (AdditionalField field : inputForm.getAdditionalFields()) {
1876+ if (key.equals(field.getTitel())) {
1877+ field.setWert(value);
1878+ return;
1879+ }
1880+ }
1881+
1882+ throw new RuntimeException("Couldn’t set “" + key + "” to “" + value + "”: No such field in record.");
1883+ }
1884+
1885+}
1886
1887=== added file 'src/org/goobi/mq/processors/FinaliseStepProcessor.java'
1888--- src/org/goobi/mq/processors/FinaliseStepProcessor.java 1970-01-01 00:00:00 +0000
1889+++ src/org/goobi/mq/processors/FinaliseStepProcessor.java 2012-07-27 10:58:19 +0000
1890@@ -0,0 +1,97 @@
1891+/*
1892+ * This file is part of the Goobi Application - a Workflow tool for the support of
1893+ * mass digitization.
1894+ *
1895+ * Visit the websites for more information.
1896+ * - http://gdz.sub.uni-goettingen.de
1897+ * - http://www.goobi.org
1898+ * - http://launchpad.net/goobi-production
1899+ *
1900+ * This program is free software; you can redistribute it and/or modify it under
1901+ * the terms of the GNU General Public License as published by the Free Software
1902+ * Foundation; either version 2 of the License, or (at your option) any later
1903+ * version.
1904+ *
1905+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
1906+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1907+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
1908+ * should have received a copy of the GNU General Public License along with this
1909+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
1910+ * Suite 330, Boston, MA 02111-1307 USA
1911+ */
1912+
1913+package org.goobi.mq.processors;
1914+
1915+import org.apache.commons.lang.StringEscapeUtils;
1916+import org.goobi.mq.ActiveMQProcessor;
1917+import org.goobi.mq.MapMessageObjectReader;
1918+
1919+import de.sub.goobi.config.ConfigMain;
1920+import de.sub.goobi.forms.AktuelleSchritteForm;
1921+import de.sub.goobi.persistence.SchrittDAO;
1922+
1923+/**
1924+ * This is a web service interface to close steps. You have to provide the step
1925+ * id as “id”; you can add a field “message” which will be added to the wiki
1926+ * field.
1927+ *
1928+ * @author Matthias Ronge <matthias.ronge@zeutschel.de>
1929+ */
1930+public class FinaliseStepProcessor extends ActiveMQProcessor {
1931+
1932+ /**
1933+ * The default constructor looks up the queue name to use in
1934+ * GoobiConfig.properties. If that is not configured and “null” is passed to
1935+ * the super constructor, this will prevent
1936+ * ActiveMQDirector.registerListeners() from starting this service.
1937+ */
1938+ public FinaliseStepProcessor() {
1939+ super(ConfigMain.getParameter("activeMQ.finaliseStep.queue", null));
1940+ }
1941+
1942+ /**
1943+ * This is the main routine processing incoming tickets. It gets an
1944+ * AktuelleSchritteForm object, sets it to the appropriate step which is
1945+ * retrieved from the database, appends the message − if any − to the wiki
1946+ * field, and executes the form’s the step close function.
1947+ *
1948+ * @param ticket
1949+ * the incoming message
1950+ *
1951+ * @see org.goobi.mq.ActiveMQProcessor#process(org.goobi.mq.MapMessageObjectReader)
1952+ */
1953+ protected void process(MapMessageObjectReader ticket) throws Exception {
1954+ AktuelleSchritteForm dialog = new AktuelleSchritteForm();
1955+ Integer stepID = ticket.getMandatoryInteger("id");
1956+ dialog.setMySchritt(new SchrittDAO().get(stepID));
1957+ if (ticket.hasField("message"))
1958+ addMessageToWikiField(dialog, ticket.getString("message"));
1959+ dialog.SchrittDurchBenutzerAbschliessen();
1960+ }
1961+
1962+ /**
1963+ * The addMessageToWikiField() method is a helper method which composes the
1964+ * new wiki field using a StringBuilder. The message is encoded using HTML
1965+ * entities to prevent certain characters from playing merry havoc when the
1966+ * message box shall be rendered in a browser later.
1967+ *
1968+ * @param form
1969+ * the AktuelleSchritteForm which is the owner of the wiki field
1970+ * @param message
1971+ * the message to append
1972+ */
1973+ protected void addMessageToWikiField(AktuelleSchritteForm form, String message) {
1974+ StringBuilder composer = new StringBuilder();
1975+ String wikiField = form.getWikiField();
1976+ if (wikiField != null && wikiField.length() > 0) {
1977+ composer.append(wikiField);
1978+ composer.append("\r\n");
1979+ }
1980+ composer.append("<p>");
1981+ composer.append(StringEscapeUtils.escapeHtml(message));
1982+ composer.append("</p>");
1983+ form.setWikiField(composer.toString());
1984+ return;
1985+ }
1986+
1987+}
1988
1989=== modified file 'src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java'
1990--- src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java 2012-02-22 07:43:02 +0000
1991+++ src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java 2012-07-27 10:58:19 +0000
1992@@ -94,7 +94,7 @@
1993 /* identify current user */
1994 LoginForm login = (LoginForm) Helper
1995 .getManagedBeanValue("#{LoginForm}");
1996- if (login.getMyBenutzer() == null)
1997+ if (login == null || login.getMyBenutzer() == null)
1998 return;
1999 /* init id-list, preset with item 0 */
2000 List<Integer> idList = new ArrayList<Integer>();

Subscribers

People subscribed via source and target branches

to all changes: