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
=== modified file 'WEB-INF/web.xml'
--- WEB-INF/web.xml 2012-04-20 06:57:46 +0000
+++ WEB-INF/web.xml 2012-07-27 10:58:19 +0000
@@ -215,6 +215,11 @@
215 <listener-class>org.goobi.production.ImageIOInitializer</listener-class>215 <listener-class>org.goobi.production.ImageIOInitializer</listener-class>
216 </listener>216 </listener>
217217
218 <!-- Listener to run ActiveMQ services -->
219 <listener>
220 <listener-class>org.goobi.mq.ActiveMQDirector</listener-class>
221 </listener>
222
218 <!--223 <!--
219 xml-Rpc-Server starten <listener> <listener-class>224 xml-Rpc-Server starten <listener> <listener-class>
220 de.sub.goobi.XmlRpc.Listener </listener-class> </listener>225 de.sub.goobi.XmlRpc.Listener </listener-class> </listener>
221226
=== modified file 'config/GoobiConfig.properties'
--- config/GoobiConfig.properties 2012-07-05 13:00:48 +0000
+++ config/GoobiConfig.properties 2012-07-27 10:58:19 +0000
@@ -184,6 +184,41 @@
184# Password encryption SHA or MD5184# Password encryption SHA or MD5
185ldap_encryption=SHA185ldap_encryption=SHA
186186
187# -----------------------------------
188# ActiveMQ web services
189# -----------------------------------
190
191# If you want to use Goobi's ActiveMQ web servic interface, set the host here
192#activeMQ.hostURL=tcp://localhost:61616
193
194# You can provide a topic that Goobi reports results and status messages to
195#activeMQ.results.topic=GoobiProduction.ResultMessages.Topic
196
197# By default, Goobi instructs the server to keep status messages for a
198# equivalent of 7 days. You can change this value in (milliseconds to) meet
199# your needs. 0 will disable the deletion of messages completely. (However,
200# the messages will only available on the Active MQ server if your
201# TopicSubscriber is online with the Active MQ server before the message is
202# sent. You migth therefore consider to configure the timeToLive for offline
203# usage within the Active MQ server’s activemq.xml file by adding a
204#
205# <policyEntry topic="GoobiProduction.ResultMessages.Topic">
206# <subscriptionRecoveryPolicy>
207# <timedSubscriptionRecoveryPolicy recoverDuration="604800000" />
208# </subscriptionRecoveryPolicy>
209# </policyEntry>
210#
211# block inside the <policyEntries>-Element. “recoverDuration” has to be given
212# in milliseconds here, too.)
213#activeMQ.results.timeToLive=604800000
214
215# You can provide a queue from which messages are read to create new processes
216#activeMQ.createNewProcess.queue=GoobiProduction.CreateNewProcesses.Queue
217
218# You can provide a queue from which messages are read to finalise steps
219#activeMQ.finaliseStep.queue=GoobiProduction.FinaliseStep.Queue
220
221
187##################################222##################################
188# DO NOT CHANGE THE OPTIONS BELOW!223# DO NOT CHANGE THE OPTIONS BELOW!
189##################################224##################################
190225
=== added directory 'doc'
=== added file 'doc/implementing_active_mq_web_services_for_goobi.txt'
--- doc/implementing_active_mq_web_services_for_goobi.txt 1970-01-01 00:00:00 +0000
+++ doc/implementing_active_mq_web_services_for_goobi.txt 2012-07-27 10:58:19 +0000
@@ -0,0 +1,80 @@
1 Implementing Active MQ web services for Goobi
2
3Active Message Queue is an open source Java Messaging (JMS) implementation
4provided by the Apache Software Foundation. It is intended to be used to
5to connect software components in a flexible way. The core is the Active MQ
6server which can be pictured like a post office. The mail boxes are named
7“queue” or “topic”. Queues work as expected: A producer sends a message where
8a consumer can pick it up. Topics can be pictured as black boards: The main
9difference is: A message read from a queue is removed from the queue. A message
10read from a topic is still available to others. Consumer clients can actively
11check the server or may register listeners with the server to be notified of
12new messages.
13
14This behaviour has already been implemented to Goobi: The org.goobi.mq.
15ActiveMQDirector is a ServletContextListener which is registered in web.xml.
16On application startup, it registers all consumers from its “services” variable
17to the server configured in “activeMQ.hostURL”.
18
19The elements of this variable are classes extending the abstract class
20ActiveMQProcessor. This class implements the MessageListener and provides
21facilities to handle exceptions and to store the consumer which is required on
22shutdown to disconnect.
23
24To implement another web service processor, you have to implement a class which
25extends ActiveMQProcessor and implements its abstract void process(MapMessage).
26Here is the right place to do whatever your processor is intended to do. There
27is a class MapMessageObjectReader which shall be used to type safe retrieve
28complex objects from MapMessages. You must add your new class to the “services”
29variable of ActiveMQDirector then.
30
31The Goobi server administrator shall be in control which processors are being
32started, and which queue names they listen on. Implementation of this
33configurability is designed this way: The implementing class must pass its
34queue name to the constructor of the parent class. This is done by implementing
35the constructor like in the following skeleton. If the queue name is not
36configured, it will return null which will prevent the ActiveMQDirector from
37registering it to the server. Inside the class, the queue name is available in
38the global variable “queueName” which is set by the parent class. The
39implementation may use arbitrary “activeMQ.myService.*” entries in
40GoobiConfig.properties for configuration.
41
42---------------------[ Service processor skeleton sample ]---------------------
43package org.goobi.mq.processores;
44
45import org.goobi.mq.*;
46import de.sub.goobi.config.ConfigMain;
47import de.sub.goobi.helper.enums.ReportLevel;
48
49public class MyServiceProcessor extends ActiveMQProcessor {
50
51 public MyServiceProcessor() {
52 super(ConfigMain.getParameter("activeMQ.myService.queue", null));
53 }
54
55 @Override
56 protected void process(MapMessageObjectReader args) throws Exception {
57 // TODO Auto-generated method stub
58 }
59}
60-------------------------------------------------------------------------------
61
62Responses from processors are designed to be handled as WebServiceResult
63objects. Those objects are MapMessages which send themselves to a topic
64configured in “activeMQ.results.topic”. They consist of the Strings “queue”
65(the name of the queue the job ticket was sent to), “id” (a String “id” in
66the MapMessage which is mandatory), “level” and an optional “message”. When
67designing the MapMessage layout to parameterise your web service processor,
68please keep in mind that a String element “id” is mandatory.
69
70If process() terminates without error, it is meant to have done its job
71successfully and a WebServiceResult with level “success” will be sent. If
72process() returns an exception, a WebServiceResult with level “fatal” will be
73sent. The exception will be returned as the “message” String. You may also use
74the WebServiceResult class to send messages with the levels “error”, “warn”,
75“info”, “debug”, “verbose” and “ludicrous” which are meant to be informative
76only:
77 new WebServiceResult(queueName, args.getMandatoryString("id"),
78 ReportLevel.INFO, "Remote host is down, trying again later.")
79 .send();
80
081
=== added file 'doc/web_service_to_create_new_processes.txt'
--- doc/web_service_to_create_new_processes.txt 1970-01-01 00:00:00 +0000
+++ doc/web_service_to_create_new_processes.txt 2012-07-27 10:58:19 +0000
@@ -0,0 +1,104 @@
1 Web service to create new processes
2
3Goobi.Production is equiped with a web service interface to automatically
4create new processes based on a given template. This allows the digitization
5process to be initiated from outside the application, for example by assigning
6a new digital ID to a record in a library catalogue (or—at choice of the
7library—by duplicating a record and assigning a new digital ID to the
8duplicate) and then running a script.
9
10The web service infrastructure is providet by an Active MQ server (see
11http://activemq.apache.org/ for details) which needs to be downloaded and
12started. Without further configuration, it provides everything necessary on
13port 61616 of the machine in question.
14
15The “activeMQ.hostURL” must be set in GoobiConfig.properties to point to this
16server. The “activeMQ.createNewProcess.queue” must be set to point to a queue
17of your choice where Goobi.Production shall pick up orders to create new
18processes.
19
20Orders must be javax.jms.MapMessage objects with the following key-value-pairs
21provided:
22
23 String template
24 name of the process template to use
25 String opac
26 Cataloge to use for lookup
27 String field
28 Field to look into, usually 12 (PPN)
29 String value
30 Value to look for, id of physical medium
31 String id
32 Ticket ID (used in log responses)
33 List<String> collections
34 Collections to be selected
35 Map<String, String> userFields (optional)
36 May be used to populates AdditionalField entries
37
38Here is a sample java client to do the job. It expects to be passed from the
39command line the Active MQ host (e.g. tcp://localhost:61616), the queue name
40and the parameters as listed above.
41
42To run this application, the following JARs from the ActiveMQ server’s /lib
43folder are required on the classpath:
44 * activemq-core
45 * geronimo-j2ee-management_1.1_spec
46 * genonimo-jms_1.1_spec
47 * log4j
48 * slf4j-api
49 * slf4j-log4j12
50
51--------------------------------[ Main.java ]----------------------------------
52import java.util.*;
53import javax.jms.*;
54import org.apache.activemq.ActiveMQConnectionFactory;
55
56public class Main {
57 public static int main(String[] args) { try {
58
59 // Check arguments
60 if (args.length < 8 || (args.length % 2) != 0) {
61 System.out.println("Parameters: Active MQ host, queue name, "
62 + "template name, opac name,");
63 System.out.println(" no. of search field, search "
64 + "string, digital id, collection name,");
65 System.out.println(" [additional details field, "
66 + "value, [add. details field, value, [...");
67 return 1;
68 }
69
70 // Connect to server
71 Connection connection = new ActiveMQConnectionFactory(args[0])
72 .createConnection();
73 connection.start();
74 Session session = connection.createSession(false,
75 Session.AUTO_ACKNOWLEDGE);
76 Destination destination = session.createQueue(args[1]);
77 MessageProducer producer = session.createProducer(destination);
78 producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
79
80 // Create job ticket
81 MapMessage message = session.createMapMessage();
82 message.setString("template", args[2]);
83 message.setString("opac", args[3]);
84 message.setString("field", args[4]);
85 message.setString("value", args[5]);
86 message.setString("id", args[6]);
87 List<String> collections = new ArrayList<String>();
88 collections.add(args[7]);
89 message.setObject("collections", collections);
90 Map<String, String> userFields = new HashMap<String, String>();
91 for (int i = 8; i < args.length; i += 2)
92 userFields.put(args[i], args[i + 1]);
93 if (userFields.size() != 0)
94 message.setObject("userFields", userFields);
95
96 // Send job ticket
97 producer.send(message);
98
99 // Shutdown
100 session.close();
101 connection.close();
102 } catch (Exception e) { e.printStackTrace(); return 2; }
103 return 0;
104} }
0105
=== added file 'lib/activemq-core-5.5.1.jar'
1Binary 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 differ106Binary 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
=== added file 'lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar'
2Binary 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 differ107Binary 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
=== added file 'lib/geronimo-jms_1.1_spec-1.1.1.jar'
3Binary 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 differ108Binary 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
=== added file 'lib/json-simple-1.1.1.jar'
4Binary 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 differ109Binary 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
=== modified file 'src-dubious/dubious/sub/goobi/helper/Page.java'
--- src-dubious/dubious/sub/goobi/helper/Page.java 2012-07-03 13:32:53 +0000
+++ src-dubious/dubious/sub/goobi/helper/Page.java 2012-07-27 10:58:19 +0000
@@ -47,7 +47,7 @@
47 public Page(Criteria criteria, int page) {47 public Page(Criteria criteria, int page) {
48 this.page = page;48 this.page = page;
49 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");49 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
50 if (login.getMyBenutzer() == null)50 if (login == null || login.getMyBenutzer() == null)
51 this.pageSize = 10;51 this.pageSize = 10;
52 else52 else
53 this.pageSize = login.getMyBenutzer().getTabellengroesse().intValue();53 this.pageSize = login.getMyBenutzer().getTabellengroesse().intValue();
5454
=== modified file 'src/de/sub/goobi/config/ConfigMain.java'
--- src/de/sub/goobi/config/ConfigMain.java 2012-05-30 08:24:19 +0000
+++ src/de/sub/goobi/config/ConfigMain.java 2012-07-27 10:58:19 +0000
@@ -119,9 +119,9 @@
119 119
120120
121 /**121 /**
122 * Ermitteln eines bestimmten Paramters der Konfiguration122 * Ermitteln eines bestimmten Parameters der Konfiguration
123 * 123 *
124 * @return Paramter als String124 * @return Parameter als String
125 */125 */
126 public static String getParameter(String inParameter) {126 public static String getParameter(String inParameter) {
127 try {127 try {
@@ -135,10 +135,10 @@
135 135
136 //TODO: Remove this methods, they are provided by Commons Configuration136 //TODO: Remove this methods, they are provided by Commons Configuration
137 /**137 /**
138 * Ermitteln eines bestimmten Paramters der Konfiguration mit Angabe eines138 * Ermitteln eines bestimmten Parameters der Konfiguration mit Angabe eines
139 * Default-Wertes139 * Default-Wertes
140 * 140 *
141 * @return Paramter als String141 * @return Parameter als String
142 */142 */
143 public static String getParameter(String inParameter, String inDefaultIfNull) {143 public static String getParameter(String inParameter, String inDefaultIfNull) {
144 try {144 try {
@@ -152,18 +152,18 @@
152 152
153153
154 /**154 /**
155 * Ermitteln eines boolean-Paramters der Konfiguration, default if missing: false155 * Ermitteln eines boolean-Parameters der Konfiguration, default if missing: false
156 * 156 *
157 * @return Paramter als String157 * @return Parameter als String
158 */158 */
159 public static boolean getBooleanParameter(String inParameter) {159 public static boolean getBooleanParameter(String inParameter) {
160 return getBooleanParameter(inParameter, false);160 return getBooleanParameter(inParameter, false);
161 }161 }
162162
163 /**163 /**
164 * Ermitteln eines boolean-Paramters der Konfiguration164 * Ermitteln eines boolean-Parameters der Konfiguration
165 * 165 *
166 * @return Paramter als String166 * @return Parameter als String
167 */167 */
168 public static boolean getBooleanParameter(String inParameter, boolean inDefault) {168 public static boolean getBooleanParameter(String inParameter, boolean inDefault) {
169 return config.getBoolean(inParameter, inDefault);169 return config.getBoolean(inParameter, inDefault);
@@ -172,17 +172,12 @@
172 172
173173
174 /**174 /**
175 * Ermitteln eines long-Paramters der Konfiguration175 * Ermitteln eines long-Parameters der Konfiguration
176 * 176 *
177 * @return Paramter als Long177 * @return Parameter als Long
178 */178 */
179 public static long getLongParameter(String inParameter, int inDefault) {179 public static long getLongParameter(String inParameter, long inDefault) {
180 try {180 return config.getLong(inParameter, inDefault);
181 return config.getLong(inParameter, inDefault);
182 } catch (RuntimeException e) {
183 myLogger.error(e);
184 return 0;
185 }
186 }181 }
187182
188 183
@@ -190,7 +185,7 @@
190 /**185 /**
191 * Request int-parameter from Configuration186 * Request int-parameter from Configuration
192 * 187 *
193 * @return Paramter as Int188 * @return Parameter as Int
194 */189 */
195 public static int getIntParameter(String inParameter) {190 public static int getIntParameter(String inParameter) {
196 return getIntParameter(inParameter, 0);191 return getIntParameter(inParameter, 0);
@@ -199,7 +194,7 @@
199 /**194 /**
200 * Request int-parameter from Configuration with default-value195 * Request int-parameter from Configuration with default-value
201 * 196 *
202 * @return Paramter as Int197 * @return Parameter as Int
203 */198 */
204 public static int getIntParameter(String inParameter, int inDefault) {199 public static int getIntParameter(String inParameter, int inDefault) {
205 try {200 try {
206201
=== added file 'src/de/sub/goobi/config/DigitalCollections.java'
--- src/de/sub/goobi/config/DigitalCollections.java 1970-01-01 00:00:00 +0000
+++ src/de/sub/goobi/config/DigitalCollections.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,77 @@
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 de.sub.goobi.config;
24
25import java.io.File;
26import java.io.FileNotFoundException;
27import java.io.IOException;
28import java.util.ArrayList;
29import java.util.Iterator;
30import java.util.List;
31
32import org.jdom.Document;
33import org.jdom.Element;
34import org.jdom.JDOMException;
35import org.jdom.input.SAXBuilder;
36
37import de.sub.goobi.beans.Prozess;
38
39public class DigitalCollections {
40
41 @SuppressWarnings("unchecked")
42 public static List<String> possibleDigitalCollectionsForProcess(
43 Prozess process) throws JDOMException, IOException {
44
45 List<String> result = new ArrayList<String>();
46 String filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "digitalCollections.xml";
47 if (!(new File(filename).exists())) {
48 throw new FileNotFoundException("File not found: " + filename);
49 }
50
51 /* Datei einlesen und Root ermitteln */
52 SAXBuilder builder = new SAXBuilder();
53 Document doc = builder.build(new File(filename));
54 Element root = doc.getRootElement();
55 /* alle Projekte durchlaufen */
56 List<Element> projekte = root.getChildren();
57 for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
58 Element projekt = (Element) iter.next();
59 List<Element> projektnamen = projekt.getChildren("name");
60 for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
61 Element projektname = (Element) iterator.next();
62
63 /*
64 * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
65 */
66 if (projektname.getText().equalsIgnoreCase(process.getProjekt().getTitel())) {
67 List<Element> myCols = projekt.getChildren("DigitalCollection");
68 for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
69 Element col = (Element) it2.next();
70 result.add(col.getText());
71 }
72 }
73 }
74 }
75 return result;
76 }
77}
078
=== modified file 'src/de/sub/goobi/forms/AktuelleSchritteForm.java'
--- src/de/sub/goobi/forms/AktuelleSchritteForm.java 2012-04-24 13:22:01 +0000
+++ src/de/sub/goobi/forms/AktuelleSchritteForm.java 2012-07-27 10:58:19 +0000
@@ -109,7 +109,7 @@
109 * --------------------- Vorgangsdatum generell anzeigen? -------------------109 * --------------------- Vorgangsdatum generell anzeigen? -------------------
110 */110 */
111 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");111 LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
112 if (login.getMyBenutzer() != null)112 if (login != null && login.getMyBenutzer() != null)
113 anzeigeAnpassen.put("processDate", login.getMyBenutzer().isConfVorgangsdatumAnzeigen());113 anzeigeAnpassen.put("processDate", login.getMyBenutzer().isConfVorgangsdatumAnzeigen());
114 else114 else
115 anzeigeAnpassen.put("processDate", false);115 anzeigeAnpassen.put("processDate", false);
116116
=== modified file 'src/de/sub/goobi/forms/ProzesskopieForm.java'
--- src/de/sub/goobi/forms/ProzesskopieForm.java 2012-05-09 15:19:19 +0000
+++ src/de/sub/goobi/forms/ProzesskopieForm.java 2012-07-27 10:58:19 +0000
@@ -23,6 +23,7 @@
23package de.sub.goobi.forms;23package de.sub.goobi.forms;
2424
25import java.io.File;25import java.io.File;
26import java.io.FileNotFoundException;
26import java.io.IOException;27import java.io.IOException;
27import java.sql.SQLException;28import java.sql.SQLException;
28import java.util.ArrayList;29import java.util.ArrayList;
@@ -84,6 +85,7 @@
84import de.sub.goobi.config.ConfigOpac;85import de.sub.goobi.config.ConfigOpac;
85import de.sub.goobi.config.ConfigOpacDoctype;86import de.sub.goobi.config.ConfigOpacDoctype;
86import de.sub.goobi.config.ConfigProjects;87import de.sub.goobi.config.ConfigProjects;
88import de.sub.goobi.config.DigitalCollections;
87import de.sub.goobi.helper.BeanHelper;89import de.sub.goobi.helper.BeanHelper;
88import de.sub.goobi.helper.Helper;90import de.sub.goobi.helper.Helper;
89import de.sub.goobi.helper.Messages;91import de.sub.goobi.helper.Messages;
@@ -932,46 +934,22 @@
932 return possibleDigitalCollection;934 return possibleDigitalCollection;
933 }935 }
934936
935 @SuppressWarnings("unchecked")
936 private void initializePossibleDigitalCollections() {937 private void initializePossibleDigitalCollections() {
937 possibleDigitalCollection = new ArrayList<String>();938 possibleDigitalCollection = new ArrayList<String>();
938 String filename = help.getGoobiConfigDirectory() + "digitalCollections.xml";939 try{
939 if (!(new File(filename).exists())) {940 possibleDigitalCollection = DigitalCollections.possibleDigitalCollectionsForProcess(prozessKopie);
940 Helper.setFehlerMeldung("File not found: ", filename);941 } catch (FileNotFoundException e1) {
941 return;942 myLogger.error("File not found: ", e1);
942 }943 Helper.setFehlerMeldung("File not found: ", e1);
943
944 try {
945 /* Datei einlesen und Root ermitteln */
946 SAXBuilder builder = new SAXBuilder();
947 Document doc = builder.build(new File(filename));
948 Element root = doc.getRootElement();
949 /* alle Projekte durchlaufen */
950 List<Element> projekte = root.getChildren();
951 for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
952 Element projekt = (Element) iter.next();
953 List<Element> projektnamen = projekt.getChildren("name");
954 for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
955 Element projektname = (Element) iterator.next();
956
957 /*
958 * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
959 */
960 if (projektname.getText().equalsIgnoreCase(prozessKopie.getProjekt().getTitel())) {
961 List<Element> myCols = projekt.getChildren("DigitalCollection");
962 for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
963 Element col = (Element) it2.next();
964 possibleDigitalCollection.add(col.getText());
965 }
966 }
967 }
968 }
969 } catch (JDOMException e1) {944 } catch (JDOMException e1) {
970 myLogger.error("error while parsing digital collections", e1);945 myLogger.error("error while parsing digital collections", e1);
971 Helper.setFehlerMeldung("Error while parsing digital collections", e1);946 Helper.setFehlerMeldung("Error while parsing digital collections", e1);
972 } catch (IOException e1) {947 } catch (IOException e1) {
973 myLogger.error("error while parsing digital collections", e1);948 myLogger.error("error while parsing digital collections", e1);
974 Helper.setFehlerMeldung("Error while parsing digital collections", e1);949 Helper.setFehlerMeldung("Error while parsing digital collections", e1);
950 } finally {
951 if(possibleDigitalCollection == null)
952 possibleDigitalCollection = new ArrayList<String>();
975 }953 }
976954
977 // if only one collection is possible take it directly955 // if only one collection is possible take it directly
978956
=== modified file 'src/de/sub/goobi/helper/Helper.java'
--- src/de/sub/goobi/helper/Helper.java 2012-07-03 13:32:53 +0000
+++ src/de/sub/goobi/helper/Helper.java 2012-07-27 10:58:19 +0000
@@ -42,13 +42,16 @@
42import javax.faces.context.FacesContext;42import javax.faces.context.FacesContext;
43import javax.servlet.http.HttpServletRequest;43import javax.servlet.http.HttpServletRequest;
4444
45import org.apache.log4j.Level;
45import org.apache.log4j.Logger;46import org.apache.log4j.Logger;
47import org.goobi.mq.WebServiceResult;
46import org.hibernate.Session;48import org.hibernate.Session;
47import org.jdom.Element;49import org.jdom.Element;
4850
49import de.sub.goobi.beans.Benutzer;51import de.sub.goobi.beans.Benutzer;
50import de.sub.goobi.config.ConfigMain;52import de.sub.goobi.config.ConfigMain;
51import de.sub.goobi.forms.LoginForm;53import de.sub.goobi.forms.LoginForm;
54import de.sub.goobi.helper.enums.ReportLevel;
52import de.sub.goobi.persistence.HibernateUtilOld;55import de.sub.goobi.persistence.HibernateUtilOld;
5356
54//TODO: Check if more method can be made static57//TODO: Check if more method can be made static
@@ -59,6 +62,7 @@
5962
60 private String myMetadatenVerzeichnis;63 private String myMetadatenVerzeichnis;
61 private String myConfigVerzeichnis;64 private String myConfigVerzeichnis;
65 public static Map<String, String> activeMQReporting = null;
6266
63 /**67 /**
64 * Ermitteln eines bestimmten Paramters des Requests68 * Ermitteln eines bestimmten Paramters des Requests
@@ -145,43 +149,59 @@
145 }149 }
146150
147 /**151 /**
148 * Dem aktuellen Formular eine Fehlermeldung für ein bestimmtes Control übergeben152 * The method setMeldung() adds an error message for a given control to the
153 * current form.
154 *
155 * @param control
156 * Name of control that caused the error or “null” if the error
157 * was not caused by a control
158 * @param messageKey
159 * The key of the error message. The method will try to resolve
160 * the key against its messages file.
161 * @param descriptionKey
162 * The description key of the error. The method will try to
163 * resolve the key against its messages file.
164 * @param infoOnly
165 * Set to false for error messages. Set to true for info
166 * messages.
149 */167 */
150 private static void setMeldung(String control, String meldung, String beschreibung, boolean nurInfo) {168 private static void setMeldung(String control, String messageKey,
169 String descriptionKey, boolean infoOnly) {
151 FacesContext context = FacesContext.getCurrentInstance();170 FacesContext context = FacesContext.getCurrentInstance();
152171
153 // Never forget: Strings are immutable172 String message;
154 meldung = meldung.replaceAll("<", "&lt;");173 String description;
155 meldung = meldung.replaceAll(">", "&gt;");174 try {
156 beschreibung = beschreibung.replaceAll("<", "&lt;");175 message = Messages.getString(messageKey);
157 beschreibung = beschreibung.replaceAll(">", "&gt;");176 } catch (RuntimeException e) {
158 /* wenn kein Kontext da ist, dann die Meldungen in Log */177 message = messageKey;
159 if (context == null) {178 }
160 if (nurInfo) {179 try {
161 myLogger.info(meldung + " " + beschreibung);180 description = Messages.getString(descriptionKey);
162 } else {181 } catch (RuntimeException e) {
163 myLogger.error(meldung + " " + beschreibung);182 description = descriptionKey;
164 }183 }
165 return;184
166 }185 message = message.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
167186 description = description.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
168 String msg = "";187
169 String beschr = "";188 String compoundMessage = message.replaceFirst(":\\s*$", "") + ": "
170 try {189 + description;
171 msg = Messages.getString(meldung);190
172 } catch (RuntimeException e) {191 /* If the Active MQ service is at work, report errors there, too. */
173 msg = meldung;192 if (activeMQReporting != null) {
174 }193 new WebServiceResult(activeMQReporting.get("queueName"),
175 try {194 activeMQReporting.get("id"), infoOnly ? ReportLevel.INFO
176 beschr = Messages.getString(beschreibung);195 : ReportLevel.ERROR, compoundMessage).send();
177 } catch (RuntimeException e) {196 }
178 beschr = beschreibung;197
179 }198 if (context != null) {
180199 context.addMessage(
181 if (nurInfo) {200 control,
182 context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, beschr));201 new FacesMessage(infoOnly ? FacesMessage.SEVERITY_INFO
183 } else {202 : FacesMessage.SEVERITY_ERROR, message, description));
184 context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, beschr));203 } else { // wenn kein Kontext da ist, dann die Meldungen in Log
204 myLogger.log(infoOnly ? Level.INFO : Level.ERROR, compoundMessage);
185 }205 }
186 }206 }
187207
188208
=== modified file 'src/de/sub/goobi/helper/UghHelper.java'
--- src/de/sub/goobi/helper/UghHelper.java 2012-02-22 07:43:02 +0000
+++ src/de/sub/goobi/helper/UghHelper.java 2012-07-27 10:58:19 +0000
@@ -42,6 +42,7 @@
42import ugh.exceptions.DocStructHasNoTypeException;42import ugh.exceptions.DocStructHasNoTypeException;
43import ugh.exceptions.MetadataTypeNotAllowedException;43import ugh.exceptions.MetadataTypeNotAllowedException;
44import de.sub.goobi.beans.Prozess;44import de.sub.goobi.beans.Prozess;
45import de.sub.goobi.config.ConfigMain;
45import de.sub.goobi.helper.exceptions.UghHelperException;46import de.sub.goobi.helper.exceptions.UghHelperException;
4647
47//TODO: Try to move this methods to UGH (ugh.util.UGHUtils would be a better place)48//TODO: Try to move this methods to UGH (ugh.util.UGHUtils would be a better place)
@@ -206,8 +207,13 @@
206 public String convertLanguage(String inLanguage) {207 public String convertLanguage(String inLanguage) {
207 /* Pfad zur Datei ermitteln */208 /* Pfad zur Datei ermitteln */
208 FacesContext context = FacesContext.getCurrentInstance();209 FacesContext context = FacesContext.getCurrentInstance();
209 HttpSession session = (HttpSession) context.getExternalContext().getSession(false);210 String filename;
210 String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";211 if (context != null) {
212 HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
213 filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";
214 } else {
215 filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opaclanguages.txt";
216 }
211 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */217 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */
212 try {218 try {
213 FileInputStream fis = new FileInputStream(filename);219 FileInputStream fis = new FileInputStream(filename);
@@ -233,8 +239,13 @@
233 String temp = inString;239 String temp = inString;
234 /* Pfad zur Datei ermitteln */240 /* Pfad zur Datei ermitteln */
235 FacesContext context = FacesContext.getCurrentInstance();241 FacesContext context = FacesContext.getCurrentInstance();
236 HttpSession session = (HttpSession) context.getExternalContext().getSession(false);242 String filename;
237 String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";243 if (context != null) {
244 HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
245 filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";
246 } else {
247 filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opacumlaut.txt";
248 }
238249
239 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */250 /* Datei zeilenweise durchlaufen und die Sprache vergleichen */
240 try {251 try {
241252
=== modified file 'src/de/sub/goobi/helper/WebDav.java'
--- src/de/sub/goobi/helper/WebDav.java 2012-07-03 13:49:14 +0000
+++ src/de/sub/goobi/helper/WebDav.java 2012-07-27 10:58:19 +0000
@@ -111,7 +111,8 @@
111111
112 public void UploadFromHome(Prozess myProzess) {112 public void UploadFromHome(Prozess myProzess) {
113 Benutzer aktuellerBenutzer = (Benutzer) Helper.getManagedBeanValue("#{LoginForm.myBenutzer}");113 Benutzer aktuellerBenutzer = (Benutzer) Helper.getManagedBeanValue("#{LoginForm.myBenutzer}");
114 UploadFromHome(aktuellerBenutzer, myProzess);114 if (aktuellerBenutzer != null)
115 UploadFromHome(aktuellerBenutzer, myProzess);
115 }116 }
116117
117 public void UploadFromHome(Benutzer inBenutzer, Prozess myProzess) {118 public void UploadFromHome(Benutzer inBenutzer, Prozess myProzess) {
118119
=== added file 'src/de/sub/goobi/helper/enums/ReportLevel.java'
--- src/de/sub/goobi/helper/enums/ReportLevel.java 1970-01-01 00:00:00 +0000
+++ src/de/sub/goobi/helper/enums/ReportLevel.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,36 @@
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 de.sub.goobi.helper.enums;
24
25/**
26 * These are the possible states for output to “activeMQ.results.topic”.
27 *
28 * @author Matthias Ronge <matthias.ronge@zeutschel.de>
29 */
30public enum ReportLevel {
31 FATAL, ERROR, WARN, INFO, SUCCESS, DEBUG, VERBOSE, LUDICROUS;
32
33 public String toLowerCase() {
34 return name().toLowerCase();
35 }
36}
037
=== added directory 'src/org/goobi/mq'
=== added file 'src/org/goobi/mq/ActiveMQDirector.java'
--- src/org/goobi/mq/ActiveMQDirector.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/ActiveMQDirector.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,231 @@
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.mq;
24
25import javax.jms.Connection;
26import javax.jms.DeliveryMode;
27import javax.jms.Destination;
28import javax.jms.ExceptionListener;
29import javax.jms.JMSException;
30import javax.jms.MessageConsumer;
31import javax.jms.MessageProducer;
32import javax.jms.Session;
33import javax.servlet.ServletContextEvent;
34import javax.servlet.ServletContextListener;
35
36import org.apache.activemq.ActiveMQConnectionFactory;
37import org.apache.log4j.Logger;
38import org.goobi.mq.processors.CreateNewProcessProcessor;
39import org.goobi.mq.processors.FinaliseStepProcessor;
40
41import de.sub.goobi.config.ConfigMain;
42
43/**
44 * The class ActiveMQDirector is the head of all Active MQ processors. It
45 * implements the ServletContextListener interface and is − if configured in
46 * web.xml − called automatically upon server starup. Its job is to connect to
47 * the Active MQ server and register the listeners configured.
48 *
49 * The ActiveMQDirector should ALWAYS be declared in web.xml. The Active MQ
50 * services are intended to be run in case that “activeMQ.hostURL” is configured
51 * in the GoobiConfig.properties file. To disable the service, the entry there
52 * should be emptied or commented out. Otherwise, a valid configuration would
53 * not start working and an administrator will hardly have a chance to find out
54 * why without inspecting the source code.
55 *
56 * The class ActiveMQDirector also provides a basic ExceptionListener
57 * implementation as required for the connection.
58 *
59 * @author Matthias Ronge <matthias.ronge@zeutschel.de>
60 */
61public class ActiveMQDirector implements ServletContextListener,
62 ExceptionListener {
63 private static final Logger logger = Logger
64 .getLogger(ActiveMQDirector.class);
65
66 // *** CONFIGURATION ***
67 // When implementing new Services, add them to this list:
68
69 protected static ActiveMQProcessor[] services;
70 static{
71 services = new ActiveMQProcessor[] {
72 new CreateNewProcessProcessor(),
73 new FinaliseStepProcessor()
74 };
75 }
76
77 protected static Connection connection = null;
78 protected static Session session = null;
79 protected static MessageProducer resultsTopic;
80
81 /**
82 * The method contextInitialized() is called by the web container on startup
83 * and is used to start up the active MQ connection. All processors from
84 * services[] are registered.
85 *
86 * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet
87 * .ServletContextEvent)
88 */
89 @Override
90 public void contextInitialized(ServletContextEvent initialisation) {
91 String activeMQHost = ConfigMain.getParameter("activeMQ.hostURL", null);
92 if (activeMQHost != null) {
93 session = connectToServer(activeMQHost);
94 if (session != null) {
95 registerListeners(services);
96 if (ConfigMain.getParameter("activeMQ.results.topic", null) != null) {
97 resultsTopic = setUpReportChannel(ConfigMain.getParameter("activeMQ.results.topic"));
98 } } } }
99
100 /**
101 * Sets up a connection to an active MQ server. The connection object is
102 * global because it is needed later to shut down the connection.
103 *
104 * @param server
105 * should be “tcp://{host}:{port}” or “vm://localhost” in case
106 * that the server is run inside the same virtual machine
107 * @return the session object or “null” upon error
108 */
109 protected Session connectToServer(String server) {
110 try {
111 connection = new ActiveMQConnectionFactory(server).createConnection();
112 connection.start();
113 connection.setExceptionListener(this); // → ActiveMQDirector.onException()
114 return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
115 } catch (Exception e) {
116 logger.fatal("Error connecting to ActiveMQ server, giving up.", e);
117 }
118 return null;
119 }
120
121 /**
122 * This method registers the listeners with the active MQ server.
123 *
124 * If a queue name was configured for a service, a MessageConsumer is set up
125 * to listen on that queue and, in case of incoming messages, make the
126 * service process the message. The message checker is saved inside the
127 * service to be able to shut it down later.
128 */
129 protected void registerListeners(ActiveMQProcessor[] processors) {
130 for (ActiveMQProcessor processor : processors) {
131 if (processor.getQueueName() != null) {
132 MessageConsumer messageChecker = null;
133 try {
134 Destination queue = session.createQueue(processor.getQueueName());
135 messageChecker = session.createConsumer(queue);
136 messageChecker.setMessageListener(processor);
137 processor.saveChecker(messageChecker);
138 } catch (Exception e) {
139 logger.fatal("Error setting up monitoring for \"" + processor.getQueueName() + "\": Giving up.", e);
140 } } } }
141
142 /**
143 * This sets up a connection to the topic the results shall be written to.
144 * The delivery mode is set so “persistent” to allow consumers not online
145 * with the server in the moment of message submission to read the messages
146 * later. The log messages are set to be kept on the server for 7 days. This
147 * value can be overridden from the GoobiConfig.properties parameter
148 * “activeMQ.results.timeToLive”. The time to live must be specified in
149 * milliseconds; 0 disables the oblivion. (See also:
150 * http://docs.oracle.com/javaee/6/api/javax/jms/MessageProducer.html#setTimeToLive%28long%29 )
151 *
152 * @param topic
153 * name of the active MQ topic
154 * @return a MessageProducer object ready for writing or “null” on error
155 */
156 protected MessageProducer setUpReportChannel(String topic) {
157 MessageProducer result;
158 try {
159 Destination channel = session.createTopic(topic);
160 result = session.createProducer(channel);
161 result.setDeliveryMode(DeliveryMode.PERSISTENT);
162 result.setTimeToLive(ConfigMain.getLongParameter("activeMQ.results.timeToLive", 604800000));
163 return result;
164 } catch (Exception e) {
165 logger.fatal("Error setting up report channel \"" + topic + "\": Giving up.", e);
166 }
167 return null;
168 }
169
170 /**
171 * This method is referenced from this.connectToServer() − see there.
172 *
173 * @see javax.jms.ExceptionListener#onException(javax.jms.JMSException)
174 */
175 @Override
176 public void onException(JMSException exce) {
177 logger.error(exce);
178 }
179
180 /**
181 * Any class that wants to create new Active MQ Messages needs read access
182 * to the session, since Active MQ messages don’t have a constructor.
183 *
184 * @return the session object
185 */
186 public static Session getSession() {
187 return session;
188 }
189
190 /**
191 * Instances of WebServiceResult can be sent by calling their send() method.
192 * Therefore, they need read access on their topic.
193 *
194 * @return the resultsTopic object
195 */
196 public static MessageProducer getResultsTopic() {
197 return resultsTopic;
198 }
199
200 /**
201 * The method contextDestroyed is called by the web container on shutdown.
202 * It shuts down all listeners, the session and last, the connection.
203 *
204 * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.
205 * ServletContextEvent)
206 */
207 @Override
208 public void contextDestroyed(ServletContextEvent destruction) {
209 // Shut down all watchers on any queues
210 for (ActiveMQProcessor service : services) {
211 MessageConsumer watcher = service.getChecker();
212 if (watcher != null) {
213 try {
214 watcher.close();
215 } catch (JMSException e) {
216 logger.error(e);
217 } } }
218
219 // quit session
220 try {
221 session.close();
222 } catch (JMSException e) {
223 logger.error(e);
224 }
225
226 // shut down connection
227 try {
228 connection.close();
229 } catch (JMSException e) {
230 logger.error(e);
231} } }
0232
=== added file 'src/org/goobi/mq/ActiveMQProcessor.java'
--- src/org/goobi/mq/ActiveMQProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/ActiveMQProcessor.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,161 @@
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.mq;
24
25import java.util.HashMap;
26import java.util.Map;
27
28import javax.jms.MapMessage;
29import javax.jms.Message;
30import javax.jms.MessageConsumer;
31import javax.jms.MessageListener;
32
33import de.sub.goobi.helper.Helper;
34import de.sub.goobi.helper.enums.ReportLevel;
35
36/**
37 * The class ActiveMQProcessor offers general services, such as making the
38 * incoming messages available as MapMessages and publishing the results. When I
39 * came clear that this code would be necessary for every processor, I thought
40 * an abstract class would be the right place for it. ActiveMQProcessor also
41 * provides a place to save the checker for the ActiveMQDirector, to be able to
42 * shut it down later.
43 *
44 * @author Matthias Ronge <matthias.ronge@zeutschel.de>
45 */
46public abstract class ActiveMQProcessor implements MessageListener {
47
48 protected String queueName; // the queue name will be available here
49 private MessageConsumer checker;
50
51 /**
52 * Implement the method process() to let your service actually do what you
53 * want him to do.
54 *
55 * @param ticket
56 * A MapMessage which can be processor-specific except that it
57 * requires to have a field “id”.
58 */
59 protected abstract void process(MapMessageObjectReader ticket) throws Exception;
60
61 /**
62 * Instantiating the class ActiveMQProcessor always requires to pass the
63 * name of the queue it should be attached to. That means, your constructor
64 * should look like this:
65 *
66 * <pre>
67 * public MyServiceProcessor(){
68 * super(ConfigMain.getParameter("activeMQ.myService.queue", null));
69 * }
70 * </pre>
71 *
72 * If the parameter is not set in GoobiConfig.properties, it will return
73 * “null” and so prevents it from being set up in ActiveMQDirector.
74 *
75 * @param queueName
76 * the queue name, if configured, or “null” to prevent the
77 * processor from being connected.
78 */
79 public ActiveMQProcessor(String queueName) {
80 this.queueName = queueName;
81 }
82
83 /**
84 * The method onMessage() provides a corset which checks the message being a
85 * MapMessage, performs a type safe type cast, and extracts the job’s ID to
86 * be able to send useful results to the results topic, which it does after
87 * the abstract method process() has finished.
88 *
89 * Since this will be the same for all processors which use MapMessages, I
90 * extracted the portion into the abstract class.
91 *
92 * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
93 */
94 @Override
95 public void onMessage(Message arg) {
96 MapMessageObjectReader ticket = null;
97 String ticketID = null;
98
99 try {
100 // Basic check ticket
101 if (arg instanceof MapMessage)
102 ticket = new MapMessageObjectReader((MapMessage) arg);
103 else
104 throw new IllegalArgumentException("Incompatible types.");
105 ticketID = ticket.getMandatoryString("id");
106
107 // turn on logging
108 Map<String,String> loggingConfig = new HashMap<String,String>();
109 loggingConfig.put("queueName", queueName);
110 loggingConfig.put("id", ticketID);
111 Helper.activeMQReporting = loggingConfig;
112
113 // process ticket
114 process(ticket);
115
116 // turn off logging again
117 Helper.activeMQReporting = null;
118
119 // if everything ‘s fine, report success
120 new WebServiceResult(queueName, ticketID, ReportLevel.SUCCESS)
121 .send();
122
123 } catch (Exception exce) {
124 // report any errors
125 new WebServiceResult(queueName, ticketID, ReportLevel.FATAL,
126 exce.getMessage()).send();
127 }
128 }
129
130 /**
131 * This method is used to get the queue name upon initialisation.
132 *
133 * @return the queue name
134 */
135 public String getQueueName() {
136 return queueName;
137 }
138
139 /**
140 * The parent object which is there to check for new messages and to trigger
141 * the method onMessage() is saved inside the class, to have it lately for
142 * shutting down the service again.
143 *
144 * @param checker
145 * the MessageConsumer object responsible for checking messages
146 */
147
148 public void saveChecker(MessageConsumer checker) {
149 this.checker = checker;
150 }
151
152 /**
153 * This method is used to get back the message checking object upon
154 * shutdown.
155 *
156 * @return the MessageConsumer object responsible for checking messages
157 */
158 public MessageConsumer getChecker() {
159 return checker;
160 }
161}
0162
=== added file 'src/org/goobi/mq/MapMessageObjectReader.java'
--- src/org/goobi/mq/MapMessageObjectReader.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/MapMessageObjectReader.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,242 @@
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.mq;
24
25import java.util.Collection;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.Map;
29import java.util.Set;
30
31import javax.jms.JMSException;
32import javax.jms.MapMessage;
33
34public class MapMessageObjectReader {
35
36 private MapMessage ticket;
37
38 /**
39 * This instantiates a new MapMessageObjectReader which is attached to a
40 * given MapMessage.
41 *
42 * @param message
43 */
44 public MapMessageObjectReader(MapMessage message) {
45 if (message == null)
46 throw new IllegalArgumentException(
47 "MapMessageObjectReader: null argument in constructor.");
48 this.ticket = message;
49 }
50
51 /**
52 * The function getMandatorySetOfString() fetches a Set<String> from a MapMessage.
53 * This is a strict implementation that requires the collection not to be
54 * empty and not to contain the null element.
55 *
56 * Please note that the Set is allowed to contain the empty String (“”).
57 *
58 * @param key
59 * the name of the set to return
60 * @return the set requested
61 * @throws IllegalArgumentException
62 * in case that getObject returns null, the returned object is
63 * not of type Collection, any of the content elements are not
64 * of type String or the collection is empty at all.
65 * @throws JMSException
66 * can be thrown by MapMessage.getObject(String)
67 */
68 public Set<String> getMandatorySetOfString(String key)
69 throws IllegalArgumentException, JMSException {
70 Set<String> result = new HashSet<String>();
71 Boolean emptiness = Boolean.TRUE;
72
73 Object collectionObject = ticket.getObject(key);
74 if (collectionObject == null)
75 throw new IllegalArgumentException("Missing mandatory argument: \""
76 + key + "\"");
77 if (!(collectionObject instanceof Collection<?>))
78 throw new IllegalArgumentException("Incompatible types: \"" + key
79 + "\" was not found to be of type Collection<?>.");
80 for (Object contentObject : (Collection<?>) collectionObject) {
81 if (contentObject == null || !(contentObject instanceof String))
82 throw new IllegalArgumentException(
83 "Incompatible types: An element of \"" + key
84 + "\" was not found to be of type String.");
85 result.add((String) contentObject);
86 emptiness = false;
87 }
88 if (emptiness)
89 throw new IllegalArgumentException("Missing mandatory argument: \""
90 + key + "\" must not be empty.");
91 return result;
92 }
93
94 /**
95 * The function getMandatoryString() fetches a String from a MapMessage. This is
96 * a strict implementation that requires the string not to be null and not
97 * to be empty.
98 *
99 * @param key
100 * the name of the string to return
101 * @return the string requested
102 * @throws IllegalArgumentException
103 * in case that getObject returns null or the returned string is
104 * of length “0”.
105 * @throws JMSException
106 * can be thrown by MapMessage.getString(String)
107 */
108 public String getMandatoryString(String key) throws IllegalArgumentException,
109 JMSException {
110 String result = ticket.getString(key);
111 if (result == null || result.length() == 0)
112 throw new IllegalArgumentException("Missing mandatory argument: \""
113 + key + "\"");
114 return result;
115 }
116
117 /**
118 * The function getString() fetches a String from a MapMessage. This is an
119 * access forward to the native function of the MapMessage. You may
120 * consider to use getMandatoryString() instead.
121 *
122 * @param key
123 * the name of the string to return
124 * @return the string requested (may be null or empty)
125 * @throws JMSException
126 * can be thrown by MapMessage.getString(String)
127 */
128
129 public String getString(String key) throws JMSException {
130 return ticket.getString(key);
131 }
132
133 /**
134 * The function getMandatoryInteger() fetches an Integer object from a MapMessage. This is
135 * a strict implementation that requires the Integer not to be null.
136 *
137 * @param key
138 * the name of the string to return
139 * @return the string requested
140 * @throws IllegalArgumentException
141 * in case that getObject returns null
142 * @throws JMSException
143 * can be thrown by MapMessage.getString(String)
144 */
145 public Integer getMandatoryInteger(String key) throws IllegalArgumentException,
146 JMSException {
147 Integer result = ticket.getInt(key);
148 if (result == null)
149 throw new IllegalArgumentException("Missing mandatory argument: \""
150 + key + "\"");
151 return result;
152 }
153
154 /**
155 * The function getMapOfStringToString() fetches a Map<String,String> from a
156 * MapMessage. This is a partly strict implementation that allows no null
157 * element neither as key, nor as value. However, if no object was found for
158 * the given key, or the key returned a null object, the function returns
159 * null. Also, the Map is allowed to contain the empty String (“”) as key
160 * and as values.
161 *
162 * @param key
163 * the name of the map to return
164 * @return the map requested
165 * @throws IllegalArgumentException
166 * in case that the object returned by getObject returned object
167 * is not of type Map or any of the content elements are not of
168 * type String.
169 */
170 public Map<String, String> getMapOfStringToString(String key) {
171 Map<String, String> result = new HashMap<String, String>();
172
173 Object mapObject = null;
174 try {
175 mapObject = ticket.getObject(key);
176 } catch (Exception irrelevant) {
177 }
178 if (mapObject == null)
179 return null;
180
181 if (!(mapObject instanceof Map<?, ?>))
182 throw new IllegalArgumentException("Incompatible types: \"" + key
183 + "\" was not found to be of type Map<?, ?>.");
184 for (Object keyObject : ((Map<?, ?>) mapObject).keySet()) {
185 Object valueObject = ((Map<?, ?>) mapObject).get(keyObject);
186 if (keyObject == null || !(keyObject instanceof String))
187 throw new IllegalArgumentException(
188 "Incompatible types: A key element of \"" + key
189 + "\" was not found to be of type String.");
190 if (valueObject == null || !(valueObject instanceof String))
191 throw new IllegalArgumentException(
192 "Incompatible types: A value element of \"" + key
193 + "\" was not found to be of type String.");
194 result.put((String) keyObject, (String) valueObject);
195 }
196
197 return result;
198 }
199
200 /**
201 * The function hasField() tests whether a field can be obtained from a
202 * MapMessage.
203 *
204 * @param string
205 * name of the field
206 * @return true or false
207 * @throws IllegalArgumentException
208 * can be thrown by MapMessage
209 * @throws JMSException
210 * can be thrown by MapMessage
211 */
212 public boolean hasField(String string) throws IllegalArgumentException,
213 JMSException {
214 String result = ticket.getString(string);
215 return (result != null && result.length() > 0);
216 }
217}
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
0243
=== added file 'src/org/goobi/mq/WebServiceResult.java'
--- src/org/goobi/mq/WebServiceResult.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/WebServiceResult.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,88 @@
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.mq;
24
25import javax.jms.MapMessage;
26
27import org.apache.log4j.Level;
28import org.apache.log4j.Logger;
29import org.joda.time.DateTime;
30import org.joda.time.format.DateTimeFormatter;
31import org.joda.time.format.ISODateTimeFormat;
32
33import de.sub.goobi.helper.enums.ReportLevel;
34
35public class WebServiceResult {
36 private static final Logger logger = Logger.getLogger(ActiveMQDirector.class);
37
38 private String queueName;
39 private String id;
40 private ReportLevel level;
41 private String message = null;
42
43 public WebServiceResult(String queueName, String id, ReportLevel level,
44 String message){
45 this.queueName = queueName;
46 this.id = id;
47 this.level = level;
48 this.message = message;
49 }
50
51 public WebServiceResult(String queueName, String id, ReportLevel level){
52 this.queueName = queueName;
53 this.id = id;
54 this.level = level;
55 }
56
57 public void send() {
58 if (ActiveMQDirector.getResultsTopic() == null) {
59
60 // If reporting to ActiveMQ is disabled, write log message
61 logger.log(level == ReportLevel.SUCCESS ? Level.INFO : Level.WARN,
62 "Processing message \"" + id + '@' + queueName
63 + "\" reports " + level.toLowerCase() + "."
64 + (message != null ? " (" + message + ")" : ""));
65 } else {
66 try {
67 MapMessage report = ActiveMQDirector.getSession().createMapMessage();
68
69 DateTime now = new DateTime();
70 DateTimeFormatter iso8601formatter = ISODateTimeFormat.dateTime();
71 report.setString("timestamp", iso8601formatter.print(now));
72 report.setString("queue", queueName);
73 report.setString("id", id);
74 report.setString("level", level.toLowerCase());
75 if (message != null)
76 report.setString("message", message);
77
78 ActiveMQDirector.getResultsTopic().send(report);
79
80 } catch (Exception exce) {
81 logger.fatal("Error sending report for \"" + id + '@'
82 + queueName + "\" (" + level.toLowerCase()
83 + (message != null ? ": " + message : "")
84 + "): Giving up.", exce);
85 }
86 }
87 }
88}
089
=== added directory 'src/org/goobi/mq/processors'
=== added file 'src/org/goobi/mq/processors/CreateNewProcessProcessor.java'
--- src/org/goobi/mq/processors/CreateNewProcessProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/processors/CreateNewProcessProcessor.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,375 @@
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.mq.processors;
24
25import java.util.ArrayList;
26import java.util.HashSet;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31
32import org.apache.log4j.Logger;
33import org.goobi.mq.ActiveMQProcessor;
34import org.goobi.mq.MapMessageObjectReader;
35import org.hibernate.Criteria;
36import org.hibernate.Session;
37
38import de.sub.goobi.beans.Prozess;
39import de.sub.goobi.config.ConfigMain;
40import de.sub.goobi.config.ConfigOpacDoctype;
41import de.sub.goobi.forms.AdditionalField;
42import de.sub.goobi.forms.ProzesskopieForm;
43import de.sub.goobi.helper.Helper;
44
45/**
46 * CreateNewProcessProcessor is an Apache Active MQ consumer which registers to
47 * a queue configured by "activeMQ.createNewProcess.queue" on application
48 * startup. It was designed to create new processes from outside Goobi. There
49 * are two ways providing to create new processes. If the MapMessage on that
50 * queue contains of all the fields listed, the bibliographic data is retrieved
51 * using a catalogue configured within Goobi. If “opac” is missing, it will try
52 * to create a process just upon the data passed in the “userFields” − “field”
53 * and “value” will be ignored in that case, and the “docType” can be set
54 * manually.
55 *
56 * Field summary:
57 *
58 * <dl>
59 * <dt>String template</dt>
60 * <dd>name of the process template to use.</dd>
61 * <dt>String opac</dt>
62 * <dd>Cataloge to use for lookup.</dd>
63 * <dt>String field</dt>
64 * <dd>Field to look into, usually 12 (PPN).</dd>
65 * <dt>String value</dt>
66 * <dd>Value to look for, id of physical medium</dd>
67 * <dt>String docType</dt>
68 * <dd>DocType value to use if no catalogue request is performed.</dd>
69 * <dt>Set&lt;String&gt; collections</dt>
70 * <dd>Collections to be selected.</dd>
71 * <dt>Map&lt;String, String&gt; userFields collections</dt>
72 * <dd>Fields to be populated manually.
73 * </dd>
74 * </dl>
75 *
76 * @author Matthias Ronge <matthias.ronge@zeutschel.de>
77 */
78public class CreateNewProcessProcessor extends ActiveMQProcessor {
79 private static final Logger logger = Logger.getLogger(CreateNewProcessProcessor.class);
80
81 public CreateNewProcessProcessor() {
82 super(ConfigMain.getParameter("activeMQ.createNewProcess.queue", null));
83 }
84
85 @Override
86 protected void process(MapMessageObjectReader args) throws Exception {
87
88 Set<String> collections = args.getMandatorySetOfString("collections");
89 String id = args.getMandatoryString("id");
90 String template = args.getMandatoryString("template");
91 Map<String, String> userFields = args.getMapOfStringToString("userFields");
92 if (args.hasField("opac")) {
93 String opac = args.getMandatoryString("opac");
94 String field = args.getMandatoryString("field");
95 String value = args.getMandatoryString("value");
96 createNewProcessMain(template, opac, field, value, id, null, collections, userFields);
97 } else {
98 String docType = args.getString("docType");
99 createNewProcessMain(template, null, null, null, id, docType, collections, userFields);
100 }
101
102 }
103
104 /**
105 * This is the main routine used to create new processes.
106 *
107 * @param template
108 * titel of the process template the new process shall be derived
109 * from
110 * @param opac
111 * name of the connection to a library catalogue to load the
112 * bibliographic data from (may be null)
113 * @param field
114 * number of the catalogue search field (ignored if “opac” is
115 * null)
116 * @param value
117 * search string (ignored if “opac” is null)
118 * @param id
119 * identifier to be used for the digitisation
120 * @param docType
121 * docType to set (may be null)
122 * @param collections
123 * collections to add the digitisation to
124 * @param userFields
125 * Values for additional fields can be set here (may be null)
126 * @throws Exception
127 * in various cases, such as bad parameters or errors in the
128 * underlying layers
129 */
130 protected void createNewProcessMain(String template, String opac, String field, String value, String id, String docType,
131 Set<String> collections, Map<String, String> userFields) throws Exception {
132
133 try {
134 ProzesskopieForm newProcess = newProcessFromTemplate(template);
135 newProcess.setDigitalCollections(validCollectionsForProcess(collections, newProcess));
136 if (opac != null)
137 getBibliorgaphicData(newProcess, opac, field, value);
138 if (docType != null && docTypeIsPossible(newProcess, docType))
139 newProcess.setDocType(docType);
140 if (userFields != null)
141 setUserFields(newProcess, userFields);
142 newProcess.CalcProzesstitel();
143 String state = newProcess.NeuenProzessAnlegen();
144 if (!state.equals("ProzessverwaltungKopie3"))
145 throw new RuntimeException();
146 logger.info("Created new process: " + id);
147 } catch (Exception exited) {
148 logger.error("Failed to create new process: " + id, exited);
149 throw exited;
150 }
151 }
152
153 /**
154 * The function newProcessFromTemplate() derives a ProzesskopieForm object
155 * from a given template.
156 *
157 * @param templateTitle
158 * titel value of the template to look for
159 * @return a ProzesskopieForm object, prepared from a given template
160 * @throws IllegalArgumentException
161 * if no suitable template is found
162 */
163 protected ProzesskopieForm newProcessFromTemplate(String templateTitle) throws IllegalArgumentException {
164 ProzesskopieForm result = new ProzesskopieForm();
165
166 List<Prozess> allTemplates = allTemplatesFromDatabase();
167 Prozess selectedTemplate = selectTemplateByTitle(allTemplates, templateTitle);
168 result.setProzessVorlage(selectedTemplate);
169 result.Prepare();
170 return result;
171 }
172
173 /**
174 * This method reads all Prozess objects from the hibernate.
175 *
176 * @return a List<Prozess> holding all templates
177 */
178 protected List<Prozess> allTemplatesFromDatabase() {
179 Session hibernateSession = Helper.getHibernateSession();
180 Criteria request = hibernateSession.createCriteria(Prozess.class);
181
182 @SuppressWarnings("unchecked")
183 List<Prozess> result = (List<Prozess>) request.list();
184
185 return result;
186 }
187
188 /**
189 * The function selectTemplateByTitle() iterates over a List of Prozess and
190 * returns the first element whose titel equals the given templateTitle.
191 *
192 * @param allTemplates
193 * a List<Prozess> which shall be examined
194 * @param templateTitle
195 * the title of the template to be picked up
196 * @return the template, if found
197 * @throws IllegalArgumentException
198 * is thrown, if there is no template matching the given
199 * templateTitle
200 */
201 protected Prozess selectTemplateByTitle(List<Prozess> allTemplates, String templateTitle) throws IllegalArgumentException {
202
203 Prozess result = null;
204 for (Prozess aTemplate : allTemplates) {
205 if (aTemplate.getTitel().equals(templateTitle)) {
206 result = aTemplate;
207 break;
208 }
209 }
210 if (result == null)
211 throw new IllegalArgumentException("Bad argument: No template \"" + templateTitle + "\" available.");
212 return result;
213 }
214
215 /**
216 * The function validCollectionsForProcess() tests whether a given set of
217 * collections can be assigned to new process. If so, the set of collections
218 * is returned as a list ready for assignment.
219 *
220 * @param collections
221 * a set of collection names to be tested
222 * @param process
223 * a ProzesskopieForm object whose prozessVorlage has been set
224 * @return an ArrayList which can be used to set the digitalCollections of a
225 * ProzesskopieForm
226 * @throws IllegalArgumentException
227 * in case that the given collection isn’t a valid subset of the
228 * digitalCollections possible here
229 */
230 protected List<String> validCollectionsForProcess(Set<String> collections, ProzesskopieForm process) throws IllegalArgumentException {
231
232 HashSet<String> possibleCollections = new HashSet<String>(process.getPossibleDigitalCollections());
233 if (!possibleCollections.containsAll(collections))
234 throw new IllegalArgumentException("Bad argument: One or more elements of \"collections\" is not available for template \""
235 + process.getProzessVorlage().getTitel() + "\".");
236 return new ArrayList<String>(collections);
237 }
238
239 /**
240 * The function docTypeIsPossible() tests whether a given docType String can
241 * be applied to a given process template. If so, it will return “true”,
242 * otherwise, it will throw an informative IllegalArgumentException.
243 *
244 * @param dialog
245 * the ProzesskopieForm object to test against
246 * @param docType
247 * the desired docType ID string
248 * @return true on success
249 * @throws IllegalArgumentException
250 * if a docType is not applicable to the template or the docType
251 * isn’t valid
252 */
253 protected boolean docTypeIsPossible(ProzesskopieForm dialog, String docType) throws IllegalArgumentException {
254 Boolean fieldIsUsed = dialog.getStandardFields().get("doctype");
255 if (fieldIsUsed == null || fieldIsUsed.equals(Boolean.FALSE))
256 throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide the standard field “doctype”.");
257
258 boolean valueIsValid = false;
259 Iterator<ConfigOpacDoctype> configOpacDoctypeIterator = dialog.getAllDoctypes().iterator();
260 do {
261 ConfigOpacDoctype option = configOpacDoctypeIterator.next();
262 valueIsValid = docType.equals(option.getTitle());
263 } while (!valueIsValid && configOpacDoctypeIterator.hasNext());
264 if (valueIsValid)
265 return true;
266 throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide a docType “{0}”.".replace("{0}",
267 docType));
268 }
269
270 /**
271 * The method setUserFields() allows to set any AdditionalField to a user
272 * specific value.
273 *
274 * @param form
275 * a ProzesskopieForm object whose AdditionalField objects are
276 * subject to the change
277 * @param userFields
278 * the data to pass to the form
279 * @throws RuntimeException
280 * in case that no field with a matching title was found in the
281 * ProzesskopieForm object
282 */
283 protected void setUserFields(ProzesskopieForm form, Map<String, String> userFields) throws RuntimeException {
284
285 for (String key : userFields.keySet()) {
286 setAdditionalField(form, key, userFields.get(key));
287 }
288
289 }
290
291 /**
292 * Sets the bibliographic data for a new process from a library catalogue.
293 * This is equal to manually choosing a catalogue and a search field,
294 * entering the search string and clicking “Apply”.
295 *
296 * Since the underlying OpacAuswerten() method doesn’t raise exceptions, we
297 * count the populated “additional details” fields before and after running
298 * the request and assume the method to have failed if not even one more
299 * field was populated by the method call.
300 *
301 * @param inputForm
302 * the ProzesskopieForm to be set
303 * @param id
304 * the ticket’s id
305 * @param opac
306 * the value for “Search in Opac”
307 * @param field
308 * the number of the search field, e.g. “12” for PPN.
309 * @param value
310 * the search string
311 * @throws RuntimeException
312 * is thrown if the search didn’t bring any results
313 */
314 protected void getBibliorgaphicData(ProzesskopieForm inputForm, String opac, String field, String value) throws RuntimeException {
315
316 inputForm.setOpacKatalog(opac);
317 inputForm.setOpacSuchfeld(field);
318 inputForm.setOpacSuchbegriff(value);
319
320 int before = countPopulatedAdditionalFields(inputForm);
321 inputForm.OpacAuswerten();
322 int afterwards = countPopulatedAdditionalFields(inputForm);
323
324 if (!(afterwards > before))
325 throw new RuntimeException("Searching the OPAC didn’t yield any results.");
326 }
327
328 /**
329 * The function countPopulatedAdditionalFields() returns the number of
330 * AdditionalFields in the given ProzesskopieForm that have meaningful
331 * content.
332 *
333 * @param form
334 * a ProzesskopieForm object to examine
335 * @return the number of AdditionalFields populated
336 */
337 protected int countPopulatedAdditionalFields(ProzesskopieForm form) {
338 int result = 0;
339
340 for (AdditionalField field : form.getAdditionalFields()) {
341 String value = field.getWert();
342 if (value != null && value.length() > 0)
343 result++;
344 }
345
346 return result;
347 }
348
349 /**
350 * The method setAdditionalField() sets the value of an AdditionalField held
351 * by a ProzesskopieForm object.
352 *
353 * @param inputForm
354 * a ProzesskopieForm object
355 * @param key
356 * the title of the AdditionalField whose value shall be modified
357 * @param value
358 * the new value for the AdditionalField
359 * @throws RuntimeException
360 * in case that no field with a matching title was found in the
361 * ProzesskopieForm object
362 */
363 protected void setAdditionalField(ProzesskopieForm inputForm, String key, String value) throws RuntimeException {
364
365 for (AdditionalField field : inputForm.getAdditionalFields()) {
366 if (key.equals(field.getTitel())) {
367 field.setWert(value);
368 return;
369 }
370 }
371
372 throw new RuntimeException("Couldn’t set “" + key + "” to “" + value + "”: No such field in record.");
373 }
374
375}
0376
=== added file 'src/org/goobi/mq/processors/FinaliseStepProcessor.java'
--- src/org/goobi/mq/processors/FinaliseStepProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/mq/processors/FinaliseStepProcessor.java 2012-07-27 10:58:19 +0000
@@ -0,0 +1,97 @@
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.mq.processors;
24
25import org.apache.commons.lang.StringEscapeUtils;
26import org.goobi.mq.ActiveMQProcessor;
27import org.goobi.mq.MapMessageObjectReader;
28
29import de.sub.goobi.config.ConfigMain;
30import de.sub.goobi.forms.AktuelleSchritteForm;
31import de.sub.goobi.persistence.SchrittDAO;
32
33/**
34 * This is a web service interface to close steps. You have to provide the step
35 * id as “id”; you can add a field “message” which will be added to the wiki
36 * field.
37 *
38 * @author Matthias Ronge <matthias.ronge@zeutschel.de>
39 */
40public class FinaliseStepProcessor extends ActiveMQProcessor {
41
42 /**
43 * The default constructor looks up the queue name to use in
44 * GoobiConfig.properties. If that is not configured and “null” is passed to
45 * the super constructor, this will prevent
46 * ActiveMQDirector.registerListeners() from starting this service.
47 */
48 public FinaliseStepProcessor() {
49 super(ConfigMain.getParameter("activeMQ.finaliseStep.queue", null));
50 }
51
52 /**
53 * This is the main routine processing incoming tickets. It gets an
54 * AktuelleSchritteForm object, sets it to the appropriate step which is
55 * retrieved from the database, appends the message − if any − to the wiki
56 * field, and executes the form’s the step close function.
57 *
58 * @param ticket
59 * the incoming message
60 *
61 * @see org.goobi.mq.ActiveMQProcessor#process(org.goobi.mq.MapMessageObjectReader)
62 */
63 protected void process(MapMessageObjectReader ticket) throws Exception {
64 AktuelleSchritteForm dialog = new AktuelleSchritteForm();
65 Integer stepID = ticket.getMandatoryInteger("id");
66 dialog.setMySchritt(new SchrittDAO().get(stepID));
67 if (ticket.hasField("message"))
68 addMessageToWikiField(dialog, ticket.getString("message"));
69 dialog.SchrittDurchBenutzerAbschliessen();
70 }
71
72 /**
73 * The addMessageToWikiField() method is a helper method which composes the
74 * new wiki field using a StringBuilder. The message is encoded using HTML
75 * entities to prevent certain characters from playing merry havoc when the
76 * message box shall be rendered in a browser later.
77 *
78 * @param form
79 * the AktuelleSchritteForm which is the owner of the wiki field
80 * @param message
81 * the message to append
82 */
83 protected void addMessageToWikiField(AktuelleSchritteForm form, String message) {
84 StringBuilder composer = new StringBuilder();
85 String wikiField = form.getWikiField();
86 if (wikiField != null && wikiField.length() > 0) {
87 composer.append(wikiField);
88 composer.append("\r\n");
89 }
90 composer.append("<p>");
91 composer.append(StringEscapeUtils.escapeHtml(message));
92 composer.append("</p>");
93 form.setWikiField(composer.toString());
94 return;
95 }
96
97}
098
=== modified file 'src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java'
--- src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java 2012-02-22 07:43:02 +0000
+++ src/org/goobi/production/flow/statistics/hibernate/FilterHelper.java 2012-07-27 10:58:19 +0000
@@ -94,7 +94,7 @@
94 /* identify current user */94 /* identify current user */
95 LoginForm login = (LoginForm) Helper95 LoginForm login = (LoginForm) Helper
96 .getManagedBeanValue("#{LoginForm}");96 .getManagedBeanValue("#{LoginForm}");
97 if (login.getMyBenutzer() == null)97 if (login == null || login.getMyBenutzer() == null)
98 return;98 return;
99 /* init id-list, preset with item 0 */99 /* init id-list, preset with item 0 */
100 List<Integer> idList = new ArrayList<Integer>();100 List<Integer> idList = new ArrayList<Integer>();

Subscribers

People subscribed via source and target branches

to all changes: