Die Anwendungsszenarien und Konfigurationsmöglichkeiten für den jadice server sind ebenso zahlreich wie vielfältig. Deshalb sollen hier nur die gängigsten Szenarien aufgegriffen werden, die als Ausgangspunkt für eigene Implementierungen dienen können.
In den meisten Fällen sind diese nach dem Schema
Datei vom Client empfangen
Serverseitige Verarbeitung
Ergebnis zum Client zurücksenden
aufgebaut. Dies ist der Einfachheit der Beispiele geschuldet. In realen Anwendungen werden die Ergebnisse üblicherweise nicht direkt nach der Verarbeitung zurückgesendet, sondern in kaskadierten Schritten verarbeitet. Auch die Quelle und das Ziel der Datenströme sind nicht zwingenderweise der Client, sondern können beispielsweise ein zentraler Datei-, Mail-, Archivserver o. ä. sein.
Der Job
wird zunächst clientseitig erstellt, um ihm anschließend 1…n
Nodes anzuhängen.
Konfigurationsmöglichkeiten des Jobs sind:
JobListener
-Implementatierung, siehe Abschnitt „Erstellung eines JobListeners“TimeLimit
und ähnlicheLimit
s, siehe Abschnitt „Konfiguration von Limits“Workflow (= azyklischer Graph, der durch miteinander verkettete
Node
s definiert wird)Typ: Mit diesem Bezeichner werden
Job
s von der gleichen Art ausgezeichnet. Serverseitig wird diese Angabe u.a. zu statistischen Zwecken verwendtet.
Beispiel 6.1. Job erstellen (mit ActiveMQ als Messagebroker)
public Job createServerJob() throws JMSException { // Job-Factory erstellen mit den Parametern "brokerUrl" und dem Standard "queueName" JMSJobFactory jobFactory = new JMSJobFactory( new ActiveMQConnectionFactory("tcp://<Broker_IP>:<Broker-Port>"), JMSJobFactory.DEFAULT_QUEUE_NAME); // Server-Job erstellen Job job = jobFactory.createJob(); return job; }
Beispiel 6.2. Job konfigurieren und ausführen
// Job erstellen Job job = createServerJob(); // Timeout setzen (60 Sekunden) job.apply(new TimeLimit(60, TimeUnit.SECONDS)); // Job-Typ deklarieren job.setType("first-example"); // JobListener registrieren (siehe unten) job.addJobListener(…); // Workflow definieren (siehe unten) job.attach(…); // Job an Server senden job.submit();
Die Funktion createServerJob()
aus Beispiel 6.1, „Job erstellen (mit ActiveMQ als Messagebroker)“ wird als Basis für die kommenden Beispiel verwendet.
Mit dem JobListener
können Zustände des Job
s und serverseitige Fehlermeldungen
verarbeitet werden.
Beispiel 6.3. Beispiel einer JobListener
-Implementation
public class MyJobListener implements JobListener { public void stateChanged(Job job, State oldState, State newState) { dump("stateChanged", job, oldState, newState, null, null); } public void executionFailed(Job job, Node node, String messageId, String reason, Throwable cause) { dump("executionFailed", job, node, messageId, reason, cause); } public void errorOccurred(Job job, Node node, String messageId, String message, Throwable cause) { dump("errorOccurred", job, node, messageId, message, cause); } public void warningOccurred(Job job, Node node, String messageId, String message, Throwable cause) { dump("warningOccurred", job, node, messageId, message, cause); } public void subPipelineCreated(Job job, Node parent, Set<? extends Node> createdNodes) { dump("subPipelineCreated", job, parent); } private void dump(String ctx, Job job, Object... args) { System.err.println("Context: " + ctx); System.err.println("Job: " + job.toString()); if (args == null) return; for (Object arg : args) System.err.println(" " + arg.toString()); } }
jadice server liegen zwei Implementierungen dieses Interfaces bei, die in der Integration eingesetzt werden können:
TraceListener
Leitet Fehlermeldung via Apache Commons Logging an das Client-Log weiter
JobListenerAdapter
Leere Standard-Implementierung des
JobListener
-Interfaces. Davon abgeleitete Klassen müssen nur die gewünschten Methoden überschreiben
Um den Ressourcen-Verbrauch eines Job
s oder dessen Knoten zu beschränken, ist es
sinnvoll, Limit
s darauf anzuwenden. Folgende Limit
s sind hierbei möglich:
Tabelle 6.1. Verfügbare Limit
s
Typ des Limits | Beschreibung | Kontext | |
---|---|---|---|
Job | Node | ||
TimeLimit |
Maximale Verarbeitungszeit | ☑ | ☑ |
Maximale Anzahl an | ☐ | ☑ | |
Maximale Größe der | ☐ | ☑ | |
Maximale Anzahl an Seiten eines generierten Dokuments | ☐ | ☑[a] | |
Maximale Anzahl an Knoten, die ein | ☑ | ☒ | |
[a] Für Knoten, die Dokumente generieren, die einen Seiten-Begriff kennen; vgl. javadoc |
Tabelle 6.2. Legende zu Tabelle 6.1, „Verfügbare Limit
s“
☑ | wird direkt an dieser Stelle berücksichtigt |
☒ | wird nicht an dieser Stelle berücksichtigt |
☐ | wird nicht berücksichtigt, aber an die Knoten vererbt (s.u.) |
Bei der Definition eines Limit
s kann ausgewählt werden, was passieren soll, wenn es
überschritten wird:
Beispiel 6.4. Beispielhafte Verwendung von Limit
s
TimeLimit tl = new TimeLimit(60, TimeUnit.SECONDS); tl.setExceedAction(WhenExceedAction.ABORT); // default NodeCountLimit ncl = new NodeCountLimit(20); ncl.setExceedAction(WhenExceedAction.WARN);
Bei der Limit.WhenExceedAction
ABORT bricht der komplette Job ab, bei der Limit.WhenExceedAction
WARN wird dem
Client eine Warning gemeldet.
Da bei der clientseitigen Workflow-Definition möglicherweise nicht alle Knoten bekannt sind oder es nicht sinnvoll ist, jeden einzelnen Knoten mit Limits zu belegen, können diese auch zentral auf den Job angewendet werden. Diese werden an die einzelnen Knoten vererbt. Dabei gelten folgenden Vererbungsregeln:
Limit
s mit derLimit.WhenExceedAction
WARN werden in jedem Fall vererbt.Limit
s mit derLimit.WhenExceedAction
ABORT werden nicht an Knoten vererbt, auf die bereits ein Limit der selben Klasse mit derLimit.WhenExceedAction
ABORT angewendet wurde, auch wenn dieses weniger restriktiv ist.Werden
Limit
s der selben Klasse mitLimit.WhenExceedAction
ABORT sowohl clientseitig als auch über die Security-Schnittstelle gesetzt, haben die restriktiveren Vorrang. Vgl. Abschnitt „Restriktionen“
Der jadice server bietet mächtige Module zur Erkennung unbekannter Dateiformate. Diese werden in den Modulen zur automatischen Konvertierung unbekannter Dateien bzw. E-Mails eingesetzt (siehe Abschnitte „Konvertierung unbekannter Eingabedaten in ein einheitliches Format (PDF)“ und „Konvertierung von E-Mails nach PDF“).
Darüber hinaus ist es auch möglich, diese Module durch den StreamAnalysisNode
anzusprechen und für eigene Zwecke zu verwenden.
Beispiel 6.5. Verwendung des StreamAnalysisNode
Job job = createServerJob(); job.setType("run stream analysis"); // Nodes erstellen: // 1. Dateneingabe-Node StreamInputNode siNode = new StreamInputNode(); // 2. Analyseknoten StreamAnalysisNode saNode = new StreamAnalysisNode(); // 3. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen job.attach(siNode.appendSuccessor(saNode).appendSuccessor(soNode)); // Job ausführen und Dokumentdatenstrom senden job.submit(); siNode.addStream(…); siNode.complete(); // Auf Antwort vom Server warten //@Callout Die Methode getStreamBundle() blockiert, bis der Server die Abarbeitung beendet hat. Eine asynchrone Verarbeitung ist durch die Verwendung einer JobListener-Implementierung zu realisieren for (Stream stream : soNode.getStreamBundle()) { // Lesen der beschreibenden Daten final StreamDescriptor descr = stream.getDescriptor(); final String mimeType = descr.getMimeType(); }
Der JadiceDocumentInfoNode
analysiert ein Dokument. Dazu wird das Dokument mit der jadice document platform geladen und Metadaten extrahiert.
Mit diesen Informationen wird der StreamDescriptor
angereichtert und an den nächsten Knoten als IDocumentInfo
weitergegeben.
Das jeweils untersuchte Format muss dabei von der jadice document platform 5
unterstützt werden.
Als einfachstes Beispiel kann diese Information mit Hilfe der &NotifacNode direkt an den Client übergegeben und dort auf die Konsole ausgegeben werden.
Beispiel 6.6. Verwendung des JadiceDocumentInfoNode
// Server-Job erstellen Job job = createServerJob(); job.setType("retrieve document info"); // InfoNode erstellen JadiceDocumentInfoNode infoNode = new JadiceDocumentInfoNode(); // Listener erstellen und an NotificatioNode anhängen DocumentInfoListener documentInfoListener = new DocumentInfoListener(); NotificationNode notifyNode = new NotificationNode(); notifyNode.addNotificationResultListener(documentInfoListener); // Workflow zusammenstellen StreamInputNode siNode = new StreamInputNode(); siNode.appendSuccessor(infoNode); infoNode.appendSuccessor(notifyNode); // Datenstrom am Ende der Analyse verwerfen notifyNode.appendSuccessor(new NullNode()); // 1. Node dem Job übergeben... job.attach(siNode); // ...und Job starten job.submit(); // Nach dem Start des Jobs den Datenstrom dem Inputnode übergeben siNode.addStream(…); // Dateneingabe beenden siNode.complete(); // Auf Abarbeitung durch Server warten (siehe oben) documentInfoListener.waitForDocumentInfo(); // DokumentInfo holen und Daten ausgeben IDocumentInfo documentInfo = documentInfoListener.getDocumentInfo(); System.out.println("Anzahl Seiten : " + documentInfo.getPageCount()); // Beispielhaft: Daten der ersten Seite System.out.println("Format : " + documentInfo.getFormat(0)); System.out.println("Größe (Pixel) : " + documentInfo.getSize(0).getWidth() + "x" + documentInfo.getSize(0).getHeight()); System.out.println("Auflösung (dpi): " + documentInfo.getVerticalResolution(0) + "x" + documentInfo.getHorizontalResolution(0));
Beispiel 6.7. In Beispiel 6.6, „Verwendung des JadiceDocumentInfoNode
“ verwendeter NotificationNode.NotificationListener
public class DocumentInfoListener implements NotificationListener { /** * Dokumentinfo, wird vom JadiceDocumentInfoNode erzeugt und an den StreamDescriptor gehängt */ private IDocumentInfo documentInfo; /** * Latch um Thread zu blockieren, bis {@link #documentInfo} verfügbar ist. Hinweis: * Fehlerbehandlung, wenn Job abbricht / keine Ergebnis geliefert wird, im Beispiel nicht * umgesetzt */ private CountDownLatch latch = new CountDownLatch(1); @Override public void notificationRecieved(StreamDescriptor streamDescriptor) { final Serializable prop = streamDescriptor.getProperties().get(JadiceDocumentInfoNode.PROPERTY_NAME); if (prop != null && prop instanceof IDocumentInfo) { documentInfo = (IDocumentInfo) prop; latch.countDown(); } } public void waitForDocumentInfo() throws InterruptedException { // Blockieren bis IDocumentInfo erhalten wurde latch.await(); } public IDocumentInfo getDocumentInfo() { return documentInfo; } }
Mit dem PDFMergeNode
ist es möglich, mehrere PDF-Dokumente zu einem einzigen zusammenzufassen.
Beispiel 6.8. Verwendung des PDFMergeNode
Job job = createServerJob(); job.setType("merge pdfs"); // Nodes erstellen: // 1. Dateneingabe-Node StreamInputNode siNode = new StreamInputNode(); // 2. Zusammenfassen von Eingabedaten (1...n zu 1) PDFMergeNode pmNode = new PDFMergeNode(); // 3. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen job.attach(siNode.appendSuccessor(pmNode).appendSuccessor(soNode)); // Job ausführen job.submit(); // PDF-Dokument-Datenstrom senden siNode.addStream(…); siNode.addStream(…); // ... evtl. weitere Datenströme // Dateneingabe beenden siNode.complete(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten InputStream is = stream.getInputStream(); }
Die meisten Konvertierungsvorgänge (z. B. über LibreOffice) erzeugen PDF. Es ist jedoch
möglich, durch das Einfügen des ReshapeNode
das Ergebnis weiter nach TIFF zu
konvertieren.
Im folgenden Beispiel wird der Workflow aus Beispiel 6.8, „Verwendung des PDFMergeNode
“ verändert; anstelle des
PDFMergeNode
wird eine Konvertierung nach TIFF mit anschließender Aggretation
angehängt:
Beispiel 6.9. Konvertierung nach Tiff
// (...) ReshapeNode reshapeNode = new ReshapeNode(); // gewünschtes Zielformet reshapeNode.setTargetMimeType("image/tiff"); // alle eingehenden Streams zusammenfassen reshapeNode.setOutputMode(OutputMode.JOINED); // Workflow erstellen, Tiff-Konverter-Node einfügen job.attach(siNode. appendSuccessor(reshapeNode). appendSuccessor(soNode)); // (...)
Um Dokumente und deren Annotationen mit Standardprogrammen anzeigen zu können, müssen diese als „normale“ Objekte im Ausgangsformat verankert werden.
Dies kann ebenfalls mit dem ReshapeNode
realisiert werden. Die notwendige
Assoziation zwischen dem Dokument- und den Annotationsdatenströmen zeigt das
folgende Beispiel:
Beispiel 6.10. Dauerhafte Verankerung von Annotationen
/** * Beispiel-Interface zur Bündelung von Dokument und Annotationen */ interface DocumentAndAnnotations { InputStream getContent(); List<InputStream> getAnnotations(); } public void convert(DocumentAndAnnotations doc) throws JMSException, JobException, IOException { Job job = createServerJob(); job.setType("imprint annotations"); // Nodes erstellen StreamInputNode inputNode = new StreamInputNode(); ReshapeNode reshapeNode = new ReshapeNode(); StreamOutputNode outputNode = new StreamOutputNode(); // Konfiguration des Ergebnis-Typs (z.B. PDF) reshapeNode.setTargetMimeType("application/pdf"); // Annotations-Datenströme mit Content assoziieren reshapeNode.setOutputMode(OutputMode.ASSOCIATED_STREAM); // Workflow zusammenstellen job.attach(inputNode.appendSuccessor(reshapeNode).appendSuccessor(outputNode)); job.submit(); // Sende Dokument-Inhalt // (hier mit explizitem Mime-Type) final StreamDescriptor contentSD = new StreamDescriptor("application/pdf"); inputNode.addStream(doc.getContent(), contentSD); // Verarbeitung der Annotationen: for (InputStream annoStream : doc.getAnnotations()) { StreamDescriptor annoSD = new StreamDescriptor(); // Assoziation zw. Dokument und Annotation: annoSD.setParent(contentSD); // Setzen des Annotationstyps (z.B. Filenet P8): annoSD.setMimeType(ReshapeNode.AnnotationMimeTypes.FILENET_P8); // Senden des Annotationsdatenstroms inputNode.addStream(annoStream, annoSD); } inputNode.complete(); // Nicht gezeigt: Verarbeitung des Ergebnisses }
In dieser Konfiguration sind zwei Einstellungen besonders relevant:
Dokument- und Annotationsdatenströme müssen über die StreamDescriptor
-Hierarchie
miteinander verknüpft werden. Dazu muss der StreamDescriptor
des Dokuments als
Parent der StreamDescriptor
en der Annotationen gesetzt werden.
Für die verfügbaren Mime-Types von Annotationen gibt es in der Klasse
ReshapeNode
vordefinierte Konstanten, die zwingend gesetzt werden müssen.
Weitere Informationen über die Annotationsformate und deren Eigenschaften können Sie
dem Annotationshandbuch der jadice document platform 5 entnehmen.
Vertrauliche Dokumentinhalte
Bitte beachten Sie, dass keine Analyse des Dokumentinhalts stattfindet. Inhalte, die von Annotationen verdeckt werden, können je nach Datenformat weiterhin im Zieldatenstrom vorhanden sein. Außerdem ist es möglich, dass im zweiten Fall unerwünschte (Meta-)Daten im Dokument verbleiben können, die einer Vertraulichkeit unterliegen.
Um die Netzwerklast zu verringern, werden Dateien häufig komprimiert. Diese können vor der Weiterverarbeitung durch den jadice server entpackt werden. Dies geschieht je nach Dateiformat in unterschiedlichen Nodeklassen:
Tabelle 6.3. Legende
Dateiformat | Nodeklasse | Bemerkung |
---|---|---|
ZIP | UnZIPNode | |
RAR | UnRARNode | |
GZIP | UnGZIPNode |
|
TAR | UnTARNode |
Wie dies für den UnZIPNode
aussieht, zeigt folgendes Codebeispiel:
Beispiel 6.11. Verwendung des UnZIPNode
Job job = createServerJob(); job.setType("unpack zip"); // Nodes erstellen: // 1. Dateneingabe-Node StreamInputNode siNode = new StreamInputNode(); // 2. Entpacken von ZIP-Archiven UnZIPNode unzipNode = new UnZIPNode(); // 3. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen job.attach(siNode.appendSuccessor(unzipNode).appendSuccessor(soNode)); // Job ausführen job.submit(); // Dokument-Datenstrom senden siNode.addStream(…); // Dateneingabe beenden siNode.complete(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten (1 Stream je Datei im Archiv) System.out.println("filename: " + stream.getDescriptor().getFileName()); InputStream is = stream.getInputStream(); }
Eine Vereinheitlichung von Dokumenten ist besonders im Bereich der Langzeitarchivierung von Nutzen. Der Zugriff auf die Datenquelle, die automatische Analyse von Daten, eine zielvorgabenorientierte, dynamische Weiterverarbeitung und eine abschließende Archivierung ins Archiv bringt folgende Vorteile:
Die aufrufende Anwendung braucht keinerlei Kenntnis über Quelldateien und Formate. Es besteht keine Gefährdung durch bösartige Daten oder Dokumente. Darüber hinaus ist eine Minimierung des Netzwerktransfers die Folge. Durch seine Struktur ermöglicht es der jadice server, zu jeder Zeit das Konvertierungsergebnis flexibel zu steuern.
Beispiel 6.12. Konvertierung beliebiger Datenströme nach PDF
Job job = createServerJob(); job.setType("convert to pdf"); // Nodes erstellen: // 1. Dateneingabe-Node StreamInputNode siNode = new StreamInputNode(); // 2. Node zur dynamischen Konvertierung DynamicPipelineNode dpNode = new DynamicPipelineNode(); dpNode.setRulesetName("default"); // 3. Zusammenfassen von Eingabedaten (1...n zu 1) PDFMergeNode pmNode = new PDFMergeNode(); // 4. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow durch Verkettung der Nodes erstellen job.attach(siNode.appendSuccessor(dpNode).appendSuccessor(pmNode).appendSuccessor(soNode)); // Job ausführen und Dokument-Datenstrom senden job.submit(); siNode.addStream(…); siNode.complete(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten InputStream is = stream.getInputStream(); }
Ergänzen Sie diesem Job um eine eigene Implementierung eines JobListener
s, so können
Sie über die Methode subPipelineCreated
erfahren, welche
weiteren Node
s dynamisch von jadice server erzeugt wurden.
Das verwendete Regelwerk finden Sie im Verzeichnis
/server-config/dynamic-pipeline-rules
. Die XML-basierten Regelwerke können auf
einen Bedürfnisse angepasst werden. Dazu hilft das ebenfalls in diesem Ordner zu findenden XML
Schema.
Beispiel 6.13. Ansteuerung von LibreOffice
Job job = createServerJob(); job.setType("libreoffice to pdf"); // Nodes erstellen: // 1. Dateneingabe Node StreamInputNode siNode = new StreamInputNode(); // 2. LibreOffice-Konvertierung LibreOfficeConversionNode loNode = new LibreOfficeConversionNode(); // 3. Zusammenfassen von Eingabedaten (1...n zu 1) PDFMergeNode pmNode = new PDFMergeNode(); // 4. Ausgabe Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen job.attach(siNode.appendSuccessor(loNode).appendSuccessor(pmNode).appendSuccessor(soNode)); // Job ausführen und Dokument senden job.submit(); siNode.addStream(…); siNode.complete(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten InputStream is = stream.getInputStream(); }
Anmerkung
Der Klassenpfad muss zur Ansteuerung von LibreOffice wie in Abschnitt „Konfiguration LibreOffice“ beschrieben gesetzt werden.
Anmerkung
Dokumente im Word2007-Format (Dateiendung docx
) müssen
vor der Konvertierung mit LibreOffice durch den StreamAnalysisNode
vorverarbeitet werden (vgl. Abschnitt „Identifikation unbekannter Eingabedaten“).
Bei der E-Mailkonvertierung wird die E-Mail direkt vom Mailserver geholt. Hierzu müssen die entsprechenden Zugriffsdaten angegeben werden.
Der Vorgang ist ähnlich der dynamischen Konvertierung (siehe Abschnitt „Konvertierung unbekannter Eingabedaten in ein einheitliches Format (PDF)“). Die E-Mail wird analysiert, eventuelle Anhänge wie z. B. Office-Dokumente, Bilder usw. werden alle konvertiert, in einer Übersicht zusammengefasst und an den E-Mail-Text angehängt.
Archivdateien werden dabei ausgepackt und deren Inhalt in den Konvertierungsvorgang eingebunden.
Beispiel 6.14. E-Mail-Konvertierung mit direktem Abruf vom Server
Job job = createServerJob(); job.setType("mail to pdf"); // Nodes erstellen: // 1. Eingabe-Node, hier wird serverseitig ein Mailserver angesprochen. JavamailInputNode jiNode = new JavamailInputNode(); // Mailserver-spezifische Daten setzen jiNode.setStoreProtocol("<Protokol>"); // POP3 oder IMAP jiNode.setHostName("<Server>"); jiNode.setUsername("<Benutzer>"); jiNode.setPassword("<Passwort>"); jiNode.setFolderName("<E-Mail Ordner>"); jiNode.setImapMessageUID(…); // 2. Analyse-Node mit Skript zur E-Mail-Konvertierung ScriptNode scNode = new ScriptNode(); scNode.setScript(new URI("resource:email-conversion/EmailConversion.groovy")); // 3. Zusammenfassen von Eingabedaten (1...n zu 1) PDFMergeNode pmNode = new PDFMergeNode(); // 4. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen und Job starten job.attach(jiNode.appendSuccessor(scNode).appendSuccessor(pmNode).appendSuccessor(soNode)); job.submit(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten InputStream is = stream.getInputStream(); }
Sollen E-Mails nicht mit dem JavamailInputNode
über einen IMAP- oder POP3-Account
abgerufen, sondern z. B. als eml
-Datei eingelesen werden,
muss zusätzlich der
MessageRFC822Node
zwischengeschaltet werden, der die Abtrennung von E-Mail-Header
und -Body vornimmt:
Beispiel 6.15. E-Mail-Konvertierung einer eml
-Datei
Job job = createServerJob(); // Nodes erstellen: // 1. Eingabe-Node, hier als Datei vom Client. StreamInputNode siNode = new StreamInputNode(); // 2. Trennung von E-Mail-Header und -Body MessageRFC822Node msgNode = new MessageRFC822Node(); // 3. Analyse-Node mit Skript zur E-Mail-Konvertierung ScriptNode scNode = new ScriptNode(); scNode.setScript(new URI("resource:email-conversion/EmailConversion.groovy")); // Rest s.o.
E-Mails, die im MS Outlook-Format (msg
-Dateien) vorliegen, können durch den
TNEFNode
ohne Aufruf von MS Outlook in ein von jadice server unterstütztes Format
und über den diesen Weg konvertiert werden:
Beispiel 6.16. E-Mail-Konvertierung einer msg
-Datei
Job job = createServerJob(); // Nodes erstellen: // 1. Eingabe-Node, hier als Datei vom Client. StreamInputNode siNode = new StreamInputNode(); // 2. Vor-Verarbeitung von MSG-Dateien TNEFNode tnefNode = new TNEFNode(); tnefNode.setInputFormat(InputFormat.MSG); // 3. Analyse-Node mit Skript zur E-Mail-Konvertierung ScriptNode scNode = new ScriptNode(); scNode.setScript(new URI("resource:email-conversion/EmailConversion.groovy")); // Rest s.o.
Anmerkung
Bitte beachten Sie, dass der Mailbody in msg
-Dateien üblicherweise als Rich Text
(rtf
) vorliegt und daher in der Standardkonfiguration über LibreOffice nach PDF
konvertiert wird.
In der oben gezeigten Konfiguration wird standardmäßig zu jedem Dateianhang eine
Trennseite generiert, die die Metadaten des jeweiligen Anhangs enthält. Sind keine
Trennseiten gewünscht, können diese mit der folgenden Konfiguration des ScriptNode
s
für alle Dateianhänge deaktiviert werden:
scNode.getParameters().put("showAttachmentSeparators", false);
Eine weitere Konfigurationsmöglichkeit betrifft formatierte E-Mails. Wurden diese
sowohl im HTML- als auch Plaintext-Format gesendet, wird standardmäßig der HTML-Teil
konvertiert. Soll stattdessen der Plaintext-Teil konvertiert werden, ist folgende
Konfiguration des ScriptNode
s vorzunehmen:
scNode.getParameters().put("preferPlainTextBody", true);
Davon unabhängig ist es möglich, denjenigen Teil, der normalerweise nicht konvertiert wird, als zusätzliches Attachment an die E-Mail anzuhängen. Somit kann die konvertierte E-Mail sowohl im HTML- als auch im Plaintext-Format dargestellt werden. Die nötige Konfiguration ist:
scNode.getParameters().put("showAllAlternativeBody", true);
Um zu verhindern, dass jadice server Bilder und andere in E-Mails referenzierte Dateien von unbekannten Quellen nachlädt, kann dies über folgende Einstellung verhindert werden:
scNode.getParameters().put("allowExternalHTTPResolution", false);
Die Behandlung von Attachments, deren Format nicht erkannt wurde oder nicht für
die Konvertierung durch jadice server vorgesehen ist, kann über den Parameter
unhandledAttachmentAction
gesteuert werden:
scNode.getParameters().put("unhandledAttachmentAction", "failure");
Folgende Werte werden hierbei akzeptiert:
Wert | Bedeutung |
---|---|
|
Es wird eine Warnung in das Log geschrieben. |
|
Es wird ein Error in das Log geschrieben (Standardwert). |
|
Der zugehörige Job bricht mit einem Fehler ab. |
Zur Kenntlichmachung von Bilddateien, die in einer E-Mail referenziert sind, aber nicht konvertiert wurden, werden anstelle dieser folgende Platzhalter eingefügt:
Wert | Bedeutung |
---|---|
![]() |
Das Bild wurde aufgrund der Einstellung
|
![]() |
Die Bilddatei konnte nicht geladen werden. |
Durch den ExternalProcessCallNode
ist die Ansteuerung externer Programme sehr einfach
möglich. Dabei kümmert sich der jadice server automatisch darum, dass ein- und
ausgehende Datenströme automatisch zu temporären Dateien umgewandelt und diese nach
der Verarbeitung durch das externe Programm gelöscht werden.
Einzige Voraussetzung ist, dass das Programm auf dem Server über Kommandozeile angesprochen werden kann.
Beispiel 6.17. Verwendung des ExternalProcessCallNode
Job job = createServerJob(); job.setType("external my converter"); // Nodes erstellen: // 1. Dateneingabe-Node StreamInputNode siNode = new StreamInputNode(); // 2. Externer Prozess ExternalProcessCallNode epcNode = new ExternalProcessCallNode(); // Konfiguration: // Programmname (Backslashes müssen escaped werden!) epcNode.setProgramName("C:\\Programme\\MyConverter\\MyConverter.exe"); // Kommandozeilen-Parameter // ${infile} und ${outfile:pdf} ersetzt jadice server epcNode.setArguments("-s -a ${infile} /convert=${outfile:pdf}"); // 3. Ausgabe-Node StreamOutputNode soNode = new StreamOutputNode(); // Workflow erstellen job.attach(siNode.appendSuccessor(epcNode).appendSuccessor(soNode)); // Job abschicken und Eingabedatenstrom übergeben job.submit(); StreamDescriptor sd = new StreamDescriptor(); // jadice server speichert die Datei bei Uebergabe an das externe Programm // unter diesem Namen ab. sd.setFileName("myfile.dat"); siNode.addStream(new BundledStream(…, sd)); siNode.complete(); // Auf Antwort vom Server warten for (Stream stream : soNode.getStreamBundle()) { // Lesen der Daten InputStream is = stream.getInputStream(); }