/*
* RapidMiner
*
* Copyright (C) 2001-2008 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.rapidminer.Process;
import com.rapidminer.ProcessListener;
import com.rapidminer.RapidMiner;
import com.rapidminer.operator.condition.InnerOperatorCondition;
import com.rapidminer.operator.condition.SimpleChainInnerOperatorCondition;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeFile;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.ParameterTypeString;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.Tools;
/**
* Each process must contain exactly one operator of this class and it must
* be the root operator of the process. The only purpose of this operator is
* to provide some parameters that have global relevance.
*
* @author Ingo Mierswa
* @version $Id: ProcessRootOperator.java,v 2.25 2006/04/14 11:42:27 ingomierswa
* Exp $
*/
public final class ProcessRootOperator extends OperatorChain {
/** The property name for "The default random seed (-1: random random seed)." */
public static final String PROPERTY_RAPIDMINER_GENERAL_RANDOMSEED = "rapidminer.general.randomseed";
public static final String PARAMETER_ENCODING = "encoding";
public static final String PARAMETER_LOGVERBOSITY = "logverbosity";
public static final String PARAMETER_LOGFILE = "logfile";
public static final String PARAMETER_RESULTFILE = "resultfile";
public static final String PARAMETER_TEMP_DIR = "temp_dir";
public static final String PARAMETER_DELETE_TEMP_FILES = "delete_temp_files";
public static final String PARAMETER_RANDOM_SEED = "random_seed";
public static final String PARAMETER_NOTIFICATION_EMAIL = "notification_email";
static {
RapidMiner.registerRapidMinerProperty(new ParameterTypeInt(PROPERTY_RAPIDMINER_GENERAL_RANDOMSEED, "The default random seed (-1: random random seed).", 1, Integer.MAX_VALUE, 2001));
}
/** The list of listeners for process events. */
private List<ProcessListener> listenerList = new LinkedList<ProcessListener>();
/** The process which is connected to this process operator. */
private Process process;
/** Creates a new process operator without reference to an process. */
public ProcessRootOperator(OperatorDescription description) {
this(description, null);
}
/** Creates a new process operator which directly references to the given process. */
public ProcessRootOperator(OperatorDescription description, Process process) {
super(description);
addValue(new ValueDouble("memory", "The current memory usage.") {
public double getDoubleValue() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
});
setProcess(process);
rename("Root");
}
/** Sets the process. */
public void setProcess(Process process) {
this.process = process;
registerOperator(this.process);
}
/**
* Returns the process of this operator if available. Overwrites the method
* from the superclass.
*/
public Process getProcess() {
return process;
}
/**
* Returns true since this operator chain should just return the output of
* the last inner operator.
*/
public boolean shouldReturnInnerOutput() {
return true;
}
/** Returns a simple chain condition. */
public InnerOperatorCondition getInnerOperatorCondition() {
return new SimpleChainInnerOperatorCondition();
}
/**
* Since the apply methods of the inner operators already add additional
* output, the handle additional output method should simply return a new
* container which is build from the additional output objects. Therefore
* this method returns true.
*/
public boolean getAddOnlyAdditionalOutput() {
return true;
}
/**
* Returns the highest possible value for the maximum number of innner
* operators.
*/
public int getMaxNumberOfInnerOperators() {
return Integer.MAX_VALUE;
}
/** Returns 0 for the minimum number of innner operators. */
public int getMinNumberOfInnerOperators() {
return 0;
}
/** Adds an process listener to the list of listeners. */
public void addProcessListener(ProcessListener l) {
listenerList.add(l);
}
/** Removes an process listener from the list of listeners. */
public void removeProcessListener(ProcessListener l) {
listenerList.remove(l);
}
/** Clears all process listeners. */
public void clearListeners() {
listenerList.clear();
}
/**
* Called at the beginning of the process. Notifies all listeners and the
* children operators (super method).
*/
public void processStarts() throws OperatorException {
super.processStarts();
Iterator i = listenerList.iterator();
while (i.hasNext()) {
((ProcessListener) i.next()).processStarts();
}
}
/** Counts the step and notifies all process listeners. */
protected void processStartedOperator(Operator op) {
for(ProcessListener listener: listenerList)
listener.processStartedOperator(op);
}
/** Counts the step and notifies all process listeners. */
protected void processFinishedOperator(Operator op) {
for(ProcessListener listener: listenerList)
listener.processFinishedOperator(op);
}
/**
* Called at the end of the process. Notifies all listeners and the
* children operators (super method).
*/
public void processFinished() throws OperatorException {
super.processFinished();
Iterator i = listenerList.iterator();
while (i.hasNext()) {
((ProcessListener) i.next()).processEnded();
}
}
/** This method can be used to send an email after the process has finished. Currently
* only a working sendmail server is supported. */
public void sendEmail(IOContainer results, Throwable e) throws UndefinedParameterError {
String email = getParameterAsString(PARAMETER_NOTIFICATION_EMAIL);
if (email == null)
return;
log("Sending notification email to '" + email + "'");
String name = email;
int at = name.indexOf("@");
if (at >= 0)
name = name.substring(0, at);
String subject = "Process " + getName() + " finished";
StringBuffer content = new StringBuffer("Hello " + name + "," + Tools.getLineSeparator() + Tools.getLineSeparator());
content.append("I'm sending you a notification message on your process '" + getName() + "'." + Tools.getLineSeparator());
File logFile = getLog().getLogFile();
if (logFile != null) {
content.append("Logfile is file://" + logFile.getAbsolutePath() + Tools.getLineSeparator() + Tools.getLineSeparator());
}
if (e != null) {
content.append("Process failed: " + e.toString());
subject = "Process " + getName() + " failed";
}
if (results != null) {
content.append(Tools.getLineSeparator() + Tools.getLineSeparator() + "Results:");
ResultObject result;
int i = 0;
while (true) {
try {
result = results.get(ResultObject.class, i);
content.append(Tools.getLineSeparator() + Tools.getLineSeparator() + Tools.getLineSeparator() + result.toResultString());
i++;
} catch (MissingIOObjectException exc) {
break;
}
}
}
Tools.sendEmail(email, subject, content.toString());
}
public Class<?>[] getInputClasses() {
return new Class[0];
}
public Class<?>[] getOutputClasses() {
return new Class[0];
}
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
ParameterType type = new ParameterTypeCategory(PARAMETER_LOGVERBOSITY, "Log verbosity level.", LogService.LOG_VERBOSITY_NAMES, LogService.INIT);
type.setExpert(false);
types.add(type);
type = new ParameterTypeFile(PARAMETER_LOGFILE, "File to write logging information to.", "log", true);
type.setExpert(false);
types.add(type);
types.add(new ParameterTypeFile(PARAMETER_RESULTFILE, "File to write inputs of the ResultWriter operators to.", "res", true));
int seed = 2001;
String seedProperty = System.getProperty(PROPERTY_RAPIDMINER_GENERAL_RANDOMSEED);
try {
if (seedProperty != null)
seed = Integer.parseInt(seedProperty);
} catch (NumberFormatException e) {
logWarning("Bad integer in property 'rapidminer.general.randomseed', using default seed (2001).");
}
types.add(new ParameterTypeInt(PARAMETER_RANDOM_SEED, "Global random seed for random generators (-1 for initialization by system time).", Integer.MIN_VALUE, Integer.MAX_VALUE, seed));
types.add(new ParameterTypeString(PARAMETER_NOTIFICATION_EMAIL, "Email address for the notification mail.", true));
String encoding = RapidMiner.SYSTEM_ENCODING_NAME;
String encodingProperty = System.getProperty(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_DEFAULT_ENCODING);
if (encodingProperty != null)
encoding = encodingProperty;
types.add(new ParameterTypeString(PARAMETER_ENCODING, "The encoding of the process XML description.", encoding));
return types;
}
}