/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 1999-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.headless;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.io.PrintWriter;
import java.io.CharArrayWriter;
import java.text.NumberFormat;
import java.text.FieldPosition;
import javax.mail.Session;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Transport;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.AddressException;
import org.geotoolkit.resources.Vocabulary;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.process.ProgressController;
import static org.apache.sis.util.CharSequences.length;
/**
* Reports progress by sending email to the specified address at regular interval.
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
* @author Guilhem Legal (Geomatys)
* @version 3.20
*
* @since 2.0
* @module
*/
public class ProgressMailer extends ProgressController {
/**
* The session for sending emails.
*/
private final Session session;
/**
* Where to send the emails.
*/
private final Address[] address;
/**
* How long to wait before 2 emails.
*/
private long timeInterval = 3*60*60*1000L;
/**
* When to send the next email.
*/
private long nextTime;
/**
* Constructs an objects reporting progress to the specified email address.
*
* @param host The server to use for sending emails.
* @param address Email address where to send progress reports.
* @throws AddressException if the specified address use an invalid syntax.
*/
public ProgressMailer(final String host, final String address) throws AddressException {
this(Session.getDefaultInstance(properties(host)), new InternetAddress[] {
new InternetAddress(address)});
}
/**
* Constructs an objects reporting progress to the specified email addresses.
*
* @param session Session to use for sending emails.
* @param address Email address where to send progress reports.
*/
public ProgressMailer(final Session session, final Address... address) {
this.session = session;
this.address = address;
nextTime = System.currentTimeMillis();
}
/**
* Returns the set of properties required for opening a new session. This is a workaround for
* RFE #4093999 ("Relax constraint on placement of this()/super() call in constructors").
*
* @param host The name of the server where to send the emails.
*/
private static Properties properties(final String host) {
final Properties props = new Properties();
props.setProperty("mail.smtp.host", host);
return props;
}
/**
* Returns the time laps (in milliseconds) between two emails.
*
* @return The current time laps in milliseconds.
*/
public synchronized long getTimeInterval() {
return timeInterval;
}
/**
* Sets the time laps (in milliseconds) between two emails.
* The default value is 3 hours.
*
* @param interval The new time laps in milliseconds.
*/
public synchronized void setTimeInterval(final long interval) {
this.timeInterval = interval;
}
/**
* Sends the given message by email.
*
* @param method Name of the caller method. Used only in case of failure.
* @param subjectKey Subject resource key: {@link ResourceKeys#PROGRESS},
* {@link ResourceKeys#WARNING} or {@link ResourceKeys#EXCEPTION}.
* @param messageText The message to send by email.
*/
private void send(final String method, final short subjectKey, final String messageText) {
final Locale locale = getLocale();
try {
final Message message = new MimeMessage(session);
message.setFrom();
message.setRecipients(Message.RecipientType.TO, address);
message.setSubject(Vocabulary.getResources(locale).getString(subjectKey));
message.setSentDate(new Date());
message.setText(messageText);
Transport.send(message);
} catch (MessagingException exception) {
Logging.unexpectedException(null, ProgressMailer.class, method, exception);
}
}
/**
* Send a progress report by email.
*
* @param method Name of the caller method. Used only in case of failure.
* @param percent percentage.
*/
private void send(final String method, final float percent) {
super.setProgress(percent);
final Locale locale = getLocale();
final Runtime system = Runtime.getRuntime();
final float MEMORY_UNIT = (1024f*1024f);
final float freeMemory = system.freeMemory() / MEMORY_UNIT;
final float totalMemory = system.totalMemory() / MEMORY_UNIT;
final Vocabulary resources = Vocabulary.getResources(locale);
final NumberFormat format = NumberFormat.getPercentInstance(locale);
CharSequence task = getTask();
if (task == null) {
task = resources.getString(Vocabulary.Keys.Progression);
}
final StringBuffer buffer = new StringBuffer(task).append(": ");
format.format(percent/100, buffer, new FieldPosition(0)).append('\n');
buffer.append(resources.getString(Vocabulary.Keys.MemoryHeapSize_1, totalMemory)).append('\n')
.append(resources.getString(Vocabulary.Keys.MemoryHeapUsage_1, 1f - freeMemory/totalMemory)).append('\n');
send(method, Vocabulary.Keys.Progression, buffer.toString());
}
/**
* Sends an email saying that the operation started.
*/
@Override
public synchronized void started() {
send(Vocabulary.format(Vocabulary.Keys.Started), 0);
}
/**
* Notifies progress. This method will send an email only if at least the amount
* of time specified by {@link #setTimeInterval} is elapsed since the last email.
*/
@Override
public synchronized void setProgress(float percent) {
final long time = System.currentTimeMillis();
if (time > nextTime) {
nextTime = time + timeInterval;
if (percent < 1f) percent = 1f;
if (percent > 99f) percent = 99f;
send(Vocabulary.format(Vocabulary.Keys.Progression), percent);
}
}
/**
* Sends an email saying that the operation paused.
*
* @since 3.20
*/
@Override
public void paused() {
send(Vocabulary.format(Vocabulary.Keys.Paused), getProgress());
}
/**
* Sends an email saying that the operation resumed.
*
* @since 3.20
*/
@Override
public void resumed() {
send(Vocabulary.format(Vocabulary.Keys.Resumed), getProgress());
}
/**
* Sends an email saying that the operation finished.
*/
@Override
public synchronized void completed() {
send(Vocabulary.format(Vocabulary.Keys.Completed), 100);
}
/**
* Sends a warning by email.
*
* @param source
* Name of the warning source, or {@code null} if none. This is typically the
* filename in process of being parsed or the URL of the data being processed
* @param location
* Text to write on the left side of the warning message, or {@code null} if none.
* This is typically the line number where the error occurred in the {@code source}
* file or the feature ID of the feature that produced the message
* @param warning
* The warning message.
*/
@Override
public synchronized void warningOccurred(final String source, final String location, final String warning) {
final StringBuilder buffer = new StringBuilder(length(source) + length(location) + length(warning) + 5);
if (source != null) {
buffer.append(source);
if (location != null) {
buffer.append(" (").append(location).append(')');
}
buffer.append(": ");
} else if (location != null) {
buffer.append(location).append(": ");
}
buffer.append(warning);
send("warningOccurred", Vocabulary.Keys.Warning, buffer.toString());
}
/**
* Send an exception stack trace by email.
*/
@Override
public synchronized void exceptionOccurred(final Throwable exception) {
final CharArrayWriter buffer = new CharArrayWriter();
exception.printStackTrace(new PrintWriter(buffer));
send("exceptionOccurred", Vocabulary.Keys.Exception, buffer.toString());
}
}