slub.team team mailing list archive
-
slub.team team
-
Mailing list archive
-
Message #00279
lp:~zeutschel/goobi-production/web-service-interface-to-create-processes into lp:goobi-production
Ralf Claussnitzer has proposed merging lp:~zeutschel/goobi-production/web-service-interface-to-create-processes into lp:goobi-production.
Requested reviews:
Saxon State Library Team (slub.team)
For more details, see:
https://code.launchpad.net/~zeutschel/goobi-production/web-service-interface-to-create-processes/+merge/116688
Provides Active MQ web service interface with two service processors (one to create new processes from process templates, and one to close steps) and provides a JSTL function tag library with JSP files to print out some related configuration information.
Note: It’s necessary to add the new directory “/ws” to the “deployment assembly” in order to use the new JSPs.
--
https://code.launchpad.net/~zeutschel/goobi-production/web-service-interface-to-create-processes/+merge/116688
Your team Saxon State Library Team is requested to review the proposed merge of lp:~zeutschel/goobi-production/web-service-interface-to-create-processes into lp:goobi-production.
=== modified file 'WEB-INF/web.xml'
--- WEB-INF/web.xml 2012-04-20 06:57:46 +0000
+++ WEB-INF/web.xml 2012-07-25 16:05:24 +0000
@@ -215,6 +215,11 @@
<listener-class>org.goobi.production.ImageIOInitializer</listener-class>
</listener>
+ <!-- Listener to run ActiveMQ services -->
+ <listener>
+ <listener-class>org.goobi.webservice.ActiveMQDirector</listener-class>
+ </listener>
+
<!--
xml-Rpc-Server starten <listener> <listener-class>
de.sub.goobi.XmlRpc.Listener </listener-class> </listener>
=== added file 'WEB-INF/webserviceTaglib.tld'
--- WEB-INF/webserviceTaglib.tld 1970-01-01 00:00:00 +0000
+++ WEB-INF/webserviceTaglib.tld 2012-07-25 16:05:24 +0000
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ -->
+
+<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
+ <tlib-version>1.0</tlib-version>
+ <short-name>ws</short-name>
+ <uri>http://taglib.production.goobi.org/webService</uri>
+ <function>
+ <name>getAdditionalFieldsToBeCompletedManually</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getAdditionalFieldsToBeCompletedManually()</function-signature>
+ </function>
+ <function>
+ <name>getAllFields</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getAllFields()</function-signature>
+ </function>
+ <function>
+ <name>getCatalogues</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getCatalogues()</function-signature>
+ </function>
+ <function>
+ <name>getCollections</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getCollections()</function-signature>
+ </function>
+ <function>
+ <name>getAllDoctypes</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getAllDoctypes()</function-signature>
+ </function>
+ <function>
+ <name>getProjects</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getProjects()</function-signature>
+ </function>
+ <function>
+ <name>getProjectsAndTemplates</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getProjectsAndTemplates()</function-signature>
+ </function>
+ <function>
+ <name>getSearchFields</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getSearchFields()</function-signature>
+ </function>
+ <function>
+ <name>getTemplates</name>
+ <function-class>org.goobi.webservice.taglib.WebServiceTaglib</function-class>
+ <function-signature>java.util.String getTemplates()</function-signature>
+ </function>
+</taglib>
=== modified file 'config/GoobiConfig.properties'
--- config/GoobiConfig.properties 2012-05-02 12:02:23 +0000
+++ config/GoobiConfig.properties 2012-07-25 16:05:24 +0000
@@ -179,6 +179,41 @@
# Password encryption SHA or MD5
ldap_encryption=SHA
+# -----------------------------------
+# ActiveMQ web services
+# -----------------------------------
+
+# If you want to use Goobi's ActiveMQ web servic interface, set the host here
+#activeMQ.hostURL=tcp://localhost:61616
+
+# You can provide a topic that Goobi reports results and status messages to
+#activeMQ.results.topic=GoobiProduction.ResultMessages.Topic
+
+# By default, Goobi instructs the server to keep status messages for a
+# equivalent of 7 days. You can change this value in (milliseconds to) meet
+# your needs. 0 will disable the deletion of messages completely. (However,
+# the messages will only available on the Active MQ server if your
+# TopicSubscriber is online with the Active MQ server before the message is
+# sent. You migth therefore consider to configure the timeToLive for offline
+# usage within the Active MQ server’s activemq.xml file by adding a
+#
+# <policyEntry topic="GoobiProduction.ResultMessages.Topic">
+# <subscriptionRecoveryPolicy>
+# <timedSubscriptionRecoveryPolicy recoverDuration="604800000" />
+# </subscriptionRecoveryPolicy>
+# </policyEntry>
+#
+# block inside the <policyEntries>-Element. “recoverDuration” has to be given
+# in milliseconds here, too.)
+#activeMQ.results.timeToLive=604800000
+
+# You can provide a queue from which messages are read to create new processes
+#activeMQ.createNewProcess.queue=GoobiProduction.CreateNewProcesses.Queue
+
+# You can provide a queue from which messages are read to finalise steps
+#activeMQ.finaliseStep.queue=GoobiProduction.FinaliseStep.Queue
+
+
##################################
# DO NOT CHANGE THE OPTIONS BELOW!
##################################
=== added file 'lib/activemq-core-5.5.1.jar'
Binary 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-25 16:05:24 +0000 differ
=== added file 'lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar'
Binary 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-25 16:05:24 +0000 differ
=== added file 'lib/geronimo-jms_1.1_spec-1.1.1.jar'
Binary 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-25 16:05:24 +0000 differ
=== added file 'lib/json-simple-1.1.1.jar'
Binary 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-25 16:05:24 +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-25 16:05:24 +0000
@@ -47,7 +47,7 @@
public Page(Criteria criteria, int page) {
this.page = page;
LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
- if (login.getMyBenutzer() == null)
+ if (login == null || login.getMyBenutzer() == null)
this.pageSize = 10;
else
this.pageSize = login.getMyBenutzer().getTabellengroesse().intValue();
=== 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-25 16:05:24 +0000
@@ -119,9 +119,9 @@
/**
- * Ermitteln eines bestimmten Paramters der Konfiguration
+ * Ermitteln eines bestimmten Parameters der Konfiguration
*
- * @return Paramter als String
+ * @return Parameter als String
*/
public static String getParameter(String inParameter) {
try {
@@ -135,10 +135,10 @@
//TODO: Remove this methods, they are provided by Commons Configuration
/**
- * Ermitteln eines bestimmten Paramters der Konfiguration mit Angabe eines
+ * Ermitteln eines bestimmten Parameters der Konfiguration mit Angabe eines
* Default-Wertes
*
- * @return Paramter als String
+ * @return Parameter als String
*/
public static String getParameter(String inParameter, String inDefaultIfNull) {
try {
@@ -152,18 +152,18 @@
/**
- * Ermitteln eines boolean-Paramters der Konfiguration, default if missing: false
+ * Ermitteln eines boolean-Parameters der Konfiguration, default if missing: false
*
- * @return Paramter als String
+ * @return Parameter als String
*/
public static boolean getBooleanParameter(String inParameter) {
return getBooleanParameter(inParameter, false);
}
/**
- * Ermitteln eines boolean-Paramters der Konfiguration
+ * Ermitteln eines boolean-Parameters der Konfiguration
*
- * @return Paramter als String
+ * @return Parameter als String
*/
public static boolean getBooleanParameter(String inParameter, boolean inDefault) {
return config.getBoolean(inParameter, inDefault);
@@ -172,17 +172,12 @@
/**
- * Ermitteln eines long-Paramters der Konfiguration
+ * Ermitteln eines long-Parameters der Konfiguration
*
- * @return Paramter als Long
+ * @return Parameter als Long
*/
- public static long getLongParameter(String inParameter, int inDefault) {
- try {
- return config.getLong(inParameter, inDefault);
- } catch (RuntimeException e) {
- myLogger.error(e);
- return 0;
- }
+ public static long getLongParameter(String inParameter, long inDefault) {
+ return config.getLong(inParameter, inDefault);
}
@@ -190,7 +185,7 @@
/**
* Request int-parameter from Configuration
*
- * @return Paramter as Int
+ * @return Parameter as Int
*/
public static int getIntParameter(String inParameter) {
return getIntParameter(inParameter, 0);
@@ -199,7 +194,7 @@
/**
* Request int-parameter from Configuration with default-value
*
- * @return Paramter as Int
+ * @return Parameter as Int
*/
public static int getIntParameter(String inParameter, int inDefault) {
try {
=== 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-25 16:05:24 +0000
@@ -0,0 +1,55 @@
+package de.sub.goobi.config;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+
+import de.sub.goobi.beans.Prozess;
+
+public class DigitalCollections {
+
+ @SuppressWarnings("unchecked")
+ public static List<String> possibleDigitalCollectionsForProcess(
+ Prozess process) throws JDOMException, IOException {
+
+ List<String> result = new ArrayList<String>();
+ String filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "digitalCollections.xml";
+ if (!(new File(filename).exists())) {
+ throw new FileNotFoundException("File not found: " + filename);
+ }
+
+ /* Datei einlesen und Root ermitteln */
+ SAXBuilder builder = new SAXBuilder();
+ Document doc = builder.build(new File(filename));
+ Element root = doc.getRootElement();
+ /* alle Projekte durchlaufen */
+ List<Element> projekte = root.getChildren();
+ for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
+ Element projekt = (Element) iter.next();
+ List<Element> projektnamen = projekt.getChildren("name");
+ for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
+ Element projektname = (Element) iterator.next();
+
+ /*
+ * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
+ */
+ if (projektname.getText().equalsIgnoreCase(process.getProjekt().getTitel())) {
+ List<Element> myCols = projekt.getChildren("DigitalCollection");
+ for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
+ Element col = (Element) it2.next();
+ result.add(col.getText());
+ }
+ }
+ }
+ }
+ return result;
+ }
+}
=== 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-25 16:05:24 +0000
@@ -109,7 +109,7 @@
* --------------------- Vorgangsdatum generell anzeigen? -------------------
*/
LoginForm login = (LoginForm) Helper.getManagedBeanValue("#{LoginForm}");
- if (login.getMyBenutzer() != null)
+ if (login != null && login.getMyBenutzer() != null)
anzeigeAnpassen.put("processDate", login.getMyBenutzer().isConfVorgangsdatumAnzeigen());
else
anzeigeAnpassen.put("processDate", false);
=== 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-25 16:05:24 +0000
@@ -23,6 +23,7 @@
package de.sub.goobi.forms;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -84,6 +85,7 @@
import de.sub.goobi.config.ConfigOpac;
import de.sub.goobi.config.ConfigOpacDoctype;
import de.sub.goobi.config.ConfigProjects;
+import de.sub.goobi.config.DigitalCollections;
import de.sub.goobi.helper.BeanHelper;
import de.sub.goobi.helper.Helper;
import de.sub.goobi.helper.Messages;
@@ -932,46 +934,22 @@
return possibleDigitalCollection;
}
- @SuppressWarnings("unchecked")
private void initializePossibleDigitalCollections() {
possibleDigitalCollection = new ArrayList<String>();
- String filename = help.getGoobiConfigDirectory() + "digitalCollections.xml";
- if (!(new File(filename).exists())) {
- Helper.setFehlerMeldung("File not found: ", filename);
- return;
- }
-
- try {
- /* Datei einlesen und Root ermitteln */
- SAXBuilder builder = new SAXBuilder();
- Document doc = builder.build(new File(filename));
- Element root = doc.getRootElement();
- /* alle Projekte durchlaufen */
- List<Element> projekte = root.getChildren();
- for (Iterator<Element> iter = projekte.iterator(); iter.hasNext();) {
- Element projekt = (Element) iter.next();
- List<Element> projektnamen = projekt.getChildren("name");
- for (Iterator<Element> iterator = projektnamen.iterator(); iterator.hasNext();) {
- Element projektname = (Element) iterator.next();
-
- /*
- * wenn der Projektname aufgeführt wird, dann alle Digitalen Collectionen in die Liste
- */
- if (projektname.getText().equalsIgnoreCase(prozessKopie.getProjekt().getTitel())) {
- List<Element> myCols = projekt.getChildren("DigitalCollection");
- for (Iterator<Element> it2 = myCols.iterator(); it2.hasNext();) {
- Element col = (Element) it2.next();
- possibleDigitalCollection.add(col.getText());
- }
- }
- }
- }
+ try{
+ possibleDigitalCollection = DigitalCollections.possibleDigitalCollectionsForProcess(prozessKopie);
+ } catch (FileNotFoundException e1) {
+ myLogger.error("File not found: ", e1);
+ Helper.setFehlerMeldung("File not found: ", e1);
} catch (JDOMException e1) {
myLogger.error("error while parsing digital collections", e1);
Helper.setFehlerMeldung("Error while parsing digital collections", e1);
} catch (IOException e1) {
myLogger.error("error while parsing digital collections", e1);
Helper.setFehlerMeldung("Error while parsing digital collections", e1);
+ } finally {
+ if(possibleDigitalCollection == null)
+ possibleDigitalCollection = new ArrayList<String>();
}
// if only one collection is possible take it directly
=== 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-25 16:05:24 +0000
@@ -42,13 +42,16 @@
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
+import org.apache.log4j.Level;
import org.apache.log4j.Logger;
+import org.goobi.webservice.WebServiceResult;
import org.hibernate.Session;
import org.jdom.Element;
import de.sub.goobi.beans.Benutzer;
import de.sub.goobi.config.ConfigMain;
import de.sub.goobi.forms.LoginForm;
+import de.sub.goobi.helper.enums.ReportLevel;
import de.sub.goobi.persistence.HibernateUtilOld;
//TODO: Check if more method can be made static
@@ -59,6 +62,7 @@
private String myMetadatenVerzeichnis;
private String myConfigVerzeichnis;
+ public static Map<String, String> activeMQReporting = null;
/**
* Ermitteln eines bestimmten Paramters des Requests
@@ -145,43 +149,59 @@
}
/**
- * Dem aktuellen Formular eine Fehlermeldung für ein bestimmtes Control übergeben
+ * The method setMeldung() adds an error message for a given control to the
+ * current form.
+ *
+ * @param control
+ * Name of control that caused the error or “null” if the error
+ * was not caused by a control
+ * @param messageKey
+ * The key of the error message. The method will try to resolve
+ * the key against its messages file.
+ * @param descriptionKey
+ * The description key of the error. The method will try to
+ * resolve the key against its messages file.
+ * @param infoOnly
+ * Set to false for error messages. Set to true for info
+ * messages.
*/
- private static void setMeldung(String control, String meldung, String beschreibung, boolean nurInfo) {
+ private static void setMeldung(String control, String messageKey,
+ String descriptionKey, boolean infoOnly) {
FacesContext context = FacesContext.getCurrentInstance();
- // Never forget: Strings are immutable
- meldung = meldung.replaceAll("<", "<");
- meldung = meldung.replaceAll(">", ">");
- beschreibung = beschreibung.replaceAll("<", "<");
- beschreibung = beschreibung.replaceAll(">", ">");
- /* wenn kein Kontext da ist, dann die Meldungen in Log */
- if (context == null) {
- if (nurInfo) {
- myLogger.info(meldung + " " + beschreibung);
- } else {
- myLogger.error(meldung + " " + beschreibung);
- }
- return;
- }
-
- String msg = "";
- String beschr = "";
- try {
- msg = Messages.getString(meldung);
- } catch (RuntimeException e) {
- msg = meldung;
- }
- try {
- beschr = Messages.getString(beschreibung);
- } catch (RuntimeException e) {
- beschr = beschreibung;
- }
-
- if (nurInfo) {
- context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, beschr));
- } else {
- context.addMessage(control, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, beschr));
+ String message;
+ String description;
+ try {
+ message = Messages.getString(messageKey);
+ } catch (RuntimeException e) {
+ message = messageKey;
+ }
+ try {
+ description = Messages.getString(descriptionKey);
+ } catch (RuntimeException e) {
+ description = descriptionKey;
+ }
+
+ message = message.replaceAll("<", "<").replaceAll(">", ">");
+ description = description.replaceAll("<", "<").replaceAll(">", ">");
+
+ String compoundMessage = message.replaceFirst(":\\s*$", "") + ": "
+ + description;
+
+ /* If the Active MQ service is at work, report errors there, too. */
+ if (activeMQReporting != null) {
+ new WebServiceResult(activeMQReporting.get("queueName"),
+ activeMQReporting.get("id"), infoOnly ? ReportLevel.INFO
+ : ReportLevel.ERROR, compoundMessage).send();
+ }
+
+ if (context != null) {
+ context.addMessage(
+ control,
+ new FacesMessage(infoOnly ? FacesMessage.SEVERITY_INFO
+ : FacesMessage.SEVERITY_ERROR, message, description));
+ } else { // wenn kein Kontext da ist, dann die Meldungen in Log
+ myLogger.log(infoOnly ? Level.INFO : Level.ERROR, compoundMessage);
}
}
=== 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-25 16:05:24 +0000
@@ -42,6 +42,7 @@
import ugh.exceptions.DocStructHasNoTypeException;
import ugh.exceptions.MetadataTypeNotAllowedException;
import de.sub.goobi.beans.Prozess;
+import de.sub.goobi.config.ConfigMain;
import de.sub.goobi.helper.exceptions.UghHelperException;
//TODO: Try to move this methods to UGH (ugh.util.UGHUtils would be a better place)
@@ -206,8 +207,13 @@
public String convertLanguage(String inLanguage) {
/* Pfad zur Datei ermitteln */
FacesContext context = FacesContext.getCurrentInstance();
- HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
- String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";
+ String filename;
+ if (context != null) {
+ HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
+ filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opaclanguages.txt";
+ } else {
+ filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opaclanguages.txt";
+ }
/* Datei zeilenweise durchlaufen und die Sprache vergleichen */
try {
FileInputStream fis = new FileInputStream(filename);
@@ -233,8 +239,13 @@
String temp = inString;
/* Pfad zur Datei ermitteln */
FacesContext context = FacesContext.getCurrentInstance();
- HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
- String filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";
+ String filename;
+ if (context != null) {
+ HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
+ filename = session.getServletContext().getRealPath("/WEB-INF") + File.separator + "classes" + File.separator + "opacumlaut.txt";
+ } else {
+ filename = ConfigMain.getParameter("KonfigurationVerzeichnis") + "opacumlaut.txt";
+ }
/* Datei zeilenweise durchlaufen und die Sprache vergleichen */
try {
=== 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-25 16:05:24 +0000
@@ -111,7 +111,8 @@
public void UploadFromHome(Prozess myProzess) {
Benutzer aktuellerBenutzer = (Benutzer) Helper.getManagedBeanValue("#{LoginForm.myBenutzer}");
- UploadFromHome(aktuellerBenutzer, myProzess);
+ if (aktuellerBenutzer != null)
+ UploadFromHome(aktuellerBenutzer, myProzess);
}
public void UploadFromHome(Benutzer inBenutzer, Prozess myProzess) {
=== 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-25 16:05:24 +0000
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package de.sub.goobi.helper.enums;
+
+/**
+ * These are the possible states for output to “activeMQ.results.topic”.
+ *
+ * @author Matthias Ronge <matthias.ronge@xxxxxxxxxxxx>
+ */
+public enum ReportLevel {
+ FATAL, ERROR, WARN, INFO, SUCCESS, DEBUG, VERBOSE, LUDICROUS;
+
+ public String toLowerCase() {
+ return name().toLowerCase();
+ }
+}
=== 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-25 16:05:24 +0000
@@ -94,7 +94,7 @@
/* identify current user */
LoginForm login = (LoginForm) Helper
.getManagedBeanValue("#{LoginForm}");
- if (login.getMyBenutzer() == null)
+ if (login == null || login.getMyBenutzer() == null)
return;
/* init id-list, preset with item 0 */
List<Integer> idList = new ArrayList<Integer>();
=== added directory 'src/org/goobi/webservice'
=== added file 'src/org/goobi/webservice/ActiveMQDirector.java'
--- src/org/goobi/webservice/ActiveMQDirector.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/ActiveMQDirector.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,231 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice;
+
+import javax.jms.Connection;
+import javax.jms.DeliveryMode;
+import javax.jms.Destination;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.activemq.ActiveMQConnectionFactory;
+import org.apache.log4j.Logger;
+import org.goobi.webservice.processors.CreateNewProcessProcessor;
+import org.goobi.webservice.processors.FinaliseStepProcessor;
+
+import de.sub.goobi.config.ConfigMain;
+
+/**
+ * The class ActiveMQDirector is the head of all Active MQ processors. It
+ * implements the ServletContextListener interface and is − if configured in
+ * web.xml − called automatically upon server starup. Its job is to connect to
+ * the Active MQ server and register the listeners configured.
+ *
+ * The ActiveMQDirector should ALWAYS be declared in web.xml. The Active MQ
+ * services are intended to be run in case that “activeMQ.hostURL” is configured
+ * in the GoobiConfig.properties file. To disable the service, the entry there
+ * should be emptied or commented out. Otherwise, a valid configuration would
+ * not start working and an administrator will hardly have a chance to find out
+ * why without inspecting the source code.
+ *
+ * The class ActiveMQDirector also provides a basic ExceptionListener
+ * implementation as required for the connection.
+ *
+ * @author Matthias Ronge <matthias.ronge@xxxxxxxxxxxx>
+ */
+public class ActiveMQDirector implements ServletContextListener,
+ ExceptionListener {
+ private static final Logger logger = Logger
+ .getLogger(ActiveMQDirector.class);
+
+ // *** CONFIGURATION ***
+ // When implementing new Services, add them to this list:
+
+ protected static ActiveMQProcessor[] services;
+ static{
+ services = new ActiveMQProcessor[] {
+ new CreateNewProcessProcessor(),
+ new FinaliseStepProcessor()
+ };
+ }
+
+ protected static Connection connection = null;
+ protected static Session session = null;
+ protected static MessageProducer resultsTopic;
+
+ /**
+ * The method contextInitialized() is called by the web container on startup
+ * and is used to start up the active MQ connection. All processors from
+ * services[] are registered.
+ *
+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet
+ * .ServletContextEvent)
+ */
+ @Override
+ public void contextInitialized(ServletContextEvent initialisation) {
+ String activeMQHost = ConfigMain.getParameter("activeMQ.hostURL", null);
+ if (activeMQHost != null) {
+ session = connectToServer(activeMQHost);
+ if (session != null) {
+ registerListeners(services);
+ if (ConfigMain.getParameter("activeMQ.results.topic", null) != null) {
+ resultsTopic = setUpReportChannel(ConfigMain.getParameter("activeMQ.results.topic"));
+ } } } }
+
+ /**
+ * Sets up a connection to an active MQ server. The connection object is
+ * global because it is needed later to shut down the connection.
+ *
+ * @param server
+ * should be “tcp://{host}:{port}” or “vm://localhost” in case
+ * that the server is run inside the same virtual machine
+ * @return the session object or “null” upon error
+ */
+ protected Session connectToServer(String server) {
+ try {
+ connection = new ActiveMQConnectionFactory(server).createConnection();
+ connection.start();
+ connection.setExceptionListener(this); // → ActiveMQDirector.onException()
+ return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ } catch (Exception e) {
+ logger.fatal("Error connecting to ActiveMQ server, giving up.", e);
+ }
+ return null;
+ }
+
+ /**
+ * This method registers the listeners with the active MQ server.
+ *
+ * If a queue name was configured for a service, a MessageConsumer is set up
+ * to listen on that queue and, in case of incoming messages, make the
+ * service process the message. The message checker is saved inside the
+ * service to be able to shut it down later.
+ */
+ protected void registerListeners(ActiveMQProcessor[] processors) {
+ for (ActiveMQProcessor processor : processors) {
+ if (processor.getQueueName() != null) {
+ MessageConsumer messageChecker = null;
+ try {
+ Destination queue = session.createQueue(processor.getQueueName());
+ messageChecker = session.createConsumer(queue);
+ messageChecker.setMessageListener(processor);
+ processor.saveChecker(messageChecker);
+ } catch (Exception e) {
+ logger.fatal("Error setting up monitoring for \"" + processor.getQueueName() + "\": Giving up.", e);
+ } } } }
+
+ /**
+ * This sets up a connection to the topic the results shall be written to.
+ * The delivery mode is set so “persistent” to allow consumers not online
+ * with the server in the moment of message submission to read the messages
+ * later. The log messages are set to be kept on the server for 7 days. This
+ * value can be overridden from the GoobiConfig.properties parameter
+ * “activeMQ.results.timeToLive”. The time to live must be specified in
+ * milliseconds; 0 disables the oblivion. (See also:
+ * http://docs.oracle.com/javaee/6/api/javax/jms/MessageProducer.html#setTimeToLive%28long%29 )
+ *
+ * @param topic
+ * name of the active MQ topic
+ * @return a MessageProducer object ready for writing or “null” on error
+ */
+ protected MessageProducer setUpReportChannel(String topic) {
+ MessageProducer result;
+ try {
+ Destination channel = session.createTopic(topic);
+ result = session.createProducer(channel);
+ result.setDeliveryMode(DeliveryMode.PERSISTENT);
+ result.setTimeToLive(ConfigMain.getLongParameter("activeMQ.results.timeToLive", 604800000));
+ return result;
+ } catch (Exception e) {
+ logger.fatal("Error setting up report channel \"" + topic + "\": Giving up.", e);
+ }
+ return null;
+ }
+
+ /**
+ * This method is referenced from this.connectToServer() − see there.
+ *
+ * @see javax.jms.ExceptionListener#onException(javax.jms.JMSException)
+ */
+ @Override
+ public void onException(JMSException exce) {
+ logger.error(exce);
+ }
+
+ /**
+ * Any class that wants to create new Active MQ Messages needs read access
+ * to the session, since Active MQ messages don’t have a constructor.
+ *
+ * @return the session object
+ */
+ public static Session getSession() {
+ return session;
+ }
+
+ /**
+ * Instances of WebServiceResult can be sent by calling their send() method.
+ * Therefore, they need read access on their topic.
+ *
+ * @return the resultsTopic object
+ */
+ public static MessageProducer getResultsTopic() {
+ return resultsTopic;
+ }
+
+ /**
+ * The method contextDestroyed is called by the web container on shutdown.
+ * It shuts down all listeners, the session and last, the connection.
+ *
+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.
+ * ServletContextEvent)
+ */
+ @Override
+ public void contextDestroyed(ServletContextEvent destruction) {
+ // Shut down all watchers on any queues
+ for (ActiveMQProcessor service : services) {
+ MessageConsumer watcher = service.getChecker();
+ if (watcher != null) {
+ try {
+ watcher.close();
+ } catch (JMSException e) {
+ logger.error(e);
+ } } }
+
+ // quit session
+ try {
+ session.close();
+ } catch (JMSException e) {
+ logger.error(e);
+ }
+
+ // shut down connection
+ try {
+ connection.close();
+ } catch (JMSException e) {
+ logger.error(e);
+} } }
=== added file 'src/org/goobi/webservice/ActiveMQProcessor.java'
--- src/org/goobi/webservice/ActiveMQProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/ActiveMQProcessor.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jms.MapMessage;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+
+import de.sub.goobi.helper.Helper;
+import de.sub.goobi.helper.enums.ReportLevel;
+
+/**
+ * The class ActiveMQProcessor offers general services, such as making the
+ * incoming messages available as MapMessages and publishing the results. When I
+ * came clear that this code would be necessary for every processor, I thought
+ * an abstract class would be the right place for it. ActiveMQProcessor also
+ * provides a place to save the checker for the ActiveMQDirector, to be able to
+ * shut it down later.
+ *
+ * @author Matthias Ronge <matthias.ronge@xxxxxxxxxxxx>
+ */
+public abstract class ActiveMQProcessor implements MessageListener {
+
+ protected String queueName; // the queue name will be available here
+ private MessageConsumer checker;
+
+ /**
+ * Implement the method process() to let your service actually do what you
+ * want him to do.
+ *
+ * @param ticket
+ * A MapMessage which can be processor-specific except that it
+ * requires to have a field “id”.
+ */
+ protected abstract void process(MapMessageObjectReader ticket) throws Exception;
+
+ /**
+ * Instantiating the class ActiveMQProcessor always requires to pass the
+ * name of the queue it should be attached to. That means, your constructor
+ * should look like this:
+ *
+ * <pre>
+ * public MyServiceProcessor(){
+ * super(ConfigMain.getParameter("activeMQ.myService.queue", null));
+ * }
+ * </pre>
+ *
+ * If the parameter is not set in GoobiConfig.properties, it will return
+ * “null” and so prevents it from being set up in ActiveMQDirector.
+ *
+ * @param queueName
+ * the queue name, if configured, or “null” to prevent the
+ * processor from being connected.
+ */
+ public ActiveMQProcessor(String queueName) {
+ this.queueName = queueName;
+ }
+
+ /**
+ * The method onMessage() provides a corset which checks the message being a
+ * MapMessage, performs a type safe type cast, and extracts the job’s ID to
+ * be able to send useful results to the results topic, which it does after
+ * the abstract method process() has finished.
+ *
+ * Since this will be the same for all processors which use MapMessages, I
+ * extracted the portion into the abstract class.
+ *
+ * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
+ */
+ @Override
+ public void onMessage(Message arg) {
+ MapMessageObjectReader ticket = null;
+ String ticketID = null;
+
+ try {
+ // Basic check ticket
+ if (arg instanceof MapMessage)
+ ticket = new MapMessageObjectReader((MapMessage) arg);
+ else
+ throw new IllegalArgumentException("Incompatible types.");
+ ticketID = ticket.getMandatoryString("id");
+
+ // turn on logging
+ Map<String,String> loggingConfig = new HashMap<String,String>();
+ loggingConfig.put("queueName", queueName);
+ loggingConfig.put("id", ticketID);
+ Helper.activeMQReporting = loggingConfig;
+
+ // process ticket
+ process(ticket);
+
+ // turn off logging again
+ Helper.activeMQReporting = null;
+
+ // if everything ‘s fine, report success
+ new WebServiceResult(queueName, ticketID, ReportLevel.SUCCESS)
+ .send();
+
+ } catch (Exception exce) {
+ // report any errors
+ new WebServiceResult(queueName, ticketID, ReportLevel.FATAL,
+ exce.getMessage()).send();
+ }
+ }
+
+ /**
+ * This method is used to get the queue name upon initialisation.
+ *
+ * @return the queue name
+ */
+ public String getQueueName() {
+ return queueName;
+ }
+
+ /**
+ * The parent object which is there to check for new messages and to trigger
+ * the method onMessage() is saved inside the class, to have it lately for
+ * shutting down the service again.
+ *
+ * @param checker
+ * the MessageConsumer object responsible for checking messages
+ */
+
+ public void saveChecker(MessageConsumer checker) {
+ this.checker = checker;
+ }
+
+ /**
+ * This method is used to get back the message checking object upon
+ * shutdown.
+ *
+ * @return the MessageConsumer object responsible for checking messages
+ */
+ public MessageConsumer getChecker() {
+ return checker;
+ }
+}
=== added file 'src/org/goobi/webservice/MapMessageObjectReader.java'
--- src/org/goobi/webservice/MapMessageObjectReader.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/MapMessageObjectReader.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jms.JMSException;
+import javax.jms.MapMessage;
+
+public class MapMessageObjectReader {
+
+ private MapMessage ticket;
+
+ /**
+ * This instantiates a new MapMessageObjectReader which is attached to a
+ * given MapMessage.
+ *
+ * @param message
+ */
+ public MapMessageObjectReader(MapMessage message) {
+ if (message == null)
+ throw new IllegalArgumentException(
+ "MapMessageObjectReader: null argument in constructor.");
+ this.ticket = message;
+ }
+
+ /**
+ * The function getMandatorySetOfString() fetches a Set<String> from a MapMessage.
+ * This is a strict implementation that requires the collection not to be
+ * empty and not to contain the null element.
+ *
+ * Please note that the Set is allowed to contain the empty String (“”).
+ *
+ * @param key
+ * the name of the set to return
+ * @return the set requested
+ * @throws IllegalArgumentException
+ * in case that getObject returns null, the returned object is
+ * not of type Collection, any of the content elements are not
+ * of type String or the collection is empty at all.
+ * @throws JMSException
+ * can be thrown by MapMessage.getObject(String)
+ */
+ public Set<String> getMandatorySetOfString(String key)
+ throws IllegalArgumentException, JMSException {
+ Set<String> result = new HashSet<String>();
+ Boolean emptiness = Boolean.TRUE;
+
+ Object collectionObject = ticket.getObject(key);
+ if (collectionObject == null)
+ throw new IllegalArgumentException("Missing mandatory argument: \""
+ + key + "\"");
+ if (!(collectionObject instanceof Collection<?>))
+ throw new IllegalArgumentException("Incompatible types: \"" + key
+ + "\" was not found to be of type Collection<?>.");
+ for (Object contentObject : (Collection<?>) collectionObject) {
+ if (contentObject == null || !(contentObject instanceof String))
+ throw new IllegalArgumentException(
+ "Incompatible types: An element of \"" + key
+ + "\" was not found to be of type String.");
+ result.add((String) contentObject);
+ emptiness = false;
+ }
+ if (emptiness)
+ throw new IllegalArgumentException("Missing mandatory argument: \""
+ + key + "\" must not be empty.");
+ return result;
+ }
+
+ /**
+ * The function getMandatoryString() fetches a String from a MapMessage. This is
+ * a strict implementation that requires the string not to be null and not
+ * to be empty.
+ *
+ * @param key
+ * the name of the string to return
+ * @return the string requested
+ * @throws IllegalArgumentException
+ * in case that getObject returns null or the returned string is
+ * of length “0”.
+ * @throws JMSException
+ * can be thrown by MapMessage.getString(String)
+ */
+ public String getMandatoryString(String key) throws IllegalArgumentException,
+ JMSException {
+ String result = ticket.getString(key);
+ if (result == null || result.length() == 0)
+ throw new IllegalArgumentException("Missing mandatory argument: \""
+ + key + "\"");
+ return result;
+ }
+
+ /**
+ * The function getString() fetches a String from a MapMessage. This is an
+ * access forward to the native function of the MapMessage. You may
+ * consider to use getMandatoryString() instead.
+ *
+ * @param key
+ * the name of the string to return
+ * @return the string requested (may be null or empty)
+ * @throws JMSException
+ * can be thrown by MapMessage.getString(String)
+ */
+
+ public String getString(String key) throws JMSException {
+ return ticket.getString(key);
+ }
+
+ /**
+ * The function getMandatoryInteger() fetches an Integer object from a MapMessage. This is
+ * a strict implementation that requires the Integer not to be null.
+ *
+ * @param key
+ * the name of the string to return
+ * @return the string requested
+ * @throws IllegalArgumentException
+ * in case that getObject returns null
+ * @throws JMSException
+ * can be thrown by MapMessage.getString(String)
+ */
+ public Integer getMandatoryInteger(String key) throws IllegalArgumentException,
+ JMSException {
+ Integer result = ticket.getInt(key);
+ if (result == null)
+ throw new IllegalArgumentException("Missing mandatory argument: \""
+ + key + "\"");
+ return result;
+ }
+
+ /**
+ * The function getMapOfStringToString() fetches a Map<String,String> from a
+ * MapMessage. This is a partly strict implementation that allows no null
+ * element neither as key, nor as value. However, if no object was found for
+ * the given key, or the key returned a null object, the function returns
+ * null. Also, the Map is allowed to contain the empty String (“”) as key
+ * and as values.
+ *
+ * @param key
+ * the name of the map to return
+ * @return the map requested
+ * @throws IllegalArgumentException
+ * in case that the object returned by getObject returned object
+ * is not of type Map or any of the content elements are not of
+ * type String.
+ */
+ public Map<String, String> getMapOfStringToString(String key) {
+ Map<String, String> result = new HashMap<String, String>();
+
+ Object mapObject = null;
+ try {
+ mapObject = ticket.getObject(key);
+ } catch (Exception irrelevant) {
+ }
+ if (mapObject == null)
+ return null;
+
+ if (!(mapObject instanceof Map<?, ?>))
+ throw new IllegalArgumentException("Incompatible types: \"" + key
+ + "\" was not found to be of type Map<?, ?>.");
+ for (Object keyObject : ((Map<?, ?>) mapObject).keySet()) {
+ Object valueObject = ((Map<?, ?>) mapObject).get(keyObject);
+ if (keyObject == null || !(keyObject instanceof String))
+ throw new IllegalArgumentException(
+ "Incompatible types: A key element of \"" + key
+ + "\" was not found to be of type String.");
+ if (valueObject == null || !(valueObject instanceof String))
+ throw new IllegalArgumentException(
+ "Incompatible types: A value element of \"" + key
+ + "\" was not found to be of type String.");
+ result.put((String) keyObject, (String) valueObject);
+ }
+
+ return result;
+ }
+
+ /**
+ * The function hasField() tests whether a field can be obtained from a
+ * MapMessage.
+ *
+ * @param string
+ * name of the field
+ * @return true or false
+ * @throws IllegalArgumentException
+ * can be thrown by MapMessage
+ * @throws JMSException
+ * can be thrown by MapMessage
+ */
+ public boolean hasField(String string) throws IllegalArgumentException,
+ JMSException {
+ String result = ticket.getString(string);
+ return (result != null && result.length() > 0);
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
=== added file 'src/org/goobi/webservice/WebServiceResult.java'
--- src/org/goobi/webservice/WebServiceResult.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/WebServiceResult.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice;
+
+import javax.jms.MapMessage;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import de.sub.goobi.helper.enums.ReportLevel;
+
+public class WebServiceResult {
+ private static final Logger logger = Logger.getLogger(ActiveMQDirector.class);
+
+ private String queueName;
+ private String id;
+ private ReportLevel level;
+ private String message = null;
+
+ public WebServiceResult(String queueName, String id, ReportLevel level,
+ String message){
+ this.queueName = queueName;
+ this.id = id;
+ this.level = level;
+ this.message = message;
+ }
+
+ public WebServiceResult(String queueName, String id, ReportLevel level){
+ this.queueName = queueName;
+ this.id = id;
+ this.level = level;
+ }
+
+ public void send() {
+ if (ActiveMQDirector.getResultsTopic() == null) {
+
+ // If reporting to ActiveMQ is disabled, write log message
+ logger.log(level == ReportLevel.SUCCESS ? Level.INFO : Level.WARN,
+ "Processing message \"" + id + '@' + queueName
+ + "\" reports " + level.toLowerCase() + "."
+ + (message != null ? " (" + message + ")" : ""));
+ } else {
+ try {
+ MapMessage report = ActiveMQDirector.getSession().createMapMessage();
+
+ DateTime now = new DateTime();
+ DateTimeFormatter iso8601formatter = ISODateTimeFormat.dateTime();
+ report.setString("timestamp", iso8601formatter.print(now));
+ report.setString("queue", queueName);
+ report.setString("id", id);
+ report.setString("level", level.toLowerCase());
+ if (message != null)
+ report.setString("message", message);
+
+ ActiveMQDirector.getResultsTopic().send(report);
+
+ } catch (Exception exce) {
+ logger.fatal("Error sending report for \"" + id + '@'
+ + queueName + "\" (" + level.toLowerCase()
+ + (message != null ? ": " + message : "")
+ + "): Giving up.", exce);
+ }
+ }
+ }
+}
=== added directory 'src/org/goobi/webservice/doc'
=== added file 'src/org/goobi/webservice/doc/implementing_active_mq_web_services_for_goobi.txt'
--- src/org/goobi/webservice/doc/implementing_active_mq_web_services_for_goobi.txt 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/doc/implementing_active_mq_web_services_for_goobi.txt 2012-07-25 16:05:24 +0000
@@ -0,0 +1,80 @@
+ Implementing Active MQ web services for Goobi
+
+Active Message Queue is an open source Java Messaging (JMS) implementation
+provided by the Apache Software Foundation. It is intended to be used to
+to connect software components in a flexible way. The core is the Active MQ
+server which can be pictured like a post office. The mail boxes are named
+“queue” or “topic”. Queues work as expected: A producer sends a message where
+a consumer can pick it up. Topics can be pictured as black boards: The main
+difference is: A message read from a queue is removed from the queue. A message
+read from a topic is still available to others. Consumer clients can actively
+check the server or may register listeners with the server to be notified of
+new messages.
+
+This behaviour has already been implemented to Goobi: The org.goobi.webservice.
+ActiveMQDirector is a ServletContextListener which is registered in web.xml.
+On application startup, it registers all consumers from its “services” variable
+to the server configured in “activeMQ.hostURL”.
+
+The elements of this variable are classes extending the abstract class
+ActiveMQProcessor. This class implements the MessageListener and provides
+facilities to handle exceptions and to store the consumer which is required on
+shutdown to disconnect.
+
+To implement another web service processor, you have to implement a class which
+extends ActiveMQProcessor and implements its abstract void process(MapMessage).
+Here is the right place to do whatever your processor is intended to do. There
+is a class MapMessageObjectReader which shall be used to type safe retrieve
+complex objects from MapMessages. You must add your new class to the “services”
+variable of ActiveMQDirector then.
+
+The Goobi server administrator shall be in control which processors are being
+started, and which queue names they listen on. Implementation of this
+configurability is designed this way: The implementing class must pass its
+queue name to the constructor of the parent class. This is done by implementing
+the constructor like in the following skeleton. If the queue name is not
+configured, it will return null which will prevent the ActiveMQDirector from
+registering it to the server. Inside the class, the queue name is available in
+the global variable “queueName” which is set by the parent class. The
+implementation may use arbitrary “activeMQ.myService.*” entries in
+GoobiConfig.properties for configuration.
+
+---------------------[ Service processor skeleton sample ]---------------------
+package org.goobi.webservice.processores;
+
+import org.goobi.webservice.*;
+import de.sub.goobi.config.ConfigMain;
+import de.sub.goobi.helper.enums.ReportLevel;
+
+public class MyServiceProcessor extends ActiveMQProcessor {
+
+ public MyServiceProcessor() {
+ super(ConfigMain.getParameter("activeMQ.myService.queue", null));
+ }
+
+ @Override
+ protected void process(MapMessageObjectReader args) throws Exception {
+ // TODO Auto-generated method stub
+ }
+}
+-------------------------------------------------------------------------------
+
+Responses from processors are designed to be handled as WebServiceResult
+objects. Those objects are MapMessages which send themselves to a topic
+configured in “activeMQ.results.topic”. They consist of the Strings “queue”
+(the name of the queue the job ticket was sent to), “id” (a String “id” in
+the MapMessage which is mandatory), “level” and an optional “message”. When
+designing the MapMessage layout to parameterise your web service processor,
+please keep in mind that a String element “id” is mandatory.
+
+If process() terminates without error, it is meant to have done its job
+successfully and a WebServiceResult with level “success” will be sent. If
+process() returns an exception, a WebServiceResult with level “fatal” will be
+sent. The exception will be returned as the “message” String. You may also use
+the WebServiceResult class to send messages with the levels “error”, “warn”,
+“info”, “debug”, “verbose” and “ludicrous” which are meant to be informative
+only:
+ new WebServiceResult(queueName, args.getMandatoryString("id"),
+ ReportLevel.INFO, "Remote host is down, trying again later.")
+ .send();
+
=== added file 'src/org/goobi/webservice/doc/web_service_to_create_new_processes.txt'
--- src/org/goobi/webservice/doc/web_service_to_create_new_processes.txt 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/doc/web_service_to_create_new_processes.txt 2012-07-25 16:05:24 +0000
@@ -0,0 +1,104 @@
+ Web service to create new processes
+
+Goobi.Production is equiped with a web service interface to automatically
+create new processes based on a given template. This allows the digitization
+process to be initiated from outside the application, for example by assigning
+a new digital ID to a record in a library catalogue (or—at choice of the
+library—by duplicating a record and assigning a new digital ID to the
+duplicate) and then running a script.
+
+The web service infrastructure is providet by an Active MQ server (see
+http://activemq.apache.org/ for details) which needs to be downloaded and
+started. Without further configuration, it provides everything necessary on
+port 61616 of the machine in question.
+
+The “activeMQ.hostURL” must be set in GoobiConfig.properties to point to this
+server. The “activeMQ.createNewProcess.queue” must be set to point to a queue
+of your choice where Goobi.Production shall pick up orders to create new
+processes.
+
+Orders must be javax.jms.MapMessage objects with the following key-value-pairs
+provided:
+
+ String template
+ name of the process template to use
+ String opac
+ Cataloge to use for lookup
+ String field
+ Field to look into, usually 12 (PPN)
+ String value
+ Value to look for, id of physical medium
+ String id
+ Ticket ID (used in log responses)
+ List<String> collections
+ Collections to be selected
+ Map<String, String> userFields (optional)
+ May be used to populates AdditionalField entries
+
+Here is a sample java client to do the job. It expects to be passed from the
+command line the Active MQ host (e.g. tcp://localhost:61616), the queue name
+and the parameters as listed above.
+
+To run this application, the following JARs from the ActiveMQ server’s /lib
+folder are required on the classpath:
+ * activemq-core
+ * geronimo-j2ee-management_1.1_spec
+ * genonimo-jms_1.1_spec
+ * log4j
+ * slf4j-api
+ * slf4j-log4j12
+
+--------------------------------[ Main.java ]----------------------------------
+import java.util.*;
+import javax.jms.*;
+import org.apache.activemq.ActiveMQConnectionFactory;
+
+public class Main {
+ public static int main(String[] args) { try {
+
+ // Check arguments
+ if (args.length < 8 || (args.length % 2) != 0) {
+ System.out.println("Parameters: Active MQ host, queue name, "
+ + "template name, opac name,");
+ System.out.println(" no. of search field, search "
+ + "string, digital id, collection name,");
+ System.out.println(" [additional details field, "
+ + "value, [add. details field, value, [...");
+ return 1;
+ }
+
+ // Connect to server
+ Connection connection = new ActiveMQConnectionFactory(args[0])
+ .createConnection();
+ connection.start();
+ Session session = connection.createSession(false,
+ Session.AUTO_ACKNOWLEDGE);
+ Destination destination = session.createQueue(args[1]);
+ MessageProducer producer = session.createProducer(destination);
+ producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+
+ // Create job ticket
+ MapMessage message = session.createMapMessage();
+ message.setString("template", args[2]);
+ message.setString("opac", args[3]);
+ message.setString("field", args[4]);
+ message.setString("value", args[5]);
+ message.setString("id", args[6]);
+ List<String> collections = new ArrayList<String>();
+ collections.add(args[7]);
+ message.setObject("collections", collections);
+ Map<String, String> userFields = new HashMap<String, String>();
+ for (int i = 8; i < args.length; i += 2)
+ userFields.put(args[i], args[i + 1]);
+ if (userFields.size() != 0)
+ message.setObject("userFields", userFields);
+
+ // Send job ticket
+ producer.send(message);
+
+ // Shutdown
+ session.close();
+ connection.close();
+ } catch (Exception e) { e.printStackTrace(); return 2; }
+ return 0;
+} }
=== added directory 'src/org/goobi/webservice/processors'
=== added file 'src/org/goobi/webservice/processors/CreateNewProcessProcessor.java'
--- src/org/goobi/webservice/processors/CreateNewProcessProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/processors/CreateNewProcessProcessor.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,387 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice.processors;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.goobi.webservice.ActiveMQProcessor;
+import org.goobi.webservice.MapMessageObjectReader;
+import org.hibernate.Criteria;
+import org.hibernate.Session;
+
+import de.sub.goobi.beans.Prozess;
+import de.sub.goobi.config.ConfigMain;
+import de.sub.goobi.config.ConfigOpacDoctype;
+import de.sub.goobi.forms.AdditionalField;
+import de.sub.goobi.forms.ProzesskopieForm;
+import de.sub.goobi.helper.Helper;
+
+/**
+ * CreateNewProcessProcessor is an Apache Active MQ consumer which registers to
+ * a queue configured by "activeMQ.createNewProcess.queue" on application
+ * startup. It was designed to create new processes from outside Goobi. There
+ * are two ways providing to create new processes. If the MapMessage on that
+ * queue contains of all the fields listed, the bibliographic data is retrieved
+ * using a catalogue configured within Goobi. If “opac” is missing, it will try
+ * to create a process just upon the data passed in the “userFields” − “field”
+ * and “value” will be ignored in that case, and the “docType” can be set
+ * manually.
+ *
+ * Field summary:
+ *
+ * <dl>
+ * <dt>String template</dt>
+ * <dd>name of the process template to use. A list of all available
+ * templates can be obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listTemplates.jsp</kbd>.</dd>
+ * <dt>String opac</dt>
+ * <dd>Cataloge to use for lookup. A list of all available catalogues
+ * can be obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listCatalogues.jsp</kbd>.</dd>
+ * <dt>String field</dt>
+ * <dd>Field to look into, usually 12 (PPN). A list of all available
+ * search fields can be obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listSearchFields.jsp</kbd>.</dd>
+ * <dt>String value</dt>
+ * <dd>Value to look for, id of physical medium</dd>
+ * <dt>String docType</dt>
+ * <dd>DocType value to use if no catalogue request is performed. A
+ * list of possible values can be obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listAllDoctypes.jsp</kbd></dd>
+ * <dt>Set<String> collections</dt>
+ * <dd>Collections to be selected. A list of all available collections
+ * can be obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listCollections.jsp</kbd>.</dd>
+ * <dt>Map<String, String> userFields collections</dt>
+ * <dd>Fields to be populated manually. A list of fields can be
+ * obtained in JSON format calling
+ * <kbd><em>{ContextRoot}</em>/ws/listFieldConfig.jsp</kbd>
+ * </dd>
+ * </dl>
+ *
+ * @author Matthias Ronge <matthias.ronge@xxxxxxxxxxxx>
+ */
+public class CreateNewProcessProcessor extends ActiveMQProcessor {
+ private static final Logger logger = Logger.getLogger(CreateNewProcessProcessor.class);
+
+ public CreateNewProcessProcessor() {
+ super(ConfigMain.getParameter("activeMQ.createNewProcess.queue", null));
+ }
+
+ @Override
+ protected void process(MapMessageObjectReader args) throws Exception {
+
+ Set<String> collections = args.getMandatorySetOfString("collections");
+ String id = args.getMandatoryString("id");
+ String template = args.getMandatoryString("template");
+ Map<String, String> userFields = args.getMapOfStringToString("userFields");
+ if (args.hasField("opac")) {
+ String opac = args.getMandatoryString("opac");
+ String field = args.getMandatoryString("field");
+ String value = args.getMandatoryString("value");
+ createNewProcessMain(template, opac, field, value, id, null, collections, userFields);
+ } else {
+ String docType = args.getString("docType");
+ createNewProcessMain(template, null, null, null, id, docType, collections, userFields);
+ }
+
+ }
+
+ /**
+ * This is the main routine used to create new processes.
+ *
+ * @param template
+ * titel of the process template the new process shall be derived
+ * from
+ * @param opac
+ * name of the connection to a library catalogue to load the
+ * bibliographic data from (may be null)
+ * @param field
+ * number of the catalogue search field (ignored if “opac” is
+ * null)
+ * @param value
+ * search string (ignored if “opac” is null)
+ * @param id
+ * identifier to be used for the digitisation
+ * @param docType
+ * docType to set (may be null)
+ * @param collections
+ * collections to add the digitisation to
+ * @param userFields
+ * Values for additional fields can be set here (may be null)
+ * @throws Exception
+ * in various cases, such as bad parameters or errors in the
+ * underlying layers
+ */
+ protected void createNewProcessMain(String template, String opac, String field, String value, String id, String docType,
+ Set<String> collections, Map<String, String> userFields) throws Exception {
+
+ try {
+ ProzesskopieForm newProcess = newProcessFromTemplate(template);
+ newProcess.setDigitalCollections(validCollectionsForProcess(collections, newProcess));
+ if (opac != null)
+ getBibliorgaphicData(newProcess, opac, field, value);
+ if (docType != null && docTypeIsPossible(newProcess, docType))
+ newProcess.setDocType(docType);
+ if (userFields != null)
+ setUserFields(newProcess, userFields);
+ newProcess.CalcProzesstitel();
+ String state = newProcess.NeuenProzessAnlegen();
+ if (!state.equals("ProzessverwaltungKopie3"))
+ throw new RuntimeException();
+ logger.info("Created new process: " + id);
+ } catch (Exception exited) {
+ logger.error("Failed to create new process: " + id, exited);
+ throw exited;
+ }
+ }
+
+ /**
+ * The function newProcessFromTemplate() derives a ProzesskopieForm object
+ * from a given template.
+ *
+ * @param templateTitle
+ * titel value of the template to look for
+ * @return a ProzesskopieForm object, prepared from a given template
+ * @throws IllegalArgumentException
+ * if no suitable template is found
+ */
+ protected ProzesskopieForm newProcessFromTemplate(String templateTitle) throws IllegalArgumentException {
+ ProzesskopieForm result = new ProzesskopieForm();
+
+ List<Prozess> allTemplates = allTemplatesFromDatabase();
+ Prozess selectedTemplate = selectTemplateByTitle(allTemplates, templateTitle);
+ result.setProzessVorlage(selectedTemplate);
+ result.Prepare();
+ return result;
+ }
+
+ /**
+ * This method reads all Prozess objects from the hibernate.
+ *
+ * @return a List<Prozess> holding all templates
+ */
+ protected List<Prozess> allTemplatesFromDatabase() {
+ Session hibernateSession = Helper.getHibernateSession();
+ Criteria request = hibernateSession.createCriteria(Prozess.class);
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> result = (List<Prozess>) request.list();
+
+ return result;
+ }
+
+ /**
+ * The function selectTemplateByTitle() iterates over a List of Prozess and
+ * returns the first element whose titel equals the given templateTitle.
+ *
+ * @param allTemplates
+ * a List<Prozess> which shall be examined
+ * @param templateTitle
+ * the title of the template to be picked up
+ * @return the template, if found
+ * @throws IllegalArgumentException
+ * is thrown, if there is no template matching the given
+ * templateTitle
+ */
+ protected Prozess selectTemplateByTitle(List<Prozess> allTemplates, String templateTitle) throws IllegalArgumentException {
+
+ Prozess result = null;
+ for (Prozess aTemplate : allTemplates) {
+ if (aTemplate.getTitel().equals(templateTitle)) {
+ result = aTemplate;
+ break;
+ }
+ }
+ if (result == null)
+ throw new IllegalArgumentException("Bad argument: No template \"" + templateTitle + "\" available.");
+ return result;
+ }
+
+ /**
+ * The function validCollectionsForProcess() tests whether a given set of
+ * collections can be assigned to new process. If so, the set of collections
+ * is returned as a list ready for assignment.
+ *
+ * @param collections
+ * a set of collection names to be tested
+ * @param process
+ * a ProzesskopieForm object whose prozessVorlage has been set
+ * @return an ArrayList which can be used to set the digitalCollections of a
+ * ProzesskopieForm
+ * @throws IllegalArgumentException
+ * in case that the given collection isn’t a valid subset of the
+ * digitalCollections possible here
+ */
+ protected List<String> validCollectionsForProcess(Set<String> collections, ProzesskopieForm process) throws IllegalArgumentException {
+
+ HashSet<String> possibleCollections = new HashSet<String>(process.getPossibleDigitalCollections());
+ if (!possibleCollections.containsAll(collections))
+ throw new IllegalArgumentException("Bad argument: One or more elements of \"collections\" is not available for template \""
+ + process.getProzessVorlage().getTitel() + "\".");
+ return new ArrayList<String>(collections);
+ }
+
+ /**
+ * The function docTypeIsPossible() tests whether a given docType String can
+ * be applied to a given process template. If so, it will return “true”,
+ * otherwise, it will throw an informative IllegalArgumentException.
+ *
+ * @param dialog
+ * the ProzesskopieForm object to test against
+ * @param docType
+ * the desired docType ID string
+ * @return true on success
+ * @throws IllegalArgumentException
+ * if a docType is not applicable to the template or the docType
+ * isn’t valid
+ */
+ protected boolean docTypeIsPossible(ProzesskopieForm dialog, String docType) throws IllegalArgumentException {
+ Boolean fieldIsUsed = dialog.getStandardFields().get("doctype");
+ if (fieldIsUsed == null || fieldIsUsed.equals(Boolean.FALSE))
+ throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide the standard field “doctype”.");
+
+ boolean valueIsValid = false;
+ Iterator<ConfigOpacDoctype> configOpacDoctypeIterator = dialog.getAllDoctypes().iterator();
+ do {
+ ConfigOpacDoctype option = configOpacDoctypeIterator.next();
+ valueIsValid = docType.equals(option.getTitle());
+ } while (!valueIsValid && configOpacDoctypeIterator.hasNext());
+ if (valueIsValid)
+ return true;
+ throw new IllegalArgumentException("Bad argument “docType”: Selected template doesn’t provide a docType “{0}”.".replace("{0}",
+ docType));
+ }
+
+ /**
+ * The method setUserFields() allows to set any AdditionalField to a user
+ * specific value.
+ *
+ * @param form
+ * a ProzesskopieForm object whose AdditionalField objects are
+ * subject to the change
+ * @param userFields
+ * the data to pass to the form
+ * @throws RuntimeException
+ * in case that no field with a matching title was found in the
+ * ProzesskopieForm object
+ */
+ protected void setUserFields(ProzesskopieForm form, Map<String, String> userFields) throws RuntimeException {
+
+ for (String key : userFields.keySet()) {
+ setAdditionalField(form, key, userFields.get(key));
+ }
+
+ }
+
+ /**
+ * Sets the bibliographic data for a new process from a library catalogue.
+ * This is equal to manually choosing a catalogue and a search field,
+ * entering the search string and clicking “Apply”.
+ *
+ * Since the underlying OpacAuswerten() method doesn’t raise exceptions, we
+ * count the populated “additional details” fields before and after running
+ * the request and assume the method to have failed if not even one more
+ * field was populated by the method call.
+ *
+ * @param inputForm
+ * the ProzesskopieForm to be set
+ * @param id
+ * the ticket’s id
+ * @param opac
+ * the value for “Search in Opac”
+ * @param field
+ * the number of the search field, e.g. “12” for PPN.
+ * @param value
+ * the search string
+ * @throws RuntimeException
+ * is thrown if the search didn’t bring any results
+ */
+ protected void getBibliorgaphicData(ProzesskopieForm inputForm, String opac, String field, String value) throws RuntimeException {
+
+ inputForm.setOpacKatalog(opac);
+ inputForm.setOpacSuchfeld(field);
+ inputForm.setOpacSuchbegriff(value);
+
+ int before = countPopulatedAdditionalFields(inputForm);
+ inputForm.OpacAuswerten();
+ int afterwards = countPopulatedAdditionalFields(inputForm);
+
+ if (!(afterwards > before))
+ throw new RuntimeException("Searching the OPAC didn’t yield any results.");
+ }
+
+ /**
+ * The function countPopulatedAdditionalFields() returns the number of
+ * AdditionalFields in the given ProzesskopieForm that have meaningful
+ * content.
+ *
+ * @param form
+ * a ProzesskopieForm object to examine
+ * @return the number of AdditionalFields populated
+ */
+ protected int countPopulatedAdditionalFields(ProzesskopieForm form) {
+ int result = 0;
+
+ for (AdditionalField field : form.getAdditionalFields()) {
+ String value = field.getWert();
+ if (value != null && value.length() > 0)
+ result++;
+ }
+
+ return result;
+ }
+
+ /**
+ * The method setAdditionalField() sets the value of an AdditionalField held
+ * by a ProzesskopieForm object.
+ *
+ * @param inputForm
+ * a ProzesskopieForm object
+ * @param key
+ * the title of the AdditionalField whose value shall be modified
+ * @param value
+ * the new value for the AdditionalField
+ * @throws RuntimeException
+ * in case that no field with a matching title was found in the
+ * ProzesskopieForm object
+ */
+ protected void setAdditionalField(ProzesskopieForm inputForm, String key, String value) throws RuntimeException {
+
+ for (AdditionalField field : inputForm.getAdditionalFields()) {
+ if (key.equals(field.getTitel())) {
+ field.setWert(value);
+ return;
+ }
+ }
+
+ throw new RuntimeException("Couldn’t set “" + key + "” to “" + value + "”: No such field in record.");
+ }
+
+}
=== added file 'src/org/goobi/webservice/processors/FinaliseStepProcessor.java'
--- src/org/goobi/webservice/processors/FinaliseStepProcessor.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/processors/FinaliseStepProcessor.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the Goobi Application - a Workflow tool for the support of
+ * mass digitization.
+ *
+ * Visit the websites for more information.
+ * - http://gdz.sub.uni-goettingen.de
+ * - http://www.goobi.org
+ * - http://launchpad.net/goobi-production
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ * should have received a copy of the GNU General Public License along with this
+ * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package org.goobi.webservice.processors;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.goobi.webservice.ActiveMQProcessor;
+import org.goobi.webservice.MapMessageObjectReader;
+
+import de.sub.goobi.config.ConfigMain;
+import de.sub.goobi.forms.AktuelleSchritteForm;
+import de.sub.goobi.persistence.SchrittDAO;
+
+/**
+ * This is a web service interface to close steps. You have to provide the step
+ * id as “id”; you can add a field “message” which will be added to the wiki
+ * field.
+ *
+ * @author Matthias Ronge <matthias.ronge@xxxxxxxxxxxx>
+ */
+public class FinaliseStepProcessor extends ActiveMQProcessor {
+
+ /**
+ * The default constructor looks up the queue name to use in
+ * GoobiConfig.properties. If that is not configured and “null” is passed to
+ * the super constructor, this will prevent
+ * ActiveMQDirector.registerListeners() from starting this service.
+ */
+ public FinaliseStepProcessor() {
+ super(ConfigMain.getParameter("activeMQ.finaliseStep.queue", null));
+ }
+
+ /**
+ * This is the main routine processing incoming tickets. It gets an
+ * AktuelleSchritteForm object, sets it to the appropriate step which is
+ * retrieved from the database, appends the message − if any − to the wiki
+ * field, and executes the form’s the step close function.
+ *
+ * @param ticket
+ * the incoming message
+ *
+ * @see org.goobi.webservice.ActiveMQProcessor#process(org.goobi.webservice.MapMessageObjectReader)
+ */
+ protected void process(MapMessageObjectReader ticket) throws Exception {
+ AktuelleSchritteForm dialog = new AktuelleSchritteForm();
+ Integer stepID = ticket.getMandatoryInteger("id");
+ dialog.setMySchritt(new SchrittDAO().get(stepID));
+ if (ticket.hasField("message"))
+ addMessageToWikiField(dialog, ticket.getString("message"));
+ dialog.SchrittDurchBenutzerAbschliessen();
+ }
+
+ /**
+ * The addMessageToWikiField() method is a helper method which composes the
+ * new wiki field using a StringBuilder. The message is encoded using HTML
+ * entities to prevent certain characters from playing merry havoc when the
+ * message box shall be rendered in a browser later.
+ *
+ * @param form
+ * the AktuelleSchritteForm which is the owner of the wiki field
+ * @param message
+ * the message to append
+ */
+ protected void addMessageToWikiField(AktuelleSchritteForm form, String message) {
+ StringBuilder composer = new StringBuilder();
+ String wikiField = form.getWikiField();
+ if (wikiField != null && wikiField.length() > 0) {
+ composer.append(wikiField);
+ composer.append("\r\n");
+ }
+ composer.append("<p>");
+ composer.append(StringEscapeUtils.escapeHtml(message));
+ composer.append("</p>");
+ form.setWikiField(composer.toString());
+ return;
+ }
+
+}
=== added directory 'src/org/goobi/webservice/taglib'
=== added file 'src/org/goobi/webservice/taglib/WebServiceTaglib.java'
--- src/org/goobi/webservice/taglib/WebServiceTaglib.java 1970-01-01 00:00:00 +0000
+++ src/org/goobi/webservice/taglib/WebServiceTaglib.java 2012-07-25 16:05:24 +0000
@@ -0,0 +1,214 @@
+package org.goobi.webservice.taglib;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.json.simple.JSONValue;
+
+import de.sub.goobi.beans.Prozess;
+import de.sub.goobi.config.ConfigOpac;
+import de.sub.goobi.config.ConfigOpacDoctype;
+import de.sub.goobi.config.ConfigProjects;
+import de.sub.goobi.config.DigitalCollections;
+import de.sub.goobi.helper.Helper;
+
+public class WebServiceTaglib {
+
+ /**
+ * The function getAllFields() returns a map that contains all fields for
+ * each process template with their respective configurations.
+ *
+ * @return A map in JSON format
+ */
+
+ public static String getAllFields() throws Exception {
+ Map<String, Map<String, Object>> result = new HashMap<String, Map<String, Object>>();
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> processes = (List<Prozess>) Helper.getHibernateSession().createCriteria(Prozess.class).list();
+
+ for (Prozess p : processes) {
+ if (p.isIstTemplate()) {
+ Map<String, Object> fields = new HashMap<String, Object>();
+ ConfigProjects projectConfig = new ConfigProjects(p.getProjekt());
+ Integer numFields = projectConfig.getParamList("createNewProcess.itemlist.item").size();
+ for (Integer field = 0; field < numFields; field++) {
+ String fieldRef = "createNewProcess.itemlist.item(" + field + ")";
+ String fieldName = projectConfig.getParamString(fieldRef);
+ Map<String, Object> fieldConfig = new HashMap<String, Object>();
+ fieldConfig.put("from", projectConfig.getParamString(fieldRef + "[@from]"));
+ if (projectConfig.getParamBoolean(fieldRef + "[@ughbinding]")) {
+ fieldConfig.put("ughbinding", Boolean.TRUE);
+ fieldConfig.put("docstruct", projectConfig.getParamString(fieldRef + "[@docstruct]"));
+ fieldConfig.put("metadata", projectConfig.getParamString(fieldRef + "[@metadata]"));
+ } else {
+ fieldConfig.put("ughbinding", Boolean.FALSE);
+ }
+ Integer selectEntries = projectConfig.getParamList(fieldRef + ".select").size();
+ if (selectEntries > 0) {
+ Map<String, String> selectConfig = new HashMap<String, String>();
+ for (Integer selectEntry = 0; selectEntry < selectEntries; selectEntry++) {
+ String key = projectConfig.getParamString(fieldRef + ".select(" + selectEntry + ")");
+ String value = projectConfig.getParamString(fieldRef + ".select(" + selectEntry + ")[@label]");
+ selectConfig.put(key, value);
+ }
+ fieldConfig.put("select", selectConfig);
+ }
+ fieldConfig.put("required", projectConfig.getParamBoolean(fieldRef + "[@required]"));
+ fields.put(fieldName, fieldConfig);
+ }
+ result.put(p.getTitel(), fields);
+ }
+ }
+ return JSONValue.toJSONString(result);
+ }
+
+ /**
+ * The function getCatalogues() returns a list that contains all catalogues
+ * configured to read in bibliographic data from
+ *
+ * @return A list in JSON format
+ */
+ public static String getCatalogues() throws Exception {
+ List<String> catalogues = new ConfigOpac().getAllCatalogueTitles();
+ return JSONValue.toJSONString(catalogues);
+ }
+
+ /**
+ * The function getCollections returns a map of all possible digital
+ * collections for all available templates
+ *
+ * @return A map in JSON format
+ */
+ public static String getCollections() throws Exception {
+ HashMap<String, List<String>> result = new HashMap<String, List<String>>();
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> processes = (List<Prozess>) Helper.getHibernateSession().createCriteria(Prozess.class).list();
+ for (Prozess process : processes) {
+ if (process.isIstTemplate()) {
+ result.put(process.getTitel(), DigitalCollections.possibleDigitalCollectionsForProcess(process));
+ }
+ }
+ return JSONValue.toJSONString(result);
+ }
+
+ /**
+ * The function getAllDoctypes() returns a list of all doctypes configured
+ * in Goobi.
+ *
+ * @return A list in JSON format
+ */
+ public static String getAllDoctypes() throws Exception {
+ HashMap<String, Map<String, Object>> result = new HashMap<String, Map<String, Object>>();
+ ConfigOpac co = new ConfigOpac();
+ List<ConfigOpacDoctype> mediaTypesList = co.getAllDoctypes();
+ for (ConfigOpacDoctype mediaTypeEntry : mediaTypesList) {
+ Map<String, Object> resultEntry = new HashMap<String, Object>();
+ resultEntry.put("containedWork", Boolean.valueOf(mediaTypeEntry.isContainedWork()));
+ resultEntry.put("labels", mediaTypeEntry.getLabels());
+ resultEntry.put("mappings", mediaTypeEntry.getMappings());
+ resultEntry.put("multiVolume", Boolean.valueOf(mediaTypeEntry.isMultiVolume()));
+ resultEntry.put("periodical", Boolean.valueOf(mediaTypeEntry.isPeriodical()));
+ resultEntry.put("rulesetType", mediaTypeEntry.getRulesetType());
+ resultEntry.put("tifHeaderType", mediaTypeEntry.getTifHeaderType());
+ result.put(mediaTypeEntry.getTitle(), resultEntry);
+ }
+ return JSONValue.toJSONString(result);
+ }
+
+ /**
+ * The function getProjects() returns a list of all projects configured.
+ *
+ * @return A list in JSON format
+ */
+ public static String getProjects() {
+ Set<String> projects = new HashSet<String>();
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> processes = (List<Prozess>) Helper.getHibernateSession().createCriteria(Prozess.class).list();
+ for (Prozess process : processes) {
+ if (process.isIstTemplate()) {
+ projects.add(process.getProjekt().getTitel());
+ }
+ }
+ return JSONValue.toJSONString(new ArrayList<String>(projects));
+ }
+
+ /**
+ * The function getProjectsAndTemplates() returns a map with all projects
+ * and their associated templates.
+ *
+ * @return a map in JSON format.
+ */
+ public static String getProjectsAndTemplates() {
+ Map<String, Set<String>> data = new HashMap<String, Set<String>>();
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> processes = (List<Prozess>) Helper.getHibernateSession().createCriteria(Prozess.class).list();
+ for (Prozess process : processes) {
+ if (process.isIstTemplate()) {
+ String projectName = process.getProjekt().getTitel();
+ Set<String> templateList = data.containsKey(projectName) ? data.get(projectName) : new HashSet<String>();
+ templateList.add(process.getTitel());
+ data.put(projectName, templateList);
+ }
+ }
+
+ Map<String, List<String>> result = new HashMap<String, List<String>>();
+ for (String projectName : data.keySet())
+ result.put(projectName, new ArrayList<String>(data.get(projectName)));
+
+ return JSONValue.toJSONString(result);
+ }
+
+ /**
+ * The function getSearchFields returns a map of all search fields available
+ * for any of the catalogs configured with their respective labels.
+ *
+ * This is coded statically in /newpages/NewProcess/inc_process.jsp and so
+ * it is also hardcoded here by now.
+ *
+ * @return A map in JSON format.
+ */
+ public static String getSearchFields() throws Exception {
+ Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
+
+ // Not yet configurable within Goobi.Production
+ Map<String, String> staticSearchFields = new LinkedHashMap<String, String>();
+ staticSearchFields.put("12", "PPN");
+ staticSearchFields.put("8535", "Barcode");
+ staticSearchFields.put("8200", "Barcode 8200");
+ staticSearchFields.put("7", "ISBN");
+ staticSearchFields.put("8", "ISSN");
+
+ for (String catalogue : new ConfigOpac().getAllCatalogueTitles()) {
+ result.put(catalogue, staticSearchFields);
+ }
+ return JSONValue.toJSONString(result);
+ }
+
+ /**
+ * The function getTemplates() returns a list of all available process
+ * templates.
+ *
+ * @return a list in JSON format.
+ */
+ public static String getTemplates() {
+ ArrayList<String> result = new ArrayList<String>();
+
+ @SuppressWarnings("unchecked")
+ List<Prozess> processes = (List<Prozess>) Helper.getHibernateSession().createCriteria(Prozess.class).list();
+ for (Prozess process : processes) {
+ if (process.isIstTemplate()) {
+ result.add(process.getTitel());
+ }
+ }
+ return JSONValue.toJSONString(result);
+ }
+}
=== added directory 'ws'
=== added file 'ws/listAllDoctypes.jsp'
--- ws/listAllDoctypes.jsp 1970-01-01 00:00:00 +0000
+++ ws/listAllDoctypes.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,5 @@
+<%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getAllDoctypes()}
=== added file 'ws/listCatalogues.jsp'
--- ws/listCatalogues.jsp 1970-01-01 00:00:00 +0000
+++ ws/listCatalogues.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getCatalogues()}
=== added file 'ws/listCollections.jsp'
--- ws/listCollections.jsp 1970-01-01 00:00:00 +0000
+++ ws/listCollections.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getCollections()}
=== added file 'ws/listFieldConfig.jsp'
--- ws/listFieldConfig.jsp 1970-01-01 00:00:00 +0000
+++ ws/listFieldConfig.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getAllFields()}
\ No newline at end of file
=== added file 'ws/listProjects.jsp'
--- ws/listProjects.jsp 1970-01-01 00:00:00 +0000
+++ ws/listProjects.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getProjects()}
=== added file 'ws/listProjectsTemplates.jsp'
--- ws/listProjectsTemplates.jsp 1970-01-01 00:00:00 +0000
+++ ws/listProjectsTemplates.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getProjectsAndTemplates()}
=== added file 'ws/listSearchFields.jsp'
--- ws/listSearchFields.jsp 1970-01-01 00:00:00 +0000
+++ ws/listSearchFields.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getSearchFields()}
\ No newline at end of file
=== added file 'ws/listTemplates.jsp'
--- ws/listTemplates.jsp 1970-01-01 00:00:00 +0000
+++ ws/listTemplates.jsp 2012-07-25 16:05:24 +0000
@@ -0,0 +1,27 @@
+<%--
+ ~ This file is part of the Goobi Application - a Workflow tool for the support of
+ ~ mass digitization.
+ ~
+ ~ Visit the websites for more information.
+ ~ - http://gdz.sub.uni-goettingen.de
+ ~ - http://www.goobi.org
+ ~ - http://launchpad.net/goobi-production
+ ~
+ ~ This program is free software; you can redistribute it and/or modify it under
+ ~ the terms of the GNU General Public License as published by the Free Software
+ ~ Foundation; either version 2 of the License, or (at your option) any later
+ ~ version.
+ ~
+ ~ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ ~ PARTICULAR PURPOSE. See the GNU General Public License for more details. You
+ ~ should have received a copy of the GNU General Public License along with this
+ ~ program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ ~ Suite 330, Boston, MA 02111-1307 USA
+ --%>
+
+ <%@ page language="java"
+ contentType="application/json; charset=utf-8" pageEncoding="utf-8"
+ session="false" trimDirectiveWhitespaces="true"%>
+<%@ taglib uri="http://taglib.production.goobi.org/webService" prefix="ws" %>
+${ws:getTemplates()}
Follow ups