/* OpenSyncro - A web-based enterprise application integration tool
* Copyright (C) 2008 Smilehouse Oy, support@opensyncro.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package smilehouse.opensyncro.pipes;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import smilehouse.opensyncro.pipes.component.AbortTransferException;
import smilehouse.opensyncro.pipes.component.ConverterIF;
import smilehouse.opensyncro.pipes.component.DestinationIF;
import smilehouse.opensyncro.pipes.component.FailTransferException;
import smilehouse.opensyncro.pipes.component.PipeComponentData;
import smilehouse.opensyncro.pipes.component.PipeComponentIF;
import smilehouse.opensyncro.pipes.component.SourceIF;
import smilehouse.opensyncro.pipes.log.LogEntry;
import smilehouse.opensyncro.pipes.log.LogMessageEntry;
import smilehouse.opensyncro.pipes.log.MessageLogger;
import smilehouse.opensyncro.pipes.metadata.TransferInfo;
import smilehouse.opensyncro.servlets.PipeComponentCreationException;
import smilehouse.opensyncro.system.Environment;
import smilehouse.opensyncro.system.Persister;
import smilehouse.util.Utils;
public class Pipe implements Serializable,Cloneable {
private static final String MAIL_MESSAGE_NO_ENTRIES = "There are no log entries that match the notification level setting for this pipe";
private static final String MAIL_SENDER = "OpenSyncro";
private static final String[] MAIL_ADDRESS_DELIMITERS = {",", ";"};
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm";
private static DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
/** identifier field */
private Long id;
/** nullable persistent field */
private String name;
private String startPassword;
// RPC Start is not currently supported
//private boolean rpcStartEnabled;
private boolean httpStartEnabled;
private boolean abortMailEnabled;
/** persistent field */
private String sourceID;
/** non-persistent properties */
private SourceIF source;
private PipeComponentData sourceData;
/** persistent field */
private String destinationID;
/** non-persistent properties */
private DestinationIF destination;
private PipeComponentData destinationData;
private List converterList;
/** persistent field */
private Set log;
private int loggingVerbosityLevel;
private int transferLogNotificationLevel;
private String mailHost;
private String recipientAddress;
private Date startTime;
private Date endTime;
private String lastStatus;
private Long duration;
private String user;
private String database;
/** default constructor */
public Pipe() {
this.name = "Unnamed Pipe";
this.startPassword = "";
//this.rpcStartEnabled = false;
this.httpStartEnabled = false;
this.abortMailEnabled = true;
loggingVerbosityLevel = MessageLogger.LOG_DYNAMIC;
transferLogNotificationLevel = 1;
mailHost = "";
recipientAddress = "";
this.converterList = new LinkedList();
this.source = null;
this.sourceData = null;
this.destination = null;
this.destinationData = null;
this.startTime=null;
this.endTime=null;
this.duration=new Long("0");
this.user="";
this.lastStatus="";
}
public PipeComponentData getSourceData() {
return this.sourceData;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getSourceID() {
return this.sourceID;
}
public void setSourceID(String sourceID) {
this.sourceID = sourceID;
this.source = null;
}
public String getDestinationID() {
return this.destinationID;
}
public void setDestinationID(String destinationID) {
this.destinationID = destinationID;
this.destination = null;
}
public String getStartPassword() {
return this.startPassword;
}
public void setStartPassword(String startPassword) {
this.startPassword = startPassword;
}
/*
* public boolean isRpcStartEnabled() { return this.rpcStartEnabled; }
*
* public void setRpcStartEnabled(boolean rpcStartEnabled) { this.rpcStartEnabled =
* rpcStartEnabled; }
*/
public boolean isHttpStartEnabled() {
return this.httpStartEnabled;
}
public void setHttpStartEnabled(boolean httpStartEnabled) {
this.httpStartEnabled = httpStartEnabled;
}
public boolean isAbortMailEnabled() {
return this.abortMailEnabled;
}
public void setAbortMailEnabled(boolean emptyMailEnabled) {
this.abortMailEnabled = emptyMailEnabled;
}
public SourceIF getSource() {
try {
if((this.source == null) && (this.sourceID != null)) {
this.source = (SourceIF) Persister.getInstance(getSourceID());
}
} catch(PipeComponentCreationException e) {
Environment.getInstance().log("Error loading Source component for Pipe \"" +
getName() + "\"", e);
this.source = null;
}
return this.source;
}
public SourceIF getCurrentSource() {
return this.source;
}
public void setSourceData(PipeComponentData sourceData) {
this.sourceData = sourceData;
}
public void setSource(SourceIF source) {
this.source = source;
}
public DestinationIF getDestination() {
try {
if((this.destination == null) && (this.destinationID != null)) {
this.destination = (DestinationIF) Persister.getInstance(getDestinationID());
}
} catch(PipeComponentCreationException e) {
Environment.getInstance().log("Error loading Destination component for Pipe \""
+ getName() + "\"", e);
this.destination = null;
}
return this.destination;
}
public DestinationIF getCurrentDestination() {
return this.destination;
}
public void setDestinationData(PipeComponentData destinationData) {
this.destinationData = destinationData;
}
public void setDestination(DestinationIF destination) {
this.destination = destination;
}
public List getConverterList() {
return this.converterList;
}
public void setConverterList(List converters) {
Iterator iter = converters.iterator();
while(iter.hasNext()) {
ConverterListItem convListItem = (ConverterListItem) iter.next();
convListItem.setParent(this);
}
this.converterList = converters;
}
public ConverterListItem addConverter(String converterID, PipeComponentData pcdata) {
ConverterListItem convListItem = new ConverterListItem(converterID, pcdata);
convListItem.setParent(this);
this.converterList.add(convListItem);
return convListItem;
}
public ConverterListItem addConverter(ConverterIF converter, PipeComponentData pcdata) {
ConverterListItem convListItem = new ConverterListItem(converter, pcdata);
convListItem.setParent(this);
this.converterList.add(convListItem);
return convListItem;
}
public PipeComponentData getDestinationData() {
return this.destinationData;
}
public Set getLog() {
return this.log;
}
public void setLog(Set log) {
this.log = log;
}
public void setLoggingVerbosityLevel(int level) {
this.loggingVerbosityLevel = level;
}
public int getLoggingVerbosityLevel() {
return this.loggingVerbosityLevel;
}
public void setTransferLogNotificationLevel(int level) {
this.transferLogNotificationLevel = level;
}
public int getTransferLogNotificationLevel() {
return this.transferLogNotificationLevel;
}
public String getMailHost() {
return this.mailHost;
}
public void setMailHost(String host) {
this.mailHost = host;
}
public String getRecipientAddress() {
return this.recipientAddress;
}
public void setRecipientAddress(String address) {
this.recipientAddress = address;
}
public String toString() {
return new ToStringBuilder(this).append("id", getId()).toString();
}
public boolean equals(Object other) {
if(!(other instanceof Pipe))
return false;
Pipe castOther = (Pipe) other;
return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(getId()).toHashCode();
}
public synchronized void transfer(TransferInfo info){
transfer(info,null);
}
/** Execute the Pipe */
public synchronized void transfer(TransferInfo info, Date requestTime) {
LogEntry logEntry = new LogEntry(this, info);
database = info.getDatabaseName();
//Set start time so that duration can be calculated when pipe finishes
long start=System.currentTimeMillis();
Date startTime=new Date();
setExecutionStartInfo(info.getUserName(), startTime);
logEntry.setUserName(info.getUserName());
// TODO: Replace these with proper Status classes
final int PIPE_EXECUTION_OK = 1;
final int PIPE_EXECUTION_FAILED = 0;
//final int PIPE_EXECUTION_ABORTED = -1;
// currentTask information will be used for error/exception logging
String currentTask = "Pipe transfer initialization";
// Initialize Pipe execution status to STATUS_OK
int statusCode = LogEntry.STATUS_OK;
// This status flag indicates whether calling closeComponentSessions is required
// at the end of Pipe execution
boolean componentSessionsOpen = false;
if(requestTime != null) {
logEntry.logMessage("Received Pipe execution request", this, MessageLogger.DEBUG, requestTime);
}
// Get Source component and initialize its parameters
{
SourceIF source = getSource();
if(source == null) {
if(getSourceID() == null) {
logEntry.logMessage(
"Pipe does not have a Source component, aborting",
this,
MessageLogger.ERROR);
} else {
logEntry.logMessage(
"Pipe Source component cannot be loaded, aborting",
this,
MessageLogger.ERROR);
}
setExecutionEndInfo(new Date(), System.currentTimeMillis()-start, LogEntry.STATUS_SOURCE_ERROR);
addLogEntry(logEntry, LogEntry.STATUS_SOURCE_ERROR);
return;
}
source.setData(getSourceData());
}
// Get Converter components and initialize their parameters
{
int converterIndex = 0;
for(Iterator it = converterList.iterator(); it.hasNext();) {
ConverterListItem converterItem = (ConverterListItem) it.next();
converterIndex++;
try {
ConverterIF converter = converterItem.getConverter();
converter.setData(converterItem.getConverterData());
} catch(PipeComponentCreationException e) {
logEntry.logMessage("Pipe Converter component #" + converterIndex
+ " cannot be loaded, aborting", this, MessageLogger.ERROR);
Environment.getInstance().log("Error loading Converter component #" + converterIndex +
" for Pipe \"" + getName() + "\"", e);
setExecutionEndInfo(new Date(), System.currentTimeMillis()-start, LogEntry.STATUS_CONVERSION_ERROR);
addLogEntry(logEntry, LogEntry.STATUS_CONVERSION_ERROR);
return;
}
}
}
// Get Destination component and initialize its parameters
{
DestinationIF destination = getDestination();
if(destination == null) {
if(getDestinationID() == null) {
logEntry.logMessage(
"Pipe does not have a Destination component, aborting",
this,
MessageLogger.ERROR);
} else {
logEntry.logMessage(
"Pipe Destination component cannot be loaded, aborting",
this,
MessageLogger.ERROR);
}
setExecutionEndInfo(new Date(), System.currentTimeMillis()-start, LogEntry.STATUS_DESTINATION_ERROR);
addLogEntry(logEntry, LogEntry.STATUS_DESTINATION_ERROR);
return;
}
destination.setData(getDestinationData());
}
logEntry.logMessage("Starting Pipe execution", this, MessageLogger.DEBUG);
try {
/** Open Source, Converter and Destination components' sessions */
currentTask = "Pipe Component initialization";
statusCode = openComponentSessions(
source,
destination,
getConverterList(),
info,
logEntry);
if(statusCode != LogEntry.STATUS_OK) {
// Initialization failed, LogEntry's statusCode is used at the end of this method
// for addLogEntry() call
throw new FailTransferException();
}
componentSessionsOpen = true;
/** Pipe iteration loop starts here */
int i = 0;
while(true) {
try {
logEntry.logMessage(
"Requesting data block #" + ++i + " from Source component",
this,
MessageLogger.DEBUG);
currentTask = "Source component " + this.source.getName();
statusCode = LogEntry.STATUS_SOURCE_ERROR;
String sourceResults[] = source.give(info, logEntry);
// Test if Source component returned no (more) data
if(sourceResults == null || sourceResults[0] == null) {
logEntry.logMessage(
"Source component returned no data",
this,
MessageLogger.DEBUG);
/*
* Exit iteration loop when Source component returns null instead of data
* String
*/
break;
}
if(sourceResults.length > 1) {
logEntry.logMessage("Source component returned " + sourceResults.length
+ " data blocks", this, MessageLogger.DEBUG);
}
// Iterate over Strings returned by Source component
for(int j = 0; j < sourceResults.length; j++) {
// Proceed with conversions
statusCode = LogEntry.STATUS_CONVERSION_ERROR;
currentTask = "Preparing to call first Converter component";
getConverterList();
Iterator it = this.converterList.iterator();
// Check if we have any Converters on the list
if(it.hasNext() == true) {
/*
* Converters (such as SplitConverter) may return multiple data parts
* based on a single input String
*/
String[] processedData = new String[1];
/*
* For first Converter on the list we call the regular 'convert' method,
* which takes String as input and returns String array
*/
ConverterListItem converterItem = (ConverterListItem) it.next();
ConverterIF converter = converterItem.getConverter();
currentTask = "Converter component " + converter.getName();
processedData = converter.convert(sourceResults[j], info, logEntry);
/*
* Test if any of the first Converter's result Strings is null, if yes ->
* report an error and abort Pipe execution
*/
if(arrayContainsNull(processedData)) {
logEntry.logMessage(
"Error: Converter " + converter.getName()
+ " returned null result, aborting",
this,
MessageLogger.ERROR);
throw new FailTransferException();
}
/*
* Repeat over the rest of the Converters list and call 'convertAll'
* method for processing every String in a String array
*/
while(it.hasNext()) {
converterItem = (ConverterListItem) it.next();
converter = converterItem.getConverter();
currentTask = "Converter component " + converter.getName();
processedData = converter.convertAll(processedData, info, logEntry);
/*
* Test if any of the Converter's result Strings is null, if yes ->
* report an error and abort Pipe execution
*/
if(arrayContainsNull(processedData)) {
logEntry.logMessage(
"Error: Converter " + converter.getName()
+ " returned null result, aborting",
this,
MessageLogger.ERROR);
throw new FailTransferException();
}
}
statusCode = LogEntry.STATUS_DESTINATION_ERROR;
currentTask = "Destination component " + this.destination.getName();
destination.takeAll(processedData, info, logEntry);
} else {
/*
* There were no Converters in this Pipe, so we pass the data from
* Source component directly to Destination component
*/
statusCode = LogEntry.STATUS_DESTINATION_ERROR;
currentTask = "Destination component " + this.destination.getName();
destination.take(sourceResults[j], info, logEntry);
}
statusCode = LogEntry.STATUS_OK;
}
/*
* Notify the Source component that last data block(s) were processed
* successfully
*/
statusCode = LogEntry.STATUS_SOURCE_ERROR;
currentTask = "while notifying Source component " + this.source.getName() + " of "
+ " successful processing of last data block through the Pipe";
source.lastBlockStatus(PIPE_EXECUTION_OK);
currentTask = "End of Pipe iteration loop";
statusCode = LogEntry.STATUS_OK;
} catch(AbortTransferException ate) {
/*
* Pass on AbortTransferException to the outside-loop catcher, since we want to
* abort the entire transfer.
*/
throw ate;
} catch(Throwable t) {
if(t instanceof Exception || t instanceof VirtualMachineError) {
logPipeExecutionError(t, currentTask, logEntry);
/**
* Notify the Source component of last data block failing to process, unless
* an unknown exception has occured in the Source component itself
*/
if(statusCode != LogEntry.STATUS_SOURCE_ERROR) {
currentTask = "Notifying Source component " + this.source.getName()
+ " of last data block failing to process through the Pipe";
// TODO: test support for FailTransferException and AbortTransferException during lastBlockStatus!
source.lastBlockStatus(PIPE_EXECUTION_FAILED);
}
logEntry.logMessage(
"Data block processing failed, aborting Pipe execution",
this,
MessageLogger.ERROR);
if(t instanceof Exception) {
// Pass Exception for the outer Exception handler
throw (Exception) t;
} else {
// Convert VirtualMachineErrors to FailTransferExceptions as we have
// already written the detailed error message to Transfer log
throw new FailTransferException();
}
}
}
} // Iteration loop ends here
logEntry.logMessage("Data block processing complete", this, MessageLogger.DEBUG);
statusCode = LogEntry.STATUS_OK;
} catch(AbortTransferException ate) {
statusCode = LogEntry.STATUS_ABORTED;
} catch(Exception e) {
// -----------------------------------------------------------
// If some component did not deliberately fail the transfer by
// throwing AbortTransferException, log the exception
// -----------------------------------------------------------
if(!(e instanceof FailTransferException)) {
Environment.getInstance().log(Utils.getThrowableName(e) + " during transfer, " + currentTask, e);
logEntry.logMessage(
Utils.getThrowableName(e) + " during transfer, " + currentTask + ": " + e.getMessage()
+ ". See OpenSyncro log file for details.",
this,
MessageLogger.ERROR);
}
} finally {
// Close iteration session (if open).
// Discard return code since an error has already happened.
// Preserve the earlier statusCode for addLogEntry() at the end of this method.
if(componentSessionsOpen) {
currentTask = "Closing Pipe component sessions";
// Preserve the Pipe's original statusCode unless
// something goes wrong during closing the component iteration sessions.
int closeSessionStatusCode = closeComponentSessions(info, logEntry);
if(closeSessionStatusCode != LogEntry.STATUS_OK) {
statusCode = closeSessionStatusCode;
}
componentSessionsOpen = false;
}
switch(statusCode) {
case LogEntry.STATUS_OK:
break;
case LogEntry.STATUS_ABORTED:
logEntry.logMessage("TRANSFER ABORTED", this, MessageLogger.WARNING);
break;
// The rest of the statusCodes are errors
default:
logEntry.logMessage("TRANSFER FAILED!", this, MessageLogger.ERROR);
break;
}
logEntry.logMessage("Pipe execution finished", this, MessageLogger.DEBUG);
setExecutionEndInfo( new Date(), System.currentTimeMillis()-start, statusCode);
addLogEntry(logEntry, statusCode);
// Add "--" log message entry when there are no other entries.
// Needed to display log entries that don't have log message entries.
Persister persister = new Persister(database);
if (persister.getLogMessageEntries(logEntry.getId(), MessageLogger.LOG_DYNAMIC).size() == 0){
logEntry.setIndex(1);
logEntry.logMessage("--", MessageLogger.ERROR);
}
}
}
private int openComponentSessions(SourceIF source,
DestinationIF destination,
List converterList,
TransferInfo info,
LogEntry logEntry) {
int statusCode = LogEntry.STATUS_OK;
/** Open iteration session at Source component */
if(openComponentSession(getSource(), info, logEntry) == PipeComponentIF.ITERATION_OPEN_STATUS_ERROR) {
statusCode = LogEntry.STATUS_SOURCE_ERROR;
return statusCode;
}
/** Open iteration session at Converter components */
// Initialize converters
int converterIndex = 0;
for(Iterator it = converterList.iterator(); it.hasNext();) {
ConverterListItem converterItem = (ConverterListItem) it.next();
ConverterIF converter = converterItem.getConverter();
converterIndex++;
// Set PipeComponentData to Converter components before opening iteration session
converter.setData(converterItem.getConverterData());
// Open Converter's iteration session
if(openComponentSession(converter, converterIndex, info, logEntry) == PipeComponentIF.ITERATION_OPEN_STATUS_ERROR) {
// Close preceding Converter and Source component sessions
// Warning: make sure (converterIndex - 1) here is at least 0, because
// -1 means that all Converter sessions are to be closed
closeConverterComponentSessions(converterIndex - 1, info, logEntry);
closeComponentSession(source, info, logEntry);
statusCode = LogEntry.STATUS_CONVERSION_ERROR;
return statusCode;
}
}
/** Open iteration session at Destination component */
if(openComponentSession(getDestination(), info, logEntry) == PipeComponentIF.ITERATION_OPEN_STATUS_ERROR) {
// Close Converter and Source component sessions
closeConverterComponentSessions(info, logEntry);
closeComponentSession(source, info, logEntry);
statusCode = LogEntry.STATUS_DESTINATION_ERROR;
return statusCode;
}
return statusCode;
}
private int openComponentSession(DestinationIF destination, TransferInfo info, LogEntry logEntry) {
// Open Destination component session
try {
int retCode;
// Initialize Destination component for iteration mode
logEntry.logMessage(
"Opening iteration session at Destination component",
this,
MessageLogger.DEBUG);
retCode = destination.open(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_OPEN_STATUS_OK) {
logEntry.logMessage("Error opening session at Destination component "
+ this.destination.getName(), this, MessageLogger.ERROR);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
} catch(Exception e) {
logSessionOpenError(e, "destination", destination, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionOpenError(e, "destination", destination, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_OPEN_STATUS_OK;
}
private int openComponentSession(ConverterIF converter,
int converterIndex,
TransferInfo info,
LogEntry logEntry) {
// Open Converter component session
try {
int retCode;
logEntry.logMessage("Opening iteration session at Converter component #"
+ converterIndex + " (" + converter.getName() + ")", this, MessageLogger.DEBUG);
retCode = converter.open(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_OPEN_STATUS_OK) {
logEntry.logMessage(
"Error opening iteration session at Converter component #" + converterIndex
+ " (" + converter.getName() + ")",
this,
MessageLogger.ERROR);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
} catch(Exception e) {
logSessionOpenError(e, "converter", converter, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionOpenError(e, "converter", converter, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_OPEN_STATUS_OK;
}
private int openComponentSession(SourceIF source, TransferInfo info, LogEntry logEntry) {
// Open Source component session
try {
int retCode;
// Initialize Source component for iteration mode
logEntry.logMessage(
"Opening iteration session at Source component",
this,
MessageLogger.DEBUG);
retCode = source.open(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_OPEN_STATUS_OK) {
logEntry.logMessage(
"Error opening session at Source component " + source.getName(),
this,
MessageLogger.ERROR);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
} catch(Exception e) {
logSessionOpenError(e, "source", source, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionOpenError(e, "source", source, logEntry);
return PipeComponentIF.ITERATION_OPEN_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_OPEN_STATUS_OK;
}
public int closeComponentSessions(TransferInfo info, LogEntry logEntry) {
int statusCode = LogEntry.STATUS_OK;
/** Close iteration session at Source component */
if(closeComponentSession(getSource(), info, logEntry) == PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR) {
statusCode = LogEntry.STATUS_SOURCE_ERROR;
}
/** Close iteration session at Converter component(s) */
if(closeConverterComponentSessions(info, logEntry) == PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR) {
statusCode = LogEntry.STATUS_CONVERSION_ERROR;
}
/** Close iteration session at Destination component */
if(closeComponentSession(getDestination(), info, logEntry) == PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR) {
statusCode = LogEntry.STATUS_DESTINATION_ERROR;
}
return statusCode;
}
private int closeConverterComponentSessions(TransferInfo info, LogEntry logEntry) {
return closeConverterComponentSessions(-1, info, logEntry);
}
/**
* @param stopIndex Number of Converters to process. -1 closes all Converters' sessions.
* @return
* @see smilehouse.opensyncro.pipes.log.LogEntry#STATUS_*
*/
private int closeConverterComponentSessions(int stopIndex, TransferInfo info, LogEntry logEntry) {
int converterIndex = 0;
for(Iterator it = getConverterList().iterator(); it.hasNext();) {
// If stopIndex is specified, break out of the loop when stopIndex
// is reached
if((stopIndex != -1) && (converterIndex >= stopIndex))
break;
ConverterListItem converterItem = (ConverterListItem) it.next();
converterIndex++;
if(closeComponentSession(converterItem.getConverter(), converterIndex, info, logEntry) == PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR) {
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
}
return PipeComponentIF.ITERATION_CLOSE_STATUS_OK;
}
private int closeComponentSession(DestinationIF destination,
TransferInfo info,
LogEntry logEntry) {
int retCode;
try {
logEntry.logMessage(
"Closing iteration session at Destination component",
this,
MessageLogger.DEBUG);
retCode = destination.close(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_CLOSE_STATUS_OK) {
logEntry.logMessage(
"Error closing iteration session at Destination component",
this,
MessageLogger.ERROR);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
} catch(Exception e) {
logSessionCloseError(e, "destination", destination, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionCloseError(e, "destination", destination, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_CLOSE_STATUS_OK;
}
private int closeComponentSession(ConverterIF converter,
int converterIndex,
TransferInfo info,
LogEntry logEntry) {
try {
int retCode;
logEntry.logMessage("Closing iteration session at Converter component #"
+ converterIndex + " (" + converter.getName() + ")", this, MessageLogger.DEBUG);
retCode = converter.close(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_CLOSE_STATUS_OK) {
logEntry.logMessage(
"Error closing iteration session at Converter component #" + converterIndex
+ " (" + converter.getName() + ")",
this,
MessageLogger.ERROR);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
} catch(Exception e) {
logSessionCloseError(e, "converter", converter, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionCloseError(e, "converter", converter, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_CLOSE_STATUS_OK;
}
private int closeComponentSession(SourceIF source, TransferInfo info, LogEntry logEntry) {
try {
int retCode;
logEntry.logMessage(
"Closing iteration session at Source component",
this,
MessageLogger.DEBUG);
retCode = source.close(info, logEntry);
if(retCode != PipeComponentIF.ITERATION_CLOSE_STATUS_OK) {
logEntry.logMessage(
"Error closing iteration session at Source component",
this,
MessageLogger.ERROR);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
} catch(Exception e) {
logSessionCloseError(e, "source", source, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
} catch(VirtualMachineError e) {
logSessionCloseError(e, "source", source, logEntry);
return PipeComponentIF.ITERATION_CLOSE_STATUS_ERROR;
}
return PipeComponentIF.ITERATION_CLOSE_STATUS_OK;
}
private void logSessionError(Throwable t,
String componentType,
PipeComponentIF component,
String taskDescription,
LogEntry logEntry) {
if(t instanceof FailTransferException || t instanceof AbortTransferException) {
logEntry.logMessage(
"Error while " + taskDescription + " at " + componentType + " component " + component.getName(),
this,
MessageLogger.ERROR);
} else {
// Log unknown Throwable
Environment.getInstance().log(
Utils.getThrowableName(t) + " while " + taskDescription + " at " + componentType + " component "
+ component.getName(), t);
logEntry.logMessage(
Utils.getThrowableName(t) + " while " + taskDescription + " at " + componentType + " component "
+ component.getName() + ", see OpenSyncro application log file for details",
this,
MessageLogger.ERROR);
}
}
private void logSessionOpenError(Throwable t,
String componentType,
PipeComponentIF component,
LogEntry logEntry) {
logSessionError(t, componentType, component, "opening session", logEntry);
}
private void logSessionCloseError(Throwable t,
String componentType,
PipeComponentIF component,
LogEntry logEntry) {
logSessionError(t, componentType, component, "closing session", logEntry);
}
private void logPipeExecutionError(Throwable t, String currentTask, LogEntry logEntry) {
if(!(t instanceof FailTransferException)) {
Environment.getInstance().log("Exception while executing Pipe, " + currentTask, t);
logEntry.logMessage(
Utils.getThrowableName(t) + " while executing Pipe, " + currentTask + ": " + t.getMessage()
+ ", see OpenSyncro log file for details",
this,
MessageLogger.ERROR);
} else {
// FailTransferException
logEntry.logMessage(
"Error while executing Pipe, " + currentTask + ".",
this,
MessageLogger.ERROR);
}
}
private void addLogEntry(LogEntry logEntry, int statusCode) {
if(this.log == null)
this.log = new HashSet();
logEntry.setStatusCode(statusCode);
logEntry.setTime(new Date());
// The LogEntry is not explicitly added to the Pipe's log Set, since it causes
// Hibernate to query all LogEntries from the database and thus consume
// ever-increasing amount of server resources.
//this.log.add(logEntry);
String status = getStatusString(statusCode);
Persister persister = new Persister(database);
try {
persister.update(logEntry);
List messages;
boolean mailAddressNotSet =((getMailHost() == null || getRecipientAddress() == null) ||
(getMailHost().length() == 0 || getRecipientAddress().length() == 0));
// Notification via email only if host and email address are present
if(!mailAddressNotSet&&!(getTransferLogNotificationLevel()==MessageLogger.MAIL_NONE)) {
String date = dateFormat.format(new Date());
String subject = this.getName() + " " + status + " " + date + " (" + database + ")";
String message = "";
// Get number of log messages at or below transferLogNotificationLevel.
int entries=persister.getLogMessageEntries(logEntry.getId(), getTransferLogNotificationLevel()).size();
//Generate mail message
if(entries > 0) {
messages = persister.getLogMessageEntries(
logEntry.getId(),
getLoggingVerbosityLevel());
for(Iterator m = messages.iterator(); m.hasNext();) {
LogMessageEntry messageEntry = (LogMessageEntry) m.next();
message += (messageEntry.getMessage()) + "\n";
}
} else {
message += MAIL_MESSAGE_NO_ENTRIES;
}
// Send notification email except when the message is
// MAIL_MESSAGE_NO_ENTRIES or the pipe has aborted and
// isAbortMailEnabled()==true
if (!message.equals(MAIL_MESSAGE_NO_ENTRIES)
|| (statusCode==LogEntry.STATUS_ABORTED && isAbortMailEnabled())) {
try {
Properties props = new Properties();
props.put("mail.host", getMailHost());
Session mailConnection = Session.getInstance(props,
null);
Message msg = new MimeMessage(mailConnection);
Address sender = new InternetAddress(MAIL_SENDER + "@"
+ getMailHost(), MAIL_SENDER);
Address[] receivers = receiverAddresses(getRecipientAddress());
// Set mail content and subject
msg.setContent(message, "text/plain");
msg.setFrom(sender);
msg.setRecipients(Message.RecipientType.TO, receivers);
msg.setSubject(subject);
// Send the mail
Transport.send(msg);
} catch (MessagingException e) {
String error = "An error occurred when sending mail report from "
+ MAIL_SENDER
+ "@"
+ getMailHost()
+ " to "
+ getRecipientAddress()
+ ":\n"
+ e.getMessage();
Environment.getInstance().log(error);
logEntry.logMessage(error, this, MessageLogger.ERROR);
persister.update(logEntry);
} catch (RuntimeException ex) {
Environment.getInstance().log(
"A RuntimeException has occurred: "
+ ex.getMessage()
+ ex.getStackTrace().toString());
}
}
}
// Remove unnecessary (debug level) messages from the LogEntry if Transfer log
// verbosity level is set to DYNAMIC and the current LogEntry's status is either
// OK or ABORTED
if(getLoggingVerbosityLevel() == MessageLogger.LOG_DYNAMIC
&& (statusCode == LogEntry.STATUS_OK || statusCode == LogEntry.STATUS_ABORTED) ) {
messages = persister.getLogMessageEntries(logEntry.getId(), MessageLogger.DEBUG);
if(messages.size() > 0) {
for(Iterator m = messages.iterator(); m.hasNext();) {
LogMessageEntry messageEntry = (LogMessageEntry) m.next();
if(messageEntry.getMessageType() == MessageLogger.DEBUG) {
persister.delete(messageEntry);
}
}
}
}
} catch(Exception e) {
Environment.getInstance().log(e.getMessage());
} finally {
persister.close();
}
}
// Utility method for splitting a String of email addresses to an array of JavaMail Addresses
private Address[] receiverAddresses(String recipientAddress) throws AddressException {
Address[] addresses = null;
for(int i = 0; i < MAIL_ADDRESS_DELIMITERS.length; i++) {
if(recipientAddress.indexOf(MAIL_ADDRESS_DELIMITERS[i]) != -1) {
String[] separatedAddresses = Utils.split(
recipientAddress,
MAIL_ADDRESS_DELIMITERS[i]);
addresses = new Address[separatedAddresses.length];
for(int j = 0; j < separatedAddresses.length; j++) {
addresses[j] = new InternetAddress(separatedAddresses[j]);
}
return (addresses);
}
}
addresses = new Address[1];
addresses[0] = new InternetAddress(recipientAddress);
return addresses;
}
// Returns true if a String array contains a null entry
private boolean arrayContainsNull(String[] array) {
for(int k = 0; k < array.length; k++) {
if(array[k] == null) {
return true;
}
}
return false;
}
/**Get end time of last pipe execution
* @return End time of last pipe execution
*/
public Date getEndTime() {
return endTime;
}
/**Set end time of pipe execution
* @param endTime
*/
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
/**Get last pipe execution status
* @return last pipe execution status
*/
public String getLastStatus() {
return lastStatus;
}
/**Set last execution status
* @param lastStatus
*/
public void setLastStatus(String lastStatus) {
this.lastStatus = lastStatus;
}
/**Get start time of last execution
* @return Start time of last execution
*/
public Date getStartTime() {
return startTime;
}
/**Set the start time of execution
* @param startTime
*/
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
/**Get the duration of last pipe execution
* @return Duration of last pipe execution
*/
public Long getDuration() {
return duration;
}
/**Set duration of pipe execution
* @param duration
*/
public void setDuration(Long duration) {
this.duration = duration;
}
/**Get the user the pipe was started by
* @return user Name of the user who started the pipe last
*/
public String getUser() {
return user;
}
/**Set the user name who started the pipe
* @param user Name of the user
*/
public void setUser(String user) {
this.user = user;
}
/**
* Sets some parameters of the pipe to same values as of the given pipe.
* All parameters, except those relating to last execution info are copied
*
* @param pipe The pipe to get the settings from
*/
public Pipe clone() throws ClassCastException{
Pipe pipe=new Pipe();
pipe.setAbortMailEnabled(this.isAbortMailEnabled());
Iterator it=this.getConverterList().iterator();
List newClist=new LinkedList();
while(it.hasNext()){
ConverterListItem cvi=(ConverterListItem)it.next();
ConverterListItem ci=new ConverterListItem();
ci.setConverter(cvi.getConverter());
HashMap cmap=new HashMap(cvi.getConverterData().getAttributes());
ci.setConverterData(new PipeComponentData(cmap));
ci.setConverterID(cvi.getConverterID());
ci.setParent(this);
newClist.add(ci);
}
pipe.setConverterList(newClist);
HashMap dmap=new HashMap();
if(this.getDestinationData()!=null){
dmap.putAll(this.getDestinationData().getAttributes());
}
pipe.setDestinationData(new PipeComponentData(dmap));
pipe.setDestinationID(this.getDestinationID());
pipe.setHttpStartEnabled(this.isHttpStartEnabled());
pipe.setMailHost(this.getMailHost());
pipe.setName("Copy of "+this.getName());
pipe.setRecipientAddress(this.getRecipientAddress());
HashMap smap=new HashMap();
if(this.getSourceData()!=null){
smap.putAll(this.getSourceData().getAttributes());
}
pipe.setSourceData(new PipeComponentData(smap));
pipe.setSourceID(this.getSourceID());
pipe.setStartPassword(this.getStartPassword());
pipe.setTransferLogNotificationLevel(this.getTransferLogNotificationLevel());
pipe.setLoggingVerbosityLevel(this.getLoggingVerbosityLevel());
return pipe;
}
private void setExecutionEndInfo(Date finish, long duration, int statusCode){
this.setDuration(duration);
this.setEndTime(finish);
this.setLastStatus(getStatusString(statusCode));
}
private void setExecutionStartInfo(String userName, Date startTime) {
this.setUser(userName);
this.setStartTime(startTime);
}
private String getStatusString(int statusCode) {
String status = "";
switch(statusCode) {
case LogEntry.STATUS_OK:
status = LogEntry.STAT_OK;
break;
case LogEntry.STATUS_SOURCE_ERROR:
status = LogEntry.STAT_SOURCE_ERROR;
break;
case LogEntry.STATUS_CONVERSION_ERROR:
status = LogEntry.STAT_CONVERSION_ERROR;
break;
case LogEntry.STATUS_DESTINATION_ERROR:
status = LogEntry.STAT_DESTINATION_ERROR;
break;
case LogEntry.STATUS_ABORTED:
status = LogEntry.STAT_ABORTED;
break;
default:
status = LogEntry.STAT_UNKNOWN;
}
return status;
}
}